about summary refs log tree commit diff
diff options
context:
space:
mode:
authorRalf Jung <post@ralfj.de>2022-12-02 18:16:08 +0100
committerRalf Jung <post@ralfj.de>2022-12-02 18:16:08 +0100
commitb64d8670e4c512c29e6a58c73c0aba65f00bb800 (patch)
tree8ab5152a8172f85f987a6e84fa1e088e7d67339a
parent80ab672b8611d34bf811d4498b9b9d116a085e7c (diff)
parentcef44f53034eac46be3a0e3eec7b2b3d4ef5140b (diff)
downloadrust-b64d8670e4c512c29e6a58c73c0aba65f00bb800.tar.gz
rust-b64d8670e4c512c29e6a58c73c0aba65f00bb800.zip
Merge from rustc
-rw-r--r--.mailmap13
-rw-r--r--Cargo.lock101
-rw-r--r--compiler/rustc_abi/src/lib.rs96
-rw-r--r--compiler/rustc_ast/src/ast.rs109
-rw-r--r--compiler/rustc_ast/src/attr/mod.rs205
-rw-r--r--compiler/rustc_ast/src/mut_visit.rs14
-rw-r--r--compiler/rustc_ast/src/token.rs20
-rw-r--r--compiler/rustc_ast/src/tokenstream.rs20
-rw-r--r--compiler/rustc_ast/src/util/literal.rs40
-rw-r--r--compiler/rustc_ast/src/visit.rs2
-rw-r--r--compiler/rustc_ast_lowering/src/expr.rs17
-rw-r--r--compiler/rustc_ast_lowering/src/index.rs2
-rw-r--r--compiler/rustc_ast_lowering/src/item.rs95
-rw-r--r--compiler/rustc_ast_lowering/src/lib.rs17
-rw-r--r--compiler/rustc_ast_lowering/src/path.rs21
-rw-r--r--compiler/rustc_ast_passes/src/ast_validation.rs97
-rw-r--r--compiler/rustc_ast_passes/src/feature_gate.rs20
-rw-r--r--compiler/rustc_ast_pretty/src/helpers.rs4
-rw-r--r--compiler/rustc_ast_pretty/src/pprust/state.rs204
-rw-r--r--compiler/rustc_ast_pretty/src/pprust/state/expr.rs156
-rw-r--r--compiler/rustc_ast_pretty/src/pprust/state/item.rs113
-rw-r--r--compiler/rustc_attr/src/builtin.rs10
-rw-r--r--compiler/rustc_attr/src/session_diagnostics.rs4
-rw-r--r--compiler/rustc_borrowck/src/diagnostics/bound_region_errors.rs8
-rw-r--r--compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs16
-rw-r--r--compiler/rustc_borrowck/src/diagnostics/mod.rs4
-rw-r--r--compiler/rustc_borrowck/src/diagnostics/region_errors.rs7
-rw-r--r--compiler/rustc_borrowck/src/lib.rs2
-rw-r--r--compiler/rustc_borrowck/src/region_infer/mod.rs12
-rw-r--r--compiler/rustc_borrowck/src/type_check/liveness/polonius.rs4
-rw-r--r--compiler/rustc_builtin_macros/src/alloc_error_handler.rs4
-rw-r--r--compiler/rustc_builtin_macros/src/assert/context.rs8
-rw-r--r--compiler/rustc_builtin_macros/src/cfg_accessible.rs6
-rw-r--r--compiler/rustc_builtin_macros/src/concat_bytes.rs6
-rw-r--r--compiler/rustc_builtin_macros/src/derive.rs52
-rw-r--r--compiler/rustc_builtin_macros/src/deriving/clone.rs3
-rw-r--r--compiler/rustc_builtin_macros/src/deriving/cmp/eq.rs12
-rw-r--r--compiler/rustc_builtin_macros/src/deriving/cmp/ord.rs3
-rw-r--r--compiler/rustc_builtin_macros/src/deriving/cmp/partial_eq.rs3
-rw-r--r--compiler/rustc_builtin_macros/src/deriving/cmp/partial_ord.rs3
-rw-r--r--compiler/rustc_builtin_macros/src/deriving/default.rs5
-rw-r--r--compiler/rustc_builtin_macros/src/deriving/generic/mod.rs14
-rw-r--r--compiler/rustc_builtin_macros/src/deriving/mod.rs2
-rw-r--r--compiler/rustc_builtin_macros/src/edition_panic.rs26
-rw-r--r--compiler/rustc_builtin_macros/src/global_allocator.rs4
-rw-r--r--compiler/rustc_builtin_macros/src/lib.rs2
-rw-r--r--compiler/rustc_builtin_macros/src/proc_macro_harness.rs14
-rw-r--r--compiler/rustc_builtin_macros/src/source_util.rs2
-rw-r--r--compiler/rustc_builtin_macros/src/standard_library_imports.rs4
-rw-r--r--compiler/rustc_builtin_macros/src/test.rs32
-rw-r--r--compiler/rustc_builtin_macros/src/test_harness.rs18
-rw-r--r--compiler/rustc_builtin_macros/src/type_ascribe.rs35
-rw-r--r--compiler/rustc_builtin_macros/src/util.rs11
-rw-r--r--compiler/rustc_codegen_cranelift/src/value_and_place.rs4
-rw-r--r--compiler/rustc_codegen_gcc/example/alloc_system.rs8
-rw-r--r--compiler/rustc_codegen_gcc/src/context.rs4
-rw-r--r--compiler/rustc_codegen_llvm/src/consts.rs16
-rw-r--r--compiler/rustc_codegen_llvm/src/coverageinfo/mod.rs2
-rw-r--r--compiler/rustc_codegen_llvm/src/llvm/ffi.rs2
-rw-r--r--compiler/rustc_codegen_llvm/src/llvm_util.rs43
-rw-r--r--compiler/rustc_codegen_llvm/src/type_.rs2
-rw-r--r--compiler/rustc_codegen_ssa/src/back/link.rs18
-rw-r--r--compiler/rustc_codegen_ssa/src/back/linker.rs8
-rw-r--r--compiler/rustc_codegen_ssa/src/back/metadata.rs64
-rw-r--r--compiler/rustc_codegen_ssa/src/back/write.rs18
-rw-r--r--compiler/rustc_codegen_ssa/src/debuginfo/type_names.rs10
-rw-r--r--compiler/rustc_codegen_ssa/src/lib.rs4
-rw-r--r--compiler/rustc_codegen_ssa/src/mir/operand.rs4
-rw-r--r--compiler/rustc_const_eval/src/const_eval/eval_queries.rs2
-rw-r--r--compiler/rustc_const_eval/src/interpret/machine.rs4
-rw-r--r--compiler/rustc_const_eval/src/interpret/projection.rs4
-rw-r--r--compiler/rustc_const_eval/src/interpret/step.rs5
-rw-r--r--compiler/rustc_const_eval/src/interpret/visitor.rs2
-rw-r--r--compiler/rustc_const_eval/src/transform/check_consts/check.rs3
-rw-r--r--compiler/rustc_const_eval/src/transform/check_consts/mod.rs16
-rw-r--r--compiler/rustc_const_eval/src/transform/check_consts/ops.rs4
-rw-r--r--compiler/rustc_const_eval/src/transform/promote_consts.rs8
-rw-r--r--compiler/rustc_const_eval/src/util/aggregate.rs3
-rw-r--r--compiler/rustc_const_eval/src/util/compare_types.rs4
-rw-r--r--compiler/rustc_data_structures/src/sorted_map.rs4
-rw-r--r--compiler/rustc_data_structures/src/stable_hasher.rs2
-rw-r--r--compiler/rustc_driver/src/lib.rs4
-rw-r--r--compiler/rustc_errors/src/emitter.rs10
-rw-r--r--compiler/rustc_expand/src/base.rs8
-rw-r--r--compiler/rustc_expand/src/build.rs24
-rw-r--r--compiler/rustc_expand/src/config.rs2
-rw-r--r--compiler/rustc_expand/src/expand.rs13
-rw-r--r--compiler/rustc_expand/src/proc_macro_server.rs7
-rw-r--r--compiler/rustc_expand/src/tests.rs108
-rw-r--r--compiler/rustc_feature/src/accepted.rs2
-rw-r--r--compiler/rustc_feature/src/active.rs2
-rw-r--r--compiler/rustc_hir/src/arena.rs1
-rw-r--r--compiler/rustc_hir/src/definitions.rs8
-rw-r--r--compiler/rustc_hir/src/hir.rs41
-rw-r--r--compiler/rustc_hir/src/intravisit.rs13
-rw-r--r--compiler/rustc_hir_analysis/src/astconv/mod.rs7
-rw-r--r--compiler/rustc_hir_analysis/src/check/compare_method.rs20
-rw-r--r--compiler/rustc_hir_analysis/src/check/dropck.rs7
-rw-r--r--compiler/rustc_hir_analysis/src/check/wfcheck.rs4
-rw-r--r--compiler/rustc_hir_analysis/src/collect.rs20
-rw-r--r--compiler/rustc_hir_analysis/src/collect/lifetimes.rs2
-rw-r--r--compiler/rustc_hir_analysis/src/lib.rs2
-rw-r--r--compiler/rustc_hir_analysis/src/outlives/mod.rs26
-rw-r--r--compiler/rustc_hir_analysis/src/variance/terms.rs14
-rw-r--r--compiler/rustc_hir_pretty/src/lib.rs2
-rw-r--r--compiler/rustc_hir_typeck/src/callee.rs18
-rw-r--r--compiler/rustc_hir_typeck/src/cast.rs17
-rw-r--r--compiler/rustc_hir_typeck/src/check.rs21
-rw-r--r--compiler/rustc_hir_typeck/src/closure.rs24
-rw-r--r--compiler/rustc_hir_typeck/src/coercion.rs46
-rw-r--r--compiler/rustc_hir_typeck/src/demand.rs4
-rw-r--r--compiler/rustc_hir_typeck/src/expectation.rs6
-rw-r--r--compiler/rustc_hir_typeck/src/expr.rs10
-rw-r--r--compiler/rustc_hir_typeck/src/expr_use_visitor.rs17
-rw-r--r--compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs59
-rw-r--r--compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs4
-rw-r--r--compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs2
-rw-r--r--compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs7
-rw-r--r--compiler/rustc_hir_typeck/src/generator_interior/mod.rs5
-rw-r--r--compiler/rustc_hir_typeck/src/inherited.rs55
-rw-r--r--compiler/rustc_hir_typeck/src/lib.rs20
-rw-r--r--compiler/rustc_hir_typeck/src/method/confirm.rs4
-rw-r--r--compiler/rustc_hir_typeck/src/method/mod.rs39
-rw-r--r--compiler/rustc_hir_typeck/src/method/probe.rs28
-rw-r--r--compiler/rustc_hir_typeck/src/upvar.rs2
-rw-r--r--compiler/rustc_incremental/src/persist/dirty_clean.rs6
-rw-r--r--compiler/rustc_infer/src/infer/canonical/mod.rs11
-rw-r--r--compiler/rustc_infer/src/infer/combine.rs14
-rw-r--r--compiler/rustc_infer/src/infer/error_reporting/mod.rs62
-rw-r--r--compiler/rustc_infer/src/infer/error_reporting/nice_region_error/placeholder_error.rs5
-rw-r--r--compiler/rustc_infer/src/infer/free_regions.rs8
-rw-r--r--compiler/rustc_infer/src/infer/freshen.rs2
-rw-r--r--compiler/rustc_infer/src/infer/higher_ranked/mod.rs9
-rw-r--r--compiler/rustc_infer/src/infer/mod.rs12
-rw-r--r--compiler/rustc_infer/src/infer/nll_relate/mod.rs2
-rw-r--r--compiler/rustc_infer/src/infer/opaque_types.rs26
-rw-r--r--compiler/rustc_infer/src/infer/opaque_types/table.rs8
-rw-r--r--compiler/rustc_infer/src/infer/outlives/test_type_match.rs7
-rw-r--r--compiler/rustc_interface/src/interface.rs2
-rw-r--r--compiler/rustc_lint/src/builtin.rs20
-rw-r--r--compiler/rustc_lint/src/context.rs2
-rw-r--r--compiler/rustc_lint/src/internal.rs6
-rw-r--r--compiler/rustc_lint/src/late.rs2
-rw-r--r--compiler/rustc_lint/src/passes.rs2
-rw-r--r--compiler/rustc_lint/src/unused.rs7
-rw-r--r--compiler/rustc_macros/src/query.rs30
-rw-r--r--compiler/rustc_metadata/src/native_libs.rs13
-rw-r--r--compiler/rustc_metadata/src/rmeta/decoder.rs8
-rw-r--r--compiler/rustc_metadata/src/rmeta/mod.rs2
-rw-r--r--compiler/rustc_middle/Cargo.toml1
-rw-r--r--compiler/rustc_middle/src/arena.rs2
-rw-r--r--compiler/rustc_middle/src/hir/mod.rs9
-rw-r--r--compiler/rustc_middle/src/lib.rs2
-rw-r--r--compiler/rustc_middle/src/middle/lang_items.rs10
-rw-r--r--compiler/rustc_middle/src/mir/interpret/allocation.rs4
-rw-r--r--compiler/rustc_middle/src/mir/interpret/allocation/init_mask.rs2
-rw-r--r--compiler/rustc_middle/src/mir/interpret/allocation/provenance_map.rs2
-rw-r--r--compiler/rustc_middle/src/mir/interpret/pointer.rs2
-rw-r--r--compiler/rustc_middle/src/mir/interpret/value.rs8
-rw-r--r--compiler/rustc_middle/src/mir/mod.rs3
-rw-r--r--compiler/rustc_middle/src/query/mod.rs2
-rw-r--r--compiler/rustc_middle/src/traits/mod.rs2
-rw-r--r--compiler/rustc_middle/src/ty/closure.rs51
-rw-r--r--compiler/rustc_middle/src/ty/codec.rs8
-rw-r--r--compiler/rustc_middle/src/ty/consts.rs27
-rw-r--r--compiler/rustc_middle/src/ty/consts/kind.rs8
-rw-r--r--compiler/rustc_middle/src/ty/context.rs211
-rw-r--r--compiler/rustc_middle/src/ty/diagnostics.rs6
-rw-r--r--compiler/rustc_middle/src/ty/error.rs5
-rw-r--r--compiler/rustc_middle/src/ty/flags.rs2
-rw-r--r--compiler/rustc_middle/src/ty/fold.rs47
-rw-r--r--compiler/rustc_middle/src/ty/mod.rs22
-rw-r--r--compiler/rustc_middle/src/ty/parameterized.rs3
-rw-r--r--compiler/rustc_middle/src/ty/print/pretty.rs30
-rw-r--r--compiler/rustc_middle/src/ty/query.rs42
-rw-r--r--compiler/rustc_middle/src/ty/relate.rs5
-rw-r--r--compiler/rustc_middle/src/ty/sty.rs5
-rw-r--r--compiler/rustc_middle/src/ty/trait_def.rs4
-rw-r--r--compiler/rustc_middle/src/ty/util.rs10
-rw-r--r--compiler/rustc_middle/src/ty/visit.rs8
-rw-r--r--compiler/rustc_mir_build/src/build/custom/mod.rs4
-rw-r--r--compiler/rustc_mir_build/src/build/custom/parse.rs25
-rw-r--r--compiler/rustc_mir_build/src/build/custom/parse/instruction.rs47
-rw-r--r--compiler/rustc_mir_build/src/build/expr/as_constant.rs137
-rw-r--r--compiler/rustc_mir_build/src/build/expr/category.rs28
-rw-r--r--compiler/rustc_mir_build/src/build/misc.rs4
-rw-r--r--compiler/rustc_mir_build/src/build/mod.rs2
-rw-r--r--compiler/rustc_mir_build/src/build/scope.rs8
-rw-r--r--compiler/rustc_mir_build/src/thir/constant.rs2
-rw-r--r--compiler/rustc_mir_transform/src/add_moves_for_packed_drops.rs61
-rw-r--r--compiler/rustc_mir_transform/src/elaborate_box_derefs.rs2
-rw-r--r--compiler/rustc_mir_transform/src/shim.rs2
-rw-r--r--compiler/rustc_monomorphize/src/collector.rs6
-rw-r--r--compiler/rustc_parse/src/lexer/unicode_chars.rs4
-rw-r--r--compiler/rustc_parse/src/parser/attr.rs11
-rw-r--r--compiler/rustc_parse/src/parser/attr_wrapper.rs2
-rw-r--r--compiler/rustc_parse/src/parser/diagnostics.rs6
-rw-r--r--compiler/rustc_parse/src/parser/expr.rs27
-rw-r--r--compiler/rustc_parse/src/parser/item.rs2
-rw-r--r--compiler/rustc_parse/src/parser/stmt.rs2
-rw-r--r--compiler/rustc_parse/src/validate_attr.rs45
-rw-r--r--compiler/rustc_passes/src/check_attr.rs8
-rw-r--r--compiler/rustc_passes/src/dead.rs2
-rw-r--r--compiler/rustc_passes/src/hir_id_validator.rs75
-rw-r--r--compiler/rustc_passes/src/hir_stats.rs4
-rw-r--r--compiler/rustc_passes/src/reachable.rs11
-rw-r--r--compiler/rustc_passes/src/stability.rs4
-rw-r--r--compiler/rustc_passes/src/upvars.rs2
-rw-r--r--compiler/rustc_query_impl/src/on_disk_cache.rs6
-rw-r--r--compiler/rustc_query_impl/src/plumbing.rs15
-rw-r--r--compiler/rustc_query_system/src/dep_graph/graph.rs163
-rw-r--r--compiler/rustc_query_system/src/query/caches.rs4
-rw-r--r--compiler/rustc_query_system/src/query/config.rs5
-rw-r--r--compiler/rustc_query_system/src/query/plumbing.rs50
-rw-r--r--compiler/rustc_resolve/src/build_reduced_graph.rs15
-rw-r--r--compiler/rustc_resolve/src/def_collector.rs8
-rw-r--r--compiler/rustc_resolve/src/effective_visibilities.rs29
-rw-r--r--compiler/rustc_resolve/src/imports.rs13
-rw-r--r--compiler/rustc_resolve/src/late/diagnostics.rs8
-rw-r--r--compiler/rustc_resolve/src/lib.rs2
-rw-r--r--compiler/rustc_save_analysis/src/dump_visitor.rs4
-rw-r--r--compiler/rustc_save_analysis/src/lib.rs8
-rw-r--r--compiler/rustc_serialize/src/leb128.rs21
-rw-r--r--compiler/rustc_serialize/src/opaque.rs36
-rw-r--r--compiler/rustc_session/src/config.rs5
-rw-r--r--compiler/rustc_session/src/errors.rs10
-rw-r--r--compiler/rustc_span/src/analyze_source_file.rs2
-rw-r--r--compiler/rustc_span/src/lib.rs14
-rw-r--r--compiler/rustc_span/src/source_map.rs22
-rw-r--r--compiler/rustc_span/src/symbol.rs5
-rw-r--r--compiler/rustc_target/src/spec/mod.rs110
-rw-r--r--compiler/rustc_trait_selection/src/autoderef.rs11
-rw-r--r--compiler/rustc_trait_selection/src/infer.rs28
-rw-r--r--compiler/rustc_trait_selection/src/traits/auto_trait.rs35
-rw-r--r--compiler/rustc_trait_selection/src/traits/coherence.rs14
-rw-r--r--compiler/rustc_trait_selection/src/traits/engine.rs8
-rw-r--r--compiler/rustc_trait_selection/src/traits/error_reporting/ambiguity.rs4
-rw-r--r--compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs30
-rw-r--r--compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs36
-rw-r--r--compiler/rustc_trait_selection/src/traits/fulfill.rs31
-rw-r--r--compiler/rustc_trait_selection/src/traits/mod.rs7
-rw-r--r--compiler/rustc_trait_selection/src/traits/project.rs82
-rw-r--r--compiler/rustc_trait_selection/src/traits/query/normalize.rs8
-rw-r--r--compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs231
-rw-r--r--compiler/rustc_trait_selection/src/traits/select/confirmation.rs6
-rw-r--r--compiler/rustc_trait_selection/src/traits/select/mod.rs230
-rw-r--r--compiler/rustc_trait_selection/src/traits/specialize/mod.rs2
-rw-r--r--compiler/rustc_trait_selection/src/traits/util.rs12
-rw-r--r--compiler/rustc_traits/src/dropck_outlives.rs4
-rw-r--r--compiler/rustc_traits/src/normalize_erasing_regions.rs4
-rw-r--r--compiler/rustc_traits/src/type_op.rs10
-rw-r--r--compiler/rustc_ty_utils/src/consts.rs32
-rw-r--r--compiler/rustc_ty_utils/src/instance.rs2
-rw-r--r--compiler/rustc_ty_utils/src/layout_sanity_check.rs534
-rw-r--r--library/alloc/src/collections/vec_deque/drain.rs193
-rw-r--r--library/alloc/src/collections/vec_deque/iter.rs178
-rw-r--r--library/alloc/src/collections/vec_deque/iter_mut.rs149
-rw-r--r--library/alloc/src/collections/vec_deque/mod.rs1259
-rw-r--r--library/alloc/src/collections/vec_deque/pair_slices.rs67
-rw-r--r--library/alloc/src/collections/vec_deque/ring_slices.rs56
-rw-r--r--library/alloc/src/collections/vec_deque/spec_extend.rs81
-rw-r--r--library/alloc/src/collections/vec_deque/tests.rs220
-rw-r--r--library/alloc/tests/vec_deque.rs13
-rw-r--r--library/core/src/intrinsics/mir.rs174
-rw-r--r--library/core/src/macros/mod.rs12
-rw-r--r--library/core/src/mem/mod.rs61
-rw-r--r--library/core/src/prelude/v1.rs8
-rw-r--r--library/core/src/str/converts.rs2
-rw-r--r--library/std/src/fs.rs2
-rw-r--r--library/std/src/path.rs46
-rw-r--r--library/std/src/prelude/v1.rs9
-rw-r--r--library/std/src/sys/common/alloc.rs12
-rw-r--r--library/std/src/sys/hermit/fs.rs4
-rw-r--r--library/std/src/sys/hermit/thread.rs3
-rw-r--r--library/std/src/sys/wasi/net.rs10
-rw-r--r--library/std/src/sys/windows/args.rs54
-rw-r--r--library/std/src/sys_common/mod.rs1
-rw-r--r--library/std/src/sys_common/wstr.rs59
-rw-r--r--src/bootstrap/bootstrap.py1
-rw-r--r--src/bootstrap/builder.rs1
-rw-r--r--src/bootstrap/download.rs1
-rw-r--r--src/bootstrap/lib.rs5
-rw-r--r--src/bootstrap/test.rs36
-rw-r--r--src/ci/docker/host-x86_64/test-various/Dockerfile13
-rw-r--r--src/ci/docker/host-x86_64/test-various/uefi_qemu_test/run.py78
-rwxr-xr-x[-rw-r--r--]src/ci/docker/scripts/fuchsia-test-runner.py0
-rw-r--r--src/doc/rustc/src/command-line-arguments.md27
-rw-r--r--src/doc/rustc/src/platform-support.md4
-rw-r--r--src/doc/rustc/src/platform-support/fuchsia.md83
-rw-r--r--src/doc/rustc/src/platform-support/nto-qnx.md18
-rw-r--r--src/doc/rustc/src/target-tier-policy.md2
-rw-r--r--src/doc/style-guide/src/principles.md4
-rw-r--r--src/doc/unstable-book/src/language-features/native-link-modifiers-verbatim.md20
-rw-r--r--src/etc/gdb_providers.py8
-rw-r--r--src/etc/lldb_providers.py12
-rw-r--r--src/etc/natvis/liballoc.natvis15
-rw-r--r--src/librustdoc/clean/cfg.rs2
-rw-r--r--src/librustdoc/clean/mod.rs26
-rw-r--r--src/librustdoc/clean/types.rs16
-rw-r--r--src/librustdoc/clean/utils.rs4
-rw-r--r--src/librustdoc/core.rs2
-rw-r--r--src/librustdoc/doctest.rs16
-rw-r--r--src/librustdoc/html/render/mod.rs4
-rw-r--r--src/librustdoc/html/render/span_map.rs10
-rw-r--r--src/librustdoc/html/static/css/rustdoc.css36
-rw-r--r--src/librustdoc/html/static/css/themes/ayu.css5
-rw-r--r--src/librustdoc/html/static/css/themes/dark.css5
-rw-r--r--src/librustdoc/html/static/css/themes/light.css1
-rw-r--r--src/librustdoc/html/static/js/main.js30
-rw-r--r--src/librustdoc/html/static/js/search.js10
-rw-r--r--src/librustdoc/html/static/js/settings.js2
-rw-r--r--src/librustdoc/json/mod.rs2
-rw-r--r--src/librustdoc/passes/collect_trait_impls.rs6
-rw-r--r--src/librustdoc/visit_ast.rs59
-rw-r--r--src/rustdoc-json-types/lib.rs4
-rw-r--r--src/test/codegen-units/item-collection/asm-sym.rs20
-rw-r--r--src/test/codegen/auxiliary/extern_decl.rs11
-rw-r--r--src/test/codegen/dllimports/main.rs13
-rw-r--r--src/test/codegen/panic-abort-windows.rs15
-rw-r--r--src/test/codegen/static-relocation-model-msvc.rs26
-rw-r--r--src/test/debuginfo/pretty-std.rs4
-rw-r--r--src/test/mir-opt/building/custom/arbitrary_let.arbitrary_let.built.after.mir22
-rw-r--r--src/test/mir-opt/building/custom/arbitrary_let.rs28
-rw-r--r--src/test/mir-opt/building/custom/consts.consts.built.after.mir22
-rw-r--r--src/test/mir-opt/building/custom/consts.rs36
-rw-r--r--src/test/mir-opt/building/custom/consts.statics.built.after.mir27
-rw-r--r--src/test/mir-opt/building/custom/references.immut_ref.built.after.mir10
-rw-r--r--src/test/mir-opt/building/custom/references.mut_ref.built.after.mir10
-rw-r--r--src/test/mir-opt/building/custom/simple_assign.simple.built.after.mir10
-rw-r--r--src/test/mir-opt/building/custom/simple_assign.simple_ref.built.after.mir4
-rw-r--r--src/test/run-make/coverage-reports/Makefile2
-rw-r--r--src/test/run-make/native-link-modifier-verbatim-linker/Makefile4
-rw-r--r--src/test/run-make/native-link-modifier-verbatim-rustc/Makefile4
-rw-r--r--src/test/run-make/raw-dylib-c/lib.rs2
-rw-r--r--src/test/run-make/rlib-format-packed-bundled-libs-2/rust_dep.rs1
-rw-r--r--src/test/rustdoc-gui/item-decl-colors.goml2
-rw-r--r--src/test/rustdoc-gui/notable-trait.goml29
-rw-r--r--src/test/rustdoc-gui/pocket-menu.goml21
-rw-r--r--src/test/rustdoc-gui/scrape-examples-fonts.goml8
-rw-r--r--src/test/rustdoc-gui/sidebar-mobile.goml52
-rw-r--r--src/test/rustdoc-gui/src/scrape_examples/examples/check-many-1.rs3
-rw-r--r--src/test/rustdoc-gui/src/scrape_examples/examples/check-many-2.rs3
-rw-r--r--src/test/rustdoc-gui/src/scrape_examples/examples/check-many-3.rs3
-rw-r--r--src/test/rustdoc-gui/src/scrape_examples/examples/check-many-4.rs3
-rw-r--r--src/test/rustdoc-gui/src/scrape_examples/examples/check-many-5.rs3
-rw-r--r--src/test/rustdoc-gui/src/scrape_examples/examples/check-many-6.rs3
-rw-r--r--src/test/rustdoc-gui/src/scrape_examples/examples/check-many-7.rs3
-rw-r--r--src/test/rustdoc-gui/src/scrape_examples/src/lib.rs2
-rw-r--r--src/test/rustdoc-gui/toggle-docs.goml29
-rw-r--r--src/test/rustdoc-gui/where-whitespace.goml4
-rw-r--r--src/test/rustdoc-json/enums/auxiliary/color.rs5
-rw-r--r--src/test/rustdoc-json/enums/doc_link_to_foreign_variant.rs11
-rw-r--r--src/test/rustdoc-json/enums/use_variant_foreign.rs9
-rw-r--r--src/test/rustdoc-json/fns/pattern_arg.rs7
-rw-r--r--src/test/rustdoc-json/traits/uses_extern_trait.rs7
-rw-r--r--src/test/rustdoc/anchors.no_method_anchor.html2
-rw-r--r--src/test/rustdoc/anchors.no_trait_method_anchor.html2
-rw-r--r--src/test/rustdoc/anchors.no_tymethod_anchor.html2
-rw-r--r--src/test/rustdoc/decl-trailing-whitespace.declaration.html10
-rw-r--r--src/test/rustdoc/deref-to-primitive.rs15
-rw-r--r--src/test/rustdoc/extern-default-method.no_href_on_anchor.html2
-rw-r--r--src/test/rustdoc/extern-default-method.rs6
-rw-r--r--src/test/rustdoc/foreigntype.rs2
-rw-r--r--src/test/rustdoc/inline_cross/issue-24183.method_no_where_self_sized.html2
-rw-r--r--src/test/rustdoc/trait-impl-items-links-and-anchors.rs14
-rw-r--r--src/test/rustdoc/where.SWhere_TraitWhere_item-decl.html4
-rw-r--r--src/test/rustdoc/whitespace-after-where-clause.trait.html4
-rw-r--r--src/test/rustdoc/whitespace-after-where-clause.trait2.html4
-rw-r--r--src/test/ui/alloc-error/alloc-error-handler-bad-signature-1.stderr8
-rw-r--r--src/test/ui/alloc-error/alloc-error-handler-bad-signature-2.stderr16
-rw-r--r--src/test/ui/associated-consts/issue-93835.rs8
-rw-r--r--src/test/ui/associated-consts/issue-93835.stderr65
-rw-r--r--src/test/ui/async-await/async-block-control-flow-static-semantics.rs4
-rw-r--r--src/test/ui/async-await/async-block-control-flow-static-semantics.stderr8
-rw-r--r--src/test/ui/async-await/generator-desc.stderr8
-rw-r--r--src/test/ui/async-await/issue-67252-unnamed-future.stderr2
-rw-r--r--src/test/ui/async-await/issue-86507.stderr2
-rw-r--r--src/test/ui/async-await/issues/issue-65436-raw-ptr-not-send.no_drop_tracking.stderr2
-rw-r--r--src/test/ui/chalkify/bugs/async.stderr22
-rw-r--r--src/test/ui/closures/issue-90871.rs4
-rw-r--r--src/test/ui/closures/issue-90871.stderr23
-rw-r--r--src/test/ui/coercion/coerce-expect-unsized-ascribed.rs32
-rw-r--r--src/test/ui/coercion/coerce-expect-unsized-ascribed.stderr90
-rw-r--r--src/test/ui/consts/const_in_pattern/accept_structural.rs2
-rw-r--r--src/test/ui/consts/const_in_pattern/reject_non_structural.rs2
-rw-r--r--src/test/ui/consts/miri_unleashed/tls.stderr4
-rw-r--r--src/test/ui/deriving/issue-105101.rs9
-rw-r--r--src/test/ui/deriving/issue-105101.stderr29
-rw-r--r--src/test/ui/enum/issue-67945-2.rs2
-rw-r--r--src/test/ui/enum/issue-67945-2.stderr6
-rw-r--r--src/test/ui/feature-gates/feature-gate-native_link_modifiers_verbatim.rs5
-rw-r--r--src/test/ui/feature-gates/feature-gate-native_link_modifiers_verbatim.stderr12
-rw-r--r--src/test/ui/generator/clone-impl-async.rs27
-rw-r--r--src/test/ui/generator/clone-impl-async.stderr48
-rw-r--r--src/test/ui/generator/print/generator-print-verbose-1.stderr8
-rw-r--r--src/test/ui/generic-associated-types/self-outlives-lint.stderr22
-rw-r--r--src/test/ui/hygiene/panic-location.run.stderr2
-rw-r--r--src/test/ui/impl-trait/issue-55872-3.rs2
-rw-r--r--src/test/ui/impl-trait/issue-55872-3.stderr4
-rw-r--r--src/test/ui/impl-trait/issues/issue-104815.rs66
-rw-r--r--src/test/ui/impl-trait/issues/issue-78722.rs2
-rw-r--r--src/test/ui/impl-trait/issues/issue-78722.stderr2
-rw-r--r--src/test/ui/inference/deref-suggestion.stderr12
-rw-r--r--src/test/ui/inference/issue-104649.rs32
-rw-r--r--src/test/ui/inference/issue-104649.stderr14
-rw-r--r--src/test/ui/issues/issue-13497-2.stderr6
-rw-r--r--src/test/ui/lang-items/missing-clone-for-suggestion.rs20
-rw-r--r--src/test/ui/lang-items/missing-clone-for-suggestion.stderr21
-rw-r--r--src/test/ui/linkage-attr/link-attr-validation-late.rs1
-rw-r--r--src/test/ui/linkage-attr/link-attr-validation-late.stderr48
-rw-r--r--src/test/ui/lint/suggestions.stderr6
-rw-r--r--src/test/ui/lint/unused/issue-88519-unused-paren.rs15
-rw-r--r--src/test/ui/macros/issue-104769-concat_bytes-invalid-literal.rs8
-rw-r--r--src/test/ui/macros/issue-104769-concat_bytes-invalid-literal.stderr16
-rw-r--r--src/test/ui/macros/issue-105011.rs3
-rw-r--r--src/test/ui/macros/issue-105011.stderr8
-rw-r--r--src/test/ui/mir/mir_ascription_coercion.rs2
-rw-r--r--src/test/ui/nll/user-annotations/issue-57731-ascibed-coupled-types.rs6
-rw-r--r--src/test/ui/nll/user-annotations/issue-57731-ascibed-coupled-types.stderr4
-rw-r--r--src/test/ui/nll/user-annotations/type_ascription_static_lifetime.rs2
-rw-r--r--src/test/ui/nll/user-annotations/type_ascription_static_lifetime.stderr10
-rw-r--r--src/test/ui/pattern/non-structural-match-types.stderr2
-rw-r--r--src/test/ui/privacy/effective_visibilities.rs1
-rw-r--r--src/test/ui/privacy/effective_visibilities.stderr8
-rw-r--r--src/test/ui/proc-macro/auxiliary/issue-104884.rs23
-rw-r--r--src/test/ui/proc-macro/issue-104884-trait-impl-sugg-err.rs20
-rw-r--r--src/test/ui/proc-macro/issue-104884-trait-impl-sugg-err.stderr48
-rw-r--r--src/test/ui/raw-ref-op/raw-ref-temp-deref.rs6
-rw-r--r--src/test/ui/raw-ref-op/raw-ref-temp.rs40
-rw-r--r--src/test/ui/raw-ref-op/raw-ref-temp.stderr16
-rw-r--r--src/test/ui/reachable/expr_type.rs2
-rw-r--r--src/test/ui/reachable/expr_type.stderr8
-rw-r--r--src/test/ui/return/issue-86188-return-not-in-fn-body.stderr12
-rw-r--r--src/test/ui/span/macro-ty-params.rs4
-rw-r--r--src/test/ui/span/macro-ty-params.stderr8
-rw-r--r--src/test/ui/stats/hir-stats.stderr44
-rw-r--r--src/test/ui/suggestions/expected-boxed-future-isnt-pinned.stderr2
-rw-r--r--src/test/ui/suggestions/issue-99240-2.stderr6
-rw-r--r--src/test/ui/suggestions/suggest-remove-refs-3.stderr4
-rw-r--r--src/test/ui/suggestions/suggest_print_over_printf.rs8
-rw-r--r--src/test/ui/suggestions/suggest_print_over_printf.stderr14
-rw-r--r--src/test/ui/traits/issue-104322.rs80
-rw-r--r--src/test/ui/traits/predicate_can_apply-hang.rs6
-rw-r--r--src/test/ui/traits/predicate_can_apply-hang.stderr21
-rw-r--r--src/test/ui/type-alias-impl-trait/imply_bounds_from_bounds.rs25
-rw-r--r--src/test/ui/type-alias-impl-trait/imply_bounds_from_bounds_param.rs38
-rw-r--r--src/test/ui/type-alias-impl-trait/imply_bounds_from_bounds_param.stderr16
-rw-r--r--src/test/ui/type-alias-impl-trait/self_implication.rs38
-rw-r--r--src/test/ui/type/type-ascription-soundness.rs8
-rw-r--r--src/test/ui/type/type-ascription-soundness.stderr24
-rw-r--r--src/test/ui/type/type-ascription.rs20
-rw-r--r--src/test/ui/typeck/hang-in-overflow.rs19
-rw-r--r--src/test/ui/typeck/hang-in-overflow.stderr22
-rw-r--r--src/test/ui/typeck/issue-91267.rs4
-rw-r--r--src/test/ui/typeck/issue-91267.stderr21
m---------src/tools/cargo0
-rw-r--r--src/tools/clippy/CHANGELOG.md2
-rw-r--r--src/tools/clippy/README.md6
-rw-r--r--src/tools/clippy/book/src/SUMMARY.md1
-rw-r--r--src/tools/clippy/book/src/configuration.md6
-rw-r--r--src/tools/clippy/book/src/development/adding_lints.md18
-rw-r--r--src/tools/clippy/book/src/development/proposals/syntax-tree-patterns.md986
-rw-r--r--src/tools/clippy/clippy_dev/src/new_lint.rs16
-rw-r--r--src/tools/clippy/clippy_lints/src/almost_complete_letter_range.rs11
-rw-r--r--src/tools/clippy/clippy_lints/src/approx_const.rs8
-rw-r--r--src/tools/clippy/clippy_lints/src/attrs.rs16
-rw-r--r--src/tools/clippy/clippy_lints/src/casts/cast_abs_to_unsigned.rs7
-rw-r--r--src/tools/clippy/clippy_lints/src/casts/cast_lossless.rs16
-rw-r--r--src/tools/clippy/clippy_lints/src/casts/cast_possible_truncation.rs7
-rw-r--r--src/tools/clippy/clippy_lints/src/casts/cast_slice_different_sizes.rs8
-rw-r--r--src/tools/clippy/clippy_lints/src/casts/cast_slice_from_raw_parts.rs14
-rw-r--r--src/tools/clippy/clippy_lints/src/casts/mod.rs22
-rw-r--r--src/tools/clippy/clippy_lints/src/casts/ptr_as_ptr.rs7
-rw-r--r--src/tools/clippy/clippy_lints/src/casts/unnecessary_cast.rs17
-rw-r--r--src/tools/clippy/clippy_lints/src/checked_conversions.rs10
-rw-r--r--src/tools/clippy/clippy_lints/src/collapsible_if.rs10
-rw-r--r--src/tools/clippy/clippy_lints/src/declared_lints.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/dereference.rs41
-rw-r--r--src/tools/clippy/clippy_lints/src/derive.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/disallowed_types.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/doc.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/eta_reduction.rs22
-rw-r--r--src/tools/clippy/clippy_lints/src/exit.rs23
-rw-r--r--src/tools/clippy/clippy_lints/src/format_args.rs82
-rw-r--r--src/tools/clippy/clippy_lints/src/from_over_into.rs12
-rw-r--r--src/tools/clippy/clippy_lints/src/functions/misnamed_getters.rs125
-rw-r--r--src/tools/clippy/clippy_lints/src/functions/mod.rs45
-rw-r--r--src/tools/clippy/clippy_lints/src/functions/result.rs8
-rw-r--r--src/tools/clippy/clippy_lints/src/future_not_send.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/if_then_some_else_none.rs14
-rw-r--r--src/tools/clippy/clippy_lints/src/index_refutable_slice.rs13
-rw-r--r--src/tools/clippy/clippy_lints/src/instant_subtraction.rs18
-rw-r--r--src/tools/clippy/clippy_lints/src/lib.rs109
-rw-r--r--src/tools/clippy/clippy_lints/src/lifetimes.rs13
-rw-r--r--src/tools/clippy/clippy_lints/src/macro_use.rs5
-rw-r--r--src/tools/clippy/clippy_lints/src/manual_bits.rs10
-rw-r--r--src/tools/clippy/clippy_lints/src/manual_clamp.rs33
-rw-r--r--src/tools/clippy/clippy_lints/src/manual_is_ascii_check.rs15
-rw-r--r--src/tools/clippy/clippy_lints/src/manual_let_else.rs36
-rw-r--r--src/tools/clippy/clippy_lints/src/manual_non_exhaustive.rs16
-rw-r--r--src/tools/clippy/clippy_lints/src/manual_rem_euclid.rs12
-rw-r--r--src/tools/clippy/clippy_lints/src/manual_retain.rs24
-rw-r--r--src/tools/clippy/clippy_lints/src/manual_strip.rs10
-rw-r--r--src/tools/clippy/clippy_lints/src/matches/mod.rs14
-rw-r--r--src/tools/clippy/clippy_lints/src/matches/try_err.rs6
-rw-r--r--src/tools/clippy/clippy_lints/src/mem_replace.rs10
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/cloned_instead_of_copied.rs10
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/err_expect.rs8
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/filter_map_next.rs8
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/is_digit_ascii_radix.rs9
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/map_clone.rs16
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/map_unwrap_or.rs7
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/mod.rs36
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/option_as_ref_deref.rs8
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/option_map_unwrap_or.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/str_splitn.rs8
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/unnecessary_to_owned.rs61
-rw-r--r--src/tools/clippy/clippy_lints/src/missing_const_for_fn.rs14
-rw-r--r--src/tools/clippy/clippy_lints/src/missing_enforced_import_rename.rs59
-rw-r--r--src/tools/clippy/clippy_lints/src/needless_pass_by_value.rs8
-rw-r--r--src/tools/clippy/clippy_lints/src/no_effect.rs8
-rw-r--r--src/tools/clippy/clippy_lints/src/ptr.rs9
-rw-r--r--src/tools/clippy/clippy_lints/src/ranges.rs10
-rw-r--r--src/tools/clippy/clippy_lints/src/redundant_closure_call.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/redundant_field_names.rs9
-rw-r--r--src/tools/clippy/clippy_lints/src/redundant_pub_crate.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/redundant_static_lifetimes.rs9
-rw-r--r--src/tools/clippy/clippy_lints/src/returns.rs29
-rw-r--r--src/tools/clippy/clippy_lints/src/single_component_path_imports.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/transmute/mod.rs8
-rw-r--r--src/tools/clippy/clippy_lints/src/transmute/transmute_ptr_to_ref.rs10
-rw-r--r--src/tools/clippy/clippy_lints/src/undocumented_unsafe_blocks.rs429
-rw-r--r--src/tools/clippy/clippy_lints/src/unnecessary_self_imports.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/unnested_or_patterns.rs17
-rw-r--r--src/tools/clippy/clippy_lints/src/unsafe_removed_from_name.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/unused_rounding.rs21
-rw-r--r--src/tools/clippy/clippy_lints/src/use_self.rs14
-rw-r--r--src/tools/clippy/clippy_lints/src/utils/conf.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/utils/internal_lints/lint_without_lint_pass.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/utils/internal_lints/metadata_collector.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/utils/internal_lints/msrv_attr_impl.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/wildcard_imports.rs3
-rw-r--r--src/tools/clippy/clippy_utils/src/ast_utils.rs2
-rw-r--r--src/tools/clippy/clippy_utils/src/attrs.rs24
-rw-r--r--src/tools/clippy/clippy_utils/src/eager_or_lazy.rs26
-rw-r--r--src/tools/clippy/clippy_utils/src/lib.rs33
-rw-r--r--src/tools/clippy/clippy_utils/src/msrvs.rs101
-rw-r--r--src/tools/clippy/clippy_utils/src/paths.rs5
-rw-r--r--src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs28
-rw-r--r--src/tools/clippy/clippy_utils/src/source.rs38
-rw-r--r--src/tools/clippy/clippy_utils/src/sugg.rs65
-rw-r--r--src/tools/clippy/clippy_utils/src/ty.rs42
-rw-r--r--src/tools/clippy/clippy_utils/src/usage.rs2
-rw-r--r--src/tools/clippy/clippy_utils/src/visitors.rs12
-rw-r--r--src/tools/clippy/lintcheck/src/main.rs15
-rw-r--r--src/tools/clippy/rust-toolchain2
-rw-r--r--src/tools/clippy/src/driver.rs5
-rw-r--r--src/tools/clippy/tests/ui-internal/invalid_msrv_attr_impl.fixed4
-rw-r--r--src/tools/clippy/tests/ui-internal/invalid_msrv_attr_impl.rs4
-rw-r--r--src/tools/clippy/tests/ui-internal/unnecessary_def_path_hardcoded_path.stderr18
-rw-r--r--src/tools/clippy/tests/ui-toml/allow_mixed_uninlined_format_args/clippy.toml1
-rw-r--r--src/tools/clippy/tests/ui-toml/allow_mixed_uninlined_format_args/uninlined_format_args.fixed14
-rw-r--r--src/tools/clippy/tests/ui-toml/allow_mixed_uninlined_format_args/uninlined_format_args.rs14
-rw-r--r--src/tools/clippy/tests/ui-toml/allow_mixed_uninlined_format_args/uninlined_format_args.stderr76
-rw-r--r--src/tools/clippy/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr1
-rw-r--r--src/tools/clippy/tests/ui/almost_complete_letter_range.fixed5
-rw-r--r--src/tools/clippy/tests/ui/almost_complete_letter_range.rs5
-rw-r--r--src/tools/clippy/tests/ui/almost_complete_letter_range.stderr26
-rw-r--r--src/tools/clippy/tests/ui/async_yields_async.stderr20
-rw-r--r--src/tools/clippy/tests/ui/cast_abs_to_unsigned.fixed7
-rw-r--r--src/tools/clippy/tests/ui/cast_abs_to_unsigned.rs7
-rw-r--r--src/tools/clippy/tests/ui/cast_abs_to_unsigned.stderr36
-rw-r--r--src/tools/clippy/tests/ui/cast_lossless_bool.fixed7
-rw-r--r--src/tools/clippy/tests/ui/cast_lossless_bool.rs7
-rw-r--r--src/tools/clippy/tests/ui/cast_lossless_bool.stderr28
-rw-r--r--src/tools/clippy/tests/ui/cfg_attr_rustfmt.fixed8
-rw-r--r--src/tools/clippy/tests/ui/cfg_attr_rustfmt.rs8
-rw-r--r--src/tools/clippy/tests/ui/cfg_attr_rustfmt.stderr2
-rw-r--r--src/tools/clippy/tests/ui/checked_conversions.fixed7
-rw-r--r--src/tools/clippy/tests/ui/checked_conversions.rs7
-rw-r--r--src/tools/clippy/tests/ui/checked_conversions.stderr34
-rw-r--r--src/tools/clippy/tests/ui/cloned_instead_of_copied.fixed10
-rw-r--r--src/tools/clippy/tests/ui/cloned_instead_of_copied.rs10
-rw-r--r--src/tools/clippy/tests/ui/cloned_instead_of_copied.stderr16
-rw-r--r--src/tools/clippy/tests/ui/err_expect.fixed7
-rw-r--r--src/tools/clippy/tests/ui/err_expect.rs7
-rw-r--r--src/tools/clippy/tests/ui/err_expect.stderr4
-rw-r--r--src/tools/clippy/tests/ui/eta.fixed22
-rw-r--r--src/tools/clippy/tests/ui/eta.rs22
-rw-r--r--src/tools/clippy/tests/ui/eta.stderr26
-rw-r--r--src/tools/clippy/tests/ui/explicit_auto_deref.fixed4
-rw-r--r--src/tools/clippy/tests/ui/explicit_auto_deref.rs4
-rw-r--r--src/tools/clippy/tests/ui/filter_map_next_fixable.fixed7
-rw-r--r--src/tools/clippy/tests/ui/filter_map_next_fixable.rs7
-rw-r--r--src/tools/clippy/tests/ui/filter_map_next_fixable.stderr4
-rw-r--r--src/tools/clippy/tests/ui/from_over_into.fixed7
-rw-r--r--src/tools/clippy/tests/ui/from_over_into.rs7
-rw-r--r--src/tools/clippy/tests/ui/from_over_into.stderr10
-rw-r--r--src/tools/clippy/tests/ui/if_then_some_else_none.rs5
-rw-r--r--src/tools/clippy/tests/ui/if_then_some_else_none.stderr10
-rw-r--r--src/tools/clippy/tests/ui/macro_use_imports.stderr8
-rw-r--r--src/tools/clippy/tests/ui/manual_clamp.rs7
-rw-r--r--src/tools/clippy/tests/ui/manual_clamp.stderr70
-rw-r--r--src/tools/clippy/tests/ui/manual_is_ascii_check.fixed11
-rw-r--r--src/tools/clippy/tests/ui/manual_is_ascii_check.rs11
-rw-r--r--src/tools/clippy/tests/ui/manual_is_ascii_check.stderr22
-rw-r--r--src/tools/clippy/tests/ui/manual_let_else.rs14
-rw-r--r--src/tools/clippy/tests/ui/manual_let_else.stderr11
-rw-r--r--src/tools/clippy/tests/ui/manual_rem_euclid.fixed13
-rw-r--r--src/tools/clippy/tests/ui/manual_rem_euclid.rs13
-rw-r--r--src/tools/clippy/tests/ui/manual_rem_euclid.stderr20
-rw-r--r--src/tools/clippy/tests/ui/manual_retain.fixed7
-rw-r--r--src/tools/clippy/tests/ui/manual_retain.rs7
-rw-r--r--src/tools/clippy/tests/ui/manual_retain.stderr38
-rw-r--r--src/tools/clippy/tests/ui/manual_split_once.fixed5
-rw-r--r--src/tools/clippy/tests/ui/manual_split_once.rs5
-rw-r--r--src/tools/clippy/tests/ui/manual_split_once.stderr38
-rw-r--r--src/tools/clippy/tests/ui/manual_str_repeat.fixed5
-rw-r--r--src/tools/clippy/tests/ui/manual_str_repeat.rs5
-rw-r--r--src/tools/clippy/tests/ui/manual_str_repeat.stderr20
-rw-r--r--src/tools/clippy/tests/ui/manual_strip.rs7
-rw-r--r--src/tools/clippy/tests/ui/manual_strip.stderr32
-rw-r--r--src/tools/clippy/tests/ui/map_unwrap_or.rs7
-rw-r--r--src/tools/clippy/tests/ui/map_unwrap_or.stderr24
-rw-r--r--src/tools/clippy/tests/ui/match_expr_like_matches_macro.fixed7
-rw-r--r--src/tools/clippy/tests/ui/match_expr_like_matches_macro.rs7
-rw-r--r--src/tools/clippy/tests/ui/match_expr_like_matches_macro.stderr28
-rw-r--r--src/tools/clippy/tests/ui/mem_replace.fixed7
-rw-r--r--src/tools/clippy/tests/ui/mem_replace.rs7
-rw-r--r--src/tools/clippy/tests/ui/mem_replace.stderr40
-rw-r--r--src/tools/clippy/tests/ui/min_rust_version_attr.rs41
-rw-r--r--src/tools/clippy/tests/ui/min_rust_version_attr.stderr26
-rw-r--r--src/tools/clippy/tests/ui/min_rust_version_invalid_attr.stderr2
-rw-r--r--src/tools/clippy/tests/ui/misnamed_getters.rs124
-rw-r--r--src/tools/clippy/tests/ui/misnamed_getters.stderr166
-rw-r--r--src/tools/clippy/tests/ui/missing_const_for_fn/cant_be_const.rs4
-rw-r--r--src/tools/clippy/tests/ui/missing_const_for_fn/could_be_const.rs10
-rw-r--r--src/tools/clippy/tests/ui/missing_const_for_fn/could_be_const.stderr24
-rw-r--r--src/tools/clippy/tests/ui/needless_borrow.fixed33
-rw-r--r--src/tools/clippy/tests/ui/needless_borrow.rs33
-rw-r--r--src/tools/clippy/tests/ui/needless_borrow.stderr18
-rw-r--r--src/tools/clippy/tests/ui/needless_question_mark.fixed1
-rw-r--r--src/tools/clippy/tests/ui/needless_question_mark.rs1
-rw-r--r--src/tools/clippy/tests/ui/needless_question_mark.stderr28
-rw-r--r--src/tools/clippy/tests/ui/needless_return.fixed19
-rw-r--r--src/tools/clippy/tests/ui/needless_return.rs13
-rw-r--r--src/tools/clippy/tests/ui/needless_return.stderr85
-rw-r--r--src/tools/clippy/tests/ui/needless_splitn.fixed3
-rw-r--r--src/tools/clippy/tests/ui/needless_splitn.rs3
-rw-r--r--src/tools/clippy/tests/ui/needless_splitn.stderr26
-rw-r--r--src/tools/clippy/tests/ui/option_as_ref_deref.fixed7
-rw-r--r--src/tools/clippy/tests/ui/option_as_ref_deref.rs7
-rw-r--r--src/tools/clippy/tests/ui/option_as_ref_deref.stderr36
-rw-r--r--src/tools/clippy/tests/ui/ptr_as_ptr.fixed5
-rw-r--r--src/tools/clippy/tests/ui/ptr_as_ptr.rs5
-rw-r--r--src/tools/clippy/tests/ui/ptr_as_ptr.stderr16
-rw-r--r--src/tools/clippy/tests/ui/range_contains.fixed7
-rw-r--r--src/tools/clippy/tests/ui/range_contains.rs7
-rw-r--r--src/tools/clippy/tests/ui/range_contains.stderr42
-rw-r--r--src/tools/clippy/tests/ui/redundant_closure_call_fixable.fixed12
-rw-r--r--src/tools/clippy/tests/ui/redundant_closure_call_fixable.rs12
-rw-r--r--src/tools/clippy/tests/ui/redundant_closure_call_fixable.stderr24
-rw-r--r--src/tools/clippy/tests/ui/redundant_field_names.fixed7
-rw-r--r--src/tools/clippy/tests/ui/redundant_field_names.rs7
-rw-r--r--src/tools/clippy/tests/ui/redundant_field_names.stderr16
-rw-r--r--src/tools/clippy/tests/ui/redundant_static_lifetimes.fixed7
-rw-r--r--src/tools/clippy/tests/ui/redundant_static_lifetimes.rs7
-rw-r--r--src/tools/clippy/tests/ui/redundant_static_lifetimes.stderr34
-rw-r--r--src/tools/clippy/tests/ui/result_large_err.rs6
-rw-r--r--src/tools/clippy/tests/ui/result_map_unit_fn_unfixable.stderr6
-rw-r--r--src/tools/clippy/tests/ui/seek_from_current.fixed5
-rw-r--r--src/tools/clippy/tests/ui/seek_from_current.rs5
-rw-r--r--src/tools/clippy/tests/ui/seek_from_current.stderr2
-rw-r--r--src/tools/clippy/tests/ui/seek_to_start_instead_of_rewind.fixed7
-rw-r--r--src/tools/clippy/tests/ui/seek_to_start_instead_of_rewind.rs7
-rw-r--r--src/tools/clippy/tests/ui/seek_to_start_instead_of_rewind.stderr6
-rw-r--r--src/tools/clippy/tests/ui/transmute_ptr_to_ref.fixed5
-rw-r--r--src/tools/clippy/tests/ui/transmute_ptr_to_ref.rs5
-rw-r--r--src/tools/clippy/tests/ui/transmute_ptr_to_ref.stderr44
-rw-r--r--src/tools/clippy/tests/ui/undocumented_unsafe_blocks.rs2
-rw-r--r--src/tools/clippy/tests/ui/undocumented_unsafe_blocks.stderr33
-rw-r--r--src/tools/clippy/tests/ui/uninlined_format_args.fixed24
-rw-r--r--src/tools/clippy/tests/ui/uninlined_format_args.rs5
-rw-r--r--src/tools/clippy/tests/ui/uninlined_format_args.stderr213
-rw-r--r--src/tools/clippy/tests/ui/unnecessary_cast.fixed14
-rw-r--r--src/tools/clippy/tests/ui/unnecessary_cast.rs14
-rw-r--r--src/tools/clippy/tests/ui/unnecessary_cast.stderr52
-rw-r--r--src/tools/clippy/tests/ui/unnecessary_lazy_eval.fixed23
-rw-r--r--src/tools/clippy/tests/ui/unnecessary_lazy_eval.rs23
-rw-r--r--src/tools/clippy/tests/ui/unnecessary_lazy_eval.stderr84
-rw-r--r--src/tools/clippy/tests/ui/unnecessary_operation.fixed9
-rw-r--r--src/tools/clippy/tests/ui/unnecessary_operation.rs9
-rw-r--r--src/tools/clippy/tests/ui/unnecessary_safety_comment.rs51
-rw-r--r--src/tools/clippy/tests/ui/unnecessary_safety_comment.stderr115
-rw-r--r--src/tools/clippy/tests/ui/unnecessary_to_owned.fixed34
-rw-r--r--src/tools/clippy/tests/ui/unnecessary_to_owned.rs34
-rw-r--r--src/tools/clippy/tests/ui/unnecessary_to_owned.stderr168
-rw-r--r--src/tools/clippy/tests/ui/unnecessary_unsafety_doc.rs (renamed from src/tools/clippy/tests/ui/doc_unnecessary_unsafe.rs)1
-rw-r--r--src/tools/clippy/tests/ui/unnecessary_unsafety_doc.stderr (renamed from src/tools/clippy/tests/ui/doc_unnecessary_unsafe.stderr)14
-rw-r--r--src/tools/clippy/tests/ui/unnested_or_patterns.fixed8
-rw-r--r--src/tools/clippy/tests/ui/unnested_or_patterns.rs8
-rw-r--r--src/tools/clippy/tests/ui/unnested_or_patterns.stderr2
-rw-r--r--src/tools/clippy/tests/ui/unused_rounding.fixed3
-rw-r--r--src/tools/clippy/tests/ui/unused_rounding.rs3
-rw-r--r--src/tools/clippy/tests/ui/unused_rounding.stderr8
-rw-r--r--src/tools/clippy/tests/ui/use_self.fixed7
-rw-r--r--src/tools/clippy/tests/ui/use_self.rs7
-rw-r--r--src/tools/clippy/tests/ui/use_self.stderr84
-rw-r--r--src/tools/clippy/triagebot.toml21
-rw-r--r--src/tools/jsondoclint/src/json_find.rs5
-rw-r--r--src/tools/jsondoclint/src/json_find/tests.rs27
-rw-r--r--src/tools/jsondoclint/src/main.rs4
-rw-r--r--src/tools/miri/src/concurrency/data_race.rs2
-rw-r--r--src/tools/rustc-workspace-hack/Cargo.toml2
-rw-r--r--src/tools/rustfmt/src/attr.rs15
-rw-r--r--src/tools/rustfmt/src/imports.rs2
-rw-r--r--src/tools/rustfmt/src/modules/visitor.rs12
-rw-r--r--src/tools/rustfmt/src/overflow.rs4
-rw-r--r--src/tools/rustfmt/src/utils.rs2
-rw-r--r--src/tools/tidy/src/deps.rs2
-rw-r--r--src/tools/tier-check/src/main.rs18
-rw-r--r--triagebot.toml19
722 files changed, 10490 insertions, 7073 deletions
diff --git a/.mailmap b/.mailmap
index f887d29096e..022cdd0fd50 100644
--- a/.mailmap
+++ b/.mailmap
@@ -73,6 +73,8 @@ Björn Steinbrink <bsteinbr@gmail.com> <B.Steinbrink@gmx.de>
 blake2-ppc <ulrik.sverdrup@gmail.com> <blake2-ppc>
 boolean_coercion <booleancoercion@gmail.com>
 Boris Egorov <jightuse@gmail.com> <egorov@linux.com>
+bors <bors@rust-lang.org> bors[bot] <26634292+bors[bot]@users.noreply.github.com>
+bors <bors@rust-lang.org> bors[bot] <bors[bot]@users.noreply.github.com>
 Braden Nelson <moonheart08@users.noreply.github.com>
 Brandon Sanderson <singingboyo@gmail.com> Brandon Sanderson <singingboyo@hotmail.com>
 Brett Cannon <brett@python.org> Brett Cannon <brettcannon@users.noreply.github.com>
@@ -139,6 +141,10 @@ David Ross <daboross@daboross.net>
 David Wood <david@davidtw.co> <david.wood@huawei.com>
 Deadbeef <ent3rm4n@gmail.com>
 Deadbeef <ent3rm4n@gmail.com> <fee1-dead-beef@protonmail.com>
+dependabot[bot] <dependabot[bot]@users.noreply.github.com> <27856297+dependabot-preview[bot]@users.noreply.github.com>
+dependabot[bot] <dependabot[bot]@users.noreply.github.com> <49699333+dependabot[bot]@users.noreply.github.com>
+dependabot[bot] <dependabot[bot]@users.noreply.github.com> <dependabot-preview[bot]@users.noreply.github.com>
+dependabot[bot] <dependabot[bot]@users.noreply.github.com> <support@dependabot.com>
 Derek Chiang <derekchiang93@gmail.com> Derek Chiang (Enchi Jiang) <derekchiang93@gmail.com>
 DeveloperC <DeveloperC@protonmail.com>
 Devin Ragotzy <devin.ragotzy@gmail.com>
@@ -229,7 +235,7 @@ Jacob <jacob.macritchie@gmail.com>
 Jacob Greenfield <xales@naveria.com>
 Jacob Pratt <jacob@jhpratt.dev> <the.z.cuber@gmail.com>
 Jake Vossen <jake@vossen.dev>
-Jakob Degen <jakob@degen.com>
+Jakob Degen <jakob.e.degen@gmail.com> <jakob@degen.com>
 Jakob Lautrup Nysom <jako3047@gmail.com>
 Jakub Adam Wieczorek <jakub.adam.wieczorek@gmail.com>
 Jakub Adam Wieczorek <jakub.adam.wieczorek@gmail.com> <jakub.bukaj@yahoo.com>
@@ -396,6 +402,10 @@ Nathaniel Herman <nherman@post.harvard.edu> Nathaniel Herman <nherman@college.ha
 Neil Pankey <npankey@gmail.com> <neil@wire.im>
 Ngo Iok Ui (Wu Yu Wei) <wusyong9104@gmail.com>
 Nicholas Baron <nicholas.baron.ten@gmail.com>
+Nicholas Bishop <nbishop@nbishop.net> <nicholasbishop@gmail.com>
+Nicholas Bishop <nbishop@nbishop.net> <nicholasbishop@google.com>
+Nicholas Nethercote <n.nethercote@gmail.com> <nnethercote@apple.com>
+Nicholas Nethercote <n.nethercote@gmail.com> <nnethercote@mozilla.com>
 Nick Platt <platt.nicholas@gmail.com>
 Niclas Schwarzlose <15schnic@gmail.com>
 Nicolas Abram <abramlujan@gmail.com>
@@ -522,6 +532,7 @@ Tomas Koutsky <tomas@stepnivlk.net>
 Torsten Weber <TorstenWeber12@gmail.com>
 Torsten Weber <TorstenWeber12@gmail.com> <torstenweber12@gmail.com>
 Trevor Spiteri <tspiteri@ieee.org> <trevor.spiteri@um.edu.mt>
+Tshepang Mbambo <tshepang@gmail.com>
 Ty Overby <ty@pre-alpha.com>
 Tyler Mandry <tmandry@gmail.com> <tmandry@google.com>
 Tyler Ruckinger <t.ruckinger@gmail.com>
diff --git a/Cargo.lock b/Cargo.lock
index d8612b3a256..7d43dbc9e06 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -288,7 +288,6 @@ name = "cargo"
 version = "0.68.0"
 dependencies = [
  "anyhow",
- "atty",
  "bytesize",
  "cargo-platform 0.1.2",
  "cargo-test-macro",
@@ -298,7 +297,7 @@ dependencies = [
  "crates-io",
  "curl",
  "curl-sys",
- "env_logger 0.9.0",
+ "env_logger 0.10.0",
  "filetime",
  "flate2",
  "fwdansi",
@@ -312,6 +311,7 @@ dependencies = [
  "ignore",
  "im-rc",
  "indexmap",
+ "is-terminal",
  "itertools",
  "jobserver",
  "lazy_static",
@@ -871,6 +871,12 @@ dependencies = [
 ]
 
 [[package]]
+name = "convert_case"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6245d59a3e82a7fc217c5828a6692dbc6dfb63a0c8c90495621f7b9d79704a0e"
+
+[[package]]
 name = "core"
 version = "0.0.0"
 dependencies = [
@@ -1061,6 +1067,19 @@ dependencies = [
 ]
 
 [[package]]
+name = "derive_more"
+version = "0.99.17"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4fb810d30a7c1953f91334de7244731fc3f3c10d7fe163338a35b9f640960321"
+dependencies = [
+ "convert_case",
+ "proc-macro2",
+ "quote",
+ "rustc_version",
+ "syn",
+]
+
+[[package]]
 name = "diff"
 version = "0.1.13"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1214,6 +1233,40 @@ dependencies = [
 ]
 
 [[package]]
+name = "env_logger"
+version = "0.10.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "85cdab6a89accf66733ad5a1693a4dcced6aeff64602b634530dd73c1f3ee9f0"
+dependencies = [
+ "humantime 2.0.1",
+ "is-terminal",
+ "log",
+ "regex",
+ "termcolor",
+]
+
+[[package]]
+name = "errno"
+version = "0.2.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f639046355ee4f37944e44f60642c6f3a7efa3cf6b78c78a0d989a8ce6c396a1"
+dependencies = [
+ "errno-dragonfly",
+ "libc",
+ "winapi",
+]
+
+[[package]]
+name = "errno-dragonfly"
+version = "0.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf"
+dependencies = [
+ "cc",
+ "libc",
+]
+
+[[package]]
 name = "error_index_generator"
 version = "0.0.0"
 dependencies = [
@@ -1908,6 +1961,28 @@ dependencies = [
 ]
 
 [[package]]
+name = "io-lifetimes"
+version = "1.0.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e394faa0efb47f9f227f1cd89978f854542b318a6f64fa695489c9c993056656"
+dependencies = [
+ "libc",
+ "windows-sys",
+]
+
+[[package]]
+name = "is-terminal"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "aae5bc6e2eb41c9def29a3e0f1306382807764b9b53112030eff57435667352d"
+dependencies = [
+ "hermit-abi 0.2.6",
+ "io-lifetimes",
+ "rustix",
+ "windows-sys",
+]
+
+[[package]]
 name = "itertools"
 version = "0.10.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -2117,6 +2192,12 @@ dependencies = [
 ]
 
 [[package]]
+name = "linux-raw-sys"
+version = "0.1.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8f9f08d8963a6c613f4b1a78f4f4a4dbfadf8e6545b2d72861731e4858b8b47f"
+
+[[package]]
 name = "litemap"
 version = "0.6.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -3193,6 +3274,7 @@ version = "1.0.0"
 dependencies = [
  "bstr 0.2.17",
  "clap 3.2.20",
+ "libc",
  "libz-sys",
  "rand 0.8.5",
  "regex",
@@ -3916,6 +3998,7 @@ version = "0.0.0"
 dependencies = [
  "bitflags",
  "chalk-ir",
+ "derive_more",
  "either",
  "gsgdt",
  "polonius-engine",
@@ -4509,6 +4592,20 @@ dependencies = [
 ]
 
 [[package]]
+name = "rustix"
+version = "0.36.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0b1fbb4dfc4eb1d390c02df47760bb19a84bb80b301ecc947ab5406394d8223e"
+dependencies = [
+ "bitflags",
+ "errno",
+ "io-lifetimes",
+ "libc",
+ "linux-raw-sys",
+ "windows-sys",
+]
+
+[[package]]
 name = "rustversion"
 version = "1.0.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
diff --git a/compiler/rustc_abi/src/lib.rs b/compiler/rustc_abi/src/lib.rs
index 4f4a4bf314f..85693259cd0 100644
--- a/compiler/rustc_abi/src/lib.rs
+++ b/compiler/rustc_abi/src/lib.rs
@@ -211,6 +211,102 @@ pub enum TargetDataLayoutErrors<'a> {
 }
 
 impl TargetDataLayout {
+    /// Parse data layout from an [llvm data layout string](https://llvm.org/docs/LangRef.html#data-layout)
+    ///
+    /// This function doesn't fill `c_enum_min_size` and it will always be `I32` since it can not be
+    /// determined from llvm string.
+    pub fn parse_from_llvm_datalayout_string<'a>(
+        input: &'a str,
+    ) -> Result<TargetDataLayout, TargetDataLayoutErrors<'a>> {
+        // Parse an address space index from a string.
+        let parse_address_space = |s: &'a str, cause: &'a str| {
+            s.parse::<u32>().map(AddressSpace).map_err(|err| {
+                TargetDataLayoutErrors::InvalidAddressSpace { addr_space: s, cause, err }
+            })
+        };
+
+        // Parse a bit count from a string.
+        let parse_bits = |s: &'a str, kind: &'a str, cause: &'a str| {
+            s.parse::<u64>().map_err(|err| TargetDataLayoutErrors::InvalidBits {
+                kind,
+                bit: s,
+                cause,
+                err,
+            })
+        };
+
+        // Parse a size string.
+        let size = |s: &'a str, cause: &'a str| parse_bits(s, "size", cause).map(Size::from_bits);
+
+        // Parse an alignment string.
+        let align = |s: &[&'a str], cause: &'a str| {
+            if s.is_empty() {
+                return Err(TargetDataLayoutErrors::MissingAlignment { cause });
+            }
+            let align_from_bits = |bits| {
+                Align::from_bits(bits)
+                    .map_err(|err| TargetDataLayoutErrors::InvalidAlignment { cause, err })
+            };
+            let abi = parse_bits(s[0], "alignment", cause)?;
+            let pref = s.get(1).map_or(Ok(abi), |pref| parse_bits(pref, "alignment", cause))?;
+            Ok(AbiAndPrefAlign { abi: align_from_bits(abi)?, pref: align_from_bits(pref)? })
+        };
+
+        let mut dl = TargetDataLayout::default();
+        let mut i128_align_src = 64;
+        for spec in input.split('-') {
+            let spec_parts = spec.split(':').collect::<Vec<_>>();
+
+            match &*spec_parts {
+                ["e"] => dl.endian = Endian::Little,
+                ["E"] => dl.endian = Endian::Big,
+                [p] if p.starts_with('P') => {
+                    dl.instruction_address_space = parse_address_space(&p[1..], "P")?
+                }
+                ["a", ref a @ ..] => dl.aggregate_align = align(a, "a")?,
+                ["f32", ref a @ ..] => dl.f32_align = align(a, "f32")?,
+                ["f64", ref a @ ..] => dl.f64_align = align(a, "f64")?,
+                [p @ "p", s, ref a @ ..] | [p @ "p0", s, ref a @ ..] => {
+                    dl.pointer_size = size(s, p)?;
+                    dl.pointer_align = align(a, p)?;
+                }
+                [s, ref a @ ..] if s.starts_with('i') => {
+                    let Ok(bits) = s[1..].parse::<u64>() else {
+                        size(&s[1..], "i")?; // For the user error.
+                        continue;
+                    };
+                    let a = align(a, s)?;
+                    match bits {
+                        1 => dl.i1_align = a,
+                        8 => dl.i8_align = a,
+                        16 => dl.i16_align = a,
+                        32 => dl.i32_align = a,
+                        64 => dl.i64_align = a,
+                        _ => {}
+                    }
+                    if bits >= i128_align_src && bits <= 128 {
+                        // Default alignment for i128 is decided by taking the alignment of
+                        // largest-sized i{64..=128}.
+                        i128_align_src = bits;
+                        dl.i128_align = a;
+                    }
+                }
+                [s, ref a @ ..] if s.starts_with('v') => {
+                    let v_size = size(&s[1..], "v")?;
+                    let a = align(a, s)?;
+                    if let Some(v) = dl.vector_align.iter_mut().find(|v| v.0 == v_size) {
+                        v.1 = a;
+                        continue;
+                    }
+                    // No existing entry, add a new one.
+                    dl.vector_align.push((v_size, a));
+                }
+                _ => {} // Ignore everything else.
+            }
+        }
+        Ok(dl)
+    }
+
     /// Returns exclusive upper bound on object size.
     ///
     /// The theoretical maximum object size is defined as the maximum positive `isize` value.
diff --git a/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs
index b48a7d29f50..f2f8e1386a5 100644
--- a/compiler/rustc_ast/src/ast.rs
+++ b/compiler/rustc_ast/src/ast.rs
@@ -13,7 +13,7 @@
 //! - [`FnDecl`], [`FnHeader`] and [`Param`]: Metadata associated with a function declaration.
 //! - [`Generics`], [`GenericParam`], [`WhereClause`]: Metadata associated with generic parameters.
 //! - [`EnumDef`] and [`Variant`]: Enum declaration.
-//! - [`Lit`] and [`LitKind`]: Literal expressions.
+//! - [`MetaItemLit`] and [`LitKind`]: Literal expressions.
 //! - [`MacroDef`], [`MacStmtStyle`], [`MacCall`], [`MacDelimiter`]: Macro definition and invocation.
 //! - [`Attribute`]: Metadata associated with item.
 //! - [`UnOp`], [`BinOp`], and [`BinOpKind`]: Unary and binary operators.
@@ -111,8 +111,8 @@ impl<CTX: rustc_span::HashStableContext> HashStable<CTX> for Path {
 }
 
 impl Path {
-    // Convert a span and an identifier to the corresponding
-    // one-segment path.
+    /// Convert a span and an identifier to the corresponding
+    /// one-segment path.
     pub fn from_ident(ident: Ident) -> Path {
         Path { segments: thin_vec![PathSegment::from_ident(ident)], span: ident.span, tokens: None }
     }
@@ -479,20 +479,10 @@ pub struct Crate {
     pub is_placeholder: bool,
 }
 
-/// Possible values inside of compile-time attribute lists.
-///
-/// E.g., the '..' in `#[name(..)]`.
-#[derive(Clone, Encodable, Decodable, Debug, HashStable_Generic)]
-pub enum NestedMetaItem {
-    /// A full MetaItem, for recursive meta items.
-    MetaItem(MetaItem),
-    /// A literal.
-    ///
-    /// E.g., `"foo"`, `64`, `true`.
-    Literal(Lit),
-}
-
-/// A spanned compile-time attribute item.
+/// A semantic representation of a meta item. A meta item is a slightly
+/// restricted form of an attribute -- it can only contain expressions in
+/// certain leaf positions, rather than arbitrary token streams -- that is used
+/// for most built-in attributes.
 ///
 /// E.g., `#[test]`, `#[derive(..)]`, `#[rustfmt::skip]` or `#[feature = "foo"]`.
 #[derive(Clone, Encodable, Decodable, Debug, HashStable_Generic)]
@@ -502,23 +492,37 @@ pub struct MetaItem {
     pub span: Span,
 }
 
-/// A compile-time attribute item.
-///
-/// E.g., `#[test]`, `#[derive(..)]` or `#[feature = "foo"]`.
+/// The meta item kind, containing the data after the initial path.
 #[derive(Clone, Encodable, Decodable, Debug, HashStable_Generic)]
 pub enum MetaItemKind {
     /// Word meta item.
     ///
-    /// E.g., `test` as in `#[test]`.
+    /// E.g., `#[test]`, which lacks any arguments after `test`.
     Word,
+
     /// List meta item.
     ///
-    /// E.g., `derive(..)` as in `#[derive(..)]`.
+    /// E.g., `#[derive(..)]`, where the field represents the `..`.
     List(Vec<NestedMetaItem>),
+
     /// Name value meta item.
     ///
-    /// E.g., `feature = "foo"` as in `#[feature = "foo"]`.
-    NameValue(Lit),
+    /// E.g., `#[feature = "foo"]`, where the field represents the `"foo"`.
+    NameValue(MetaItemLit),
+}
+
+/// Values inside meta item lists.
+///
+/// E.g., each of `Clone`, `Copy` in `#[derive(Clone, Copy)]`.
+#[derive(Clone, Encodable, Decodable, Debug, HashStable_Generic)]
+pub enum NestedMetaItem {
+    /// A full MetaItem, for recursive meta items.
+    MetaItem(MetaItem),
+
+    /// A literal.
+    ///
+    /// E.g., `"foo"`, `64`, `true`.
+    Lit(MetaItemLit),
 }
 
 /// A block (`{ .. }`).
@@ -1283,7 +1287,7 @@ impl Expr {
         )
     }
 
-    // To a first-order approximation, is this a pattern
+    /// To a first-order approximation, is this a pattern?
     pub fn is_approximately_pattern(&self) -> bool {
         match &self.peel_parens().kind {
             ExprKind::Box(_)
@@ -1599,12 +1603,12 @@ pub enum AttrArgs {
 }
 
 // The RHS of an `AttrArgs::Eq` starts out as an expression. Once macro
-// expansion is completed, all cases end up either as a literal, which is the
-// form used after lowering to HIR, or as an error.
+// expansion is completed, all cases end up either as a meta item literal,
+// which is the form used after lowering to HIR, or as an error.
 #[derive(Clone, Encodable, Decodable, Debug)]
 pub enum AttrArgsEq {
     Ast(P<Expr>),
-    Hir(Lit),
+    Hir(MetaItemLit),
 }
 
 impl AttrArgs {
@@ -1726,19 +1730,18 @@ pub enum StrStyle {
     Raw(u8),
 }
 
-/// An AST literal.
+/// A literal in a meta item.
 #[derive(Clone, Encodable, Decodable, Debug, HashStable_Generic)]
-pub struct Lit {
+pub struct MetaItemLit {
     /// The original literal token as written in source code.
     pub token_lit: token::Lit,
     /// The "semantic" representation of the literal lowered from the original tokens.
     /// Strings are unescaped, hexadecimal forms are eliminated, etc.
-    /// FIXME: Remove this and only create the semantic representation during lowering to HIR.
     pub kind: LitKind,
     pub span: Span,
 }
 
-/// Same as `Lit`, but restricted to string literals.
+/// Similar to `MetaItemLit`, but restricted to string literals.
 #[derive(Clone, Copy, Encodable, Decodable, Debug)]
 pub struct StrLit {
     /// The original literal token as written in source code.
@@ -1747,7 +1750,6 @@ pub struct StrLit {
     pub suffix: Option<Symbol>,
     pub span: Span,
     /// The unescaped "semantic" representation of the literal lowered from the original token.
-    /// FIXME: Remove this and only create the semantic representation during lowering to HIR.
     pub symbol_unescaped: Symbol,
 }
 
@@ -1783,6 +1785,8 @@ pub enum LitFloatType {
     Unsuffixed,
 }
 
+/// This type is used within both `ast::MetaItemLit` and `hir::Lit`.
+///
 /// Note that the entire literal (including the suffix) is considered when
 /// deciding the `LitKind`. This means that float literals like `1f32` are
 /// classified by this type as `Float`. This is different to `token::LitKind`
@@ -2513,10 +2517,7 @@ pub struct Variant {
 #[derive(Clone, Encodable, Decodable, Debug)]
 pub enum UseTreeKind {
     /// `use prefix` or `use prefix as rename`
-    ///
-    /// The extra `NodeId`s are for HIR lowering, when additional statements are created for each
-    /// namespace.
-    Simple(Option<Ident>, NodeId, NodeId),
+    Simple(Option<Ident>),
     /// `use prefix::{...}`
     Nested(Vec<(UseTree, NodeId)>),
     /// `use prefix::*`
@@ -2535,8 +2536,8 @@ pub struct UseTree {
 impl UseTree {
     pub fn ident(&self) -> Ident {
         match self.kind {
-            UseTreeKind::Simple(Some(rename), ..) => rename,
-            UseTreeKind::Simple(None, ..) => {
+            UseTreeKind::Simple(Some(rename)) => rename,
+            UseTreeKind::Simple(None) => {
                 self.prefix.segments.last().expect("empty prefix in a simple import").ident
             }
             _ => panic!("`UseTree::ident` can only be used on a simple import"),
@@ -2570,17 +2571,10 @@ impl<D: Decoder> Decodable<D> for AttrId {
     }
 }
 
-#[derive(Clone, Encodable, Decodable, Debug, HashStable_Generic)]
-pub struct AttrItem {
-    pub path: Path,
-    pub args: AttrArgs,
-    pub tokens: Option<LazyAttrTokenStream>,
-}
-
 /// A list of attributes.
 pub type AttrVec = ThinVec<Attribute>;
 
-/// Metadata associated with an item.
+/// A syntax-level representation of an attribute.
 #[derive(Clone, Encodable, Decodable, Debug)]
 pub struct Attribute {
     pub kind: AttrKind,
@@ -2592,12 +2586,6 @@ pub struct Attribute {
 }
 
 #[derive(Clone, Encodable, Decodable, Debug)]
-pub struct NormalAttr {
-    pub item: AttrItem,
-    pub tokens: Option<LazyAttrTokenStream>,
-}
-
-#[derive(Clone, Encodable, Decodable, Debug)]
 pub enum AttrKind {
     /// A normal attribute.
     Normal(P<NormalAttr>),
@@ -2608,6 +2596,19 @@ pub enum AttrKind {
     DocComment(CommentKind, Symbol),
 }
 
+#[derive(Clone, Encodable, Decodable, Debug)]
+pub struct NormalAttr {
+    pub item: AttrItem,
+    pub tokens: Option<LazyAttrTokenStream>,
+}
+
+#[derive(Clone, Encodable, Decodable, Debug, HashStable_Generic)]
+pub struct AttrItem {
+    pub path: Path,
+    pub args: AttrArgs,
+    pub tokens: Option<LazyAttrTokenStream>,
+}
+
 /// `TraitRef`s appear in impls.
 ///
 /// Resolution maps each `TraitRef`'s `ref_id` to its defining trait; that's all
@@ -3096,9 +3097,9 @@ mod size_asserts {
     static_assert_size!(Impl, 184);
     static_assert_size!(Item, 184);
     static_assert_size!(ItemKind, 112);
-    static_assert_size!(Lit, 48);
     static_assert_size!(LitKind, 24);
     static_assert_size!(Local, 72);
+    static_assert_size!(MetaItemLit, 48);
     static_assert_size!(Param, 40);
     static_assert_size!(Pat, 88);
     static_assert_size!(Path, 24);
diff --git a/compiler/rustc_ast/src/attr/mod.rs b/compiler/rustc_ast/src/attr/mod.rs
index 3e012953115..057cc26b579 100644
--- a/compiler/rustc_ast/src/attr/mod.rs
+++ b/compiler/rustc_ast/src/attr/mod.rs
@@ -1,10 +1,10 @@
 //! Functions dealing with attributes and meta items.
 
 use crate::ast;
-use crate::ast::{AttrArgs, AttrArgsEq, AttrId, AttrItem, AttrKind, AttrStyle, Attribute};
-use crate::ast::{DelimArgs, Lit, LitKind};
-use crate::ast::{MacDelimiter, MetaItem, MetaItemKind, NestedMetaItem};
-use crate::ast::{Path, PathSegment};
+use crate::ast::{AttrArgs, AttrArgsEq, AttrId, AttrItem, AttrKind, AttrStyle, AttrVec, Attribute};
+use crate::ast::{DelimArgs, Expr, ExprKind, LitKind, MetaItemLit};
+use crate::ast::{MacDelimiter, MetaItem, MetaItemKind, NestedMetaItem, NormalAttr};
+use crate::ast::{Path, PathSegment, StrStyle, DUMMY_NODE_ID};
 use crate::ptr::P;
 use crate::token::{self, CommentKind, Delimiter, Token};
 use crate::tokenstream::{DelimSpan, Spacing, TokenTree};
@@ -12,7 +12,6 @@ use crate::tokenstream::{LazyAttrTokenStream, TokenStream};
 use crate::util::comments;
 use rustc_data_structures::sync::WorkerLocal;
 use rustc_index::bit_set::GrowableBitSet;
-use rustc_span::source_map::BytePos;
 use rustc_span::symbol::{sym, Ident, Symbol};
 use rustc_span::Span;
 use std::cell::Cell;
@@ -26,9 +25,9 @@ use thin_vec::thin_vec;
 pub struct MarkedAttrs(GrowableBitSet<AttrId>);
 
 impl MarkedAttrs {
-    // We have no idea how many attributes there will be, so just
-    // initiate the vectors with 0 bits. We'll grow them as necessary.
     pub fn new() -> Self {
+        // We have no idea how many attributes there will be, so just
+        // initiate the vectors with 0 bits. We'll grow them as necessary.
         MarkedAttrs(GrowableBitSet::new_empty())
     }
 
@@ -50,10 +49,10 @@ impl NestedMetaItem {
         }
     }
 
-    /// Returns the `Lit` if `self` is a `NestedMetaItem::Literal`s.
-    pub fn literal(&self) -> Option<&Lit> {
+    /// Returns the `MetaItemLit` if `self` is a `NestedMetaItem::Literal`s.
+    pub fn lit(&self) -> Option<&MetaItemLit> {
         match self {
-            NestedMetaItem::Literal(lit) => Some(lit),
+            NestedMetaItem::Lit(lit) => Some(lit),
             _ => None,
         }
     }
@@ -78,12 +77,12 @@ impl NestedMetaItem {
     }
 
     /// Returns a name and single literal value tuple of the `MetaItem`.
-    pub fn name_value_literal(&self) -> Option<(Symbol, &Lit)> {
+    pub fn name_value_literal(&self) -> Option<(Symbol, &MetaItemLit)> {
         self.meta_item().and_then(|meta_item| {
             meta_item.meta_item_list().and_then(|meta_item_list| {
                 if meta_item_list.len() == 1
                     && let Some(ident) = meta_item.ident()
-                    && let Some(lit) = meta_item_list[0].literal()
+                    && let Some(lit) = meta_item_list[0].lit()
                 {
                     return Some((ident.name, lit));
                 }
@@ -174,10 +173,12 @@ impl MetaItem {
         self.ident().unwrap_or_else(Ident::empty).name
     }
 
-    // Example:
-    //     #[attribute(name = "value")]
-    //                 ^^^^^^^^^^^^^^
-    pub fn name_value_literal(&self) -> Option<&Lit> {
+    /// ```text
+    /// Example:
+    ///     #[attribute(name = "value")]
+    ///                 ^^^^^^^^^^^^^^
+    /// ```
+    pub fn name_value_literal(&self) -> Option<&MetaItemLit> {
         match &self.kind {
             MetaItemKind::NameValue(v) => Some(v),
             _ => None,
@@ -221,11 +222,7 @@ impl AttrItem {
     }
 
     pub fn meta(&self, span: Span) -> Option<MetaItem> {
-        Some(MetaItem {
-            path: self.path.clone(),
-            kind: MetaItemKind::from_attr_args(&self.args)?,
-            span,
-        })
+        Some(MetaItem { path: self.path.clone(), kind: self.meta_kind()?, span })
     }
 
     pub fn meta_kind(&self) -> Option<MetaItemKind> {
@@ -327,26 +324,13 @@ impl Attribute {
 /* Constructors */
 
 pub fn mk_name_value_item_str(ident: Ident, str: Symbol, str_span: Span) -> MetaItem {
-    let lit_kind = LitKind::Str(str, ast::StrStyle::Cooked);
-    mk_name_value_item(ident, lit_kind, str_span)
+    mk_name_value_item(ident, LitKind::Str(str, ast::StrStyle::Cooked), str_span)
 }
 
-pub fn mk_name_value_item(ident: Ident, lit_kind: LitKind, lit_span: Span) -> MetaItem {
-    let lit = Lit::from_lit_kind(lit_kind, lit_span);
+pub fn mk_name_value_item(ident: Ident, kind: LitKind, lit_span: Span) -> MetaItem {
+    let lit = MetaItemLit { token_lit: kind.to_token_lit(), kind, span: lit_span };
     let span = ident.span.to(lit_span);
-    MetaItem { path: Path::from_ident(ident), span, kind: MetaItemKind::NameValue(lit) }
-}
-
-pub fn mk_list_item(ident: Ident, items: Vec<NestedMetaItem>) -> MetaItem {
-    MetaItem { path: Path::from_ident(ident), span: ident.span, kind: MetaItemKind::List(items) }
-}
-
-pub fn mk_word_item(ident: Ident) -> MetaItem {
-    MetaItem { path: Path::from_ident(ident), span: ident.span, kind: MetaItemKind::Word }
-}
-
-pub fn mk_nested_word_item(ident: Ident) -> NestedMetaItem {
-    NestedMetaItem::MetaItem(mk_word_item(ident))
+    MetaItem { path: Path::from_ident(ident), kind: MetaItemKind::NameValue(lit), span }
 }
 
 pub struct AttrIdGenerator(WorkerLocal<Cell<u32>>);
@@ -404,21 +388,58 @@ pub fn mk_attr_from_item(
     span: Span,
 ) -> Attribute {
     Attribute {
-        kind: AttrKind::Normal(P(ast::NormalAttr { item, tokens })),
+        kind: AttrKind::Normal(P(NormalAttr { item, tokens })),
         id: g.mk_attr_id(),
         style,
         span,
     }
 }
 
-/// Returns an inner attribute with the given value and span.
-pub fn mk_attr_inner(g: &AttrIdGenerator, item: MetaItem) -> Attribute {
-    mk_attr(g, AttrStyle::Inner, item.path, item.kind.attr_args(item.span), item.span)
+pub fn mk_attr_word(g: &AttrIdGenerator, style: AttrStyle, name: Symbol, span: Span) -> Attribute {
+    let path = Path::from_ident(Ident::new(name, span));
+    let args = AttrArgs::Empty;
+    mk_attr(g, style, path, args, span)
 }
 
-/// Returns an outer attribute with the given value and span.
-pub fn mk_attr_outer(g: &AttrIdGenerator, item: MetaItem) -> Attribute {
-    mk_attr(g, AttrStyle::Outer, item.path, item.kind.attr_args(item.span), item.span)
+pub fn mk_attr_name_value_str(
+    g: &AttrIdGenerator,
+    style: AttrStyle,
+    name: Symbol,
+    val: Symbol,
+    span: Span,
+) -> Attribute {
+    let lit = LitKind::Str(val, StrStyle::Cooked).to_token_lit();
+    let expr = P(Expr {
+        id: DUMMY_NODE_ID,
+        kind: ExprKind::Lit(lit),
+        span,
+        attrs: AttrVec::new(),
+        tokens: None,
+    });
+    let path = Path::from_ident(Ident::new(name, span));
+    let args = AttrArgs::Eq(span, AttrArgsEq::Ast(expr));
+    mk_attr(g, style, path, args, span)
+}
+
+pub fn mk_attr_nested_word(
+    g: &AttrIdGenerator,
+    style: AttrStyle,
+    outer: Symbol,
+    inner: Symbol,
+    span: Span,
+) -> Attribute {
+    let inner_tokens = TokenStream::new(vec![TokenTree::Token(
+        Token::from_ast_ident(Ident::new(inner, span)),
+        Spacing::Alone,
+    )]);
+    let outer_ident = Ident::new(outer, span);
+    let path = Path::from_ident(outer_ident);
+    let attr_args = AttrArgs::Delimited(DelimArgs {
+        dspan: DelimSpan::from_single(span),
+        delim: MacDelimiter::Parenthesis,
+        tokens: inner_tokens,
+    });
+    mk_attr(g, style, path, attr_args, span)
 }
 
 pub fn mk_doc_comment(
@@ -436,23 +457,6 @@ pub fn list_contains_name(items: &[NestedMetaItem], name: Symbol) -> bool {
 }
 
 impl MetaItem {
-    fn token_trees(&self) -> Vec<TokenTree> {
-        let mut idents = vec![];
-        let mut last_pos = BytePos(0_u32);
-        for (i, segment) in self.path.segments.iter().enumerate() {
-            let is_first = i == 0;
-            if !is_first {
-                let mod_sep_span =
-                    Span::new(last_pos, segment.ident.span.lo(), segment.ident.span.ctxt(), None);
-                idents.push(TokenTree::token_alone(token::ModSep, mod_sep_span));
-            }
-            idents.push(TokenTree::Token(Token::from_ast_ident(segment.ident), Spacing::Alone));
-            last_pos = segment.ident.span.hi();
-        }
-        idents.extend(self.kind.token_trees(self.span));
-        idents
-    }
-
     fn from_tokens<I>(tokens: &mut iter::Peekable<I>) -> Option<MetaItem>
     where
         I: Iterator<Item = TokenTree>,
@@ -524,62 +528,6 @@ impl MetaItemKind {
         }
     }
 
-    pub fn attr_args(&self, span: Span) -> AttrArgs {
-        match self {
-            MetaItemKind::Word => AttrArgs::Empty,
-            MetaItemKind::NameValue(lit) => {
-                let expr = P(ast::Expr {
-                    id: ast::DUMMY_NODE_ID,
-                    kind: ast::ExprKind::Lit(lit.token_lit.clone()),
-                    span: lit.span,
-                    attrs: ast::AttrVec::new(),
-                    tokens: None,
-                });
-                AttrArgs::Eq(span, AttrArgsEq::Ast(expr))
-            }
-            MetaItemKind::List(list) => {
-                let mut tts = Vec::new();
-                for (i, item) in list.iter().enumerate() {
-                    if i > 0 {
-                        tts.push(TokenTree::token_alone(token::Comma, span));
-                    }
-                    tts.extend(item.token_trees())
-                }
-                AttrArgs::Delimited(DelimArgs {
-                    dspan: DelimSpan::from_single(span),
-                    delim: MacDelimiter::Parenthesis,
-                    tokens: TokenStream::new(tts),
-                })
-            }
-        }
-    }
-
-    fn token_trees(&self, span: Span) -> Vec<TokenTree> {
-        match self {
-            MetaItemKind::Word => vec![],
-            MetaItemKind::NameValue(lit) => {
-                vec![
-                    TokenTree::token_alone(token::Eq, span),
-                    TokenTree::Token(lit.to_token(), Spacing::Alone),
-                ]
-            }
-            MetaItemKind::List(list) => {
-                let mut tokens = Vec::new();
-                for (i, item) in list.iter().enumerate() {
-                    if i > 0 {
-                        tokens.push(TokenTree::token_alone(token::Comma, span));
-                    }
-                    tokens.extend(item.token_trees())
-                }
-                vec![TokenTree::Delimited(
-                    DelimSpan::from_single(span),
-                    Delimiter::Parenthesis,
-                    TokenStream::new(tokens),
-                )]
-            }
-        }
-    }
-
     fn list_from_tokens(tokens: TokenStream) -> Option<MetaItemKind> {
         let mut tokens = tokens.into_trees().peekable();
         let mut result = Vec::new();
@@ -602,7 +550,7 @@ impl MetaItemKind {
                 MetaItemKind::name_value_from_tokens(&mut inner_tokens.into_trees())
             }
             Some(TokenTree::Token(token, _)) => {
-                Lit::from_token(&token).map(MetaItemKind::NameValue)
+                MetaItemLit::from_token(&token).map(MetaItemKind::NameValue)
             }
             _ => None,
         }
@@ -618,9 +566,9 @@ impl MetaItemKind {
             }) => MetaItemKind::list_from_tokens(tokens.clone()),
             AttrArgs::Delimited(..) => None,
             AttrArgs::Eq(_, AttrArgsEq::Ast(expr)) => match expr.kind {
-                ast::ExprKind::Lit(token_lit) => {
+                ExprKind::Lit(token_lit) => {
                     // Turn failures to `None`, we'll get parse errors elsewhere.
-                    Lit::from_token_lit(token_lit, expr.span)
+                    MetaItemLit::from_token_lit(token_lit, expr.span)
                         .ok()
                         .map(|lit| MetaItemKind::NameValue(lit))
                 }
@@ -653,16 +601,7 @@ impl NestedMetaItem {
     pub fn span(&self) -> Span {
         match self {
             NestedMetaItem::MetaItem(item) => item.span,
-            NestedMetaItem::Literal(lit) => lit.span,
-        }
-    }
-
-    fn token_trees(&self) -> Vec<TokenTree> {
-        match self {
-            NestedMetaItem::MetaItem(item) => item.token_trees(),
-            NestedMetaItem::Literal(lit) => {
-                vec![TokenTree::Token(lit.to_token(), Spacing::Alone)]
-            }
+            NestedMetaItem::Lit(lit) => lit.span,
         }
     }
 
@@ -672,10 +611,10 @@ impl NestedMetaItem {
     {
         match tokens.peek() {
             Some(TokenTree::Token(token, _))
-                if let Some(lit) = Lit::from_token(token) =>
+                if let Some(lit) = MetaItemLit::from_token(token) =>
             {
                 tokens.next();
-                return Some(NestedMetaItem::Literal(lit));
+                return Some(NestedMetaItem::Lit(lit));
             }
             Some(TokenTree::Delimited(_, Delimiter::Invisible, inner_tokens)) => {
                 let inner_tokens = inner_tokens.clone();
diff --git a/compiler/rustc_ast/src/mut_visit.rs b/compiler/rustc_ast/src/mut_visit.rs
index a5b24c403dd..963e5a608a4 100644
--- a/compiler/rustc_ast/src/mut_visit.rs
+++ b/compiler/rustc_ast/src/mut_visit.rs
@@ -410,11 +410,7 @@ pub fn noop_visit_use_tree<T: MutVisitor>(use_tree: &mut UseTree, vis: &mut T) {
     let UseTree { prefix, kind, span } = use_tree;
     vis.visit_path(prefix);
     match kind {
-        UseTreeKind::Simple(rename, id1, id2) => {
-            visit_opt(rename, |rename| vis.visit_ident(rename));
-            vis.visit_id(id1);
-            vis.visit_id(id2);
-        }
+        UseTreeKind::Simple(rename) => visit_opt(rename, |rename| vis.visit_ident(rename)),
         UseTreeKind::Nested(items) => {
             for (tree, id) in items {
                 vis.visit_use_tree(tree);
@@ -628,7 +624,7 @@ pub fn noop_visit_macro_def<T: MutVisitor>(macro_def: &mut MacroDef, vis: &mut T
 pub fn noop_visit_meta_list_item<T: MutVisitor>(li: &mut NestedMetaItem, vis: &mut T) {
     match li {
         NestedMetaItem::MetaItem(mi) => vis.visit_meta_item(mi),
-        NestedMetaItem::Literal(_lit) => {}
+        NestedMetaItem::Lit(_lit) => {}
     }
 }
 
@@ -725,10 +721,10 @@ pub fn visit_lazy_tts<T: MutVisitor>(lazy_tts: &mut Option<LazyAttrTokenStream>,
     visit_lazy_tts_opt_mut(lazy_tts.as_mut(), vis);
 }
 
+/// Applies ident visitor if it's an ident; applies other visits to interpolated nodes.
+/// In practice the ident part is not actually used by specific visitors right now,
+/// but there's a test below checking that it works.
 // No `noop_` prefix because there isn't a corresponding method in `MutVisitor`.
-// Applies ident visitor if it's an ident; applies other visits to interpolated nodes.
-// In practice the ident part is not actually used by specific visitors right now,
-// but there's a test below checking that it works.
 pub fn visit_token<T: MutVisitor>(t: &mut Token, vis: &mut T) {
     let Token { kind, span } = t;
     match kind {
diff --git a/compiler/rustc_ast/src/token.rs b/compiler/rustc_ast/src/token.rs
index cb32925584c..c0cc4e79a3d 100644
--- a/compiler/rustc_ast/src/token.rs
+++ b/compiler/rustc_ast/src/token.rs
@@ -302,9 +302,9 @@ impl TokenKind {
         Literal(Lit::new(kind, symbol, suffix))
     }
 
-    // An approximation to proc-macro-style single-character operators used by rustc parser.
-    // If the operator token can be broken into two tokens, the first of which is single-character,
-    // then this function performs that operation, otherwise it returns `None`.
+    /// An approximation to proc-macro-style single-character operators used by rustc parser.
+    /// If the operator token can be broken into two tokens, the first of which is single-character,
+    /// then this function performs that operation, otherwise it returns `None`.
     pub fn break_two_token_op(&self) -> Option<(TokenKind, TokenKind)> {
         Some(match *self {
             Le => (Lt, Eq),
@@ -538,10 +538,10 @@ impl Token {
         }
     }
 
-    // A convenience function for matching on identifiers during parsing.
-    // Turns interpolated identifier (`$i: ident`) or lifetime (`$l: lifetime`) token
-    // into the regular identifier or lifetime token it refers to,
-    // otherwise returns the original token.
+    /// A convenience function for matching on identifiers during parsing.
+    /// Turns interpolated identifier (`$i: ident`) or lifetime (`$l: lifetime`) token
+    /// into the regular identifier or lifetime token it refers to,
+    /// otherwise returns the original token.
     pub fn uninterpolate(&self) -> Cow<'_, Token> {
         match &self.kind {
             Interpolated(nt) => match **nt {
@@ -621,7 +621,7 @@ impl Token {
         false
     }
 
-    // Is the token an interpolated block (`$b:block`)?
+    /// Is the token an interpolated block (`$b:block`)?
     pub fn is_whole_block(&self) -> bool {
         if let Interpolated(nt) = &self.kind && let NtBlock(..) = **nt {
             return true;
@@ -665,8 +665,8 @@ impl Token {
         self.is_non_raw_ident_where(Ident::is_path_segment_keyword)
     }
 
-    // Returns true for reserved identifiers used internally for elided lifetimes,
-    // unnamed method parameters, crate root module, error recovery etc.
+    /// Returns true for reserved identifiers used internally for elided lifetimes,
+    /// unnamed method parameters, crate root module, error recovery etc.
     pub fn is_special_ident(&self) -> bool {
         self.is_non_raw_ident_where(Ident::is_special)
     }
diff --git a/compiler/rustc_ast/src/tokenstream.rs b/compiler/rustc_ast/src/tokenstream.rs
index 015f5c1ee8a..58c6d397ea2 100644
--- a/compiler/rustc_ast/src/tokenstream.rs
+++ b/compiler/rustc_ast/src/tokenstream.rs
@@ -86,12 +86,12 @@ impl TokenTree {
         }
     }
 
-    // Create a `TokenTree::Token` with alone spacing.
+    /// Create a `TokenTree::Token` with alone spacing.
     pub fn token_alone(kind: TokenKind, span: Span) -> TokenTree {
         TokenTree::Token(Token::new(kind, span), Spacing::Alone)
     }
 
-    // Create a `TokenTree::Token` with joint spacing.
+    /// Create a `TokenTree::Token` with joint spacing.
     pub fn token_joint(kind: TokenKind, span: Span) -> TokenTree {
         TokenTree::Token(Token::new(kind, span), Spacing::Joint)
     }
@@ -413,17 +413,17 @@ impl TokenStream {
         TokenStream(Lrc::new(self.0.iter().enumerate().map(|(i, tree)| f(i, tree)).collect()))
     }
 
-    // Create a token stream containing a single token with alone spacing.
+    /// Create a token stream containing a single token with alone spacing.
     pub fn token_alone(kind: TokenKind, span: Span) -> TokenStream {
         TokenStream::new(vec![TokenTree::token_alone(kind, span)])
     }
 
-    // Create a token stream containing a single token with joint spacing.
+    /// Create a token stream containing a single token with joint spacing.
     pub fn token_joint(kind: TokenKind, span: Span) -> TokenStream {
         TokenStream::new(vec![TokenTree::token_joint(kind, span)])
     }
 
-    // Create a token stream containing a single `Delimited`.
+    /// Create a token stream containing a single `Delimited`.
     pub fn delimited(span: DelimSpan, delim: Delimiter, tts: TokenStream) -> TokenStream {
         TokenStream::new(vec![TokenTree::Delimited(span, delim, tts)])
     }
@@ -522,8 +522,8 @@ impl TokenStream {
         }
     }
 
-    // Push `tt` onto the end of the stream, possibly gluing it to the last
-    // token. Uses `make_mut` to maximize efficiency.
+    /// Push `tt` onto the end of the stream, possibly gluing it to the last
+    /// token. Uses `make_mut` to maximize efficiency.
     pub fn push_tree(&mut self, tt: TokenTree) {
         let vec_mut = Lrc::make_mut(&mut self.0);
 
@@ -534,9 +534,9 @@ impl TokenStream {
         }
     }
 
-    // Push `stream` onto the end of the stream, possibly gluing the first
-    // token tree to the last token. (No other token trees will be glued.)
-    // Uses `make_mut` to maximize efficiency.
+    /// Push `stream` onto the end of the stream, possibly gluing the first
+    /// token tree to the last token. (No other token trees will be glued.)
+    /// Uses `make_mut` to maximize efficiency.
     pub fn push_stream(&mut self, stream: TokenStream) {
         let vec_mut = Lrc::make_mut(&mut self.0);
 
diff --git a/compiler/rustc_ast/src/util/literal.rs b/compiler/rustc_ast/src/util/literal.rs
index db2ac9626af..1d6e7914f3a 100644
--- a/compiler/rustc_ast/src/util/literal.rs
+++ b/compiler/rustc_ast/src/util/literal.rs
@@ -1,8 +1,7 @@
 //! Code related to parsing literals.
 
-use crate::ast::{self, Lit, LitKind};
+use crate::ast::{self, LitKind, MetaItemLit};
 use crate::token::{self, Token};
-use rustc_data_structures::sync::Lrc;
 use rustc_lexer::unescape::{byte_from_char, unescape_byte, unescape_char, unescape_literal, Mode};
 use rustc_span::symbol::{kw, sym, Symbol};
 use rustc_span::Span;
@@ -196,39 +195,16 @@ impl LitKind {
     }
 }
 
-impl Lit {
-    /// Converts literal token into an AST literal.
-    pub fn from_token_lit(token_lit: token::Lit, span: Span) -> Result<Lit, LitError> {
-        Ok(Lit { token_lit, kind: LitKind::from_token_lit(token_lit)?, span })
+impl MetaItemLit {
+    /// Converts token literal into a meta item literal.
+    pub fn from_token_lit(token_lit: token::Lit, span: Span) -> Result<MetaItemLit, LitError> {
+        Ok(MetaItemLit { token_lit, kind: LitKind::from_token_lit(token_lit)?, span })
     }
 
-    /// Converts an arbitrary token into an AST literal.
-    pub fn from_token(token: &Token) -> Option<Lit> {
+    /// Converts an arbitrary token into meta item literal.
+    pub fn from_token(token: &Token) -> Option<MetaItemLit> {
         token::Lit::from_token(token)
-            .and_then(|token_lit| Lit::from_token_lit(token_lit, token.span).ok())
-    }
-
-    /// Attempts to recover an AST literal from semantic literal.
-    /// This function is used when the original token doesn't exist (e.g. the literal is created
-    /// by an AST-based macro) or unavailable (e.g. from HIR pretty-printing).
-    pub fn from_lit_kind(kind: LitKind, span: Span) -> Lit {
-        Lit { token_lit: kind.to_token_lit(), kind, span }
-    }
-
-    /// Recovers an AST literal from a string of bytes produced by `include_bytes!`.
-    /// This requires ASCII-escaping the string, which can result in poor performance
-    /// for very large strings of bytes.
-    pub fn from_included_bytes(bytes: &Lrc<[u8]>, span: Span) -> Lit {
-        Self::from_lit_kind(LitKind::ByteStr(bytes.clone()), span)
-    }
-
-    /// Losslessly convert an AST literal into a token.
-    pub fn to_token(&self) -> Token {
-        let kind = match self.token_lit.kind {
-            token::Bool => token::Ident(self.token_lit.symbol, false),
-            _ => token::Literal(self.token_lit),
-        };
-        Token::new(kind, self.span)
+            .and_then(|token_lit| MetaItemLit::from_token_lit(token_lit, token.span).ok())
     }
 }
 
diff --git a/compiler/rustc_ast/src/visit.rs b/compiler/rustc_ast/src/visit.rs
index c528118be08..fe27d7fa8de 100644
--- a/compiler/rustc_ast/src/visit.rs
+++ b/compiler/rustc_ast/src/visit.rs
@@ -439,7 +439,7 @@ pub fn walk_path<'a, V: Visitor<'a>>(visitor: &mut V, path: &'a Path) {
 pub fn walk_use_tree<'a, V: Visitor<'a>>(visitor: &mut V, use_tree: &'a UseTree, id: NodeId) {
     visitor.visit_path(&use_tree.prefix, id);
     match &use_tree.kind {
-        UseTreeKind::Simple(rename, ..) => {
+        UseTreeKind::Simple(rename) => {
             // The extra IDs are handled during HIR lowering.
             if let &Some(rename) = rename {
                 visitor.visit_ident(rename);
diff --git a/compiler/rustc_ast_lowering/src/expr.rs b/compiler/rustc_ast_lowering/src/expr.rs
index c14c591d387..82912a733d5 100644
--- a/compiler/rustc_ast_lowering/src/expr.rs
+++ b/compiler/rustc_ast_lowering/src/expr.rs
@@ -1606,16 +1606,13 @@ impl<'hir> LoweringContext<'_, 'hir> {
         };
 
         // `#[allow(unreachable_code)]`
-        let attr = {
-            // `allow(unreachable_code)`
-            let allow = {
-                let allow_ident = Ident::new(sym::allow, self.lower_span(span));
-                let uc_ident = Ident::new(sym::unreachable_code, self.lower_span(span));
-                let uc_nested = attr::mk_nested_word_item(uc_ident);
-                attr::mk_list_item(allow_ident, vec![uc_nested])
-            };
-            attr::mk_attr_outer(&self.tcx.sess.parse_sess.attr_id_generator, allow)
-        };
+        let attr = attr::mk_attr_nested_word(
+            &self.tcx.sess.parse_sess.attr_id_generator,
+            AttrStyle::Outer,
+            sym::allow,
+            sym::unreachable_code,
+            self.lower_span(span),
+        );
         let attrs: AttrVec = thin_vec![attr];
 
         // `ControlFlow::Continue(val) => #[allow(unreachable_code)] val,`
diff --git a/compiler/rustc_ast_lowering/src/index.rs b/compiler/rustc_ast_lowering/src/index.rs
index 9def8536c82..fe0bd43815d 100644
--- a/compiler/rustc_ast_lowering/src/index.rs
+++ b/compiler/rustc_ast_lowering/src/index.rs
@@ -77,7 +77,7 @@ impl<'a, 'hir> NodeCollector<'a, 'hir> {
             if hir_id.owner != self.owner {
                 span_bug!(
                     span,
-                    "inconsistent DepNode at `{:?}` for `{:?}`: \
+                    "inconsistent HirId at `{:?}` for `{:?}`: \
                      current_dep_node_owner={} ({:?}), hir_id.owner={} ({:?})",
                     self.source_map.span_to_diagnostic_string(span),
                     node,
diff --git a/compiler/rustc_ast_lowering/src/item.rs b/compiler/rustc_ast_lowering/src/item.rs
index 2b47e908912..f6275433fc5 100644
--- a/compiler/rustc_ast_lowering/src/item.rs
+++ b/compiler/rustc_ast_lowering/src/item.rs
@@ -19,7 +19,6 @@ use rustc_span::symbol::{kw, sym, Ident};
 use rustc_span::{Span, Symbol};
 use rustc_target::spec::abi;
 use smallvec::{smallvec, SmallVec};
-use std::iter;
 use thin_vec::ThinVec;
 
 pub(super) struct ItemLowerer<'a, 'hir> {
@@ -179,36 +178,22 @@ impl<'hir> LoweringContext<'_, 'hir> {
         let mut node_ids =
             smallvec![hir::ItemId { owner_id: hir::OwnerId { def_id: self.local_def_id(i.id) } }];
         if let ItemKind::Use(use_tree) = &i.kind {
-            self.lower_item_id_use_tree(use_tree, i.id, &mut node_ids);
+            self.lower_item_id_use_tree(use_tree, &mut node_ids);
         }
         node_ids
     }
 
-    fn lower_item_id_use_tree(
-        &mut self,
-        tree: &UseTree,
-        base_id: NodeId,
-        vec: &mut SmallVec<[hir::ItemId; 1]>,
-    ) {
+    fn lower_item_id_use_tree(&mut self, tree: &UseTree, vec: &mut SmallVec<[hir::ItemId; 1]>) {
         match &tree.kind {
             UseTreeKind::Nested(nested_vec) => {
                 for &(ref nested, id) in nested_vec {
                     vec.push(hir::ItemId {
                         owner_id: hir::OwnerId { def_id: self.local_def_id(id) },
                     });
-                    self.lower_item_id_use_tree(nested, id, vec);
-                }
-            }
-            UseTreeKind::Glob => {}
-            UseTreeKind::Simple(_, id1, id2) => {
-                for (_, id) in
-                    iter::zip(self.expect_full_res_from_use(base_id).skip(1), [*id1, *id2])
-                {
-                    vec.push(hir::ItemId {
-                        owner_id: hir::OwnerId { def_id: self.local_def_id(id) },
-                    });
+                    self.lower_item_id_use_tree(nested, vec);
                 }
             }
+            UseTreeKind::Simple(..) | UseTreeKind::Glob => {}
         }
     }
 
@@ -489,7 +474,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
         let segments = prefix.segments.iter().chain(path.segments.iter()).cloned().collect();
 
         match tree.kind {
-            UseTreeKind::Simple(rename, id1, id2) => {
+            UseTreeKind::Simple(rename) => {
                 *ident = tree.ident();
 
                 // First, apply the prefix to the path.
@@ -505,66 +490,16 @@ impl<'hir> LoweringContext<'_, 'hir> {
                     }
                 }
 
-                let mut resolutions = self.expect_full_res_from_use(id).fuse();
-                // We want to return *something* from this function, so hold onto the first item
-                // for later.
-                let ret_res = self.lower_res(resolutions.next().unwrap_or(Res::Err));
-
-                // Here, we are looping over namespaces, if they exist for the definition
-                // being imported. We only handle type and value namespaces because we
-                // won't be dealing with macros in the rest of the compiler.
-                // Essentially a single `use` which imports two names is desugared into
-                // two imports.
-                for new_node_id in [id1, id2] {
-                    let new_id = self.local_def_id(new_node_id);
-                    let Some(res) = resolutions.next() else {
-                        debug_assert!(self.children.iter().find(|(id, _)| id == &new_id).is_none());
-                        // Associate an HirId to both ids even if there is no resolution.
-                        self.children.push((
-                            new_id,
-                            hir::MaybeOwner::NonOwner(hir::HirId::make_owner(new_id))),
-                        );
-                        continue;
-                    };
-                    let ident = *ident;
-                    let mut path = path.clone();
-                    for seg in &mut path.segments {
-                        // Give the cloned segment the same resolution information
-                        // as the old one (this is needed for stability checking).
-                        let new_id = self.next_node_id();
-                        self.resolver.clone_res(seg.id, new_id);
-                        seg.id = new_id;
-                    }
-                    let span = path.span;
-
-                    self.with_hir_id_owner(new_node_id, |this| {
-                        let res = this.lower_res(res);
-                        let path = this.lower_path_extra(res, &path, ParamMode::Explicit);
-                        let kind = hir::ItemKind::Use(path, hir::UseKind::Single);
-                        if let Some(attrs) = attrs {
-                            this.attrs.insert(hir::ItemLocalId::new(0), attrs);
-                        }
-
-                        let item = hir::Item {
-                            owner_id: hir::OwnerId { def_id: new_id },
-                            ident: this.lower_ident(ident),
-                            kind,
-                            vis_span,
-                            span: this.lower_span(span),
-                        };
-                        hir::OwnerNode::Item(this.arena.alloc(item))
-                    });
-                }
-
-                let path = self.lower_path_extra(ret_res, &path, ParamMode::Explicit);
+                let res =
+                    self.expect_full_res_from_use(id).map(|res| self.lower_res(res)).collect();
+                let path = self.lower_use_path(res, &path, ParamMode::Explicit);
                 hir::ItemKind::Use(path, hir::UseKind::Single)
             }
             UseTreeKind::Glob => {
-                let path = self.lower_path(
-                    id,
-                    &Path { segments, span: path.span, tokens: None },
-                    ParamMode::Explicit,
-                );
+                let res = self.expect_full_res(id);
+                let res = smallvec![self.lower_res(res)];
+                let path = Path { segments, span: path.span, tokens: None };
+                let path = self.lower_use_path(res, &path, ParamMode::Explicit);
                 hir::ItemKind::Use(path, hir::UseKind::Glob)
             }
             UseTreeKind::Nested(ref trees) => {
@@ -634,9 +569,9 @@ impl<'hir> LoweringContext<'_, 'hir> {
                     });
                 }
 
-                let res = self.expect_full_res_from_use(id).next().unwrap_or(Res::Err);
-                let res = self.lower_res(res);
-                let path = self.lower_path_extra(res, &prefix, ParamMode::Explicit);
+                let res =
+                    self.expect_full_res_from_use(id).map(|res| self.lower_res(res)).collect();
+                let path = self.lower_use_path(res, &prefix, ParamMode::Explicit);
                 hir::ItemKind::Use(path, hir::UseKind::ListStem)
             }
         }
diff --git a/compiler/rustc_ast_lowering/src/lib.rs b/compiler/rustc_ast_lowering/src/lib.rs
index a123a58a8fb..1d279706278 100644
--- a/compiler/rustc_ast_lowering/src/lib.rs
+++ b/compiler/rustc_ast_lowering/src/lib.rs
@@ -497,7 +497,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
             self.tcx.hir().def_key(self.local_def_id(node_id)),
         );
 
-        let def_id = self.tcx.create_def(parent, data);
+        let def_id = self.tcx.create_def(parent, data).def_id();
 
         debug!("create_def: def_id_to_node_id[{:?}] <-> {:?}", def_id, node_id);
         self.resolver.node_id_to_def_id.insert(node_id, def_id);
@@ -948,17 +948,12 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
             AttrArgs::Eq(eq_span, AttrArgsEq::Ast(expr)) => {
                 // In valid code the value always ends up as a single literal. Otherwise, a dummy
                 // literal suffices because the error is handled elsewhere.
-                let lit = if let ExprKind::Lit(token_lit) = expr.kind {
-                    match Lit::from_token_lit(token_lit, expr.span) {
-                        Ok(lit) => lit,
-                        Err(_err) => Lit {
-                            token_lit: token::Lit::new(token::LitKind::Err, kw::Empty, None),
-                            kind: LitKind::Err,
-                            span: DUMMY_SP,
-                        },
-                    }
+                let lit = if let ExprKind::Lit(token_lit) = expr.kind
+                    && let Ok(lit) = MetaItemLit::from_token_lit(token_lit, expr.span)
+                {
+                    lit
                 } else {
-                    Lit {
+                    MetaItemLit {
                         token_lit: token::Lit::new(token::LitKind::Err, kw::Empty, None),
                         kind: LitKind::Err,
                         span: DUMMY_SP,
diff --git a/compiler/rustc_ast_lowering/src/path.rs b/compiler/rustc_ast_lowering/src/path.rs
index dc85b5e95ea..8d23c26e603 100644
--- a/compiler/rustc_ast_lowering/src/path.rs
+++ b/compiler/rustc_ast_lowering/src/path.rs
@@ -12,7 +12,7 @@ use rustc_hir::GenericArg;
 use rustc_span::symbol::{kw, Ident};
 use rustc_span::{BytePos, Span, DUMMY_SP};
 
-use smallvec::smallvec;
+use smallvec::{smallvec, SmallVec};
 
 impl<'a, 'hir> LoweringContext<'a, 'hir> {
     #[instrument(level = "trace", skip(self))]
@@ -144,13 +144,13 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
         );
     }
 
-    pub(crate) fn lower_path_extra(
+    pub(crate) fn lower_use_path(
         &mut self,
-        res: Res,
+        res: SmallVec<[Res; 3]>,
         p: &Path,
         param_mode: ParamMode,
-    ) -> &'hir hir::Path<'hir> {
-        self.arena.alloc(hir::Path {
+    ) -> &'hir hir::UsePath<'hir> {
+        self.arena.alloc(hir::UsePath {
             res,
             segments: self.arena.alloc_from_iter(p.segments.iter().map(|segment| {
                 self.lower_path_segment(
@@ -165,17 +165,6 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
         })
     }
 
-    pub(crate) fn lower_path(
-        &mut self,
-        id: NodeId,
-        p: &Path,
-        param_mode: ParamMode,
-    ) -> &'hir hir::Path<'hir> {
-        let res = self.expect_full_res(id);
-        let res = self.lower_res(res);
-        self.lower_path_extra(res, p, param_mode)
-    }
-
     pub(crate) fn lower_path_segment(
         &mut self,
         path_span: Span,
diff --git a/compiler/rustc_ast_passes/src/ast_validation.rs b/compiler/rustc_ast_passes/src/ast_validation.rs
index acd7eb69ffc..eb9c841d80c 100644
--- a/compiler/rustc_ast_passes/src/ast_validation.rs
+++ b/compiler/rustc_ast_passes/src/ast_validation.rs
@@ -209,7 +209,7 @@ impl<'a> AstValidator<'a> {
 
     // Mirrors `visit::walk_ty`, but tracks relevant state.
     fn walk_ty(&mut self, t: &'a Ty) {
-        match t.kind {
+        match &t.kind {
             TyKind::ImplTrait(..) => {
                 self.with_impl_trait(Some(t.span), |this| visit::walk_ty(this, t))
             }
@@ -217,7 +217,7 @@ impl<'a> AstValidator<'a> {
                 .with_banned_tilde_const(DisallowTildeConstContext::TraitObject, |this| {
                     visit::walk_ty(this, t)
                 }),
-            TyKind::Path(ref qself, ref path) => {
+            TyKind::Path(qself, path) => {
                 // We allow these:
                 //  - `Option<impl Trait>`
                 //  - `option::Option<impl Trait>`
@@ -231,7 +231,7 @@ impl<'a> AstValidator<'a> {
                 // (for cases like `<impl Trait>::Foo>`)
                 // but we allow `impl Trait` in `GenericArgs`
                 // iff there are no more PathSegments.
-                if let Some(ref qself) = *qself {
+                if let Some(qself) = qself {
                     // `impl Trait` in `qself` is always illegal
                     self.with_banned_impl_trait(|this| this.visit_ty(&qself.ty));
                 }
@@ -738,8 +738,8 @@ impl<'a> AstValidator<'a> {
     }
 
     fn visit_ty_common(&mut self, ty: &'a Ty) {
-        match ty.kind {
-            TyKind::BareFn(ref bfty) => {
+        match &ty.kind {
+            TyKind::BareFn(bfty) => {
                 self.check_fn_decl(&bfty.decl, SelfSemantic::No);
                 Self::check_decl_no_pat(&bfty.decl, |span, _, _| {
                     struct_span_err!(
@@ -756,10 +756,10 @@ impl<'a> AstValidator<'a> {
                     self.maybe_lint_missing_abi(sig_span, ty.id);
                 }
             }
-            TyKind::TraitObject(ref bounds, ..) => {
+            TyKind::TraitObject(bounds, ..) => {
                 let mut any_lifetime_bounds = false;
                 for bound in bounds {
-                    if let GenericBound::Outlives(ref lifetime) = *bound {
+                    if let GenericBound::Outlives(lifetime) = bound {
                         if any_lifetime_bounds {
                             struct_span_err!(
                                 self.session,
@@ -774,7 +774,7 @@ impl<'a> AstValidator<'a> {
                     }
                 }
             }
-            TyKind::ImplTrait(_, ref bounds) => {
+            TyKind::ImplTrait(_, bounds) => {
                 if self.is_impl_trait_banned {
                     struct_span_err!(
                         self.session,
@@ -842,8 +842,8 @@ fn validate_generic_param_order(
         let (kind, bounds, span) = (&param.kind, &param.bounds, ident.span);
         let (ord_kind, ident) = match &param.kind {
             GenericParamKind::Lifetime => (ParamKindOrd::Lifetime, ident.to_string()),
-            GenericParamKind::Type { default: _ } => (ParamKindOrd::TypeOrConst, ident.to_string()),
-            GenericParamKind::Const { ref ty, kw_span: _, default: _ } => {
+            GenericParamKind::Type { .. } => (ParamKindOrd::TypeOrConst, ident.to_string()),
+            GenericParamKind::Const { ty, .. } => {
                 let ty = pprust::ty_to_string(ty);
                 (ParamKindOrd::TypeOrConst, format!("const {}: {}", ident, ty))
             }
@@ -912,7 +912,7 @@ fn validate_generic_param_order(
 
 impl<'a> Visitor<'a> for AstValidator<'a> {
     fn visit_attribute(&mut self, attr: &Attribute) {
-        validate_attr::check_meta(&self.session.parse_sess, attr);
+        validate_attr::check_attr(&self.session.parse_sess, attr);
     }
 
     fn visit_expr(&mut self, expr: &'a Expr) {
@@ -948,8 +948,8 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
                 }
                 ExprKind::Paren(local_expr) => {
                     fn has_let_expr(expr: &Expr) -> bool {
-                        match expr.kind {
-                            ExprKind::Binary(_, ref lhs, ref rhs) => has_let_expr(lhs) || has_let_expr(rhs),
+                        match &expr.kind {
+                            ExprKind::Binary(_, lhs, rhs) => has_let_expr(lhs) || has_let_expr(rhs),
                             ExprKind::Let(..) => true,
                             _ => false,
                         }
@@ -1005,18 +1005,18 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
             self.check_nomangle_item_asciionly(item.ident, item.span);
         }
 
-        match item.kind {
+        match &item.kind {
             ItemKind::Impl(box Impl {
                 unsafety,
                 polarity,
                 defaultness: _,
                 constness,
-                ref generics,
-                of_trait: Some(ref t),
-                ref self_ty,
-                ref items,
+                generics,
+                of_trait: Some(t),
+                self_ty,
+                items,
             }) => {
-                self.with_in_trait_impl(true, Some(constness), |this| {
+                self.with_in_trait_impl(true, Some(*constness), |this| {
                     this.invalid_visibility(&item.vis, None);
                     if let TyKind::Err = self_ty.kind {
                         this.err_handler()
@@ -1027,7 +1027,8 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
                             .help("use `auto trait Trait {}` instead")
                             .emit();
                     }
-                    if let (Unsafe::Yes(span), ImplPolarity::Negative(sp)) = (unsafety, polarity) {
+                    if let (&Unsafe::Yes(span), &ImplPolarity::Negative(sp)) = (unsafety, polarity)
+                    {
                         struct_span_err!(
                             this.session,
                             sp.to(t.path.span),
@@ -1061,7 +1062,7 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
                 constness,
                 generics: _,
                 of_trait: None,
-                ref self_ty,
+                self_ty,
                 items: _,
             }) => {
                 let error = |annotation_span, annotation| {
@@ -1078,25 +1079,25 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
                     &item.vis,
                     Some(InvalidVisibilityNote::IndividualImplItems),
                 );
-                if let Unsafe::Yes(span) = unsafety {
+                if let &Unsafe::Yes(span) = unsafety {
                     error(span, "unsafe").code(error_code!(E0197)).emit();
                 }
-                if let ImplPolarity::Negative(span) = polarity {
+                if let &ImplPolarity::Negative(span) = polarity {
                     error(span, "negative").emit();
                 }
-                if let Defaultness::Default(def_span) = defaultness {
+                if let &Defaultness::Default(def_span) = defaultness {
                     error(def_span, "`default`")
                         .note("only trait implementations may be annotated with `default`")
                         .emit();
                 }
-                if let Const::Yes(span) = constness {
+                if let &Const::Yes(span) = constness {
                     error(span, "`const`")
                         .note("only trait implementations may be annotated with `const`")
                         .emit();
                 }
             }
-            ItemKind::Fn(box Fn { defaultness, ref sig, ref generics, ref body }) => {
-                self.check_defaultness(item.span, defaultness);
+            ItemKind::Fn(box Fn { defaultness, sig, generics, body }) => {
+                self.check_defaultness(item.span, *defaultness);
 
                 if body.is_none() {
                     self.session.emit_err(FnWithoutBody {
@@ -1132,7 +1133,7 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
                     &item.vis,
                     Some(InvalidVisibilityNote::IndividualForeignItems),
                 );
-                if let Unsafe::Yes(span) = unsafety {
+                if let &Unsafe::Yes(span) = unsafety {
                     self.err_handler().span_err(span, "extern block cannot be declared unsafe");
                 }
                 if abi.is_none() {
@@ -1142,7 +1143,7 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
                 self.extern_mod = old_item;
                 return; // Avoid visiting again.
             }
-            ItemKind::Enum(ref def, _) => {
+            ItemKind::Enum(def, _) => {
                 for variant in &def.variants {
                     self.invalid_visibility(&variant.vis, None);
                     for field in variant.data.fields() {
@@ -1150,8 +1151,8 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
                     }
                 }
             }
-            ItemKind::Trait(box Trait { is_auto, ref generics, ref bounds, ref items, .. }) => {
-                if is_auto == IsAuto::Yes {
+            ItemKind::Trait(box Trait { is_auto, generics, bounds, items, .. }) => {
+                if *is_auto == IsAuto::Yes {
                     // Auto traits cannot have generics, super traits nor contain items.
                     self.deny_generic_params(generics, item.ident.span);
                     self.deny_super_traits(bounds, item.ident.span);
@@ -1171,8 +1172,8 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
                 walk_list!(self, visit_attribute, &item.attrs);
                 return; // Avoid visiting again
             }
-            ItemKind::Mod(unsafety, ref mod_kind) => {
-                if let Unsafe::Yes(span) = unsafety {
+            ItemKind::Mod(unsafety, mod_kind) => {
+                if let &Unsafe::Yes(span) = unsafety {
                     self.err_handler().span_err(span, "module cannot be declared unsafe");
                 }
                 // Ensure that `path` attributes on modules are recorded as used (cf. issue #35584).
@@ -1182,13 +1183,13 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
                     self.check_mod_file_item_asciionly(item.ident);
                 }
             }
-            ItemKind::Union(ref vdata, ..) => {
+            ItemKind::Union(vdata, ..) => {
                 if vdata.fields().is_empty() {
                     self.err_handler().span_err(item.span, "unions cannot have zero fields");
                 }
             }
             ItemKind::Const(def, .., None) => {
-                self.check_defaultness(item.span, def);
+                self.check_defaultness(item.span, *def);
                 self.session.emit_err(ConstWithoutBody {
                     span: item.span,
                     replace_span: self.ending_semi_or_hi(item.span),
@@ -1200,14 +1201,8 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
                     replace_span: self.ending_semi_or_hi(item.span),
                 });
             }
-            ItemKind::TyAlias(box TyAlias {
-                defaultness,
-                where_clauses,
-                ref bounds,
-                ref ty,
-                ..
-            }) => {
-                self.check_defaultness(item.span, defaultness);
+            ItemKind::TyAlias(box TyAlias { defaultness, where_clauses, bounds, ty, .. }) => {
+                self.check_defaultness(item.span, *defaultness);
                 if ty.is_none() {
                     self.session.emit_err(TyAliasWithoutBody {
                         span: item.span,
@@ -1266,8 +1261,8 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
 
     // Mirrors `visit::walk_generic_args`, but tracks relevant state.
     fn visit_generic_args(&mut self, generic_args: &'a GenericArgs) {
-        match *generic_args {
-            GenericArgs::AngleBracketed(ref data) => {
+        match generic_args {
+            GenericArgs::AngleBracketed(data) => {
                 self.check_generic_args_before_constraints(data);
 
                 for arg in &data.args {
@@ -1283,7 +1278,7 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
                     }
                 }
             }
-            GenericArgs::Parenthesized(ref data) => {
+            GenericArgs::Parenthesized(data) => {
                 walk_list!(self, visit_ty, &data.inputs);
                 if let FnRetTy::Ty(ty) = &data.output {
                     // `-> Foo` syntax is essentially an associated type binding,
@@ -1319,7 +1314,7 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
         validate_generic_param_order(self.err_handler(), &generics.params, generics.span);
 
         for predicate in &generics.where_clause.predicates {
-            if let WherePredicate::EqPredicate(ref predicate) = *predicate {
+            if let WherePredicate::EqPredicate(predicate) = predicate {
                 deny_equality_constraints(self, predicate, generics);
             }
         }
@@ -1368,7 +1363,7 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
     }
 
     fn visit_param_bound(&mut self, bound: &'a GenericBound, ctxt: BoundKind) {
-        if let GenericBound::Trait(ref poly, modify) = *bound {
+        if let GenericBound::Trait(poly, modify) = bound {
             match (ctxt, modify) {
                 (BoundKind::SuperTraits, TraitBoundModifier::Maybe) => {
                     let mut err = self
@@ -1573,8 +1568,8 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
             self.check_item_named(item.ident, "const");
         }
 
-        match item.kind {
-            AssocItemKind::Type(box TyAlias { ref generics, ref bounds, ref ty, .. })
+        match &item.kind {
+            AssocItemKind::Type(box TyAlias { generics, bounds, ty, .. })
                 if ctxt == AssocCtxt::Trait =>
             {
                 self.visit_vis(&item.vis);
@@ -1586,7 +1581,7 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
                 });
                 walk_list!(self, visit_ty, ty);
             }
-            AssocItemKind::Fn(box Fn { ref sig, ref generics, ref body, .. })
+            AssocItemKind::Fn(box Fn { sig, generics, body, .. })
                 if self.in_const_trait_impl
                     || ctxt == AssocCtxt::Trait
                     || matches!(sig.header.constness, Const::Yes(_)) =>
diff --git a/compiler/rustc_ast_passes/src/feature_gate.rs b/compiler/rustc_ast_passes/src/feature_gate.rs
index 546010135a7..32f45f8b59e 100644
--- a/compiler/rustc_ast_passes/src/feature_gate.rs
+++ b/compiler/rustc_ast_passes/src/feature_gate.rs
@@ -198,8 +198,8 @@ impl<'a> Visitor<'a> for PostExpansionVisitor<'a> {
     }
 
     fn visit_item(&mut self, i: &'a ast::Item) {
-        match i.kind {
-            ast::ItemKind::ForeignMod(ref foreign_module) => {
+        match &i.kind {
+            ast::ItemKind::ForeignMod(foreign_module) => {
                 if let Some(abi) = foreign_module.abi {
                     self.check_abi(abi, ast::Const::No);
                 }
@@ -233,8 +233,8 @@ impl<'a> Visitor<'a> for PostExpansionVisitor<'a> {
                 }
             }
 
-            ast::ItemKind::Impl(box ast::Impl { polarity, defaultness, ref of_trait, .. }) => {
-                if let ast::ImplPolarity::Negative(span) = polarity {
+            ast::ItemKind::Impl(box ast::Impl { polarity, defaultness, of_trait, .. }) => {
+                if let &ast::ImplPolarity::Negative(span) = polarity {
                     gate_feature_post!(
                         &self,
                         negative_impls,
@@ -267,7 +267,7 @@ impl<'a> Visitor<'a> for PostExpansionVisitor<'a> {
                 gate_feature_post!(&self, decl_macro, i.span, msg);
             }
 
-            ast::ItemKind::TyAlias(box ast::TyAlias { ty: Some(ref ty), .. }) => {
+            ast::ItemKind::TyAlias(box ast::TyAlias { ty: Some(ty), .. }) => {
                 self.check_impl_trait(&ty)
             }
 
@@ -302,8 +302,8 @@ impl<'a> Visitor<'a> for PostExpansionVisitor<'a> {
     }
 
     fn visit_ty(&mut self, ty: &'a ast::Ty) {
-        match ty.kind {
-            ast::TyKind::BareFn(ref bare_fn_ty) => {
+        match &ty.kind {
+            ast::TyKind::BareFn(bare_fn_ty) => {
                 // Function pointers cannot be `const`
                 self.check_extern(bare_fn_ty.ext, ast::Const::No);
             }
@@ -319,7 +319,7 @@ impl<'a> Visitor<'a> for PostExpansionVisitor<'a> {
     }
 
     fn visit_fn_ret_ty(&mut self, ret_ty: &'a ast::FnRetTy) {
-        if let ast::FnRetTy::Ty(ref output_ty) = *ret_ty {
+        if let ast::FnRetTy::Ty(output_ty) = ret_ty {
             if let ast::TyKind::Never = output_ty.kind {
                 // Do nothing.
             } else {
@@ -455,9 +455,9 @@ impl<'a> Visitor<'a> for PostExpansionVisitor<'a> {
     }
 
     fn visit_assoc_item(&mut self, i: &'a ast::AssocItem, ctxt: AssocCtxt) {
-        let is_fn = match i.kind {
+        let is_fn = match &i.kind {
             ast::AssocItemKind::Fn(_) => true,
-            ast::AssocItemKind::Type(box ast::TyAlias { ref ty, .. }) => {
+            ast::AssocItemKind::Type(box ast::TyAlias { ty, .. }) => {
                 if let (Some(_), AssocCtxt::Trait) = (ty, ctxt) {
                     gate_feature_post!(
                         &self,
diff --git a/compiler/rustc_ast_pretty/src/helpers.rs b/compiler/rustc_ast_pretty/src/helpers.rs
index 5ec71cddf7d..c3e0eccd3d4 100644
--- a/compiler/rustc_ast_pretty/src/helpers.rs
+++ b/compiler/rustc_ast_pretty/src/helpers.rs
@@ -36,8 +36,8 @@ impl Printer {
         self.nbsp()
     }
 
-    // Synthesizes a comment that was not textually present in the original
-    // source file.
+    /// Synthesizes a comment that was not textually present in the original
+    /// source file.
     pub fn synth_comment(&mut self, text: impl Into<Cow<'static, str>>) {
         self.word("/*");
         self.space();
diff --git a/compiler/rustc_ast_pretty/src/pprust/state.rs b/compiler/rustc_ast_pretty/src/pprust/state.rs
index 991f6e0ba22..ebe55a4b771 100644
--- a/compiler/rustc_ast_pretty/src/pprust/state.rs
+++ b/compiler/rustc_ast_pretty/src/pprust/state.rs
@@ -11,7 +11,7 @@ use rustc_ast::tokenstream::{TokenStream, TokenTree};
 use rustc_ast::util::classify;
 use rustc_ast::util::comments::{gather_comments, Comment, CommentStyle};
 use rustc_ast::util::parser;
-use rustc_ast::{self as ast, AttrArgs, AttrArgsEq, BlockCheckMode, Mutability, PatKind};
+use rustc_ast::{self as ast, AttrArgs, AttrArgsEq, BlockCheckMode, PatKind};
 use rustc_ast::{attr, BindingAnnotation, ByRef, DelimArgs, RangeEnd, RangeSyntax, Term};
 use rustc_ast::{GenericArg, GenericBound, SelfKind, TraitBoundModifier};
 use rustc_ast::{InlineAsmOperand, InlineAsmRegOrRegClass};
@@ -19,7 +19,7 @@ use rustc_ast::{InlineAsmOptions, InlineAsmTemplatePiece};
 use rustc_span::edition::Edition;
 use rustc_span::source_map::{SourceMap, Spanned};
 use rustc_span::symbol::{kw, sym, Ident, IdentPrinter, Symbol};
-use rustc_span::{BytePos, FileName, Span};
+use rustc_span::{BytePos, FileName, Span, DUMMY_SP};
 
 use rustc_ast::attr::AttrIdGenerator;
 use std::borrow::Cow;
@@ -64,6 +64,7 @@ impl<'a> Comments<'a> {
         Comments { sm, comments, current: 0 }
     }
 
+    // FIXME: This shouldn't probably clone lmao
     pub fn next(&self) -> Option<Comment> {
         self.comments.get(self.current).cloned()
     }
@@ -119,17 +120,20 @@ pub fn print_crate<'a>(
         // of the feature gate, so we fake them up here.
 
         // `#![feature(prelude_import)]`
-        let pi_nested = attr::mk_nested_word_item(Ident::with_dummy_span(sym::prelude_import));
-        let list = attr::mk_list_item(Ident::with_dummy_span(sym::feature), vec![pi_nested]);
-        let fake_attr = attr::mk_attr_inner(g, list);
+        let fake_attr = attr::mk_attr_nested_word(
+            g,
+            ast::AttrStyle::Inner,
+            sym::feature,
+            sym::prelude_import,
+            DUMMY_SP,
+        );
         s.print_attribute(&fake_attr);
 
         // Currently, in Rust 2018 we don't have `extern crate std;` at the crate
         // root, so this is not needed, and actually breaks things.
         if edition == Edition::Edition2015 {
             // `#![no_std]`
-            let no_std_meta = attr::mk_word_item(Ident::with_dummy_span(sym::no_std));
-            let fake_attr = attr::mk_attr_inner(g, no_std_meta);
+            let fake_attr = attr::mk_attr_word(g, ast::AttrStyle::Inner, sym::no_std, DUMMY_SP);
             s.print_attribute(&fake_attr);
         }
     }
@@ -268,10 +272,10 @@ pub trait PrintState<'a>: std::ops::Deref<Target = pp::Printer> + std::ops::Dere
 
     fn maybe_print_comment(&mut self, pos: BytePos) -> bool {
         let mut has_comment = false;
-        while let Some(ref cmnt) = self.next_comment() {
+        while let Some(cmnt) = self.next_comment() {
             if cmnt.pos < pos {
                 has_comment = true;
-                self.print_comment(cmnt);
+                self.print_comment(&cmnt);
             } else {
                 break;
             }
@@ -366,12 +370,12 @@ pub trait PrintState<'a>: std::ops::Deref<Target = pp::Printer> + std::ops::Dere
         if self.next_comment().is_none() {
             self.hardbreak();
         }
-        while let Some(ref cmnt) = self.next_comment() {
-            self.print_comment(cmnt)
+        while let Some(cmnt) = self.next_comment() {
+            self.print_comment(&cmnt)
         }
     }
 
-    fn print_literal(&mut self, lit: &ast::Lit) {
+    fn print_meta_item_lit(&mut self, lit: &ast::MetaItemLit) {
         self.print_token_literal(lit.token_lit, lit.span)
     }
 
@@ -446,8 +450,8 @@ pub trait PrintState<'a>: std::ops::Deref<Target = pp::Printer> + std::ops::Dere
             self.hardbreak_if_not_bol();
         }
         self.maybe_print_comment(attr.span.lo());
-        match attr.kind {
-            ast::AttrKind::Normal(ref normal) => {
+        match &attr.kind {
+            ast::AttrKind::Normal(normal) => {
                 match attr.style {
                     ast::AttrStyle::Inner => self.word("#!["),
                     ast::AttrStyle::Outer => self.word("#["),
@@ -456,7 +460,7 @@ pub trait PrintState<'a>: std::ops::Deref<Target = pp::Printer> + std::ops::Dere
                 self.word("]");
             }
             ast::AttrKind::DocComment(comment_kind, data) => {
-                self.word(doc_comment_to_string(comment_kind, attr.style, data));
+                self.word(doc_comment_to_string(*comment_kind, attr.style, *data));
                 self.hardbreak()
             }
         }
@@ -488,7 +492,7 @@ pub trait PrintState<'a>: std::ops::Deref<Target = pp::Printer> + std::ops::Dere
                 self.print_path(&item.path, false, 0);
                 self.space();
                 self.word_space("=");
-                let token_str = self.literal_to_string(lit);
+                let token_str = self.meta_item_lit_to_string(lit);
                 self.word(token_str);
             }
         }
@@ -497,22 +501,22 @@ pub trait PrintState<'a>: std::ops::Deref<Target = pp::Printer> + std::ops::Dere
 
     fn print_meta_list_item(&mut self, item: &ast::NestedMetaItem) {
         match item {
-            ast::NestedMetaItem::MetaItem(ref mi) => self.print_meta_item(mi),
-            ast::NestedMetaItem::Literal(ref lit) => self.print_literal(lit),
+            ast::NestedMetaItem::MetaItem(mi) => self.print_meta_item(mi),
+            ast::NestedMetaItem::Lit(lit) => self.print_meta_item_lit(lit),
         }
     }
 
     fn print_meta_item(&mut self, item: &ast::MetaItem) {
         self.ibox(INDENT_UNIT);
-        match item.kind {
+        match &item.kind {
             ast::MetaItemKind::Word => self.print_path(&item.path, false, 0),
-            ast::MetaItemKind::NameValue(ref value) => {
+            ast::MetaItemKind::NameValue(value) => {
                 self.print_path(&item.path, false, 0);
                 self.space();
                 self.word_space("=");
-                self.print_literal(value);
+                self.print_meta_item_lit(value);
             }
-            ast::MetaItemKind::List(ref items) => {
+            ast::MetaItemKind::List(items) => {
                 self.print_path(&item.path, false, 0);
                 self.popen();
                 self.commasep(Consistent, &items, |s, i| s.print_meta_list_item(i));
@@ -657,7 +661,7 @@ pub trait PrintState<'a>: std::ops::Deref<Target = pp::Printer> + std::ops::Dere
     fn print_path_segment(&mut self, segment: &ast::PathSegment, colons_before_params: bool) {
         if segment.ident.name != kw::PathRoot {
             self.print_ident(segment.ident);
-            if let Some(ref args) = segment.args {
+            if let Some(args) = &segment.args {
                 self.print_generic_args(args, colons_before_params);
             }
         }
@@ -712,19 +716,19 @@ pub trait PrintState<'a>: std::ops::Deref<Target = pp::Printer> + std::ops::Dere
     }
 
     fn nonterminal_to_string(&self, nt: &Nonterminal) -> String {
-        match *nt {
-            token::NtExpr(ref e) => self.expr_to_string(e),
-            token::NtMeta(ref e) => self.attr_item_to_string(e),
-            token::NtTy(ref e) => self.ty_to_string(e),
-            token::NtPath(ref e) => self.path_to_string(e),
-            token::NtItem(ref e) => self.item_to_string(e),
-            token::NtBlock(ref e) => self.block_to_string(e),
-            token::NtStmt(ref e) => self.stmt_to_string(e),
-            token::NtPat(ref e) => self.pat_to_string(e),
-            token::NtIdent(e, is_raw) => IdentPrinter::for_ast_ident(e, is_raw).to_string(),
+        match nt {
+            token::NtExpr(e) => self.expr_to_string(e),
+            token::NtMeta(e) => self.attr_item_to_string(e),
+            token::NtTy(e) => self.ty_to_string(e),
+            token::NtPath(e) => self.path_to_string(e),
+            token::NtItem(e) => self.item_to_string(e),
+            token::NtBlock(e) => self.block_to_string(e),
+            token::NtStmt(e) => self.stmt_to_string(e),
+            token::NtPat(e) => self.pat_to_string(e),
+            token::NtIdent(e, is_raw) => IdentPrinter::for_ast_ident(*e, *is_raw).to_string(),
             token::NtLifetime(e) => e.to_string(),
-            token::NtLiteral(ref e) => self.expr_to_string(e),
-            token::NtVis(ref e) => self.vis_to_string(e),
+            token::NtLiteral(e) => self.expr_to_string(e),
+            token::NtVis(e) => self.vis_to_string(e),
         }
     }
 
@@ -825,8 +829,8 @@ pub trait PrintState<'a>: std::ops::Deref<Target = pp::Printer> + std::ops::Dere
         Self::to_string(|s| s.print_expr(e))
     }
 
-    fn literal_to_string(&self, lit: &ast::Lit) -> String {
-        Self::to_string(|s| s.print_literal(lit))
+    fn meta_item_lit_to_string(&self, lit: &ast::MetaItemLit) -> String {
+        Self::to_string(|s| s.print_meta_item_lit(lit))
     }
 
     fn tt_to_string(&self, tt: &TokenTree) -> String {
@@ -917,8 +921,8 @@ impl<'a> PrintState<'a> for State<'a> {
             self.word("::")
         }
 
-        match *args {
-            ast::GenericArgs::AngleBracketed(ref data) => {
+        match args {
+            ast::GenericArgs::AngleBracketed(data) => {
                 self.word("<");
                 self.commasep(Inconsistent, &data.args, |s, arg| match arg {
                     ast::AngleBracketedArg::Arg(a) => s.print_generic_arg(a),
@@ -927,7 +931,7 @@ impl<'a> PrintState<'a> for State<'a> {
                 self.word(">")
             }
 
-            ast::GenericArgs::Parenthesized(ref data) => {
+            ast::GenericArgs::Parenthesized(data) => {
                 self.word("(");
                 self.commasep(Inconsistent, &data.inputs, |s, ty| s.print_type(ty));
                 self.word(")");
@@ -1011,17 +1015,17 @@ impl<'a> State<'a> {
     pub fn print_type(&mut self, ty: &ast::Ty) {
         self.maybe_print_comment(ty.span.lo());
         self.ibox(0);
-        match ty.kind {
-            ast::TyKind::Slice(ref ty) => {
+        match &ty.kind {
+            ast::TyKind::Slice(ty) => {
                 self.word("[");
                 self.print_type(ty);
                 self.word("]");
             }
-            ast::TyKind::Ptr(ref mt) => {
+            ast::TyKind::Ptr(mt) => {
                 self.word("*");
                 self.print_mt(mt, true);
             }
-            ast::TyKind::Rptr(ref lifetime, ref mt) => {
+            ast::TyKind::Rptr(lifetime, mt) => {
                 self.word("&");
                 self.print_opt_lifetime(lifetime);
                 self.print_mt(mt, false);
@@ -1029,7 +1033,7 @@ impl<'a> State<'a> {
             ast::TyKind::Never => {
                 self.word("!");
             }
-            ast::TyKind::Tup(ref elts) => {
+            ast::TyKind::Tup(elts) => {
                 self.popen();
                 self.commasep(Inconsistent, &elts, |s, ty| s.print_type(ty));
                 if elts.len() == 1 {
@@ -1037,36 +1041,36 @@ impl<'a> State<'a> {
                 }
                 self.pclose();
             }
-            ast::TyKind::Paren(ref typ) => {
+            ast::TyKind::Paren(typ) => {
                 self.popen();
                 self.print_type(typ);
                 self.pclose();
             }
-            ast::TyKind::BareFn(ref f) => {
+            ast::TyKind::BareFn(f) => {
                 self.print_ty_fn(f.ext, f.unsafety, &f.decl, None, &f.generic_params);
             }
-            ast::TyKind::Path(None, ref path) => {
+            ast::TyKind::Path(None, path) => {
                 self.print_path(path, false, 0);
             }
-            ast::TyKind::Path(Some(ref qself), ref path) => self.print_qpath(path, qself, false),
-            ast::TyKind::TraitObject(ref bounds, syntax) => {
-                if syntax == ast::TraitObjectSyntax::Dyn {
+            ast::TyKind::Path(Some(qself), path) => self.print_qpath(path, qself, false),
+            ast::TyKind::TraitObject(bounds, syntax) => {
+                if *syntax == ast::TraitObjectSyntax::Dyn {
                     self.word_nbsp("dyn");
                 }
                 self.print_type_bounds(bounds);
             }
-            ast::TyKind::ImplTrait(_, ref bounds) => {
+            ast::TyKind::ImplTrait(_, bounds) => {
                 self.word_nbsp("impl");
                 self.print_type_bounds(bounds);
             }
-            ast::TyKind::Array(ref ty, ref length) => {
+            ast::TyKind::Array(ty, length) => {
                 self.word("[");
                 self.print_type(ty);
                 self.word("; ");
                 self.print_expr(&length.value);
                 self.word("]");
             }
-            ast::TyKind::Typeof(ref e) => {
+            ast::TyKind::Typeof(e) => {
                 self.word("typeof(");
                 self.print_expr(&e.value);
                 self.word(")");
@@ -1082,7 +1086,7 @@ impl<'a> State<'a> {
             ast::TyKind::ImplicitSelf => {
                 self.word("Self");
             }
-            ast::TyKind::MacCall(ref m) => {
+            ast::TyKind::MacCall(m) => {
                 self.print_mac(m);
             }
             ast::TyKind::CVarArgs => {
@@ -1111,8 +1115,8 @@ impl<'a> State<'a> {
 
     pub(crate) fn print_stmt(&mut self, st: &ast::Stmt) {
         self.maybe_print_comment(st.span.lo());
-        match st.kind {
-            ast::StmtKind::Local(ref loc) => {
+        match &st.kind {
+            ast::StmtKind::Local(loc) => {
                 self.print_outer_attributes(&loc.attrs);
                 self.space_if_not_bol();
                 self.ibox(INDENT_UNIT);
@@ -1135,15 +1139,15 @@ impl<'a> State<'a> {
                 self.word(";");
                 self.end(); // `let` ibox
             }
-            ast::StmtKind::Item(ref item) => self.print_item(item),
-            ast::StmtKind::Expr(ref expr) => {
+            ast::StmtKind::Item(item) => self.print_item(item),
+            ast::StmtKind::Expr(expr) => {
                 self.space_if_not_bol();
                 self.print_expr_outer_attr_style(expr, false);
                 if classify::expr_requires_semi_to_be_stmt(expr) {
                     self.word(";");
                 }
             }
-            ast::StmtKind::Semi(ref expr) => {
+            ast::StmtKind::Semi(expr) => {
                 self.space_if_not_bol();
                 self.print_expr_outer_attr_style(expr, false);
                 self.word(";");
@@ -1152,7 +1156,7 @@ impl<'a> State<'a> {
                 self.space_if_not_bol();
                 self.word(";");
             }
-            ast::StmtKind::MacCall(ref mac) => {
+            ast::StmtKind::MacCall(mac) => {
                 self.space_if_not_bol();
                 self.print_outer_attributes(&mac.attrs);
                 self.print_mac(&mac.mac);
@@ -1193,8 +1197,8 @@ impl<'a> State<'a> {
         let has_attrs = self.print_inner_attributes(attrs);
 
         for (i, st) in blk.stmts.iter().enumerate() {
-            match st.kind {
-                ast::StmtKind::Expr(ref expr) if i == blk.stmts.len() - 1 => {
+            match &st.kind {
+                ast::StmtKind::Expr(expr) if i == blk.stmts.len() - 1 => {
                     self.maybe_print_comment(st.span.lo());
                     self.space_if_not_bol();
                     self.print_expr_outer_attr_style(expr, false);
@@ -1362,7 +1366,7 @@ impl<'a> State<'a> {
 
     pub(crate) fn print_local_decl(&mut self, loc: &ast::Local) {
         self.print_pat(&loc.pat);
-        if let Some(ref ty) = loc.ty {
+        if let Some(ty) = &loc.ty {
             self.word_space(":");
             self.print_type(ty);
         }
@@ -1386,7 +1390,7 @@ impl<'a> State<'a> {
         for item_segment in &path.segments[qself.position..] {
             self.word("::");
             self.print_ident(item_segment.ident);
-            if let Some(ref args) = item_segment.args {
+            if let Some(args) = &item_segment.args {
                 self.print_generic_args(args, colons_before_params)
             }
         }
@@ -1397,23 +1401,23 @@ impl<'a> State<'a> {
         self.ann.pre(self, AnnNode::Pat(pat));
         /* Pat isn't normalized, but the beauty of it
         is that it doesn't matter */
-        match pat.kind {
+        match &pat.kind {
             PatKind::Wild => self.word("_"),
-            PatKind::Ident(BindingAnnotation(by_ref, mutbl), ident, ref sub) => {
-                if by_ref == ByRef::Yes {
+            PatKind::Ident(BindingAnnotation(by_ref, mutbl), ident, sub) => {
+                if *by_ref == ByRef::Yes {
                     self.word_nbsp("ref");
                 }
-                if mutbl == Mutability::Mut {
+                if mutbl.is_mut() {
                     self.word_nbsp("mut");
                 }
-                self.print_ident(ident);
-                if let Some(ref p) = *sub {
+                self.print_ident(*ident);
+                if let Some(p) = sub {
                     self.space();
                     self.word_space("@");
                     self.print_pat(p);
                 }
             }
-            PatKind::TupleStruct(ref qself, ref path, ref elts) => {
+            PatKind::TupleStruct(qself, path, elts) => {
                 if let Some(qself) = qself {
                     self.print_qpath(path, qself, true);
                 } else {
@@ -1423,16 +1427,16 @@ impl<'a> State<'a> {
                 self.commasep(Inconsistent, &elts, |s, p| s.print_pat(p));
                 self.pclose();
             }
-            PatKind::Or(ref pats) => {
+            PatKind::Or(pats) => {
                 self.strsep("|", true, Inconsistent, &pats, |s, p| s.print_pat(p));
             }
-            PatKind::Path(None, ref path) => {
+            PatKind::Path(None, path) => {
                 self.print_path(path, true, 0);
             }
-            PatKind::Path(Some(ref qself), ref path) => {
+            PatKind::Path(Some(qself), path) => {
                 self.print_qpath(path, qself, false);
             }
-            PatKind::Struct(ref qself, ref path, ref fields, etc) => {
+            PatKind::Struct(qself, path, fields, etc) => {
                 if let Some(qself) = qself {
                     self.print_qpath(path, qself, true);
                 } else {
@@ -1458,7 +1462,7 @@ impl<'a> State<'a> {
                     },
                     |f| f.pat.span,
                 );
-                if etc {
+                if *etc {
                     if !fields.is_empty() {
                         self.word_space(",");
                     }
@@ -1469,7 +1473,7 @@ impl<'a> State<'a> {
                 }
                 self.word("}");
             }
-            PatKind::Tuple(ref elts) => {
+            PatKind::Tuple(elts) => {
                 self.popen();
                 self.commasep(Inconsistent, &elts, |s, p| s.print_pat(p));
                 if elts.len() == 1 {
@@ -1477,13 +1481,13 @@ impl<'a> State<'a> {
                 }
                 self.pclose();
             }
-            PatKind::Box(ref inner) => {
+            PatKind::Box(inner) => {
                 self.word("box ");
                 self.print_pat(inner);
             }
-            PatKind::Ref(ref inner, mutbl) => {
+            PatKind::Ref(inner, mutbl) => {
                 self.word("&");
-                if mutbl == Mutability::Mut {
+                if mutbl.is_mut() {
                     self.word("mut ");
                 }
                 if let PatKind::Ident(ast::BindingAnnotation::MUT, ..) = inner.kind {
@@ -1494,12 +1498,12 @@ impl<'a> State<'a> {
                     self.print_pat(inner);
                 }
             }
-            PatKind::Lit(ref e) => self.print_expr(&**e),
-            PatKind::Range(ref begin, ref end, Spanned { node: ref end_kind, .. }) => {
+            PatKind::Lit(e) => self.print_expr(&**e),
+            PatKind::Range(begin, end, Spanned { node: end_kind, .. }) => {
                 if let Some(e) = begin {
                     self.print_expr(e);
                 }
-                match *end_kind {
+                match end_kind {
                     RangeEnd::Included(RangeSyntax::DotDotDot) => self.word("..."),
                     RangeEnd::Included(RangeSyntax::DotDotEq) => self.word("..="),
                     RangeEnd::Excluded => self.word(".."),
@@ -1508,36 +1512,36 @@ impl<'a> State<'a> {
                     self.print_expr(e);
                 }
             }
-            PatKind::Slice(ref elts) => {
+            PatKind::Slice(elts) => {
                 self.word("[");
                 self.commasep(Inconsistent, &elts, |s, p| s.print_pat(p));
                 self.word("]");
             }
             PatKind::Rest => self.word(".."),
-            PatKind::Paren(ref inner) => {
+            PatKind::Paren(inner) => {
                 self.popen();
                 self.print_pat(inner);
                 self.pclose();
             }
-            PatKind::MacCall(ref m) => self.print_mac(m),
+            PatKind::MacCall(m) => self.print_mac(m),
         }
         self.ann.post(self, AnnNode::Pat(pat))
     }
 
     fn print_explicit_self(&mut self, explicit_self: &ast::ExplicitSelf) {
-        match explicit_self.node {
+        match &explicit_self.node {
             SelfKind::Value(m) => {
-                self.print_mutability(m, false);
+                self.print_mutability(*m, false);
                 self.word("self")
             }
-            SelfKind::Region(ref lt, m) => {
+            SelfKind::Region(lt, m) => {
                 self.word("&");
                 self.print_opt_lifetime(lt);
-                self.print_mutability(m, false);
+                self.print_mutability(*m, false);
                 self.word("self")
             }
-            SelfKind::Explicit(ref typ, m) => {
-                self.print_mutability(m, false);
+            SelfKind::Explicit(typ, m) => {
+                self.print_mutability(*m, false);
                 self.word("self");
                 self.word_space(":");
                 self.print_type(typ)
@@ -1599,7 +1603,7 @@ impl<'a> State<'a> {
         self.commasep(Inconsistent, &generic_params, |s, param| {
             s.print_outer_attributes_inline(&param.attrs);
 
-            match param.kind {
+            match &param.kind {
                 ast::GenericParamKind::Lifetime => {
                     let lt = ast::Lifetime { id: param.id, ident: param.ident };
                     s.print_lifetime(lt);
@@ -1608,19 +1612,19 @@ impl<'a> State<'a> {
                         s.print_lifetime_bounds(&param.bounds)
                     }
                 }
-                ast::GenericParamKind::Type { ref default } => {
+                ast::GenericParamKind::Type { default } => {
                     s.print_ident(param.ident);
                     if !param.bounds.is_empty() {
                         s.word_nbsp(":");
                         s.print_type_bounds(&param.bounds);
                     }
-                    if let Some(ref default) = default {
+                    if let Some(default) = default {
                         s.space();
                         s.word_space("=");
                         s.print_type(default)
                     }
                 }
-                ast::GenericParamKind::Const { ref ty, kw_span: _, ref default } => {
+                ast::GenericParamKind::Const { ty, default, .. } => {
                     s.word_space("const");
                     s.print_ident(param.ident);
                     s.space();
@@ -1630,7 +1634,7 @@ impl<'a> State<'a> {
                         s.word_nbsp(":");
                         s.print_type_bounds(&param.bounds);
                     }
-                    if let Some(ref default) = default {
+                    if let Some(default) = default {
                         s.space();
                         s.word_space("=");
                         s.print_expr(&default.value);
@@ -1712,9 +1716,9 @@ impl<'a> State<'a> {
             where_clause: ast::WhereClause {
                 has_where_token: false,
                 predicates: Vec::new(),
-                span: rustc_span::DUMMY_SP,
+                span: DUMMY_SP,
             },
-            span: rustc_span::DUMMY_SP,
+            span: DUMMY_SP,
         };
         let header = ast::FnHeader { unsafety, ext, ..ast::FnHeader::default() };
         self.print_fn(decl, header, name, &generics);
diff --git a/compiler/rustc_ast_pretty/src/pprust/state/expr.rs b/compiler/rustc_ast_pretty/src/pprust/state/expr.rs
index 4b37fa027f5..81483ac30d1 100644
--- a/compiler/rustc_ast_pretty/src/pprust/state/expr.rs
+++ b/compiler/rustc_ast_pretty/src/pprust/state/expr.rs
@@ -8,9 +8,9 @@ use rustc_ast::{self as ast, BlockCheckMode};
 impl<'a> State<'a> {
     fn print_else(&mut self, els: Option<&ast::Expr>) {
         if let Some(_else) = els {
-            match _else.kind {
+            match &_else.kind {
                 // Another `else if` block.
-                ast::ExprKind::If(ref i, ref then, ref e) => {
+                ast::ExprKind::If(i, then, e) => {
                     self.cbox(INDENT_UNIT - 1);
                     self.ibox(0);
                     self.word(" else if ");
@@ -20,7 +20,7 @@ impl<'a> State<'a> {
                     self.print_else(e.as_deref())
                 }
                 // Final `else` block.
-                ast::ExprKind::Block(ref b, _) => {
+                ast::ExprKind::Block(b, _) => {
                     self.cbox(INDENT_UNIT - 1);
                     self.ibox(0);
                     self.word(" else ");
@@ -58,10 +58,10 @@ impl<'a> State<'a> {
         self.print_expr_cond_paren(expr, Self::cond_needs_par(expr))
     }
 
-    // Does `expr` need parentheses when printed in a condition position?
-    //
-    // These cases need parens due to the parse error observed in #26461: `if return {}`
-    // parses as the erroneous construct `if (return {})`, not `if (return) {}`.
+    /// Does `expr` need parentheses when printed in a condition position?
+    ///
+    /// These cases need parens due to the parse error observed in #26461: `if return {}`
+    /// parses as the erroneous construct `if (return {})`, not `if (return) {}`.
     pub(super) fn cond_needs_par(expr: &ast::Expr) -> bool {
         match expr.kind {
             ast::ExprKind::Break(..)
@@ -202,7 +202,7 @@ impl<'a> State<'a> {
         self.print_expr_maybe_paren(receiver, parser::PREC_POSTFIX);
         self.word(".");
         self.print_ident(segment.ident);
-        if let Some(ref args) = segment.args {
+        if let Some(args) = &segment.args {
             self.print_generic_args(args, true);
         }
         self.print_call_post(base_args)
@@ -284,73 +284,66 @@ impl<'a> State<'a> {
 
         self.ibox(INDENT_UNIT);
         self.ann.pre(self, AnnNode::Expr(expr));
-        match expr.kind {
-            ast::ExprKind::Box(ref expr) => {
+        match &expr.kind {
+            ast::ExprKind::Box(expr) => {
                 self.word_space("box");
                 self.print_expr_maybe_paren(expr, parser::PREC_PREFIX);
             }
-            ast::ExprKind::Array(ref exprs) => {
+            ast::ExprKind::Array(exprs) => {
                 self.print_expr_vec(exprs);
             }
-            ast::ExprKind::ConstBlock(ref anon_const) => {
+            ast::ExprKind::ConstBlock(anon_const) => {
                 self.print_expr_anon_const(anon_const, attrs);
             }
-            ast::ExprKind::Repeat(ref element, ref count) => {
+            ast::ExprKind::Repeat(element, count) => {
                 self.print_expr_repeat(element, count);
             }
-            ast::ExprKind::Struct(ref se) => {
+            ast::ExprKind::Struct(se) => {
                 self.print_expr_struct(&se.qself, &se.path, &se.fields, &se.rest);
             }
-            ast::ExprKind::Tup(ref exprs) => {
+            ast::ExprKind::Tup(exprs) => {
                 self.print_expr_tup(exprs);
             }
-            ast::ExprKind::Call(ref func, ref args) => {
+            ast::ExprKind::Call(func, args) => {
                 self.print_expr_call(func, &args);
             }
-            ast::ExprKind::MethodCall(box ast::MethodCall {
-                ref seg,
-                ref receiver,
-                ref args,
-                ..
-            }) => {
+            ast::ExprKind::MethodCall(box ast::MethodCall { seg, receiver, args, .. }) => {
                 self.print_expr_method_call(seg, &receiver, &args);
             }
-            ast::ExprKind::Binary(op, ref lhs, ref rhs) => {
-                self.print_expr_binary(op, lhs, rhs);
+            ast::ExprKind::Binary(op, lhs, rhs) => {
+                self.print_expr_binary(*op, lhs, rhs);
             }
-            ast::ExprKind::Unary(op, ref expr) => {
-                self.print_expr_unary(op, expr);
+            ast::ExprKind::Unary(op, expr) => {
+                self.print_expr_unary(*op, expr);
             }
-            ast::ExprKind::AddrOf(k, m, ref expr) => {
-                self.print_expr_addr_of(k, m, expr);
+            ast::ExprKind::AddrOf(k, m, expr) => {
+                self.print_expr_addr_of(*k, *m, expr);
             }
             ast::ExprKind::Lit(token_lit) => {
-                self.print_token_literal(token_lit, expr.span);
+                self.print_token_literal(*token_lit, expr.span);
             }
-            ast::ExprKind::IncludedBytes(ref bytes) => {
-                let lit = ast::Lit::from_included_bytes(bytes, expr.span);
-                self.print_literal(&lit)
+            ast::ExprKind::IncludedBytes(bytes) => {
+                let lit = ast::LitKind::ByteStr(bytes.clone()).to_token_lit();
+                self.print_token_literal(lit, expr.span)
             }
-            ast::ExprKind::Cast(ref expr, ref ty) => {
+            ast::ExprKind::Cast(expr, ty) => {
                 let prec = AssocOp::As.precedence() as i8;
                 self.print_expr_maybe_paren(expr, prec);
                 self.space();
                 self.word_space("as");
                 self.print_type(ty);
             }
-            ast::ExprKind::Type(ref expr, ref ty) => {
+            ast::ExprKind::Type(expr, ty) => {
                 let prec = AssocOp::Colon.precedence() as i8;
                 self.print_expr_maybe_paren(expr, prec);
                 self.word_space(":");
                 self.print_type(ty);
             }
-            ast::ExprKind::Let(ref pat, ref scrutinee, _) => {
+            ast::ExprKind::Let(pat, scrutinee, _) => {
                 self.print_let(pat, scrutinee);
             }
-            ast::ExprKind::If(ref test, ref blk, ref elseopt) => {
-                self.print_if(test, blk, elseopt.as_deref())
-            }
-            ast::ExprKind::While(ref test, ref blk, opt_label) => {
+            ast::ExprKind::If(test, blk, elseopt) => self.print_if(test, blk, elseopt.as_deref()),
+            ast::ExprKind::While(test, blk, opt_label) => {
                 if let Some(label) = opt_label {
                     self.print_ident(label.ident);
                     self.word_space(":");
@@ -362,7 +355,7 @@ impl<'a> State<'a> {
                 self.space();
                 self.print_block_with_attrs(blk, attrs);
             }
-            ast::ExprKind::ForLoop(ref pat, ref iter, ref blk, opt_label) => {
+            ast::ExprKind::ForLoop(pat, iter, blk, opt_label) => {
                 if let Some(label) = opt_label {
                     self.print_ident(label.ident);
                     self.word_space(":");
@@ -377,7 +370,7 @@ impl<'a> State<'a> {
                 self.space();
                 self.print_block_with_attrs(blk, attrs);
             }
-            ast::ExprKind::Loop(ref blk, opt_label, _) => {
+            ast::ExprKind::Loop(blk, opt_label, _) => {
                 if let Some(label) = opt_label {
                     self.print_ident(label.ident);
                     self.word_space(":");
@@ -387,7 +380,7 @@ impl<'a> State<'a> {
                 self.word_nbsp("loop");
                 self.print_block_with_attrs(blk, attrs);
             }
-            ast::ExprKind::Match(ref expr, ref arms) => {
+            ast::ExprKind::Match(expr, arms) => {
                 self.cbox(0);
                 self.ibox(0);
                 self.word_nbsp("match");
@@ -402,18 +395,18 @@ impl<'a> State<'a> {
                 self.bclose(expr.span, empty);
             }
             ast::ExprKind::Closure(box ast::Closure {
-                ref binder,
+                binder,
                 capture_clause,
                 asyncness,
                 movability,
-                ref fn_decl,
-                ref body,
+                fn_decl,
+                body,
                 fn_decl_span: _,
             }) => {
                 self.print_closure_binder(binder);
-                self.print_movability(movability);
-                self.print_asyncness(asyncness);
-                self.print_capture_clause(capture_clause);
+                self.print_movability(*movability);
+                self.print_asyncness(*asyncness);
+                self.print_capture_clause(*capture_clause);
 
                 self.print_fn_params_and_ret(fn_decl, true);
                 self.space();
@@ -425,7 +418,7 @@ impl<'a> State<'a> {
                 // empty box to satisfy the close.
                 self.ibox(0);
             }
-            ast::ExprKind::Block(ref blk, opt_label) => {
+            ast::ExprKind::Block(blk, opt_label) => {
                 if let Some(label) = opt_label {
                     self.print_ident(label.ident);
                     self.word_space(":");
@@ -436,26 +429,26 @@ impl<'a> State<'a> {
                 self.ibox(0);
                 self.print_block_with_attrs(blk, attrs);
             }
-            ast::ExprKind::Async(capture_clause, _, ref blk) => {
+            ast::ExprKind::Async(capture_clause, _, blk) => {
                 self.word_nbsp("async");
-                self.print_capture_clause(capture_clause);
+                self.print_capture_clause(*capture_clause);
                 // cbox/ibox in analogy to the `ExprKind::Block` arm above
                 self.cbox(0);
                 self.ibox(0);
                 self.print_block_with_attrs(blk, attrs);
             }
-            ast::ExprKind::Await(ref expr) => {
+            ast::ExprKind::Await(expr) => {
                 self.print_expr_maybe_paren(expr, parser::PREC_POSTFIX);
                 self.word(".await");
             }
-            ast::ExprKind::Assign(ref lhs, ref rhs, _) => {
+            ast::ExprKind::Assign(lhs, rhs, _) => {
                 let prec = AssocOp::Assign.precedence() as i8;
                 self.print_expr_maybe_paren(lhs, prec + 1);
                 self.space();
                 self.word_space("=");
                 self.print_expr_maybe_paren(rhs, prec);
             }
-            ast::ExprKind::AssignOp(op, ref lhs, ref rhs) => {
+            ast::ExprKind::AssignOp(op, lhs, rhs) => {
                 let prec = AssocOp::Assign.precedence() as i8;
                 self.print_expr_maybe_paren(lhs, prec + 1);
                 self.space();
@@ -463,45 +456,44 @@ impl<'a> State<'a> {
                 self.word_space("=");
                 self.print_expr_maybe_paren(rhs, prec);
             }
-            ast::ExprKind::Field(ref expr, ident) => {
+            ast::ExprKind::Field(expr, ident) => {
                 self.print_expr_maybe_paren(expr, parser::PREC_POSTFIX);
                 self.word(".");
-                self.print_ident(ident);
+                self.print_ident(*ident);
             }
-            ast::ExprKind::Index(ref expr, ref index) => {
+            ast::ExprKind::Index(expr, index) => {
                 self.print_expr_maybe_paren(expr, parser::PREC_POSTFIX);
                 self.word("[");
                 self.print_expr(index);
                 self.word("]");
             }
-            ast::ExprKind::Range(ref start, ref end, limits) => {
+            ast::ExprKind::Range(start, end, limits) => {
                 // Special case for `Range`.  `AssocOp` claims that `Range` has higher precedence
                 // than `Assign`, but `x .. x = x` gives a parse error instead of `x .. (x = x)`.
                 // Here we use a fake precedence value so that any child with lower precedence than
                 // a "normal" binop gets parenthesized.  (`LOr` is the lowest-precedence binop.)
                 let fake_prec = AssocOp::LOr.precedence() as i8;
-                if let Some(ref e) = *start {
+                if let Some(e) = start {
                     self.print_expr_maybe_paren(e, fake_prec);
                 }
-                if limits == ast::RangeLimits::HalfOpen {
-                    self.word("..");
-                } else {
-                    self.word("..=");
+                match limits {
+                    ast::RangeLimits::HalfOpen => self.word(".."),
+                    ast::RangeLimits::Closed => self.word("..="),
                 }
-                if let Some(ref e) = *end {
+                if let Some(e) = end {
                     self.print_expr_maybe_paren(e, fake_prec);
                 }
             }
             ast::ExprKind::Underscore => self.word("_"),
-            ast::ExprKind::Path(None, ref path) => self.print_path(path, true, 0),
-            ast::ExprKind::Path(Some(ref qself), ref path) => self.print_qpath(path, qself, true),
-            ast::ExprKind::Break(opt_label, ref opt_expr) => {
+            ast::ExprKind::Path(None, path) => self.print_path(path, true, 0),
+            ast::ExprKind::Path(Some(qself), path) => self.print_qpath(path, qself, true),
+            ast::ExprKind::Break(opt_label, opt_expr) => {
                 self.word("break");
                 if let Some(label) = opt_label {
                     self.space();
                     self.print_ident(label.ident);
                 }
-                if let Some(ref expr) = *opt_expr {
+                if let Some(expr) = opt_expr {
                     self.space();
                     self.print_expr_maybe_paren(expr, parser::PREC_JUMP);
                 }
@@ -513,45 +505,45 @@ impl<'a> State<'a> {
                     self.print_ident(label.ident);
                 }
             }
-            ast::ExprKind::Ret(ref result) => {
+            ast::ExprKind::Ret(result) => {
                 self.word("return");
-                if let Some(ref expr) = *result {
+                if let Some(expr) = result {
                     self.word(" ");
                     self.print_expr_maybe_paren(expr, parser::PREC_JUMP);
                 }
             }
-            ast::ExprKind::Yeet(ref result) => {
+            ast::ExprKind::Yeet(result) => {
                 self.word("do");
                 self.word(" ");
                 self.word("yeet");
-                if let Some(ref expr) = *result {
+                if let Some(expr) = result {
                     self.word(" ");
                     self.print_expr_maybe_paren(expr, parser::PREC_JUMP);
                 }
             }
-            ast::ExprKind::InlineAsm(ref a) => {
+            ast::ExprKind::InlineAsm(a) => {
                 self.word("asm!");
                 self.print_inline_asm(a);
             }
-            ast::ExprKind::MacCall(ref m) => self.print_mac(m),
-            ast::ExprKind::Paren(ref e) => {
+            ast::ExprKind::MacCall(m) => self.print_mac(m),
+            ast::ExprKind::Paren(e) => {
                 self.popen();
                 self.print_expr(e);
                 self.pclose();
             }
-            ast::ExprKind::Yield(ref e) => {
+            ast::ExprKind::Yield(e) => {
                 self.word("yield");
 
-                if let Some(ref expr) = *e {
+                if let Some(expr) = e {
                     self.space();
                     self.print_expr_maybe_paren(expr, parser::PREC_JUMP);
                 }
             }
-            ast::ExprKind::Try(ref e) => {
+            ast::ExprKind::Try(e) => {
                 self.print_expr_maybe_paren(e, parser::PREC_POSTFIX);
                 self.word("?")
             }
-            ast::ExprKind::TryBlock(ref blk) => {
+            ast::ExprKind::TryBlock(blk) => {
                 self.cbox(0);
                 self.ibox(0);
                 self.word_nbsp("try");
@@ -578,15 +570,15 @@ impl<'a> State<'a> {
         self.print_outer_attributes(&arm.attrs);
         self.print_pat(&arm.pat);
         self.space();
-        if let Some(ref e) = arm.guard {
+        if let Some(e) = &arm.guard {
             self.word_space("if");
             self.print_expr(e);
             self.space();
         }
         self.word_space("=>");
 
-        match arm.body.kind {
-            ast::ExprKind::Block(ref blk, opt_label) => {
+        match &arm.body.kind {
+            ast::ExprKind::Block(blk, opt_label) => {
                 if let Some(label) = opt_label {
                     self.print_ident(label.ident);
                     self.word_space(":");
diff --git a/compiler/rustc_ast_pretty/src/pprust/state/item.rs b/compiler/rustc_ast_pretty/src/pprust/state/item.rs
index 9c4425701e0..e68a7b3f202 100644
--- a/compiler/rustc_ast_pretty/src/pprust/state/item.rs
+++ b/compiler/rustc_ast_pretty/src/pprust/state/item.rs
@@ -136,10 +136,10 @@ impl<'a> State<'a> {
         self.maybe_print_comment(item.span.lo());
         self.print_outer_attributes(&item.attrs);
         self.ann.pre(self, AnnNode::Item(item));
-        match item.kind {
+        match &item.kind {
             ast::ItemKind::ExternCrate(orig_name) => {
                 self.head(visibility_qualified(&item.vis, "extern crate"));
-                if let Some(orig_name) = orig_name {
+                if let &Some(orig_name) = orig_name {
                     self.print_name(orig_name);
                     self.space();
                     self.word("as");
@@ -150,35 +150,41 @@ impl<'a> State<'a> {
                 self.end(); // end inner head-block
                 self.end(); // end outer head-block
             }
-            ast::ItemKind::Use(ref tree) => {
+            ast::ItemKind::Use(tree) => {
                 self.print_visibility(&item.vis);
                 self.word_nbsp("use");
                 self.print_use_tree(tree);
                 self.word(";");
             }
-            ast::ItemKind::Static(ref ty, mutbl, ref body) => {
+            ast::ItemKind::Static(ty, mutbl, body) => {
                 let def = ast::Defaultness::Final;
-                self.print_item_const(item.ident, Some(mutbl), ty, body.as_deref(), &item.vis, def);
+                self.print_item_const(
+                    item.ident,
+                    Some(*mutbl),
+                    ty,
+                    body.as_deref(),
+                    &item.vis,
+                    def,
+                );
             }
-            ast::ItemKind::Const(def, ref ty, ref body) => {
-                self.print_item_const(item.ident, None, ty, body.as_deref(), &item.vis, def);
+            ast::ItemKind::Const(def, ty, body) => {
+                self.print_item_const(item.ident, None, ty, body.as_deref(), &item.vis, *def);
             }
-            ast::ItemKind::Fn(box ast::Fn { defaultness, ref sig, ref generics, ref body }) => {
-                let body = body.as_deref();
+            ast::ItemKind::Fn(box ast::Fn { defaultness, sig, generics, body }) => {
                 self.print_fn_full(
                     sig,
                     item.ident,
                     generics,
                     &item.vis,
-                    defaultness,
-                    body,
+                    *defaultness,
+                    body.as_deref(),
                     &item.attrs,
                 );
             }
-            ast::ItemKind::Mod(unsafety, ref mod_kind) => {
+            ast::ItemKind::Mod(unsafety, mod_kind) => {
                 self.head(Self::to_string(|s| {
                     s.print_visibility(&item.vis);
-                    s.print_unsafety(unsafety);
+                    s.print_unsafety(*unsafety);
                     s.word("mod");
                 }));
                 self.print_ident(item.ident);
@@ -201,7 +207,7 @@ impl<'a> State<'a> {
                     }
                 }
             }
-            ast::ItemKind::ForeignMod(ref nmod) => {
+            ast::ItemKind::ForeignMod(nmod) => {
                 self.head(Self::to_string(|s| {
                     s.print_unsafety(nmod.unsafety);
                     s.word("extern");
@@ -215,7 +221,7 @@ impl<'a> State<'a> {
                 let empty = item.attrs.is_empty() && nmod.items.is_empty();
                 self.bclose(item.span, empty);
             }
-            ast::ItemKind::GlobalAsm(ref asm) => {
+            ast::ItemKind::GlobalAsm(asm) => {
                 self.head(visibility_qualified(&item.vis, "global_asm!"));
                 self.print_inline_asm(asm);
                 self.word(";");
@@ -224,32 +230,31 @@ impl<'a> State<'a> {
             }
             ast::ItemKind::TyAlias(box ast::TyAlias {
                 defaultness,
-                ref generics,
+                generics,
                 where_clauses,
                 where_predicates_split,
-                ref bounds,
-                ref ty,
+                bounds,
+                ty,
             }) => {
-                let ty = ty.as_deref();
                 self.print_associated_type(
                     item.ident,
                     generics,
-                    where_clauses,
-                    where_predicates_split,
+                    *where_clauses,
+                    *where_predicates_split,
                     bounds,
-                    ty,
+                    ty.as_deref(),
                     &item.vis,
-                    defaultness,
+                    *defaultness,
                 );
             }
-            ast::ItemKind::Enum(ref enum_definition, ref params) => {
+            ast::ItemKind::Enum(enum_definition, params) => {
                 self.print_enum_def(enum_definition, params, item.ident, item.span, &item.vis);
             }
-            ast::ItemKind::Struct(ref struct_def, ref generics) => {
+            ast::ItemKind::Struct(struct_def, generics) => {
                 self.head(visibility_qualified(&item.vis, "struct"));
                 self.print_struct(struct_def, generics, item.ident, item.span, true);
             }
-            ast::ItemKind::Union(ref struct_def, ref generics) => {
+            ast::ItemKind::Union(struct_def, generics) => {
                 self.head(visibility_qualified(&item.vis, "union"));
                 self.print_struct(struct_def, generics, item.ident, item.span, true);
             }
@@ -258,15 +263,15 @@ impl<'a> State<'a> {
                 polarity,
                 defaultness,
                 constness,
-                ref generics,
-                ref of_trait,
-                ref self_ty,
-                ref items,
+                generics,
+                of_trait,
+                self_ty,
+                items,
             }) => {
                 self.head("");
                 self.print_visibility(&item.vis);
-                self.print_defaultness(defaultness);
-                self.print_unsafety(unsafety);
+                self.print_defaultness(*defaultness);
+                self.print_unsafety(*unsafety);
                 self.word("impl");
 
                 if generics.params.is_empty() {
@@ -276,13 +281,13 @@ impl<'a> State<'a> {
                     self.space();
                 }
 
-                self.print_constness(constness);
+                self.print_constness(*constness);
 
                 if let ast::ImplPolarity::Negative(_) = polarity {
                     self.word("!");
                 }
 
-                if let Some(ref t) = *of_trait {
+                if let Some(t) = of_trait {
                     self.print_trait_ref(t);
                     self.space();
                     self.word_space("for");
@@ -303,21 +308,21 @@ impl<'a> State<'a> {
             ast::ItemKind::Trait(box ast::Trait {
                 is_auto,
                 unsafety,
-                ref generics,
-                ref bounds,
-                ref items,
+                generics,
+                bounds,
+                items,
                 ..
             }) => {
                 self.head("");
                 self.print_visibility(&item.vis);
-                self.print_unsafety(unsafety);
-                self.print_is_auto(is_auto);
+                self.print_unsafety(*unsafety);
+                self.print_is_auto(*is_auto);
                 self.word_nbsp("trait");
                 self.print_ident(item.ident);
                 self.print_generic_params(&generics.params);
                 let mut real_bounds = Vec::with_capacity(bounds.len());
                 for b in bounds.iter() {
-                    if let GenericBound::Trait(ref ptr, ast::TraitBoundModifier::Maybe) = *b {
+                    if let GenericBound::Trait(ptr, ast::TraitBoundModifier::Maybe) = b {
                         self.space();
                         self.word_space("for ?");
                         self.print_trait_ref(&ptr.trait_ref);
@@ -339,14 +344,14 @@ impl<'a> State<'a> {
                 let empty = item.attrs.is_empty() && items.is_empty();
                 self.bclose(item.span, empty);
             }
-            ast::ItemKind::TraitAlias(ref generics, ref bounds) => {
+            ast::ItemKind::TraitAlias(generics, bounds) => {
                 self.head(visibility_qualified(&item.vis, "trait"));
                 self.print_ident(item.ident);
                 self.print_generic_params(&generics.params);
                 let mut real_bounds = Vec::with_capacity(bounds.len());
                 // FIXME(durka) this seems to be some quite outdated syntax
                 for b in bounds.iter() {
-                    if let GenericBound::Trait(ref ptr, ast::TraitBoundModifier::Maybe) = *b {
+                    if let GenericBound::Trait(ptr, ast::TraitBoundModifier::Maybe) = b {
                         self.space();
                         self.word_space("for ?");
                         self.print_trait_ref(&ptr.trait_ref);
@@ -364,13 +369,13 @@ impl<'a> State<'a> {
                 self.end(); // end inner head-block
                 self.end(); // end outer head-block
             }
-            ast::ItemKind::MacCall(ref mac) => {
+            ast::ItemKind::MacCall(mac) => {
                 self.print_mac(mac);
                 if mac.args.need_semicolon() {
                     self.word(";");
                 }
             }
-            ast::ItemKind::MacroDef(ref macro_def) => {
+            ast::ItemKind::MacroDef(macro_def) => {
                 self.print_mac_def(macro_def, &item.ident, item.span, |state| {
                     state.print_visibility(&item.vis)
                 });
@@ -412,11 +417,11 @@ impl<'a> State<'a> {
     }
 
     pub(crate) fn print_visibility(&mut self, vis: &ast::Visibility) {
-        match vis.kind {
+        match &vis.kind {
             ast::VisibilityKind::Public => self.word_nbsp("pub"),
-            ast::VisibilityKind::Restricted { ref path, id: _, shorthand } => {
+            ast::VisibilityKind::Restricted { path, shorthand, .. } => {
                 let path = Self::to_string(|s| s.print_path(path, false, 0));
-                if shorthand && (path == "crate" || path == "self" || path == "super") {
+                if *shorthand && (path == "crate" || path == "self" || path == "super") {
                     self.word_nbsp(format!("pub({})", path))
                 } else {
                     self.word_nbsp(format!("pub(in {})", path))
@@ -465,7 +470,7 @@ impl<'a> State<'a> {
     ) {
         self.print_ident(ident);
         self.print_generic_params(&generics.params);
-        match struct_def {
+        match &struct_def {
             ast::VariantData::Tuple(..) | ast::VariantData::Unit(..) => {
                 if let ast::VariantData::Tuple(..) = struct_def {
                     self.popen();
@@ -484,7 +489,7 @@ impl<'a> State<'a> {
                 self.end();
                 self.end(); // Close the outer-box.
             }
-            ast::VariantData::Struct(ref fields, ..) => {
+            ast::VariantData::Struct(fields, ..) => {
                 self.print_where_clause(&generics.where_clause);
                 self.print_record_struct_body(fields, span);
             }
@@ -496,7 +501,7 @@ impl<'a> State<'a> {
         self.print_visibility(&v.vis);
         let generics = ast::Generics::default();
         self.print_struct(&v.data, &generics, v.ident, v.span, false);
-        if let Some(ref d) = v.disr_expr {
+        if let Some(d) = &v.disr_expr {
             self.space();
             self.word_space("=");
             self.print_expr(&d.value)
@@ -657,10 +662,10 @@ impl<'a> State<'a> {
     }
 
     fn print_use_tree(&mut self, tree: &ast::UseTree) {
-        match tree.kind {
-            ast::UseTreeKind::Simple(rename, ..) => {
+        match &tree.kind {
+            ast::UseTreeKind::Simple(rename) => {
                 self.print_path(&tree.prefix, false, 0);
-                if let Some(rename) = rename {
+                if let &Some(rename) = rename {
                     self.nbsp();
                     self.word_nbsp("as");
                     self.print_ident(rename);
@@ -673,7 +678,7 @@ impl<'a> State<'a> {
                 }
                 self.word("*");
             }
-            ast::UseTreeKind::Nested(ref items) => {
+            ast::UseTreeKind::Nested(items) => {
                 if !tree.prefix.segments.is_empty() {
                     self.print_path(&tree.prefix, false, 0);
                     self.word("::");
diff --git a/compiler/rustc_attr/src/builtin.rs b/compiler/rustc_attr/src/builtin.rs
index 753f62dd589..13b48d8f89a 100644
--- a/compiler/rustc_attr/src/builtin.rs
+++ b/compiler/rustc_attr/src/builtin.rs
@@ -1,7 +1,7 @@
 //! Parsing and validation of builtin attributes
 
 use rustc_ast as ast;
-use rustc_ast::{Attribute, Lit, LitKind, MetaItem, MetaItemKind, NestedMetaItem, NodeId};
+use rustc_ast::{Attribute, LitKind, MetaItem, MetaItemKind, MetaItemLit, NestedMetaItem, NodeId};
 use rustc_ast_pretty::pprust;
 use rustc_feature::{find_gated_cfg, is_builtin_attr_name, Features, GatedCfg};
 use rustc_macros::HashStable_Generic;
@@ -486,7 +486,7 @@ where
                                     continue 'outer;
                                 }
                             },
-                            NestedMetaItem::Literal(lit) => {
+                            NestedMetaItem::Lit(lit) => {
                                 handle_errors(
                                     &sess.parse_sess,
                                     lit.span,
@@ -658,11 +658,11 @@ pub fn eval_condition(
         ast::MetaItemKind::List(ref mis) if cfg.name_or_empty() == sym::version => {
             try_gate_cfg(sym::version, cfg.span, sess, features);
             let (min_version, span) = match &mis[..] {
-                [NestedMetaItem::Literal(Lit { kind: LitKind::Str(sym, ..), span, .. })] => {
+                [NestedMetaItem::Lit(MetaItemLit { kind: LitKind::Str(sym, ..), span, .. })] => {
                     (sym, span)
                 }
                 [
-                    NestedMetaItem::Literal(Lit { span, .. })
+                    NestedMetaItem::Lit(MetaItemLit { span, .. })
                     | NestedMetaItem::MetaItem(MetaItem { span, .. }),
                 ] => {
                     sess.emit_err(session_diagnostics::ExpectedVersionLiteral { span: *span });
@@ -899,7 +899,7 @@ where
                                 continue 'outer;
                             }
                         },
-                        NestedMetaItem::Literal(lit) => {
+                        NestedMetaItem::Lit(lit) => {
                             handle_errors(
                                 &sess.parse_sess,
                                 lit.span,
diff --git a/compiler/rustc_attr/src/session_diagnostics.rs b/compiler/rustc_attr/src/session_diagnostics.rs
index edccfa1c8ff..91c6bcb08a0 100644
--- a/compiler/rustc_attr/src/session_diagnostics.rs
+++ b/compiler/rustc_attr/src/session_diagnostics.rs
@@ -41,7 +41,7 @@ pub(crate) struct IncorrectMetaItem {
     pub span: Span,
 }
 
-// Error code: E0541
+/// Error code: E0541
 pub(crate) struct UnknownMetaItem<'a> {
     pub span: Span,
     pub item: String,
@@ -200,7 +200,7 @@ pub(crate) struct InvalidReprHintNoValue {
     pub name: String,
 }
 
-// Error code: E0565
+/// Error code: E0565
 pub(crate) struct UnsupportedLiteral {
     pub span: Span,
     pub reason: UnsupportedLiteralReason,
diff --git a/compiler/rustc_borrowck/src/diagnostics/bound_region_errors.rs b/compiler/rustc_borrowck/src/diagnostics/bound_region_errors.rs
index b99bfda1a51..e05566dc2c7 100644
--- a/compiler/rustc_borrowck/src/diagnostics/bound_region_errors.rs
+++ b/compiler/rustc_borrowck/src/diagnostics/bound_region_errors.rs
@@ -291,11 +291,11 @@ where
         // FIXME(lqd): Unify and de-duplicate the following with the actual
         // `rustc_traits::type_op::type_op_normalize` query to allow the span we need in the
         // `ObligationCause`. The normalization results are currently different between
-        // `AtExt::normalize` used in the query and `normalize` called below: the former fails
-        // to normalize the `nll/relate_tys/impl-fn-ignore-binder-via-bottom.rs` test. Check
-        // after #85499 lands to see if its fixes have erased this difference.
+        // `QueryNormalizeExt::query_normalize` used in the query and `normalize` called below:
+        // the former fails to normalize the `nll/relate_tys/impl-fn-ignore-binder-via-bottom.rs` test.
+        // Check after #85499 lands to see if its fixes have erased this difference.
         let (param_env, value) = key.into_parts();
-        let _ = ocx.normalize(cause, param_env, value.value);
+        let _ = ocx.normalize(&cause, param_env, value.value);
 
         try_extract_error_from_fulfill_cx(&ocx, placeholder_region, error_region)
     }
diff --git a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs
index 5ec9c5f5c1b..d221da5c17e 100644
--- a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs
+++ b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs
@@ -732,13 +732,15 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
         let tcx = self.infcx.tcx;
         // Try to find predicates on *generic params* that would allow copying `ty`
         let infcx = tcx.infer_ctxt().build();
-        if infcx
-            .type_implements_trait(
-                tcx.lang_items().clone_trait().unwrap(),
-                [tcx.erase_regions(ty)],
-                self.param_env,
-            )
-            .must_apply_modulo_regions()
+
+        if let Some(clone_trait_def) = tcx.lang_items().clone_trait()
+            && infcx
+                .type_implements_trait(
+                    clone_trait_def,
+                    [tcx.erase_regions(ty)],
+                    self.param_env,
+                )
+                .must_apply_modulo_regions()
         {
             err.span_suggestion_verbose(
                 span.shrink_to_hi(),
diff --git a/compiler/rustc_borrowck/src/diagnostics/mod.rs b/compiler/rustc_borrowck/src/diagnostics/mod.rs
index c500cbc49e4..86c5d9cfa81 100644
--- a/compiler/rustc_borrowck/src/diagnostics/mod.rs
+++ b/compiler/rustc_borrowck/src/diagnostics/mod.rs
@@ -590,7 +590,7 @@ impl UseSpans<'_> {
         }
     }
 
-    // Add a span label to the arguments of the closure, if it exists.
+    /// Add a span label to the arguments of the closure, if it exists.
     pub(super) fn args_span_label(self, err: &mut Diagnostic, message: impl Into<String>) {
         if let UseSpans::ClosureUse { args_span, .. } = self {
             err.span_label(args_span, message);
@@ -628,7 +628,7 @@ impl UseSpans<'_> {
         }
     }
 
-    // Add a span label to the use of the captured variable, if it exists.
+    /// Add a span label to the use of the captured variable, if it exists.
     pub(super) fn var_span_label(
         self,
         err: &mut Diagnostic,
diff --git a/compiler/rustc_borrowck/src/diagnostics/region_errors.rs b/compiler/rustc_borrowck/src/diagnostics/region_errors.rs
index 534675f1dc0..7aa099433a7 100644
--- a/compiler/rustc_borrowck/src/diagnostics/region_errors.rs
+++ b/compiler/rustc_borrowck/src/diagnostics/region_errors.rs
@@ -514,12 +514,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
             span: *span,
             ty_err: match output_ty.kind() {
                 ty::Closure(_, _) => FnMutReturnTypeErr::ReturnClosure { span: *span },
-                ty::Generator(def, ..)
-                    if matches!(
-                        self.infcx.tcx.generator_kind(def),
-                        Some(hir::GeneratorKind::Async(_))
-                    ) =>
-                {
+                ty::Generator(def, ..) if self.infcx.tcx.generator_is_async(*def) => {
                     FnMutReturnTypeErr::ReturnAsyncBlock { span: *span }
                 }
                 _ => FnMutReturnTypeErr::ReturnRef { span: *span },
diff --git a/compiler/rustc_borrowck/src/lib.rs b/compiler/rustc_borrowck/src/lib.rs
index 163170a1d1a..4d87ecf5e44 100644
--- a/compiler/rustc_borrowck/src/lib.rs
+++ b/compiler/rustc_borrowck/src/lib.rs
@@ -83,7 +83,7 @@ mod type_check;
 mod universal_regions;
 mod used_muts;
 
-// A public API provided for the Rust compiler consumers.
+/// A public API provided for the Rust compiler consumers.
 pub mod consumers;
 
 use borrow_set::{BorrowData, BorrowSet};
diff --git a/compiler/rustc_borrowck/src/region_infer/mod.rs b/compiler/rustc_borrowck/src/region_infer/mod.rs
index b35abbd107b..90e2b6b698c 100644
--- a/compiler/rustc_borrowck/src/region_infer/mod.rs
+++ b/compiler/rustc_borrowck/src/region_infer/mod.rs
@@ -1705,6 +1705,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
         });
     }
 
+    #[instrument(level = "debug", skip(self, infcx, errors_buffer))]
     fn check_member_constraints(
         &self,
         infcx: &InferCtxt<'tcx>,
@@ -1712,22 +1713,21 @@ impl<'tcx> RegionInferenceContext<'tcx> {
     ) {
         let member_constraints = self.member_constraints.clone();
         for m_c_i in member_constraints.all_indices() {
-            debug!("check_member_constraint(m_c_i={:?})", m_c_i);
+            debug!(?m_c_i);
             let m_c = &member_constraints[m_c_i];
             let member_region_vid = m_c.member_region_vid;
             debug!(
-                "check_member_constraint: member_region_vid={:?} with value {}",
-                member_region_vid,
-                self.region_value_str(member_region_vid),
+                ?member_region_vid,
+                value = ?self.region_value_str(member_region_vid),
             );
             let choice_regions = member_constraints.choice_regions(m_c_i);
-            debug!("check_member_constraint: choice_regions={:?}", choice_regions);
+            debug!(?choice_regions);
 
             // Did the member region wind up equal to any of the option regions?
             if let Some(o) =
                 choice_regions.iter().find(|&&o_r| self.eval_equal(o_r, m_c.member_region_vid))
             {
-                debug!("check_member_constraint: evaluated as equal to {:?}", o);
+                debug!("evaluated as equal to {:?}", o);
                 continue;
             }
 
diff --git a/compiler/rustc_borrowck/src/type_check/liveness/polonius.rs b/compiler/rustc_borrowck/src/type_check/liveness/polonius.rs
index bc76a465e3c..b344ab46adb 100644
--- a/compiler/rustc_borrowck/src/type_check/liveness/polonius.rs
+++ b/compiler/rustc_borrowck/src/type_check/liveness/polonius.rs
@@ -121,8 +121,8 @@ pub(super) fn populate_access_facts<'a, 'tcx>(
     }
 }
 
-// For every potentially drop()-touched region `region` in `local`'s type
-// (`kind`), emit a Polonius `use_of_var_derefs_origin(local, origin)` fact.
+/// For every potentially drop()-touched region `region` in `local`'s type
+/// (`kind`), emit a Polonius `use_of_var_derefs_origin(local, origin)` fact.
 pub(super) fn add_drop_of_var_derefs_origin<'tcx>(
     typeck: &mut TypeChecker<'_, 'tcx>,
     local: Local,
diff --git a/compiler/rustc_builtin_macros/src/alloc_error_handler.rs b/compiler/rustc_builtin_macros/src/alloc_error_handler.rs
index eaf1b1167cf..460175ed2ac 100644
--- a/compiler/rustc_builtin_macros/src/alloc_error_handler.rs
+++ b/compiler/rustc_builtin_macros/src/alloc_error_handler.rs
@@ -95,9 +95,7 @@ fn generate_handler(cx: &ExtCtxt<'_>, handler: Ident, span: Span, sig_span: Span
         body,
     }));
 
-    let special = sym::rustc_std_internal_symbol;
-    let special = cx.meta_word(span, special);
-    let attrs = thin_vec![cx.attribute(special)];
+    let attrs = thin_vec![cx.attr_word(sym::rustc_std_internal_symbol, span)];
 
     let item = cx.item(span, Ident::from_str_and_span("__rg_oom", span), attrs, kind);
     cx.stmt_item(sig_span, item)
diff --git a/compiler/rustc_builtin_macros/src/assert/context.rs b/compiler/rustc_builtin_macros/src/assert/context.rs
index 9f42a0c2d58..d82bc0453f5 100644
--- a/compiler/rustc_builtin_macros/src/assert/context.rs
+++ b/compiler/rustc_builtin_macros/src/assert/context.rs
@@ -1,5 +1,4 @@
 use rustc_ast::{
-    attr,
     ptr::P,
     token,
     tokenstream::{DelimSpan, TokenStream, TokenTree},
@@ -107,7 +106,7 @@ impl<'cx, 'a> Context<'cx, 'a> {
             (
                 UseTree {
                     prefix: this.cx.path(this.span, vec![Ident::with_dummy_span(sym)]),
-                    kind: UseTreeKind::Simple(None, DUMMY_NODE_ID, DUMMY_NODE_ID),
+                    kind: UseTreeKind::Simple(None),
                     span: this.span,
                 },
                 DUMMY_NODE_ID,
@@ -118,10 +117,7 @@ impl<'cx, 'a> Context<'cx, 'a> {
             self.cx.item(
                 self.span,
                 Ident::empty(),
-                thin_vec![self.cx.attribute(attr::mk_list_item(
-                    Ident::new(sym::allow, self.span),
-                    vec![attr::mk_nested_word_item(Ident::new(sym::unused_imports, self.span))],
-                ))],
+                thin_vec![self.cx.attr_nested_word(sym::allow, sym::unused_imports, self.span)],
                 ItemKind::Use(UseTree {
                     prefix: self.cx.path(self.span, self.cx.std_path(&[sym::asserting])),
                     kind: UseTreeKind::Nested(vec![
diff --git a/compiler/rustc_builtin_macros/src/cfg_accessible.rs b/compiler/rustc_builtin_macros/src/cfg_accessible.rs
index 86df3c44eb3..4e4cafc7182 100644
--- a/compiler/rustc_builtin_macros/src/cfg_accessible.rs
+++ b/compiler/rustc_builtin_macros/src/cfg_accessible.rs
@@ -37,10 +37,10 @@ impl MultiItemModifier for Expander {
         _is_derive_const: bool,
     ) -> ExpandResult<Vec<Annotatable>, Annotatable> {
         let template = AttributeTemplate { list: Some("path"), ..Default::default() };
-        let attr = &ecx.attribute(meta_item.clone());
-        validate_attr::check_builtin_attribute(
+        validate_attr::check_builtin_meta_item(
             &ecx.sess.parse_sess,
-            attr,
+            &meta_item,
+            ast::AttrStyle::Outer,
             sym::cfg_accessible,
             template,
         );
diff --git a/compiler/rustc_builtin_macros/src/concat_bytes.rs b/compiler/rustc_builtin_macros/src/concat_bytes.rs
index 87658e60e9d..161e3499584 100644
--- a/compiler/rustc_builtin_macros/src/concat_bytes.rs
+++ b/compiler/rustc_builtin_macros/src/concat_bytes.rs
@@ -2,6 +2,7 @@ use rustc_ast as ast;
 use rustc_ast::{ptr::P, tokenstream::TokenStream};
 use rustc_errors::Applicability;
 use rustc_expand::base::{self, DummyResult};
+use rustc_session::errors::report_lit_error;
 use rustc_span::Span;
 
 /// Emits errors for literal expressions that are invalid inside and outside of an array.
@@ -68,7 +69,10 @@ fn invalid_type_err(
         Ok(ast::LitKind::Int(_, _)) => {
             cx.span_err(span, "numeric literal is not a `u8`");
         }
-        _ => unreachable!(),
+        Ok(ast::LitKind::ByteStr(_) | ast::LitKind::Byte(_)) => unreachable!(),
+        Err(err) => {
+            report_lit_error(&cx.sess.parse_sess, err, token_lit, span);
+        }
     }
 }
 
diff --git a/compiler/rustc_builtin_macros/src/derive.rs b/compiler/rustc_builtin_macros/src/derive.rs
index 01f237e6ab5..fa5a45730ac 100644
--- a/compiler/rustc_builtin_macros/src/derive.rs
+++ b/compiler/rustc_builtin_macros/src/derive.rs
@@ -1,7 +1,7 @@
 use crate::cfg_eval::cfg_eval;
 
 use rustc_ast as ast;
-use rustc_ast::{attr, token, GenericParamKind, ItemKind, MetaItemKind, NestedMetaItem, StmtKind};
+use rustc_ast::{token, GenericParamKind, ItemKind, MetaItemKind, NestedMetaItem, StmtKind};
 use rustc_errors::{struct_span_err, Applicability};
 use rustc_expand::base::{Annotatable, ExpandResult, ExtCtxt, Indeterminate, MultiItemModifier};
 use rustc_feature::AttributeTemplate;
@@ -33,34 +33,36 @@ impl MultiItemModifier for Expander {
             ecx.resolver.resolve_derives(ecx.current_expansion.id, ecx.force_mode, &|| {
                 let template =
                     AttributeTemplate { list: Some("Trait1, Trait2, ..."), ..Default::default() };
-                let attr =
-                    attr::mk_attr_outer(&sess.parse_sess.attr_id_generator, meta_item.clone());
-                validate_attr::check_builtin_attribute(
+                validate_attr::check_builtin_meta_item(
                     &sess.parse_sess,
-                    &attr,
+                    &meta_item,
+                    ast::AttrStyle::Outer,
                     sym::derive,
                     template,
                 );
 
-                let mut resolutions: Vec<_> = attr
-                    .meta_item_list()
-                    .unwrap_or_default()
-                    .into_iter()
-                    .filter_map(|nested_meta| match nested_meta {
-                        NestedMetaItem::MetaItem(meta) => Some(meta),
-                        NestedMetaItem::Literal(lit) => {
-                            // Reject `#[derive("Debug")]`.
-                            report_unexpected_literal(sess, &lit);
-                            None
-                        }
-                    })
-                    .map(|meta| {
-                        // Reject `#[derive(Debug = "value", Debug(abc))]`, but recover the paths.
-                        report_path_args(sess, &meta);
-                        meta.path
-                    })
-                    .map(|path| (path, dummy_annotatable(), None, self.0))
-                    .collect();
+                let mut resolutions = match &meta_item.kind {
+                    MetaItemKind::List(list) => {
+                        list.iter()
+                            .filter_map(|nested_meta| match nested_meta {
+                                NestedMetaItem::MetaItem(meta) => Some(meta),
+                                NestedMetaItem::Lit(lit) => {
+                                    // Reject `#[derive("Debug")]`.
+                                    report_unexpected_meta_item_lit(sess, &lit);
+                                    None
+                                }
+                            })
+                            .map(|meta| {
+                                // Reject `#[derive(Debug = "value", Debug(abc))]`, but recover the
+                                // paths.
+                                report_path_args(sess, &meta);
+                                meta.path.clone()
+                            })
+                            .map(|path| (path, dummy_annotatable(), None, self.0))
+                            .collect()
+                    }
+                    _ => vec![],
+                };
 
                 // Do not configure or clone items unless necessary.
                 match &mut resolutions[..] {
@@ -127,7 +129,7 @@ fn report_bad_target(sess: &Session, item: &Annotatable, span: Span) -> bool {
     bad_target
 }
 
-fn report_unexpected_literal(sess: &Session, lit: &ast::Lit) {
+fn report_unexpected_meta_item_lit(sess: &Session, lit: &ast::MetaItemLit) {
     let help_msg = match lit.token_lit.kind {
         token::Str if rustc_lexer::is_ident(lit.token_lit.symbol.as_str()) => {
             format!("try using `#[derive({})]`", lit.token_lit.symbol)
diff --git a/compiler/rustc_builtin_macros/src/deriving/clone.rs b/compiler/rustc_builtin_macros/src/deriving/clone.rs
index 2f19fbcac7d..23b96d4176d 100644
--- a/compiler/rustc_builtin_macros/src/deriving/clone.rs
+++ b/compiler/rustc_builtin_macros/src/deriving/clone.rs
@@ -68,8 +68,7 @@ pub fn expand_deriving_clone(
         _ => cx.span_bug(span, "`#[derive(Clone)]` on trait item or impl item"),
     }
 
-    let inline = cx.meta_word(span, sym::inline);
-    let attrs = thin_vec![cx.attribute(inline)];
+    let attrs = thin_vec![cx.attr_word(sym::inline, span)];
     let trait_def = TraitDef {
         span,
         path: path_std!(clone::Clone),
diff --git a/compiler/rustc_builtin_macros/src/deriving/cmp/eq.rs b/compiler/rustc_builtin_macros/src/deriving/cmp/eq.rs
index a0b836171be..f861d47ed40 100644
--- a/compiler/rustc_builtin_macros/src/deriving/cmp/eq.rs
+++ b/compiler/rustc_builtin_macros/src/deriving/cmp/eq.rs
@@ -5,7 +5,7 @@ use crate::deriving::path_std;
 use rustc_ast::{self as ast, MetaItem};
 use rustc_data_structures::fx::FxHashSet;
 use rustc_expand::base::{Annotatable, ExtCtxt};
-use rustc_span::symbol::{sym, Ident};
+use rustc_span::symbol::sym;
 use rustc_span::Span;
 use thin_vec::thin_vec;
 
@@ -18,11 +18,11 @@ pub fn expand_deriving_eq(
     is_const: bool,
 ) {
     let span = cx.with_def_site_ctxt(span);
-    let inline = cx.meta_word(span, sym::inline);
-    let hidden = rustc_ast::attr::mk_nested_word_item(Ident::new(sym::hidden, span));
-    let doc = rustc_ast::attr::mk_list_item(Ident::new(sym::doc, span), vec![hidden]);
-    let no_coverage = cx.meta_word(span, sym::no_coverage);
-    let attrs = thin_vec![cx.attribute(inline), cx.attribute(doc), cx.attribute(no_coverage)];
+    let attrs = thin_vec![
+        cx.attr_word(sym::inline, span),
+        cx.attr_nested_word(sym::doc, sym::hidden, span),
+        cx.attr_word(sym::no_coverage, span)
+    ];
     let trait_def = TraitDef {
         span,
         path: path_std!(cmp::Eq),
diff --git a/compiler/rustc_builtin_macros/src/deriving/cmp/ord.rs b/compiler/rustc_builtin_macros/src/deriving/cmp/ord.rs
index 52780981248..96d18c7afb9 100644
--- a/compiler/rustc_builtin_macros/src/deriving/cmp/ord.rs
+++ b/compiler/rustc_builtin_macros/src/deriving/cmp/ord.rs
@@ -15,8 +15,7 @@ pub fn expand_deriving_ord(
     push: &mut dyn FnMut(Annotatable),
     is_const: bool,
 ) {
-    let inline = cx.meta_word(span, sym::inline);
-    let attrs = thin_vec![cx.attribute(inline)];
+    let attrs = thin_vec![cx.attr_word(sym::inline, span)];
     let trait_def = TraitDef {
         span,
         path: path_std!(cmp::Ord),
diff --git a/compiler/rustc_builtin_macros/src/deriving/cmp/partial_eq.rs b/compiler/rustc_builtin_macros/src/deriving/cmp/partial_eq.rs
index 34de4a620b4..7f95551fc48 100644
--- a/compiler/rustc_builtin_macros/src/deriving/cmp/partial_eq.rs
+++ b/compiler/rustc_builtin_macros/src/deriving/cmp/partial_eq.rs
@@ -68,8 +68,7 @@ pub fn expand_deriving_partial_eq(
 
     // No need to generate `ne`, the default suffices, and not generating it is
     // faster.
-    let inline = cx.meta_word(span, sym::inline);
-    let attrs = thin_vec![cx.attribute(inline)];
+    let attrs = thin_vec![cx.attr_word(sym::inline, span)];
     let methods = vec![MethodDef {
         name: sym::eq,
         generics: Bounds::empty(),
diff --git a/compiler/rustc_builtin_macros/src/deriving/cmp/partial_ord.rs b/compiler/rustc_builtin_macros/src/deriving/cmp/partial_ord.rs
index 6cc8f26df55..5c4e5b7f816 100644
--- a/compiler/rustc_builtin_macros/src/deriving/cmp/partial_ord.rs
+++ b/compiler/rustc_builtin_macros/src/deriving/cmp/partial_ord.rs
@@ -19,8 +19,7 @@ pub fn expand_deriving_partial_ord(
     let ret_ty =
         Path(Path::new_(pathvec_std!(option::Option), vec![Box::new(ordering_ty)], PathKind::Std));
 
-    let inline = cx.meta_word(span, sym::inline);
-    let attrs = thin_vec![cx.attribute(inline)];
+    let attrs = thin_vec![cx.attr_word(sym::inline, span)];
 
     let partial_cmp_def = MethodDef {
         name: sym::partial_cmp,
diff --git a/compiler/rustc_builtin_macros/src/deriving/default.rs b/compiler/rustc_builtin_macros/src/deriving/default.rs
index 93f297ad88b..e88d2e409c6 100644
--- a/compiler/rustc_builtin_macros/src/deriving/default.rs
+++ b/compiler/rustc_builtin_macros/src/deriving/default.rs
@@ -20,8 +20,7 @@ pub fn expand_deriving_default(
 ) {
     item.visit_with(&mut DetectNonVariantDefaultAttr { cx });
 
-    let inline = cx.meta_word(span, sym::inline);
-    let attrs = thin_vec![cx.attribute(inline)];
+    let attrs = thin_vec![cx.attr_word(sym::inline, span)];
     let trait_def = TraitDef {
         span,
         path: Path::new(vec![kw::Default, sym::Default]),
@@ -146,7 +145,7 @@ fn extract_default_variant<'a>(
                 let suggestion = default_variants
                     .iter()
                     .filter_map(|v| {
-                        if v.ident == variant.ident {
+                        if v.span == variant.span {
                             None
                         } else {
                             Some((cx.sess.find_by_name(&v.attrs, kw::Default)?.span, String::new()))
diff --git a/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs b/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs
index 3309fab224f..7fcaf0b436b 100644
--- a/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs
+++ b/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs
@@ -300,12 +300,12 @@ struct TypeParameter {
     ty: P<ast::Ty>,
 }
 
-// The code snippets built up for derived code are sometimes used as blocks
-// (e.g. in a function body) and sometimes used as expressions (e.g. in a match
-// arm). This structure avoids committing to either form until necessary,
-// avoiding the insertion of any unnecessary blocks.
-//
-// The statements come before the expression.
+/// The code snippets built up for derived code are sometimes used as blocks
+/// (e.g. in a function body) and sometimes used as expressions (e.g. in a match
+/// arm). This structure avoids committing to either form until necessary,
+/// avoiding the insertion of any unnecessary blocks.
+///
+/// The statements come before the expression.
 pub struct BlockOrExpr(Vec<ast::Stmt>, Option<P<Expr>>);
 
 impl BlockOrExpr {
@@ -718,7 +718,7 @@ impl<'a> TraitDef<'a> {
         let path = cx.path_all(self.span, false, vec![type_ident], self_params);
         let self_type = cx.ty_path(path);
 
-        let attr = cx.attribute(cx.meta_word(self.span, sym::automatically_derived));
+        let attr = cx.attr_word(sym::automatically_derived, self.span);
         let attrs = thin_vec![attr];
         let opt_trait_ref = Some(trait_ref);
 
diff --git a/compiler/rustc_builtin_macros/src/deriving/mod.rs b/compiler/rustc_builtin_macros/src/deriving/mod.rs
index 73a1df5d426..13fdd4fa68c 100644
--- a/compiler/rustc_builtin_macros/src/deriving/mod.rs
+++ b/compiler/rustc_builtin_macros/src/deriving/mod.rs
@@ -188,7 +188,7 @@ fn inject_impl_of_structural_trait(
             .cloned(),
     );
     // Mark as `automatically_derived` to avoid some silly lints.
-    attrs.push(cx.attribute(cx.meta_word(span, sym::automatically_derived)));
+    attrs.push(cx.attr_word(sym::automatically_derived, span));
 
     let newitem = cx.item(
         span,
diff --git a/compiler/rustc_builtin_macros/src/edition_panic.rs b/compiler/rustc_builtin_macros/src/edition_panic.rs
index cae648cd11a..b2a21611db7 100644
--- a/compiler/rustc_builtin_macros/src/edition_panic.rs
+++ b/compiler/rustc_builtin_macros/src/edition_panic.rs
@@ -6,15 +6,15 @@ use rustc_span::edition::Edition;
 use rustc_span::symbol::sym;
 use rustc_span::Span;
 
-// This expands to either
-// - `$crate::panic::panic_2015!(...)` or
-// - `$crate::panic::panic_2021!(...)`
-// depending on the edition.
-//
-// This is used for both std::panic!() and core::panic!().
-//
-// `$crate` will refer to either the `std` or `core` crate depending on which
-// one we're expanding from.
+/// This expands to either
+/// - `$crate::panic::panic_2015!(...)` or
+/// - `$crate::panic::panic_2021!(...)`
+/// depending on the edition.
+///
+/// This is used for both std::panic!() and core::panic!().
+///
+/// `$crate` will refer to either the `std` or `core` crate depending on which
+/// one we're expanding from.
 pub fn expand_panic<'cx>(
     cx: &'cx mut ExtCtxt<'_>,
     sp: Span,
@@ -24,10 +24,10 @@ pub fn expand_panic<'cx>(
     expand(mac, cx, sp, tts)
 }
 
-// This expands to either
-// - `$crate::panic::unreachable_2015!(...)` or
-// - `$crate::panic::unreachable_2021!(...)`
-// depending on the edition.
+/// This expands to either
+/// - `$crate::panic::unreachable_2015!(...)` or
+/// - `$crate::panic::unreachable_2021!(...)`
+/// depending on the edition.
 pub fn expand_unreachable<'cx>(
     cx: &'cx mut ExtCtxt<'_>,
     sp: Span,
diff --git a/compiler/rustc_builtin_macros/src/global_allocator.rs b/compiler/rustc_builtin_macros/src/global_allocator.rs
index 45b9b8ab6b6..0817aed037e 100644
--- a/compiler/rustc_builtin_macros/src/global_allocator.rs
+++ b/compiler/rustc_builtin_macros/src/global_allocator.rs
@@ -115,9 +115,7 @@ impl AllocFnFactory<'_, '_> {
     }
 
     fn attrs(&self) -> AttrVec {
-        let special = sym::rustc_std_internal_symbol;
-        let special = self.cx.meta_word(self.span, special);
-        thin_vec![self.cx.attribute(special)]
+        thin_vec![self.cx.attr_word(sym::rustc_std_internal_symbol, self.span)]
     }
 
     fn arg_ty(
diff --git a/compiler/rustc_builtin_macros/src/lib.rs b/compiler/rustc_builtin_macros/src/lib.rs
index 1cbbfb43264..75cfac72384 100644
--- a/compiler/rustc_builtin_macros/src/lib.rs
+++ b/compiler/rustc_builtin_macros/src/lib.rs
@@ -45,6 +45,7 @@ mod log_syntax;
 mod source_util;
 mod test;
 mod trace_macros;
+mod type_ascribe;
 mod util;
 
 pub mod asm;
@@ -92,6 +93,7 @@ pub fn register_builtin_macros(resolver: &mut dyn ResolverExpand) {
         unreachable: edition_panic::expand_unreachable,
         stringify: source_util::expand_stringify,
         trace_macros: trace_macros::expand_trace_macros,
+        type_ascribe: type_ascribe::expand_type_ascribe,
     }
 
     register_attr! {
diff --git a/compiler/rustc_builtin_macros/src/proc_macro_harness.rs b/compiler/rustc_builtin_macros/src/proc_macro_harness.rs
index ebe1c3663e3..ece660cf6f6 100644
--- a/compiler/rustc_builtin_macros/src/proc_macro_harness.rs
+++ b/compiler/rustc_builtin_macros/src/proc_macro_harness.rs
@@ -1,6 +1,3 @@
-use std::mem;
-
-use rustc_ast::attr;
 use rustc_ast::ptr::P;
 use rustc_ast::visit::{self, Visitor};
 use rustc_ast::{self as ast, NodeId};
@@ -13,6 +10,7 @@ use rustc_span::source_map::SourceMap;
 use rustc_span::symbol::{kw, sym, Ident, Symbol};
 use rustc_span::{Span, DUMMY_SP};
 use smallvec::smallvec;
+use std::mem;
 
 struct ProcMacroDerive {
     id: NodeId,
@@ -365,14 +363,8 @@ fn mk_decls(cx: &mut ExtCtxt<'_>, macros: &[ProcMacro]) -> P<ast::Item> {
             cx.expr_array_ref(span, decls),
         )
         .map(|mut i| {
-            let attr = cx.meta_word(span, sym::rustc_proc_macro_decls);
-            i.attrs.push(cx.attribute(attr));
-
-            let deprecated_attr = attr::mk_nested_word_item(Ident::new(sym::deprecated, span));
-            let allow_deprecated_attr =
-                attr::mk_list_item(Ident::new(sym::allow, span), vec![deprecated_attr]);
-            i.attrs.push(cx.attribute(allow_deprecated_attr));
-
+            i.attrs.push(cx.attr_word(sym::rustc_proc_macro_decls, span));
+            i.attrs.push(cx.attr_nested_word(sym::allow, sym::deprecated, span));
             i
         });
 
diff --git a/compiler/rustc_builtin_macros/src/source_util.rs b/compiler/rustc_builtin_macros/src/source_util.rs
index 3411bd40c9d..0b17e92efe9 100644
--- a/compiler/rustc_builtin_macros/src/source_util.rs
+++ b/compiler/rustc_builtin_macros/src/source_util.rs
@@ -164,7 +164,7 @@ pub fn expand_include<'cx>(
     Box::new(ExpandResult { p, node_id: cx.current_expansion.lint_node_id })
 }
 
-// include_str! : read the given file, insert it as a literal string expr
+/// `include_str!`: read the given file, insert it as a literal string expr
 pub fn expand_include_str(
     cx: &mut ExtCtxt<'_>,
     sp: Span,
diff --git a/compiler/rustc_builtin_macros/src/standard_library_imports.rs b/compiler/rustc_builtin_macros/src/standard_library_imports.rs
index 49ef538f04e..f73f20c84a3 100644
--- a/compiler/rustc_builtin_macros/src/standard_library_imports.rs
+++ b/compiler/rustc_builtin_macros/src/standard_library_imports.rs
@@ -52,7 +52,7 @@ pub fn inject(
             cx.item(
                 span,
                 ident,
-                thin_vec![cx.attribute(cx.meta_word(span, sym::macro_use))],
+                thin_vec![cx.attr_word(sym::macro_use, span)],
                 ast::ItemKind::ExternCrate(None),
             ),
         );
@@ -79,7 +79,7 @@ pub fn inject(
     let use_item = cx.item(
         span,
         Ident::empty(),
-        thin_vec![cx.attribute(cx.meta_word(span, sym::prelude_import))],
+        thin_vec![cx.attr_word(sym::prelude_import, span)],
         ast::ItemKind::Use(ast::UseTree {
             prefix: cx.path(span, import_path),
             kind: ast::UseTreeKind::Glob,
diff --git a/compiler/rustc_builtin_macros/src/test.rs b/compiler/rustc_builtin_macros/src/test.rs
index b62840d4bc8..3bcb60478ef 100644
--- a/compiler/rustc_builtin_macros/src/test.rs
+++ b/compiler/rustc_builtin_macros/src/test.rs
@@ -2,7 +2,6 @@
 /// Ideally, this code would be in libtest but for efficiency and error messages it lives here.
 use crate::util::{check_builtin_macro_attribute, warn_on_duplicate_attribute};
 use rustc_ast as ast;
-use rustc_ast::attr;
 use rustc_ast::ptr::P;
 use rustc_ast_pretty::pprust;
 use rustc_errors::Applicability;
@@ -13,13 +12,13 @@ use rustc_span::Span;
 use std::iter;
 use thin_vec::thin_vec;
 
-// #[test_case] is used by custom test authors to mark tests
-// When building for test, it needs to make the item public and gensym the name
-// Otherwise, we'll omit the item. This behavior means that any item annotated
-// with #[test_case] is never addressable.
-//
-// We mark item with an inert attribute "rustc_test_marker" which the test generation
-// logic will pick up on.
+/// #[test_case] is used by custom test authors to mark tests
+/// When building for test, it needs to make the item public and gensym the name
+/// Otherwise, we'll omit the item. This behavior means that any item annotated
+/// with #[test_case] is never addressable.
+///
+/// We mark item with an inert attribute "rustc_test_marker" which the test generation
+/// logic will pick up on.
 pub fn expand_test_case(
     ecx: &mut ExtCtxt<'_>,
     attr_sp: Span,
@@ -47,11 +46,7 @@ pub fn expand_test_case(
             tokens: None,
         };
         item.ident.span = item.ident.span.with_ctxt(sp.ctxt());
-        item.attrs.push(ecx.attribute(attr::mk_name_value_item_str(
-            Ident::new(sym::rustc_test_marker, sp),
-            test_path_symbol,
-            sp,
-        )));
+        item.attrs.push(ecx.attr_name_value_str(sym::rustc_test_marker, test_path_symbol, sp));
         item
     });
 
@@ -241,16 +236,9 @@ pub fn expand_test_or_bench(
         Ident::new(item.ident.name, sp),
         thin_vec![
             // #[cfg(test)]
-            cx.attribute(attr::mk_list_item(
-                Ident::new(sym::cfg, attr_sp),
-                vec![attr::mk_nested_word_item(Ident::new(sym::test, attr_sp))],
-            )),
+            cx.attr_nested_word(sym::cfg, sym::test, attr_sp),
             // #[rustc_test_marker = "test_case_sort_key"]
-            cx.attribute(attr::mk_name_value_item_str(
-                Ident::new(sym::rustc_test_marker, attr_sp),
-                test_path_symbol,
-                attr_sp,
-            )),
+            cx.attr_name_value_str(sym::rustc_test_marker, test_path_symbol, attr_sp),
         ]
         .into(),
         // const $ident: test::TestDescAndFn =
diff --git a/compiler/rustc_builtin_macros/src/test_harness.rs b/compiler/rustc_builtin_macros/src/test_harness.rs
index b8b8351a36f..b5bce9278a9 100644
--- a/compiler/rustc_builtin_macros/src/test_harness.rs
+++ b/compiler/rustc_builtin_macros/src/test_harness.rs
@@ -34,8 +34,8 @@ struct TestCtxt<'a> {
     test_runner: Option<ast::Path>,
 }
 
-// Traverse the crate, collecting all the test functions, eliding any
-// existing main functions, and synthesizing a main test harness
+/// Traverse the crate, collecting all the test functions, eliding any
+/// existing main functions, and synthesizing a main test harness
 pub fn inject(sess: &Session, resolver: &mut dyn ResolverExpand, krate: &mut ast::Crate) {
     let span_diagnostic = sess.diagnostic();
     let panic_strategy = sess.panic_strategy();
@@ -185,13 +185,12 @@ impl<'a> MutVisitor for EntryPointCleaner<'a> {
         let item = match entry_point_type(self.sess, &item, self.depth) {
             EntryPointType::MainNamed | EntryPointType::RustcMainAttr | EntryPointType::Start => {
                 item.map(|ast::Item { id, ident, attrs, kind, vis, span, tokens }| {
-                    let allow_ident = Ident::new(sym::allow, self.def_site);
-                    let dc_nested =
-                        attr::mk_nested_word_item(Ident::new(sym::dead_code, self.def_site));
-                    let allow_dead_code_item = attr::mk_list_item(allow_ident, vec![dc_nested]);
-                    let allow_dead_code = attr::mk_attr_outer(
+                    let allow_dead_code = attr::mk_attr_nested_word(
                         &self.sess.parse_sess.attr_id_generator,
-                        allow_dead_code_item,
+                        ast::AttrStyle::Outer,
+                        sym::allow,
+                        sym::dead_code,
+                        self.def_site,
                     );
                     let attrs = attrs
                         .into_iter()
@@ -309,8 +308,7 @@ fn mk_main(cx: &mut TestCtxt<'_>) -> P<ast::Item> {
     );
 
     // #[rustc_main]
-    let main_meta = ecx.meta_word(sp, sym::rustc_main);
-    let main_attr = ecx.attribute(main_meta);
+    let main_attr = ecx.attr_word(sym::rustc_main, sp);
 
     // pub fn main() { ... }
     let main_ret_ty = ecx.ty(sp, ast::TyKind::Tup(vec![]));
diff --git a/compiler/rustc_builtin_macros/src/type_ascribe.rs b/compiler/rustc_builtin_macros/src/type_ascribe.rs
new file mode 100644
index 00000000000..72b85af1486
--- /dev/null
+++ b/compiler/rustc_builtin_macros/src/type_ascribe.rs
@@ -0,0 +1,35 @@
+use rustc_ast::ptr::P;
+use rustc_ast::tokenstream::TokenStream;
+use rustc_ast::{token, Expr, ExprKind, Ty};
+use rustc_errors::PResult;
+use rustc_expand::base::{self, DummyResult, ExtCtxt, MacEager};
+use rustc_span::Span;
+
+pub fn expand_type_ascribe(
+    cx: &mut ExtCtxt<'_>,
+    span: Span,
+    tts: TokenStream,
+) -> Box<dyn base::MacResult + 'static> {
+    let (expr, ty) = match parse_ascribe(cx, tts) {
+        Ok(parsed) => parsed,
+        Err(mut err) => {
+            err.emit();
+            return DummyResult::any(span);
+        }
+    };
+
+    let asc_expr = cx.expr(span, ExprKind::Type(expr, ty));
+
+    return MacEager::expr(asc_expr);
+}
+
+fn parse_ascribe<'a>(cx: &mut ExtCtxt<'a>, stream: TokenStream) -> PResult<'a, (P<Expr>, P<Ty>)> {
+    let mut parser = cx.new_parser_from_tts(stream);
+
+    let expr = parser.parse_expr()?;
+    parser.expect(&token::Comma)?;
+
+    let ty = parser.parse_ty()?;
+
+    Ok((expr, ty))
+}
diff --git a/compiler/rustc_builtin_macros/src/util.rs b/compiler/rustc_builtin_macros/src/util.rs
index 527fe50eff0..83812631c2f 100644
--- a/compiler/rustc_builtin_macros/src/util.rs
+++ b/compiler/rustc_builtin_macros/src/util.rs
@@ -1,4 +1,4 @@
-use rustc_ast::{Attribute, MetaItem};
+use rustc_ast::{AttrStyle, Attribute, MetaItem};
 use rustc_expand::base::{Annotatable, ExtCtxt};
 use rustc_feature::AttributeTemplate;
 use rustc_lint_defs::builtin::DUPLICATE_MACRO_ATTRIBUTES;
@@ -8,8 +8,13 @@ use rustc_span::Symbol;
 pub fn check_builtin_macro_attribute(ecx: &ExtCtxt<'_>, meta_item: &MetaItem, name: Symbol) {
     // All the built-in macro attributes are "words" at the moment.
     let template = AttributeTemplate { word: true, ..Default::default() };
-    let attr = ecx.attribute(meta_item.clone());
-    validate_attr::check_builtin_attribute(&ecx.sess.parse_sess, &attr, name, template);
+    validate_attr::check_builtin_meta_item(
+        &ecx.sess.parse_sess,
+        &meta_item,
+        AttrStyle::Outer,
+        name,
+        template,
+    );
 }
 
 /// Emit a warning if the item is annotated with the given attribute. This is used to diagnose when
diff --git a/compiler/rustc_codegen_cranelift/src/value_and_place.rs b/compiler/rustc_codegen_cranelift/src/value_and_place.rs
index c5bd574623d..34746ff6b66 100644
--- a/compiler/rustc_codegen_cranelift/src/value_and_place.rs
+++ b/compiler/rustc_codegen_cranelift/src/value_and_place.rs
@@ -108,8 +108,8 @@ impl<'tcx> CValue<'tcx> {
     }
 
     // FIXME remove
-    // Forces the data value of a dyn* value to the stack and returns a pointer to it as well as the
-    // vtable pointer.
+    /// Forces the data value of a dyn* value to the stack and returns a pointer to it as well as the
+    /// vtable pointer.
     pub(crate) fn dyn_star_force_data_on_stack(
         self,
         fx: &mut FunctionCx<'_, '_, 'tcx>,
diff --git a/compiler/rustc_codegen_gcc/example/alloc_system.rs b/compiler/rustc_codegen_gcc/example/alloc_system.rs
index 89661918d05..fd01fcf1fc8 100644
--- a/compiler/rustc_codegen_gcc/example/alloc_system.rs
+++ b/compiler/rustc_codegen_gcc/example/alloc_system.rs
@@ -13,17 +13,17 @@
 
 // The minimum alignment guaranteed by the architecture. This value is used to
 // add fast paths for low alignment values.
-#[cfg(all(any(target_arch = "x86",
+#[cfg(any(target_arch = "x86",
               target_arch = "arm",
               target_arch = "mips",
               target_arch = "powerpc",
-              target_arch = "powerpc64")))]
+              target_arch = "powerpc64"))]
 const MIN_ALIGN: usize = 8;
-#[cfg(all(any(target_arch = "x86_64",
+#[cfg(any(target_arch = "x86_64",
               target_arch = "aarch64",
               target_arch = "mips64",
               target_arch = "s390x",
-              target_arch = "sparc64")))]
+              target_arch = "sparc64"))]
 const MIN_ALIGN: usize = 16;
 
 pub struct System;
diff --git a/compiler/rustc_codegen_gcc/src/context.rs b/compiler/rustc_codegen_gcc/src/context.rs
index 2e71c3665da..837708aeb0e 100644
--- a/compiler/rustc_codegen_gcc/src/context.rs
+++ b/compiler/rustc_codegen_gcc/src/context.rs
@@ -88,9 +88,9 @@ pub struct CodegenCx<'gcc, 'tcx> {
     pub vtables: RefCell<FxHashMap<(Ty<'tcx>, Option<ty::PolyExistentialTraitRef<'tcx>>), RValue<'gcc>>>,
 
     // TODO(antoyo): improve the SSA API to not require those.
-    // Mapping from function pointer type to indexes of on stack parameters.
+    /// Mapping from function pointer type to indexes of on stack parameters.
     pub on_stack_params: RefCell<FxHashMap<FunctionPtrType<'gcc>, FxHashSet<usize>>>,
-    // Mapping from function to indexes of on stack parameters.
+    /// Mapping from function to indexes of on stack parameters.
     pub on_stack_function_params: RefCell<FxHashMap<Function<'gcc>, FxHashSet<usize>>>,
 
     /// Cache of emitted const globals (value -> global)
diff --git a/compiler/rustc_codegen_llvm/src/consts.rs b/compiler/rustc_codegen_llvm/src/consts.rs
index 69434280b21..3c324359565 100644
--- a/compiler/rustc_codegen_llvm/src/consts.rs
+++ b/compiler/rustc_codegen_llvm/src/consts.rs
@@ -295,8 +295,18 @@ impl<'ll> CodegenCx<'ll, '_> {
             llvm::set_thread_local_mode(g, self.tls_model);
         }
 
+        let dso_local = unsafe { self.should_assume_dso_local(g, true) };
+        if dso_local {
+            unsafe {
+                llvm::LLVMRustSetDSOLocal(g, true);
+            }
+        }
+
         if !def_id.is_local() {
             let needs_dll_storage_attr = self.use_dll_storage_attrs && !self.tcx.is_foreign_item(def_id) &&
+                // Local definitions can never be imported, so we must not apply
+                // the DLLImport annotation.
+                !dso_local &&
                 // ThinLTO can't handle this workaround in all cases, so we don't
                 // emit the attrs. Instead we make them unnecessary by disallowing
                 // dynamic linking when linker plugin based LTO is enabled.
@@ -340,12 +350,6 @@ impl<'ll> CodegenCx<'ll, '_> {
             }
         }
 
-        unsafe {
-            if self.should_assume_dso_local(g, true) {
-                llvm::LLVMRustSetDSOLocal(g, true);
-            }
-        }
-
         self.instances.borrow_mut().insert(instance, g);
         g
     }
diff --git a/compiler/rustc_codegen_llvm/src/coverageinfo/mod.rs b/compiler/rustc_codegen_llvm/src/coverageinfo/mod.rs
index 964a632b6ee..ace15cfb024 100644
--- a/compiler/rustc_codegen_llvm/src/coverageinfo/mod.rs
+++ b/compiler/rustc_codegen_llvm/src/coverageinfo/mod.rs
@@ -37,7 +37,7 @@ const VAR_ALIGN_BYTES: usize = 8;
 
 /// A context object for maintaining all state needed by the coverageinfo module.
 pub struct CrateCoverageContext<'ll, 'tcx> {
-    // Coverage data for each instrumented function identified by DefId.
+    /// Coverage data for each instrumented function identified by DefId.
     pub(crate) function_coverage_map: RefCell<FxHashMap<Instance<'tcx>, FunctionCoverage<'tcx>>>,
     pub(crate) pgo_func_name_var_map: RefCell<FxHashMap<Instance<'tcx>, &'ll llvm::Value>>,
 }
diff --git a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs
index f4519849730..c14e1656291 100644
--- a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs
+++ b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs
@@ -35,7 +35,7 @@ pub enum LLVMRustResult {
 pub struct LLVMRustCOFFShortExport {
     pub name: *const c_char,
     pub ordinal_present: bool,
-    // value of `ordinal` only important when `ordinal_present` is true
+    /// value of `ordinal` only important when `ordinal_present` is true
     pub ordinal: u16,
 }
 
diff --git a/compiler/rustc_codegen_llvm/src/llvm_util.rs b/compiler/rustc_codegen_llvm/src/llvm_util.rs
index 4af1aaec0a1..c9f5dd0f2c6 100644
--- a/compiler/rustc_codegen_llvm/src/llvm_util.rs
+++ b/compiler/rustc_codegen_llvm/src/llvm_util.rs
@@ -194,8 +194,8 @@ pub fn to_llvm_features<'a>(sess: &Session, s: &'a str) -> SmallVec<[&'a str; 2]
     }
 }
 
-// Given a map from target_features to whether they are enabled or disabled,
-// ensure only valid combinations are allowed.
+/// Given a map from target_features to whether they are enabled or disabled,
+/// ensure only valid combinations are allowed.
 pub fn check_tied_features(
     sess: &Session,
     features: &FxHashMap<&str, bool>,
@@ -213,8 +213,8 @@ pub fn check_tied_features(
     return None;
 }
 
-// Used to generate cfg variables and apply features
-// Must express features in the way Rust understands them
+/// Used to generate cfg variables and apply features
+/// Must express features in the way Rust understands them
 pub fn target_features(sess: &Session, allow_unstable: bool) -> Vec<Symbol> {
     let target_machine = create_informational_target_machine(sess);
     let mut features: Vec<Symbol> = supported_target_features(sess)
@@ -292,30 +292,33 @@ fn llvm_target_features(tm: &llvm::TargetMachine) -> Vec<(&str, &str)> {
 }
 
 fn print_target_features(sess: &Session, tm: &llvm::TargetMachine) {
-    let mut target_features = llvm_target_features(tm);
+    let mut llvm_target_features = llvm_target_features(tm);
+    let mut known_llvm_target_features = FxHashSet::<&'static str>::default();
     let mut rustc_target_features = supported_target_features(sess)
         .iter()
-        .filter_map(|(feature, _gate)| {
-            for llvm_feature in to_llvm_features(sess, *feature) {
+        .map(|(feature, _gate)| {
+            let desc = if let Some(llvm_feature) = to_llvm_features(sess, *feature).first() {
                 // LLVM asserts that these are sorted. LLVM and Rust both use byte comparison for these strings.
-                match target_features.binary_search_by_key(&llvm_feature, |(f, _d)| f).ok().map(
-                    |index| {
-                        let (_f, desc) = target_features.remove(index);
-                        (*feature, desc)
-                    },
-                ) {
-                    Some(v) => return Some(v),
-                    None => {}
+                match llvm_target_features.binary_search_by_key(&llvm_feature, |(f, _d)| f).ok() {
+                    Some(index) => {
+                        known_llvm_target_features.insert(llvm_feature);
+                        llvm_target_features[index].1
+                    }
+                    None => "",
                 }
-            }
-            None
+            } else {
+                ""
+            };
+            (*feature, desc)
         })
         .collect::<Vec<_>>();
     rustc_target_features.extend_from_slice(&[(
         "crt-static",
         "Enables C Run-time Libraries to be statically linked",
     )]);
-    let max_feature_len = target_features
+    llvm_target_features.retain(|(f, _d)| !known_llvm_target_features.contains(f));
+
+    let max_feature_len = llvm_target_features
         .iter()
         .chain(rustc_target_features.iter())
         .map(|(feature, _desc)| feature.len())
@@ -327,10 +330,10 @@ fn print_target_features(sess: &Session, tm: &llvm::TargetMachine) {
         println!("    {1:0$} - {2}.", max_feature_len, feature, desc);
     }
     println!("\nCode-generation features supported by LLVM for this target:");
-    for (feature, desc) in &target_features {
+    for (feature, desc) in &llvm_target_features {
         println!("    {1:0$} - {2}.", max_feature_len, feature, desc);
     }
-    if target_features.is_empty() {
+    if llvm_target_features.is_empty() {
         println!("    Target features listing is not supported by this LLVM version.");
     }
     println!("\nUse +feature to enable a feature, or -feature to disable it.");
diff --git a/compiler/rustc_codegen_llvm/src/type_.rs b/compiler/rustc_codegen_llvm/src/type_.rs
index 5eec7dc6130..5772b7e1d81 100644
--- a/compiler/rustc_codegen_llvm/src/type_.rs
+++ b/compiler/rustc_codegen_llvm/src/type_.rs
@@ -238,7 +238,7 @@ impl Type {
         unsafe { llvm::LLVMInt8TypeInContext(llcx) }
     }
 
-    // Creates an integer type with the given number of bits, e.g., i24
+    /// Creates an integer type with the given number of bits, e.g., i24
     pub fn ix_llcx(llcx: &llvm::Context, num_bits: u64) -> &Type {
         unsafe { llvm::LLVMIntTypeInContext(llcx, num_bits as c_uint) }
     }
diff --git a/compiler/rustc_codegen_ssa/src/back/link.rs b/compiler/rustc_codegen_ssa/src/back/link.rs
index 762430c6187..7cb4f5503a1 100644
--- a/compiler/rustc_codegen_ssa/src/back/link.rs
+++ b/compiler/rustc_codegen_ssa/src/back/link.rs
@@ -377,12 +377,8 @@ fn link_rlib<'a>(
                 find_native_static_library(name.as_str(), lib.verbatim, &lib_search_paths, sess);
             if sess.opts.unstable_opts.packed_bundled_libs && flavor == RlibFlavor::Normal {
                 let filename = lib.filename.unwrap();
-                let lib_path = find_native_static_library(
-                    filename.as_str(),
-                    Some(true),
-                    &lib_search_paths,
-                    sess,
-                );
+                let lib_path =
+                    find_native_static_library(filename.as_str(), true, &lib_search_paths, sess);
                 let src = read(lib_path)
                     .map_err(|e| sess.emit_fatal(errors::ReadFileError { message: e }))?;
                 let (data, _) = create_wrapper_file(sess, b".bundled_lib".to_vec(), &src);
@@ -465,7 +461,7 @@ fn collate_raw_dylibs<'a, 'b>(
 
     for lib in used_libraries {
         if lib.kind == NativeLibKind::RawDylib {
-            let ext = if matches!(lib.verbatim, Some(true)) { "" } else { ".dll" };
+            let ext = if lib.verbatim { "" } else { ".dll" };
             let name = format!("{}{}", lib.name.expect("unnamed raw-dylib library"), ext);
             let imports = dylib_table.entry(name.clone()).or_default();
             for import in &lib.dll_imports {
@@ -1179,7 +1175,7 @@ pub fn ignored_for_lto(sess: &Session, info: &CrateInfo, cnum: CrateNum) -> bool
         && (info.compiler_builtins == Some(cnum) || info.is_no_builtins.contains(&cnum))
 }
 
-// This functions tries to determine the appropriate linker (and corresponding LinkerFlavor) to use
+/// This functions tries to determine the appropriate linker (and corresponding LinkerFlavor) to use
 pub fn linker_and_flavor(sess: &Session) -> (PathBuf, LinkerFlavor) {
     fn infer_from(
         sess: &Session,
@@ -1335,7 +1331,7 @@ fn print_native_static_libs(sess: &Session, all_native_libs: &[NativeLib]) {
                 NativeLibKind::Static { bundle: Some(false), .. }
                 | NativeLibKind::Dylib { .. }
                 | NativeLibKind::Unspecified => {
-                    let verbatim = lib.verbatim.unwrap_or(false);
+                    let verbatim = lib.verbatim;
                     if sess.target.is_like_msvc {
                         Some(format!("{}{}", name, if verbatim { "" } else { ".lib" }))
                     } else if sess.target.linker_flavor.is_gnu() {
@@ -2306,7 +2302,7 @@ fn add_native_libs_from_crate(
         _ => &codegen_results.crate_info.native_libraries[&cnum],
     };
 
-    let mut last = (None, NativeLibKind::Unspecified, None);
+    let mut last = (None, NativeLibKind::Unspecified, false);
     for lib in native_libs {
         let Some(name) = lib.name else {
             continue;
@@ -2323,7 +2319,7 @@ fn add_native_libs_from_crate(
         };
 
         let name = name.as_str();
-        let verbatim = lib.verbatim.unwrap_or(false);
+        let verbatim = lib.verbatim;
         match lib.kind {
             NativeLibKind::Static { bundle, whole_archive } => {
                 if link_static {
diff --git a/compiler/rustc_codegen_ssa/src/back/linker.rs b/compiler/rustc_codegen_ssa/src/back/linker.rs
index 7f0c2861f7e..f087d903e55 100644
--- a/compiler/rustc_codegen_ssa/src/back/linker.rs
+++ b/compiler/rustc_codegen_ssa/src/back/linker.rs
@@ -34,9 +34,9 @@ pub fn disable_localization(linker: &mut Command) {
     linker.env("VSLANG", "1033");
 }
 
-// The third parameter is for env vars, used on windows to set up the
-// path for MSVC to find its DLLs, and gcc to find its bundled
-// toolchain
+/// The third parameter is for env vars, used on windows to set up the
+/// path for MSVC to find its DLLs, and gcc to find its bundled
+/// toolchain
 pub fn get_linker<'a>(
     sess: &'a Session,
     linker: &Path,
@@ -515,7 +515,7 @@ impl<'a> Linker for GccLinker<'a> {
             // -force_load is the macOS equivalent of --whole-archive, but it
             // involves passing the full path to the library to link.
             self.linker_arg("-force_load");
-            let lib = find_native_static_library(lib, Some(verbatim), search_path, &self.sess);
+            let lib = find_native_static_library(lib, verbatim, search_path, &self.sess);
             self.linker_arg(&lib);
         }
     }
diff --git a/compiler/rustc_codegen_ssa/src/back/metadata.rs b/compiler/rustc_codegen_ssa/src/back/metadata.rs
index 780a3850036..51c5c375d51 100644
--- a/compiler/rustc_codegen_ssa/src/back/metadata.rs
+++ b/compiler/rustc_codegen_ssa/src/back/metadata.rs
@@ -191,38 +191,38 @@ pub enum MetadataPosition {
     Last,
 }
 
-// For rlibs we "pack" rustc metadata into a dummy object file.
-//
-// Historically it was needed because rustc linked rlibs as whole-archive in some cases.
-// In that case linkers try to include all files located in an archive, so if metadata is stored
-// in an archive then it needs to be of a form that the linker is able to process.
-// Now it's not clear whether metadata still needs to be wrapped into an object file or not.
-//
-// Note, though, that we don't actually want this metadata to show up in any
-// final output of the compiler. Instead this is purely for rustc's own
-// metadata tracking purposes.
-//
-// With the above in mind, each "flavor" of object format gets special
-// handling here depending on the target:
-//
-// * MachO - macos-like targets will insert the metadata into a section that
-//   is sort of fake dwarf debug info. Inspecting the source of the macos
-//   linker this causes these sections to be skipped automatically because
-//   it's not in an allowlist of otherwise well known dwarf section names to
-//   go into the final artifact.
-//
-// * WebAssembly - we actually don't have any container format for this
-//   target. WebAssembly doesn't support the `dylib` crate type anyway so
-//   there's no need for us to support this at this time. Consequently the
-//   metadata bytes are simply stored as-is into an rlib.
-//
-// * COFF - Windows-like targets create an object with a section that has
-//   the `IMAGE_SCN_LNK_REMOVE` flag set which ensures that if the linker
-//   ever sees the section it doesn't process it and it's removed.
-//
-// * ELF - All other targets are similar to Windows in that there's a
-//   `SHF_EXCLUDE` flag we can set on sections in an object file to get
-//   automatically removed from the final output.
+/// For rlibs we "pack" rustc metadata into a dummy object file.
+///
+/// Historically it was needed because rustc linked rlibs as whole-archive in some cases.
+/// In that case linkers try to include all files located in an archive, so if metadata is stored
+/// in an archive then it needs to be of a form that the linker is able to process.
+/// Now it's not clear whether metadata still needs to be wrapped into an object file or not.
+///
+/// Note, though, that we don't actually want this metadata to show up in any
+/// final output of the compiler. Instead this is purely for rustc's own
+/// metadata tracking purposes.
+///
+/// With the above in mind, each "flavor" of object format gets special
+/// handling here depending on the target:
+///
+/// * MachO - macos-like targets will insert the metadata into a section that
+///   is sort of fake dwarf debug info. Inspecting the source of the macos
+///   linker this causes these sections to be skipped automatically because
+///   it's not in an allowlist of otherwise well known dwarf section names to
+///   go into the final artifact.
+///
+/// * WebAssembly - we actually don't have any container format for this
+///   target. WebAssembly doesn't support the `dylib` crate type anyway so
+///   there's no need for us to support this at this time. Consequently the
+///   metadata bytes are simply stored as-is into an rlib.
+///
+/// * COFF - Windows-like targets create an object with a section that has
+///   the `IMAGE_SCN_LNK_REMOVE` flag set which ensures that if the linker
+///   ever sees the section it doesn't process it and it's removed.
+///
+/// * ELF - All other targets are similar to Windows in that there's a
+///   `SHF_EXCLUDE` flag we can set on sections in an object file to get
+///   automatically removed from the final output.
 pub fn create_wrapper_file(
     sess: &Session,
     section_name: Vec<u8>,
diff --git a/compiler/rustc_codegen_ssa/src/back/write.rs b/compiler/rustc_codegen_ssa/src/back/write.rs
index e3d28a1aca2..12fca64968a 100644
--- a/compiler/rustc_codegen_ssa/src/back/write.rs
+++ b/compiler/rustc_codegen_ssa/src/back/write.rs
@@ -340,20 +340,20 @@ pub struct CodegenContext<B: WriteBackendMethods> {
     pub split_debuginfo: rustc_target::spec::SplitDebuginfo,
     pub split_dwarf_kind: rustc_session::config::SplitDwarfKind,
 
-    // Number of cgus excluding the allocator/metadata modules
+    /// Number of cgus excluding the allocator/metadata modules
     pub total_cgus: usize,
-    // Handler to use for diagnostics produced during codegen.
+    /// Handler to use for diagnostics produced during codegen.
     pub diag_emitter: SharedEmitter,
-    // LLVM optimizations for which we want to print remarks.
+    /// LLVM optimizations for which we want to print remarks.
     pub remark: Passes,
-    // Worker thread number
+    /// Worker thread number
     pub worker: usize,
-    // The incremental compilation session directory, or None if we are not
-    // compiling incrementally
+    /// The incremental compilation session directory, or None if we are not
+    /// compiling incrementally
     pub incr_comp_session_dir: Option<PathBuf>,
-    // Used to update CGU re-use information during the thinlto phase.
+    /// Used to update CGU re-use information during the thinlto phase.
     pub cgu_reuse_tracker: CguReuseTracker,
-    // Channel back to the main control thread to send messages to
+    /// Channel back to the main control thread to send messages to
     pub coordinator_send: Sender<Box<dyn Any + Send>>,
 }
 
@@ -756,7 +756,7 @@ fn execute_work_item<B: ExtraBackendMethods>(
     }
 }
 
-// Actual LTO type we end up choosing based on multiple factors.
+/// Actual LTO type we end up choosing based on multiple factors.
 pub enum ComputedLtoType {
     No,
     Thin,
diff --git a/compiler/rustc_codegen_ssa/src/debuginfo/type_names.rs b/compiler/rustc_codegen_ssa/src/debuginfo/type_names.rs
index 8647fbace2a..b004fbf85a9 100644
--- a/compiler/rustc_codegen_ssa/src/debuginfo/type_names.rs
+++ b/compiler/rustc_codegen_ssa/src/debuginfo/type_names.rs
@@ -1,4 +1,4 @@
-// Type Names for Debug Info.
+//! Type Names for Debug Info.
 
 // Notes on targeting MSVC:
 // In general, MSVC's debugger attempts to parse all arguments as C++ expressions,
@@ -26,10 +26,10 @@ use std::fmt::Write;
 
 use crate::debuginfo::wants_c_like_enum_debuginfo;
 
-// Compute the name of the type as it should be stored in debuginfo. Does not do
-// any caching, i.e., calling the function twice with the same type will also do
-// the work twice. The `qualified` parameter only affects the first level of the
-// type name, further levels (i.e., type parameters) are always fully qualified.
+/// Compute the name of the type as it should be stored in debuginfo. Does not do
+/// any caching, i.e., calling the function twice with the same type will also do
+/// the work twice. The `qualified` parameter only affects the first level of the
+/// type name, further levels (i.e., type parameters) are always fully qualified.
 pub fn compute_debuginfo_type_name<'tcx>(
     tcx: TyCtxt<'tcx>,
     t: Ty<'tcx>,
diff --git a/compiler/rustc_codegen_ssa/src/lib.rs b/compiler/rustc_codegen_ssa/src/lib.rs
index ade33b6c777..def6390f6a3 100644
--- a/compiler/rustc_codegen_ssa/src/lib.rs
+++ b/compiler/rustc_codegen_ssa/src/lib.rs
@@ -116,7 +116,7 @@ pub struct NativeLib {
     pub name: Option<Symbol>,
     pub filename: Option<Symbol>,
     pub cfg: Option<ast::MetaItem>,
-    pub verbatim: Option<bool>,
+    pub verbatim: bool,
     pub dll_imports: Vec<cstore::DllImport>,
 }
 
@@ -127,7 +127,7 @@ impl From<&cstore::NativeLib> for NativeLib {
             filename: lib.filename,
             name: lib.name,
             cfg: lib.cfg.clone(),
-            verbatim: lib.verbatim,
+            verbatim: lib.verbatim.unwrap_or(false),
             dll_imports: lib.dll_imports.clone(),
         }
     }
diff --git a/compiler/rustc_codegen_ssa/src/mir/operand.rs b/compiler/rustc_codegen_ssa/src/mir/operand.rs
index e6ba642a7ed..34a5b638d7e 100644
--- a/compiler/rustc_codegen_ssa/src/mir/operand.rs
+++ b/compiler/rustc_codegen_ssa/src/mir/operand.rs
@@ -40,10 +40,10 @@ pub enum OperandValue<V> {
 /// instead.
 #[derive(Copy, Clone)]
 pub struct OperandRef<'tcx, V> {
-    // The value.
+    /// The value.
     pub val: OperandValue<V>,
 
-    // The layout of value, based on its Rust type.
+    /// The layout of value, based on its Rust type.
     pub layout: TyAndLayout<'tcx>,
 }
 
diff --git a/compiler/rustc_const_eval/src/const_eval/eval_queries.rs b/compiler/rustc_const_eval/src/const_eval/eval_queries.rs
index c777a840f3f..c27790d8887 100644
--- a/compiler/rustc_const_eval/src/const_eval/eval_queries.rs
+++ b/compiler/rustc_const_eval/src/const_eval/eval_queries.rs
@@ -66,7 +66,7 @@ fn eval_body_using_ecx<'mir, 'tcx>(
     )?;
 
     // The main interpreter loop.
-    ecx.run()?;
+    while ecx.step()? {}
 
     // Intern the result
     let intern_kind = if cid.promoted.is_some() {
diff --git a/compiler/rustc_const_eval/src/interpret/machine.rs b/compiler/rustc_const_eval/src/interpret/machine.rs
index 351152eba01..88d25be6bd8 100644
--- a/compiler/rustc_const_eval/src/interpret/machine.rs
+++ b/compiler/rustc_const_eval/src/interpret/machine.rs
@@ -417,8 +417,8 @@ pub trait Machine<'mir, 'tcx>: Sized {
     }
 }
 
-// A lot of the flexibility above is just needed for `Miri`, but all "compile-time" machines
-// (CTFE and ConstProp) use the same instance.  Here, we share that code.
+/// A lot of the flexibility above is just needed for `Miri`, but all "compile-time" machines
+/// (CTFE and ConstProp) use the same instance.  Here, we share that code.
 pub macro compile_time_machine(<$mir: lifetime, $tcx: lifetime>) {
     type Provenance = AllocId;
     type ProvenanceExtra = ();
diff --git a/compiler/rustc_const_eval/src/interpret/projection.rs b/compiler/rustc_const_eval/src/interpret/projection.rs
index 4966fd6ea80..2ffd73eef3e 100644
--- a/compiler/rustc_const_eval/src/interpret/projection.rs
+++ b/compiler/rustc_const_eval/src/interpret/projection.rs
@@ -206,8 +206,8 @@ where
         }
     }
 
-    // Iterates over all fields of an array. Much more efficient than doing the
-    // same by repeatedly calling `operand_index`.
+    /// Iterates over all fields of an array. Much more efficient than doing the
+    /// same by repeatedly calling `operand_index`.
     pub fn operand_array_fields<'a>(
         &self,
         base: &'a OpTy<'tcx, Prov>,
diff --git a/compiler/rustc_const_eval/src/interpret/step.rs b/compiler/rustc_const_eval/src/interpret/step.rs
index 73f8bf4362e..60578246eed 100644
--- a/compiler/rustc_const_eval/src/interpret/step.rs
+++ b/compiler/rustc_const_eval/src/interpret/step.rs
@@ -32,11 +32,6 @@ fn binop_right_homogeneous(op: mir::BinOp) -> bool {
 }
 
 impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
-    pub fn run(&mut self) -> InterpResult<'tcx> {
-        while self.step()? {}
-        Ok(())
-    }
-
     /// Returns `true` as long as there are more things to do.
     ///
     /// This is used by [priroda](https://github.com/oli-obk/priroda)
diff --git a/compiler/rustc_const_eval/src/interpret/visitor.rs b/compiler/rustc_const_eval/src/interpret/visitor.rs
index aee1f93b1a3..1a10851a9f9 100644
--- a/compiler/rustc_const_eval/src/interpret/visitor.rs
+++ b/compiler/rustc_const_eval/src/interpret/visitor.rs
@@ -324,7 +324,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValueMut<'mir, 'tcx, M>
 
 macro_rules! make_value_visitor {
     ($visitor_trait:ident, $value_trait:ident, $($mutability:ident)?) => {
-        // How to traverse a value and what to do when we are at the leaves.
+        /// How to traverse a value and what to do when we are at the leaves.
         pub trait $visitor_trait<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>>: Sized {
             type V: $value_trait<'mir, 'tcx, M>;
 
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 d4cee305387..54213d55a2d 100644
--- a/compiler/rustc_const_eval/src/transform/check_consts/check.rs
+++ b/compiler/rustc_const_eval/src/transform/check_consts/check.rs
@@ -761,8 +761,7 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> {
                             hir_id,
                             ObligationCauseCode::ItemObligation(callee),
                         );
-                        let normalized_predicates =
-                            ocx.normalize(cause.clone(), param_env, predicates);
+                        let normalized_predicates = ocx.normalize(&cause, param_env, predicates);
                         ocx.register_obligations(traits::predicates_for_generics(
                             |_, _| cause.clone(),
                             self.param_env,
diff --git a/compiler/rustc_const_eval/src/transform/check_consts/mod.rs b/compiler/rustc_const_eval/src/transform/check_consts/mod.rs
index b90e0962ce6..655ec345ed3 100644
--- a/compiler/rustc_const_eval/src/transform/check_consts/mod.rs
+++ b/compiler/rustc_const_eval/src/transform/check_consts/mod.rs
@@ -75,14 +75,14 @@ pub fn rustc_allow_const_fn_unstable(
     attr::rustc_allow_const_fn_unstable(&tcx.sess, attrs).any(|name| name == feature_gate)
 }
 
-// Returns `true` if the given `const fn` is "const-stable".
-//
-// Panics if the given `DefId` does not refer to a `const fn`.
-//
-// Const-stability is only relevant for `const fn` within a `staged_api` crate. Only "const-stable"
-// functions can be called in a const-context by users of the stable compiler. "const-stable"
-// functions are subject to more stringent restrictions than "const-unstable" functions: They
-// cannot use unstable features and can only call other "const-stable" functions.
+/// Returns `true` if the given `const fn` is "const-stable".
+///
+/// Panics if the given `DefId` does not refer to a `const fn`.
+///
+/// Const-stability is only relevant for `const fn` within a `staged_api` crate. Only "const-stable"
+/// functions can be called in a const-context by users of the stable compiler. "const-stable"
+/// functions are subject to more stringent restrictions than "const-unstable" functions: They
+/// cannot use unstable features and can only call other "const-stable" functions.
 pub fn is_const_stable_const_fn(tcx: TyCtxt<'_>, def_id: DefId) -> bool {
     // A default body in a `#[const_trait]` is not const-stable because const
     // trait fns currently cannot be const-stable. We shouldn't
diff --git a/compiler/rustc_const_eval/src/transform/check_consts/ops.rs b/compiler/rustc_const_eval/src/transform/check_consts/ops.rs
index bfc950eff5c..b19d270e610 100644
--- a/compiler/rustc_const_eval/src/transform/check_consts/ops.rs
+++ b/compiler/rustc_const_eval/src/transform/check_consts/ops.rs
@@ -376,7 +376,7 @@ impl<'tcx> NonConstOp<'tcx> for Generator {
         ccx: &ConstCx<'_, 'tcx>,
         span: Span,
     ) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> {
-        let msg = format!("{}s are not allowed in {}s", self.0, ccx.const_kind());
+        let msg = format!("{}s are not allowed in {}s", self.0.descr(), ccx.const_kind());
         if let hir::GeneratorKind::Async(hir::AsyncGeneratorKind::Block) = self.0 {
             ccx.tcx.sess.create_feature_err(
                 UnallowedOpInConstContext { span, msg },
@@ -686,7 +686,7 @@ impl<'tcx> NonConstOp<'tcx> for ThreadLocalAccess {
     }
 }
 
-// Types that cannot appear in the signature or locals of a `const fn`.
+/// Types that cannot appear in the signature or locals of a `const fn`.
 pub mod ty {
     use super::*;
 
diff --git a/compiler/rustc_const_eval/src/transform/promote_consts.rs b/compiler/rustc_const_eval/src/transform/promote_consts.rs
index f48bcd90809..6777fae74f1 100644
--- a/compiler/rustc_const_eval/src/transform/promote_consts.rs
+++ b/compiler/rustc_const_eval/src/transform/promote_consts.rs
@@ -318,14 +318,14 @@ impl<'tcx> Validator<'_, 'tcx> {
                 match elem {
                     ProjectionElem::Deref => {
                         let mut promotable = false;
+                        // When a static is used by-value, that gets desugared to `*STATIC_ADDR`,
+                        // and we need to be able to promote this. So check if this deref matches
+                        // that specific pattern.
+
                         // We need to make sure this is a `Deref` of a local with no further projections.
                         // Discussion can be found at
                         // https://github.com/rust-lang/rust/pull/74945#discussion_r463063247
                         if let Some(local) = place_base.as_local() {
-                            // This is a special treatment for cases like *&STATIC where STATIC is a
-                            // global static variable.
-                            // This pattern is generated only when global static variables are directly
-                            // accessed and is qualified for promotion safely.
                             if let TempState::Defined { location, .. } = self.temps[local] {
                                 let def_stmt = self.body[location.block]
                                     .statements
diff --git a/compiler/rustc_const_eval/src/util/aggregate.rs b/compiler/rustc_const_eval/src/util/aggregate.rs
index 180a40043db..c43de3368c6 100644
--- a/compiler/rustc_const_eval/src/util/aggregate.rs
+++ b/compiler/rustc_const_eval/src/util/aggregate.rs
@@ -9,10 +9,11 @@ use std::iter::TrustedLen;
 /// Expand `lhs = Rvalue::Aggregate(kind, operands)` into assignments to the fields.
 ///
 /// Produces something like
-///
+/// ```ignore (ilustrative)
 /// (lhs as Variant).field0 = arg0;     // We only have a downcast if this is an enum
 /// (lhs as Variant).field1 = arg1;
 /// discriminant(lhs) = variant_index;  // If lhs is an enum or generator.
+/// ```
 pub fn expand_aggregate<'tcx>(
     orig_lhs: Place<'tcx>,
     operands: impl Iterator<Item = (Operand<'tcx>, Ty<'tcx>)> + TrustedLen,
diff --git a/compiler/rustc_const_eval/src/util/compare_types.rs b/compiler/rustc_const_eval/src/util/compare_types.rs
index a9cb191cc59..be786569cde 100644
--- a/compiler/rustc_const_eval/src/util/compare_types.rs
+++ b/compiler/rustc_const_eval/src/util/compare_types.rs
@@ -46,8 +46,8 @@ pub fn is_subtype<'tcx>(
     let infcx = builder.build();
     let ocx = ObligationCtxt::new(&infcx);
     let cause = ObligationCause::dummy();
-    let src = ocx.normalize(cause.clone(), param_env, src);
-    let dest = ocx.normalize(cause.clone(), param_env, dest);
+    let src = ocx.normalize(&cause, param_env, src);
+    let dest = ocx.normalize(&cause, param_env, dest);
     match ocx.sub(&cause, param_env, src, dest) {
         Ok(()) => {}
         Err(_) => return false,
diff --git a/compiler/rustc_data_structures/src/sorted_map.rs b/compiler/rustc_data_structures/src/sorted_map.rs
index fe257e10205..d607a5c8314 100644
--- a/compiler/rustc_data_structures/src/sorted_map.rs
+++ b/compiler/rustc_data_structures/src/sorted_map.rs
@@ -10,8 +10,8 @@ mod index_map;
 pub use index_map::SortedIndexMultiMap;
 
 /// `SortedMap` is a data structure with similar characteristics as BTreeMap but
-/// slightly different trade-offs: lookup, insertion, and removal are *O*(log(*n*))
-/// and elements can be iterated in order cheaply.
+/// slightly different trade-offs: lookup is *O*(log(*n*)), insertion and removal
+/// are *O*(*n*) but elements can be iterated in order cheaply.
 ///
 /// `SortedMap` can be faster than a `BTreeMap` for small sizes (<50) since it
 /// stores data in a more compact way. It also supports accessing contiguous
diff --git a/compiler/rustc_data_structures/src/stable_hasher.rs b/compiler/rustc_data_structures/src/stable_hasher.rs
index ce859173418..e2c33e7e062 100644
--- a/compiler/rustc_data_structures/src/stable_hasher.rs
+++ b/compiler/rustc_data_structures/src/stable_hasher.rs
@@ -399,7 +399,7 @@ where
     }
 }
 
-impl<A, CTX> HashStable<CTX> for SmallVec<[A; 1]>
+impl<A, const N: usize, CTX> HashStable<CTX> for SmallVec<[A; N]>
 where
     A: HashStable<CTX>,
 {
diff --git a/compiler/rustc_driver/src/lib.rs b/compiler/rustc_driver/src/lib.rs
index 8e176efb2a9..380fbd732d5 100644
--- a/compiler/rustc_driver/src/lib.rs
+++ b/compiler/rustc_driver/src/lib.rs
@@ -1336,8 +1336,8 @@ mod signal_handler {
         }
     }
 
-    // When an error signal (such as SIGABRT or SIGSEGV) is delivered to the
-    // process, print a stack trace and then exit.
+    /// When an error signal (such as SIGABRT or SIGSEGV) is delivered to the
+    /// process, print a stack trace and then exit.
     pub(super) fn install() {
         unsafe {
             const ALT_STACK_SIZE: usize = libc::MINSIGSTKSZ + 64 * 1024;
diff --git a/compiler/rustc_errors/src/emitter.rs b/compiler/rustc_errors/src/emitter.rs
index bf20c7431e4..1fabe15ff83 100644
--- a/compiler/rustc_errors/src/emitter.rs
+++ b/compiler/rustc_errors/src/emitter.rs
@@ -2282,7 +2282,7 @@ impl FileWithAnnotatedLines {
         }
 
         // Find overlapping multiline annotations, put them at different depths
-        multiline_annotations.sort_by_key(|&(_, ref ml)| (ml.line_start, ml.line_end));
+        multiline_annotations.sort_by_key(|&(_, ref ml)| (ml.line_start, usize::MAX - ml.line_end));
         for (_, ann) in multiline_annotations.clone() {
             for (_, a) in multiline_annotations.iter_mut() {
                 // Move all other multiline annotations overlapping with this one
@@ -2300,8 +2300,14 @@ impl FileWithAnnotatedLines {
         }
 
         let mut max_depth = 0; // max overlapping multiline spans
-        for (file, ann) in multiline_annotations {
+        for (_, ann) in &multiline_annotations {
             max_depth = max(max_depth, ann.depth);
+        }
+        // Change order of multispan depth to minimize the number of overlaps in the ASCII art.
+        for (_, a) in multiline_annotations.iter_mut() {
+            a.depth = max_depth - a.depth + 1;
+        }
+        for (file, ann) in multiline_annotations {
             let mut end_ann = ann.as_end();
             if !ann.overlaps_exactly {
                 // avoid output like
diff --git a/compiler/rustc_expand/src/base.rs b/compiler/rustc_expand/src/base.rs
index 8955abebf1e..13e2d1ebbe7 100644
--- a/compiler/rustc_expand/src/base.rs
+++ b/compiler/rustc_expand/src/base.rs
@@ -16,6 +16,7 @@ use rustc_errors::{
 use rustc_lint_defs::builtin::PROC_MACRO_BACK_COMPAT;
 use rustc_lint_defs::{BufferedEarlyLint, BuiltinLintDiagnostics};
 use rustc_parse::{self, parser, MACRO_ARGUMENTS};
+use rustc_session::errors::report_lit_error;
 use rustc_session::{parse::ParseSess, Limit, Session};
 use rustc_span::def_id::{CrateNum, DefId, LocalDefId};
 use rustc_span::edition::Edition;
@@ -242,8 +243,8 @@ pub enum ExpandResult<T, U> {
     Retry(U),
 }
 
-// `meta_item` is the attribute, and `item` is the item being modified.
 pub trait MultiItemModifier {
+    /// `meta_item` is the attribute, and `item` is the item being modified.
     fn expand(
         &self,
         ecx: &mut ExtCtxt<'_>,
@@ -1245,7 +1246,10 @@ pub fn expr_to_spanned_string<'a>(
                 Some((err, true))
             }
             Ok(ast::LitKind::Err) => None,
-            Err(_) => None,
+            Err(err) => {
+                report_lit_error(&cx.sess.parse_sess, err, token_lit, expr.span);
+                None
+            }
             _ => Some((cx.struct_span_err(expr.span, err_msg), false)),
         },
         ast::ExprKind::Err => None,
diff --git a/compiler/rustc_expand/src/build.rs b/compiler/rustc_expand/src/build.rs
index e17cba1478a..c978297295d 100644
--- a/compiler/rustc_expand/src/build.rs
+++ b/compiler/rustc_expand/src/build.rs
@@ -193,7 +193,7 @@ impl<'a> ExtCtxt<'a> {
         self.stmt_local(local, sp)
     }
 
-    // Generates `let _: Type;`, which is usually used for type assertions.
+    /// Generates `let _: Type;`, which is usually used for type assertions.
     pub fn stmt_let_type_only(&self, span: Span, ty: P<ast::Ty>) -> ast::Stmt {
         let local = P(ast::Local {
             pat: self.pat_wild(span),
@@ -579,8 +579,6 @@ impl<'a> ExtCtxt<'a> {
         attrs: ast::AttrVec,
         kind: ast::ItemKind,
     ) -> P<ast::Item> {
-        // FIXME: Would be nice if our generated code didn't violate
-        // Rust coding conventions
         P(ast::Item {
             ident: name,
             attrs,
@@ -618,11 +616,23 @@ impl<'a> ExtCtxt<'a> {
         self.item(span, name, AttrVec::new(), ast::ItemKind::Const(def, ty, Some(expr)))
     }
 
-    pub fn attribute(&self, mi: ast::MetaItem) -> ast::Attribute {
-        attr::mk_attr_outer(&self.sess.parse_sess.attr_id_generator, mi)
+    // Builds `#[name]`.
+    pub fn attr_word(&self, name: Symbol, span: Span) -> ast::Attribute {
+        let g = &self.sess.parse_sess.attr_id_generator;
+        attr::mk_attr_word(g, ast::AttrStyle::Outer, name, span)
     }
 
-    pub fn meta_word(&self, sp: Span, w: Symbol) -> ast::MetaItem {
-        attr::mk_word_item(Ident::new(w, sp))
+    // Builds `#[name = val]`.
+    //
+    // Note: `span` is used for both the identifer and the value.
+    pub fn attr_name_value_str(&self, name: Symbol, val: Symbol, span: Span) -> ast::Attribute {
+        let g = &self.sess.parse_sess.attr_id_generator;
+        attr::mk_attr_name_value_str(g, ast::AttrStyle::Outer, name, val, span)
+    }
+
+    // Builds `#[outer(inner)]`.
+    pub fn attr_nested_word(&self, outer: Symbol, inner: Symbol, span: Span) -> ast::Attribute {
+        let g = &self.sess.parse_sess.attr_id_generator;
+        attr::mk_attr_nested_word(g, ast::AttrStyle::Outer, outer, inner, span)
     }
 }
diff --git a/compiler/rustc_expand/src/config.rs b/compiler/rustc_expand/src/config.rs
index 1d2b1298a68..2510795c2e3 100644
--- a/compiler/rustc_expand/src/config.rs
+++ b/compiler/rustc_expand/src/config.rs
@@ -200,7 +200,7 @@ fn get_features(
     features
 }
 
-// `cfg_attr`-process the crate's attributes and compute the crate's features.
+/// `cfg_attr`-process the crate's attributes and compute the crate's features.
 pub fn features(
     sess: &Session,
     mut krate: ast::Crate,
diff --git a/compiler/rustc_expand/src/expand.rs b/compiler/rustc_expand/src/expand.rs
index c2b1b96cd64..e799fa404f6 100644
--- a/compiler/rustc_expand/src/expand.rs
+++ b/compiler/rustc_expand/src/expand.rs
@@ -401,7 +401,7 @@ impl<'a, 'b> MacroExpander<'a, 'b> {
         krate
     }
 
-    // Recursively expand all macro invocations in this AST fragment.
+    /// Recursively expand all macro invocations in this AST fragment.
     pub fn fully_expand_fragment(&mut self, input_fragment: AstFragment) -> AstFragment {
         let orig_expansion_data = self.cx.current_expansion.clone();
         let orig_force_mode = self.cx.force_mode;
@@ -1644,7 +1644,7 @@ impl<'a, 'b> InvocationCollector<'a, 'b> {
         let mut span: Option<Span> = None;
         while let Some(attr) = attrs.next() {
             rustc_ast_passes::feature_gate::check_attribute(attr, self.cx.sess, features);
-            validate_attr::check_meta(&self.cx.sess.parse_sess, attr);
+            validate_attr::check_attr(&self.cx.sess.parse_sess, attr);
 
             let current_span = if let Some(sp) = span { sp.to(attr.span) } else { attr.span };
             span = Some(current_span);
@@ -1931,9 +1931,12 @@ pub struct ExpansionConfig<'feat> {
     pub features: Option<&'feat Features>,
     pub recursion_limit: Limit,
     pub trace_mac: bool,
-    pub should_test: bool,          // If false, strip `#[test]` nodes
-    pub span_debug: bool,           // If true, use verbose debugging for `proc_macro::Span`
-    pub proc_macro_backtrace: bool, // If true, show backtraces for proc-macro panics
+    /// If false, strip `#[test]` nodes
+    pub should_test: bool,
+    /// If true, use verbose debugging for `proc_macro::Span`
+    pub span_debug: bool,
+    /// If true, show backtraces for proc-macro panics
+    pub proc_macro_backtrace: bool,
 }
 
 impl<'feat> ExpansionConfig<'feat> {
diff --git a/compiler/rustc_expand/src/proc_macro_server.rs b/compiler/rustc_expand/src/proc_macro_server.rs
index 2e832deeecd..76165796117 100644
--- a/compiler/rustc_expand/src/proc_macro_server.rs
+++ b/compiler/rustc_expand/src/proc_macro_server.rs
@@ -526,11 +526,8 @@ impl server::TokenStream for Rustc<'_, '_> {
                 Ok(tokenstream::TokenStream::token_alone(token::Literal(*token_lit), expr.span))
             }
             ast::ExprKind::IncludedBytes(bytes) => {
-                let lit = ast::Lit::from_included_bytes(bytes, expr.span);
-                Ok(tokenstream::TokenStream::token_alone(
-                    token::TokenKind::Literal(lit.token_lit),
-                    expr.span,
-                ))
+                let lit = ast::LitKind::ByteStr(bytes.clone()).to_token_lit();
+                Ok(tokenstream::TokenStream::token_alone(token::TokenKind::Literal(lit), expr.span))
             }
             ast::ExprKind::Unary(ast::UnOp::Neg, e) => match &e.kind {
                 ast::ExprKind::Lit(token_lit) => match token_lit {
diff --git a/compiler/rustc_expand/src/tests.rs b/compiler/rustc_expand/src/tests.rs
index d82a7a54030..539b04535a0 100644
--- a/compiler/rustc_expand/src/tests.rs
+++ b/compiler/rustc_expand/src/tests.rs
@@ -272,13 +272,13 @@ error: foo
  --> test.rs:3:3
   |
 3 |      X0 Y0
-  |  ____^__-
-  | | ___|
+  |   ___^__-
+  |  |___|
   | ||
 4 | ||   X1 Y1
 5 | ||   X2 Y2
   | ||____^__- `Y` is a good letter too
-  |  |____|
+  | |_____|
   |       `X` is a good letter
 
 "#,
@@ -311,12 +311,12 @@ error: foo
  --> test.rs:3:3
   |
 3 |      X0 Y0
-  |  ____^__-
-  | | ___|
+  |   ___^__-
+  |  |___|
   | ||
 4 | ||   Y1 X1
   | ||____-__^ `X` is a good letter
-  | |_____|
+  |  |____|
   |       `Y` is a good letter too
 
 "#,
@@ -351,13 +351,13 @@ error: foo
  --> test.rs:3:6
   |
 3 |      X0 Y0 Z0
-  |   ______^
-4 |  |   X1 Y1 Z1
-  |  |_________-
+  |  _______^
+4 | |    X1 Y1 Z1
+  | | _________-
 5 | ||   X2 Y2 Z2
   | ||____^ `X` is a good letter
-6 | |    X3 Y3 Z3
-  | |_____- `Y` is a good letter too
+6 |  |   X3 Y3 Z3
+  |  |____- `Y` is a good letter too
 
 "#,
     );
@@ -395,15 +395,15 @@ error: foo
  --> test.rs:3:3
   |
 3 |       X0 Y0 Z0
-  |  _____^__-__-
-  | | ____|__|
-  | || ___|
+  |    ___^__-__-
+  |   |___|__|
+  |  ||___|
   | |||
 4 | |||   X1 Y1 Z1
 5 | |||   X2 Y2 Z2
   | |||____^__-__- `Z` label
-  |  ||____|__|
-  |   |____|  `Y` is a good letter too
+  | ||_____|__|
+  | |______|  `Y` is a good letter too
   |        `X` is a good letter
 
 "#,
@@ -487,17 +487,17 @@ error: foo
  --> test.rs:3:6
   |
 3 |      X0 Y0 Z0
-  |   ______^
-4 |  |   X1 Y1 Z1
-  |  |____^_-
+  |  _______^
+4 | |    X1 Y1 Z1
+  | | ____^_-
   | ||____|
-  | |     `X` is a good letter
-5 | |    X2 Y2 Z2
-  | |____-______- `Y` is a good letter too
-  |  ____|
-  | |
-6 | |    X3 Y3 Z3
-  | |________- `Z`
+  |  |    `X` is a good letter
+5 |  |   X2 Y2 Z2
+  |  |___-______- `Y` is a good letter too
+  |   ___|
+  |  |
+6 |  |   X3 Y3 Z3
+  |  |_______- `Z`
 
 "#,
     );
@@ -570,14 +570,14 @@ error: foo
  --> test.rs:3:6
   |
 3 |      X0 Y0 Z0
-  |   ______^
-4 |  |   X1 Y1 Z1
-  |  |____^____-
+  |  _______^
+4 | |    X1 Y1 Z1
+  | | ____^____-
   | ||____|
-  | |     `X` is a good letter
-5 | |    X2 Y2 Z2
-6 | |    X3 Y3 Z3
-  | |___________- `Y` is a good letter too
+  |  |    `X` is a good letter
+5 |  |   X2 Y2 Z2
+6 |  |   X3 Y3 Z3
+  |  |__________- `Y` is a good letter too
 
 "#,
     );
@@ -941,18 +941,18 @@ error: foo
   --> test.rs:3:6
    |
 3  |      X0 Y0 Z0
-   |   ______^
-4  |  |   X1 Y1 Z1
-   |  |____^____-
+   |  _______^
+4  | |    X1 Y1 Z1
+   | | ____^____-
    | ||____|
-   | |     `X` is a good letter
-5  | |  1
-6  | |  2
-7  | |  3
-...  |
-15 | |    X2 Y2 Z2
-16 | |    X3 Y3 Z3
-   | |___________- `Y` is a good letter too
+   |  |    `X` is a good letter
+5  |  | 1
+6  |  | 2
+7  |  | 3
+...   |
+15 |  |   X2 Y2 Z2
+16 |  |   X3 Y3 Z3
+   |  |__________- `Y` is a good letter too
 
 "#,
     );
@@ -996,21 +996,21 @@ error: foo
   --> test.rs:3:6
    |
 3  |      X0 Y0 Z0
-   |   ______^
-4  |  | 1
-5  |  | 2
-6  |  | 3
-7  |  |   X1 Y1 Z1
-   |  |_________-
+   |  _______^
+4  | |  1
+5  | |  2
+6  | |  3
+7  | |    X1 Y1 Z1
+   | | _________-
 8  | || 4
 9  | || 5
 10 | || 6
 11 | ||   X2 Y2 Z2
    | ||__________- `Z` is a good letter too
-...   |
-15 |  | 10
-16 |  |   X3 Y3 Z3
-   |  |_______^ `Y` is a good letter
+...  |
+15 | |  10
+16 | |    X3 Y3 Z3
+   | |________^ `Y` is a good letter
 
 "#,
     );
diff --git a/compiler/rustc_feature/src/accepted.rs b/compiler/rustc_feature/src/accepted.rs
index 1646727a1c8..7678ce323df 100644
--- a/compiler/rustc_feature/src/accepted.rs
+++ b/compiler/rustc_feature/src/accepted.rs
@@ -237,6 +237,8 @@ declare_features! (
     (accepted, native_link_modifiers, "1.61.0", Some(81490), None),
     /// Allows specifying the bundle link modifier
     (accepted, native_link_modifiers_bundle, "1.63.0", Some(81490), None),
+    /// Allows specifying the verbatim link modifier
+    (accepted, native_link_modifiers_verbatim, "CURRENT_RUSTC_VERSION", Some(81490), None),
     /// Allows specifying the whole-archive link modifier
     (accepted, native_link_modifiers_whole_archive, "1.61.0", Some(81490), None),
     /// Allows using non lexical lifetimes (RFC 2094).
diff --git a/compiler/rustc_feature/src/active.rs b/compiler/rustc_feature/src/active.rs
index 9ca63c393c6..69c5297bf6b 100644
--- a/compiler/rustc_feature/src/active.rs
+++ b/compiler/rustc_feature/src/active.rs
@@ -455,8 +455,6 @@ declare_features! (
     (active, naked_functions, "1.9.0", Some(32408), None),
     /// Allows specifying the as-needed link modifier
     (active, native_link_modifiers_as_needed, "1.53.0", Some(81490), None),
-    /// Allows specifying the verbatim link modifier
-    (active, native_link_modifiers_verbatim, "1.53.0", Some(81490), None),
     /// Allow negative trait implementations.
     (active, negative_impls, "1.44.0", Some(68318), None),
     /// Allows the `!` type. Does not imply 'exhaustive_patterns' (below) any more.
diff --git a/compiler/rustc_hir/src/arena.rs b/compiler/rustc_hir/src/arena.rs
index 44335b7f42e..c89e7eb75f8 100644
--- a/compiler/rustc_hir/src/arena.rs
+++ b/compiler/rustc_hir/src/arena.rs
@@ -39,6 +39,7 @@ macro_rules! arena_types {
             [] param: rustc_hir::Param<'tcx>,
             [] pat: rustc_hir::Pat<'tcx>,
             [] path: rustc_hir::Path<'tcx>,
+            [] use_path: rustc_hir::UsePath<'tcx>,
             [] path_segment: rustc_hir::PathSegment<'tcx>,
             [] poly_trait_ref: rustc_hir::PolyTraitRef<'tcx>,
             [] qpath: rustc_hir::QPath<'tcx>,
diff --git a/compiler/rustc_hir/src/definitions.rs b/compiler/rustc_hir/src/definitions.rs
index d85ac960f9b..dd37efb6983 100644
--- a/compiler/rustc_hir/src/definitions.rs
+++ b/compiler/rustc_hir/src/definitions.rs
@@ -368,10 +368,6 @@ impl Definitions {
         LocalDefId { local_def_index: self.table.allocate(key, def_path_hash) }
     }
 
-    pub fn iter_local_def_id(&self) -> impl Iterator<Item = LocalDefId> + '_ {
-        self.table.def_path_hashes.indices().map(|local_def_index| LocalDefId { local_def_index })
-    }
-
     #[inline(always)]
     pub fn local_def_path_hash_to_def_id(
         &self,
@@ -389,6 +385,10 @@ impl Definitions {
     pub fn def_path_hash_to_def_index_map(&self) -> &DefPathHashMap {
         &self.table.def_path_hash_to_index
     }
+
+    pub fn num_definitions(&self) -> usize {
+        self.table.def_path_hashes.len()
+    }
 }
 
 #[derive(Copy, Clone, PartialEq, Debug)]
diff --git a/compiler/rustc_hir/src/hir.rs b/compiler/rustc_hir/src/hir.rs
index 208d2fb42e4..118eafe2910 100644
--- a/compiler/rustc_hir/src/hir.rs
+++ b/compiler/rustc_hir/src/hir.rs
@@ -183,14 +183,17 @@ impl Lifetime {
 /// `std::cmp::PartialEq`. It's represented as a sequence of identifiers,
 /// along with a bunch of supporting information.
 #[derive(Debug, HashStable_Generic)]
-pub struct Path<'hir> {
+pub struct Path<'hir, R = Res> {
     pub span: Span,
     /// The resolution for the path.
-    pub res: Res,
+    pub res: R,
     /// The segments in the path: the things separated by `::`.
     pub segments: &'hir [PathSegment<'hir>],
 }
 
+/// Up to three resolutions for type, value and macro namespaces.
+pub type UsePath<'hir> = Path<'hir, SmallVec<[Res; 3]>>;
+
 impl Path<'_> {
     pub fn is_global(&self) -> bool {
         !self.segments.is_empty() && self.segments[0].ident.name == kw::PathRoot
@@ -981,8 +984,8 @@ pub struct Pat<'hir> {
     pub hir_id: HirId,
     pub kind: PatKind<'hir>,
     pub span: Span,
-    // Whether to use default binding modes.
-    // At present, this is false only for destructuring assignment.
+    /// Whether to use default binding modes.
+    /// At present, this is false only for destructuring assignment.
     pub default_binding_modes: bool,
 }
 
@@ -1090,7 +1093,7 @@ impl fmt::Display for RangeEnd {
 pub struct DotDotPos(u32);
 
 impl DotDotPos {
-    // Panics if n >= u32::MAX.
+    /// Panics if n >= u32::MAX.
     pub fn new(n: Option<usize>) -> Self {
         match n {
             Some(n) => {
@@ -1526,9 +1529,9 @@ pub enum AsyncGeneratorKind {
 impl fmt::Display for AsyncGeneratorKind {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
         f.write_str(match self {
-            AsyncGeneratorKind::Block => "`async` block",
-            AsyncGeneratorKind::Closure => "`async` closure body",
-            AsyncGeneratorKind::Fn => "`async fn` body",
+            AsyncGeneratorKind::Block => "async block",
+            AsyncGeneratorKind::Closure => "async closure body",
+            AsyncGeneratorKind::Fn => "async fn body",
         })
     }
 }
@@ -1694,10 +1697,10 @@ impl Expr<'_> {
         }
     }
 
-    // Whether this looks like a place expr, without checking for deref
-    // adjustments.
-    // This will return `true` in some potentially surprising cases such as
-    // `CONSTANT.field`.
+    /// Whether this looks like a place expr, without checking for deref
+    /// adjustments.
+    /// This will return `true` in some potentially surprising cases such as
+    /// `CONSTANT.field`.
     pub fn is_syntactic_place_expr(&self) -> bool {
         self.is_place_expr(|_| true)
     }
@@ -1838,7 +1841,7 @@ impl Expr<'_> {
         }
     }
 
-    // To a first-order approximation, is this a pattern
+    /// To a first-order approximation, is this a pattern?
     pub fn is_approximately_pattern(&self) -> bool {
         match &self.kind {
             ExprKind::Box(_)
@@ -2160,11 +2163,11 @@ impl fmt::Display for LoopIdError {
 
 #[derive(Copy, Clone, Encodable, Debug, HashStable_Generic)]
 pub struct Destination {
-    // This is `Some(_)` iff there is an explicit user-specified `label
+    /// This is `Some(_)` iff there is an explicit user-specified 'label
     pub label: Option<Label>,
 
-    // These errors are caught and then reported during the diagnostics pass in
-    // librustc_passes/loops.rs
+    /// These errors are caught and then reported during the diagnostics pass in
+    /// `librustc_passes/loops.rs`
     pub target_id: Result<HirId, LoopIdError>,
 }
 
@@ -2335,7 +2338,7 @@ pub enum ImplItemKind<'hir> {
     Type(&'hir Ty<'hir>),
 }
 
-// The name of the associated type for `Fn` return types.
+/// The name of the associated type for `Fn` return types.
 pub const FN_OUTPUT_NAME: Symbol = sym::Output;
 
 /// Bind a type to an associated type (i.e., `A = Foo`).
@@ -3068,7 +3071,7 @@ pub enum ItemKind<'hir> {
     /// or just
     ///
     /// `use foo::bar::baz;` (with `as baz` implicitly on the right).
-    Use(&'hir Path<'hir>, UseKind),
+    Use(&'hir UsePath<'hir>, UseKind),
 
     /// A `static` item.
     Static(&'hir Ty<'hir>, Mutability, BodyId),
@@ -3261,7 +3264,7 @@ pub enum ForeignItemKind<'hir> {
 /// A variable captured by a closure.
 #[derive(Debug, Copy, Clone, Encodable, HashStable_Generic)]
 pub struct Upvar {
-    // First span where it is accessed (there can be multiple).
+    /// First span where it is accessed (there can be multiple).
     pub span: Span,
 }
 
diff --git a/compiler/rustc_hir/src/intravisit.rs b/compiler/rustc_hir/src/intravisit.rs
index 957f8c1058e..cbb530424ca 100644
--- a/compiler/rustc_hir/src/intravisit.rs
+++ b/compiler/rustc_hir/src/intravisit.rs
@@ -367,7 +367,7 @@ pub trait Visitor<'v>: Sized {
     fn visit_fn(&mut self, fk: FnKind<'v>, fd: &'v FnDecl<'v>, b: BodyId, _: Span, id: HirId) {
         walk_fn(self, fk, fd, b, id)
     }
-    fn visit_use(&mut self, path: &'v Path<'v>, hir_id: HirId) {
+    fn visit_use(&mut self, path: &'v UsePath<'v>, hir_id: HirId) {
         walk_use(self, path, hir_id)
     }
     fn visit_trait_item(&mut self, ti: &'v TraitItem<'v>) {
@@ -422,7 +422,7 @@ pub trait Visitor<'v>: Sized {
     fn visit_qpath(&mut self, qpath: &'v QPath<'v>, id: HirId, _span: Span) {
         walk_qpath(self, qpath, id)
     }
-    fn visit_path(&mut self, path: &'v Path<'v>, _id: HirId) {
+    fn visit_path(&mut self, path: &Path<'v>, _id: HirId) {
         walk_path(self, path)
     }
     fn visit_path_segment(&mut self, path_segment: &'v PathSegment<'v>) {
@@ -938,9 +938,12 @@ pub fn walk_fn_kind<'v, V: Visitor<'v>>(visitor: &mut V, function_kind: FnKind<'
     }
 }
 
-pub fn walk_use<'v, V: Visitor<'v>>(visitor: &mut V, path: &'v Path<'v>, hir_id: HirId) {
+pub fn walk_use<'v, V: Visitor<'v>>(visitor: &mut V, path: &'v UsePath<'v>, hir_id: HirId) {
     visitor.visit_id(hir_id);
-    visitor.visit_path(path, hir_id);
+    let UsePath { segments, ref res, span } = *path;
+    for &res in res {
+        visitor.visit_path(&Path { segments, res, span }, hir_id);
+    }
 }
 
 pub fn walk_trait_item<'v, V: Visitor<'v>>(visitor: &mut V, trait_item: &'v TraitItem<'v>) {
@@ -1126,7 +1129,7 @@ pub fn walk_qpath<'v, V: Visitor<'v>>(visitor: &mut V, qpath: &'v QPath<'v>, id:
     }
 }
 
-pub fn walk_path<'v, V: Visitor<'v>>(visitor: &mut V, path: &'v Path<'v>) {
+pub fn walk_path<'v, V: Visitor<'v>>(visitor: &mut V, path: &Path<'v>) {
     for segment in path.segments {
         visitor.visit_path_segment(segment);
     }
diff --git a/compiler/rustc_hir_analysis/src/astconv/mod.rs b/compiler/rustc_hir_analysis/src/astconv/mod.rs
index 90469227a26..82150310638 100644
--- a/compiler/rustc_hir_analysis/src/astconv/mod.rs
+++ b/compiler/rustc_hir_analysis/src/astconv/mod.rs
@@ -109,6 +109,9 @@ pub trait AstConv<'tcx> {
     ) -> Ty<'tcx>;
 
     /// Normalize an associated type coming from the user.
+    ///
+    /// This should only be used by astconv. Use `FnCtxt::normalize`
+    /// or `ObligationCtxt::normalize` in downstream crates.
     fn normalize_ty(&self, span: Span, ty: Ty<'tcx>) -> Ty<'tcx>;
 
     /// Invoked when we encounter an error from some prior pass
@@ -850,7 +853,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
             .is_some()
     }
 
-    // Sets `implicitly_sized` to true on `Bounds` if necessary
+    /// Sets `implicitly_sized` to true on `Bounds` if necessary
     pub(crate) fn add_implicitly_sized<'hir>(
         &self,
         bounds: &mut Bounds<'hir>,
@@ -2391,7 +2394,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
         path_segs
     }
 
-    // Check a type `Path` and convert it to a `Ty`.
+    /// Check a type `Path` and convert it to a `Ty`.
     pub fn res_to_ty(
         &self,
         opt_self_ty: Option<Ty<'tcx>>,
diff --git a/compiler/rustc_hir_analysis/src/check/compare_method.rs b/compiler/rustc_hir_analysis/src/check/compare_method.rs
index de386e2d135..ba58672e759 100644
--- a/compiler/rustc_hir_analysis/src/check/compare_method.rs
+++ b/compiler/rustc_hir_analysis/src/check/compare_method.rs
@@ -221,7 +221,7 @@ fn compare_predicate_entailment<'tcx>(
     let impl_m_own_bounds = impl_m_predicates.instantiate_own(tcx, impl_to_placeholder_substs);
     for (predicate, span) in iter::zip(impl_m_own_bounds.predicates, impl_m_own_bounds.spans) {
         let normalize_cause = traits::ObligationCause::misc(span, impl_m_hir_id);
-        let predicate = ocx.normalize(normalize_cause, param_env, predicate);
+        let predicate = ocx.normalize(&normalize_cause, param_env, predicate);
 
         let cause = ObligationCause::new(
             span,
@@ -260,7 +260,7 @@ fn compare_predicate_entailment<'tcx>(
     );
 
     let norm_cause = ObligationCause::misc(impl_m_span, impl_m_hir_id);
-    let impl_sig = ocx.normalize(norm_cause.clone(), param_env, impl_sig);
+    let impl_sig = ocx.normalize(&norm_cause, param_env, impl_sig);
     let impl_fty = tcx.mk_fn_ptr(ty::Binder::dummy(impl_sig));
     debug!("compare_impl_method: impl_fty={:?}", impl_fty);
 
@@ -271,7 +271,7 @@ fn compare_predicate_entailment<'tcx>(
     // we have to do this before normalization, since the normalized ty may
     // not contain the input parameters. See issue #87748.
     wf_tys.extend(trait_sig.inputs_and_output.iter());
-    let trait_sig = ocx.normalize(norm_cause, param_env, trait_sig);
+    let trait_sig = ocx.normalize(&norm_cause, param_env, trait_sig);
     // We also have to add the normalized trait signature
     // as we don't normalize during implied bounds computation.
     wf_tys.extend(trait_sig.inputs_and_output.iter());
@@ -366,7 +366,7 @@ pub fn collect_trait_impl_trait_tys<'tcx>(
     // Normalize the impl signature with fresh variables for lifetime inference.
     let norm_cause = ObligationCause::misc(return_span, impl_m_hir_id);
     let impl_sig = ocx.normalize(
-        norm_cause.clone(),
+        &norm_cause,
         param_env,
         infcx.replace_bound_vars_with_fresh_vars(
             return_span,
@@ -387,7 +387,7 @@ pub fn collect_trait_impl_trait_tys<'tcx>(
             tcx.bound_fn_sig(trait_m.def_id).subst(tcx, trait_to_placeholder_substs),
         )
         .fold_with(&mut collector);
-    let trait_sig = ocx.normalize(norm_cause.clone(), param_env, unnormalized_trait_sig);
+    let trait_sig = ocx.normalize(&norm_cause, param_env, unnormalized_trait_sig);
     let trait_return_ty = trait_sig.output();
 
     let wf_tys = FxIndexSet::from_iter(
@@ -592,7 +592,7 @@ impl<'tcx> TypeFolder<'tcx> for ImplTraitInTraitCollector<'_, 'tcx> {
             for (pred, pred_span) in self.tcx().bound_explicit_item_bounds(proj.item_def_id).subst_iter_copied(self.tcx(), proj.substs) {
                 let pred = pred.fold_with(self);
                 let pred = self.ocx.normalize(
-                    ObligationCause::misc(self.span, self.body_id),
+                    &ObligationCause::misc(self.span, self.body_id),
                     self.param_env,
                     pred,
                 );
@@ -1403,11 +1403,11 @@ pub(crate) fn raw_compare_const_impl<'tcx>(
     );
 
     // There is no "body" here, so just pass dummy id.
-    let impl_ty = ocx.normalize(cause.clone(), param_env, impl_ty);
+    let impl_ty = ocx.normalize(&cause, param_env, impl_ty);
 
     debug!("compare_const_impl: impl_ty={:?}", impl_ty);
 
-    let trait_ty = ocx.normalize(cause.clone(), param_env, trait_ty);
+    let trait_ty = ocx.normalize(&cause, param_env, trait_ty);
 
     debug!("compare_const_impl: trait_ty={:?}", trait_ty);
 
@@ -1556,7 +1556,7 @@ fn compare_type_predicate_entailment<'tcx>(
     for (span, predicate) in std::iter::zip(impl_ty_own_bounds.spans, impl_ty_own_bounds.predicates)
     {
         let cause = ObligationCause::misc(span, impl_ty_hir_id);
-        let predicate = ocx.normalize(cause, param_env, predicate);
+        let predicate = ocx.normalize(&cause, param_env, predicate);
 
         let cause = ObligationCause::new(
             span,
@@ -1778,7 +1778,7 @@ pub fn check_type_bounds<'tcx>(
 
     for mut obligation in util::elaborate_obligations(tcx, obligations) {
         let normalized_predicate =
-            ocx.normalize(normalize_cause.clone(), normalize_param_env, obligation.predicate);
+            ocx.normalize(&normalize_cause, normalize_param_env, obligation.predicate);
         debug!("compare_projection_bounds: normalized predicate = {:?}", normalized_predicate);
         obligation.predicate = normalized_predicate;
 
diff --git a/compiler/rustc_hir_analysis/src/check/dropck.rs b/compiler/rustc_hir_analysis/src/check/dropck.rs
index d04d8ca2c32..d6e3ddb0a61 100644
--- a/compiler/rustc_hir_analysis/src/check/dropck.rs
+++ b/compiler/rustc_hir_analysis/src/check/dropck.rs
@@ -233,9 +233,10 @@ fn ensure_drop_predicates_are_implied_by_item_defn<'tcx>(
     result
 }
 
-// This is an implementation of the TypeRelation trait with the
-// aim of simply comparing for equality (without side-effects).
-// It is not intended to be used anywhere else other than here.
+/// This is an implementation of the [`TypeRelation`] trait with the
+/// aim of simply comparing for equality (without side-effects).
+///
+/// It is not intended to be used anywhere else other than here.
 pub(crate) struct SimpleEqRelation<'tcx> {
     tcx: TyCtxt<'tcx>,
     param_env: ty::ParamEnv<'tcx>,
diff --git a/compiler/rustc_hir_analysis/src/check/wfcheck.rs b/compiler/rustc_hir_analysis/src/check/wfcheck.rs
index 8171a2ab270..7daed74e9de 100644
--- a/compiler/rustc_hir_analysis/src/check/wfcheck.rs
+++ b/compiler/rustc_hir_analysis/src/check/wfcheck.rs
@@ -53,12 +53,14 @@ impl<'tcx> WfCheckingCtxt<'_, 'tcx> {
         self.ocx.infcx.tcx
     }
 
+    // Convenience function to normalize during wfcheck. This performs
+    // `ObligationCtxt::normalize`, but provides a nice `ObligationCauseCode`.
     fn normalize<T>(&self, span: Span, loc: Option<WellFormedLoc>, value: T) -> T
     where
         T: TypeFoldable<'tcx>,
     {
         self.ocx.normalize(
-            ObligationCause::new(span, self.body_id, ObligationCauseCode::WellFormed(loc)),
+            &ObligationCause::new(span, self.body_id, ObligationCauseCode::WellFormed(loc)),
             self.param_env,
             value,
         )
diff --git a/compiler/rustc_hir_analysis/src/collect.rs b/compiler/rustc_hir_analysis/src/collect.rs
index 4c1d95a452d..d623e726139 100644
--- a/compiler/rustc_hir_analysis/src/collect.rs
+++ b/compiler/rustc_hir_analysis/src/collect.rs
@@ -33,7 +33,7 @@ use rustc_middle::middle::codegen_fn_attrs::{CodegenFnAttrFlags, CodegenFnAttrs}
 use rustc_middle::mir::mono::Linkage;
 use rustc_middle::ty::query::Providers;
 use rustc_middle::ty::util::{Discr, IntTypeExt};
-use rustc_middle::ty::{self, AdtKind, Const, DefIdTree, IsSuggestable, Ty, TyCtxt};
+use rustc_middle::ty::{self, AdtKind, Const, DefIdTree, IsSuggestable, ToPredicate, Ty, TyCtxt};
 use rustc_session::lint;
 use rustc_session::parse::feature_err;
 use rustc_span::symbol::{kw, sym, Ident, Symbol};
@@ -1366,12 +1366,14 @@ fn predicates_defined_on(tcx: TyCtxt<'_>, def_id: DefId) -> ty::GenericPredicate
             "predicates_defined_on: inferred_outlives_of({:?}) = {:?}",
             def_id, inferred_outlives,
         );
+        let inferred_outlives_iter =
+            inferred_outlives.iter().map(|(clause, span)| ((*clause).to_predicate(tcx), *span));
         if result.predicates.is_empty() {
-            result.predicates = inferred_outlives;
+            result.predicates = tcx.arena.alloc_from_iter(inferred_outlives_iter);
         } else {
-            result.predicates = tcx
-                .arena
-                .alloc_from_iter(result.predicates.iter().chain(inferred_outlives).copied());
+            result.predicates = tcx.arena.alloc_from_iter(
+                result.predicates.into_iter().copied().chain(inferred_outlives_iter),
+            );
         }
     }
 
@@ -2145,7 +2147,7 @@ fn should_inherit_track_caller(tcx: TyCtxt<'_>, def_id: DefId) -> bool {
 }
 
 fn check_link_ordinal(tcx: TyCtxt<'_>, attr: &ast::Attribute) -> Option<u16> {
-    use rustc_ast::{Lit, LitIntType, LitKind};
+    use rustc_ast::{LitIntType, LitKind, MetaItemLit};
     if !tcx.features().raw_dylib && tcx.sess.target.arch == "x86" {
         feature_err(
             &tcx.sess.parse_sess,
@@ -2158,7 +2160,7 @@ fn check_link_ordinal(tcx: TyCtxt<'_>, attr: &ast::Attribute) -> Option<u16> {
     let meta_item_list = attr.meta_item_list();
     let meta_item_list = meta_item_list.as_deref();
     let sole_meta_list = match meta_item_list {
-        Some([item]) => item.literal(),
+        Some([item]) => item.lit(),
         Some(_) => {
             tcx.sess
                 .struct_span_err(attr.span, "incorrect number of arguments to `#[link_ordinal]`")
@@ -2168,7 +2170,9 @@ fn check_link_ordinal(tcx: TyCtxt<'_>, attr: &ast::Attribute) -> Option<u16> {
         }
         _ => None,
     };
-    if let Some(Lit { kind: LitKind::Int(ordinal, LitIntType::Unsuffixed), .. }) = sole_meta_list {
+    if let Some(MetaItemLit { kind: LitKind::Int(ordinal, LitIntType::Unsuffixed), .. }) =
+        sole_meta_list
+    {
         // According to the table at https://docs.microsoft.com/en-us/windows/win32/debug/pe-format#import-header,
         // the ordinal must fit into 16 bits.  Similarly, the Ordinal field in COFFShortExport (defined
         // in llvm/include/llvm/Object/COFFImportFile.h), which we use to communicate import information
diff --git a/compiler/rustc_hir_analysis/src/collect/lifetimes.rs b/compiler/rustc_hir_analysis/src/collect/lifetimes.rs
index c11eed7ad9e..9a7b261fffd 100644
--- a/compiler/rustc_hir_analysis/src/collect/lifetimes.rs
+++ b/compiler/rustc_hir_analysis/src/collect/lifetimes.rs
@@ -814,7 +814,7 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> {
         }
     }
 
-    fn visit_path(&mut self, path: &'tcx hir::Path<'tcx>, _: hir::HirId) {
+    fn visit_path(&mut self, path: &hir::Path<'tcx>, _: hir::HirId) {
         for (i, segment) in path.segments.iter().enumerate() {
             let depth = path.segments.len() - i - 1;
             if let Some(ref args) = segment.args {
diff --git a/compiler/rustc_hir_analysis/src/lib.rs b/compiler/rustc_hir_analysis/src/lib.rs
index 664d3a3a1db..2058832d5fd 100644
--- a/compiler/rustc_hir_analysis/src/lib.rs
+++ b/compiler/rustc_hir_analysis/src/lib.rs
@@ -332,7 +332,7 @@ fn check_main_fn_ty(tcx: TyCtxt<'_>, main_def_id: DefId) {
             ObligationCauseCode::MainFunctionType,
         );
         let ocx = traits::ObligationCtxt::new(&infcx);
-        let norm_return_ty = ocx.normalize(cause.clone(), param_env, return_ty);
+        let norm_return_ty = ocx.normalize(&cause, param_env, return_ty);
         ocx.register_bound(cause, param_env, norm_return_ty, term_did);
         let errors = ocx.select_all_or_error();
         if !errors.is_empty() {
diff --git a/compiler/rustc_hir_analysis/src/outlives/mod.rs b/compiler/rustc_hir_analysis/src/outlives/mod.rs
index c8f37176836..81fe32000d3 100644
--- a/compiler/rustc_hir_analysis/src/outlives/mod.rs
+++ b/compiler/rustc_hir_analysis/src/outlives/mod.rs
@@ -3,7 +3,7 @@ use rustc_hir as hir;
 use rustc_hir::def_id::DefId;
 use rustc_middle::ty::query::Providers;
 use rustc_middle::ty::subst::GenericArgKind;
-use rustc_middle::ty::{self, CratePredicatesMap, ToPredicate, TyCtxt};
+use rustc_middle::ty::{self, CratePredicatesMap, TyCtxt};
 use rustc_span::symbol::sym;
 use rustc_span::Span;
 
@@ -17,7 +17,7 @@ pub fn provide(providers: &mut Providers) {
     *providers = Providers { inferred_outlives_of, inferred_outlives_crate, ..*providers };
 }
 
-fn inferred_outlives_of(tcx: TyCtxt<'_>, item_def_id: DefId) -> &[(ty::Predicate<'_>, Span)] {
+fn inferred_outlives_of(tcx: TyCtxt<'_>, item_def_id: DefId) -> &[(ty::Clause<'_>, Span)] {
     let id = tcx.hir().local_def_id_to_hir_id(item_def_id.expect_local());
 
     if matches!(tcx.def_kind(item_def_id), hir::def::DefKind::AnonConst) && tcx.lazy_normalization()
@@ -50,12 +50,10 @@ fn inferred_outlives_of(tcx: TyCtxt<'_>, item_def_id: DefId) -> &[(ty::Predicate
                 if tcx.has_attr(item_def_id, sym::rustc_outlives) {
                     let mut pred: Vec<String> = predicates
                         .iter()
-                        .map(|(out_pred, _)| match out_pred.kind().skip_binder() {
-                            ty::PredicateKind::Clause(ty::Clause::RegionOutlives(p)) => {
-                                p.to_string()
-                            }
-                            ty::PredicateKind::Clause(ty::Clause::TypeOutlives(p)) => p.to_string(),
-                            err => bug!("unexpected predicate {:?}", err),
+                        .map(|(out_pred, _)| match out_pred {
+                            ty::Clause::RegionOutlives(p) => p.to_string(),
+                            ty::Clause::TypeOutlives(p) => p.to_string(),
+                            err => bug!("unexpected clause {:?}", err),
                         })
                         .collect();
                     pred.sort();
@@ -103,19 +101,11 @@ fn inferred_outlives_crate(tcx: TyCtxt<'_>, (): ()) -> CratePredicatesMap<'_> {
                 |(ty::OutlivesPredicate(kind1, region2), &span)| {
                     match kind1.unpack() {
                         GenericArgKind::Type(ty1) => Some((
-                            ty::Binder::dummy(ty::PredicateKind::Clause(ty::Clause::TypeOutlives(
-                                ty::OutlivesPredicate(ty1, *region2),
-                            )))
-                            .to_predicate(tcx),
+                            ty::Clause::TypeOutlives(ty::OutlivesPredicate(ty1, *region2)),
                             span,
                         )),
                         GenericArgKind::Lifetime(region1) => Some((
-                            ty::Binder::dummy(ty::PredicateKind::Clause(
-                                ty::Clause::RegionOutlives(ty::OutlivesPredicate(
-                                    region1, *region2,
-                                )),
-                            ))
-                            .to_predicate(tcx),
+                            ty::Clause::RegionOutlives(ty::OutlivesPredicate(region1, *region2)),
                             span,
                         )),
                         GenericArgKind::Const(_) => {
diff --git a/compiler/rustc_hir_analysis/src/variance/terms.rs b/compiler/rustc_hir_analysis/src/variance/terms.rs
index 58e8f474761..3b286bb9c93 100644
--- a/compiler/rustc_hir_analysis/src/variance/terms.rs
+++ b/compiler/rustc_hir_analysis/src/variance/terms.rs
@@ -42,22 +42,22 @@ impl<'a> fmt::Debug for VarianceTerm<'a> {
     }
 }
 
-// The first pass over the crate simply builds up the set of inferreds.
+/// The first pass over the crate simply builds up the set of inferreds.
 
 pub struct TermsContext<'a, 'tcx> {
     pub tcx: TyCtxt<'tcx>,
     pub arena: &'a DroplessArena,
 
-    // For marker types, UnsafeCell, and other lang items where
-    // variance is hardcoded, records the item-id and the hardcoded
-    // variance.
+    /// For marker types, `UnsafeCell`, and other lang items where
+    /// variance is hardcoded, records the item-id and the hardcoded
+    /// variance.
     pub lang_items: Vec<(LocalDefId, Vec<ty::Variance>)>,
 
-    // Maps from the node id of an item to the first inferred index
-    // used for its type & region parameters.
+    /// Maps from the node id of an item to the first inferred index
+    /// used for its type & region parameters.
     pub inferred_starts: LocalDefIdMap<InferredIndex>,
 
-    // Maps from an InferredIndex to the term for that variable.
+    /// Maps from an InferredIndex to the term for that variable.
     pub inferred_terms: Vec<VarianceTermPtr<'a>>,
 }
 
diff --git a/compiler/rustc_hir_pretty/src/lib.rs b/compiler/rustc_hir_pretty/src/lib.rs
index 95729822677..2460a23bb3f 100644
--- a/compiler/rustc_hir_pretty/src/lib.rs
+++ b/compiler/rustc_hir_pretty/src/lib.rs
@@ -1591,7 +1591,7 @@ impl<'a> State<'a> {
         self.print_ident(Ident::with_dummy_span(name))
     }
 
-    pub fn print_path(&mut self, path: &hir::Path<'_>, colons_before_params: bool) {
+    pub fn print_path<R>(&mut self, path: &hir::Path<'_, R>, colons_before_params: bool) {
         self.maybe_print_comment(path.span.lo());
 
         for (i, segment) in path.segments.iter().enumerate() {
diff --git a/compiler/rustc_hir_typeck/src/callee.rs b/compiler/rustc_hir_typeck/src/callee.rs
index 1c14c1d35f7..b09ddf80e2a 100644
--- a/compiler/rustc_hir_typeck/src/callee.rs
+++ b/compiler/rustc_hir_typeck/src/callee.rs
@@ -179,12 +179,16 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
 
             // Hack: we know that there are traits implementing Fn for &F
             // where F:Fn and so forth. In the particular case of types
-            // like `x: &mut FnMut()`, if there is a call `x()`, we would
-            // normally translate to `FnMut::call_mut(&mut x, ())`, but
-            // that winds up requiring `mut x: &mut FnMut()`. A little
-            // over the top. The simplest fix by far is to just ignore
-            // this case and deref again, so we wind up with
-            // `FnMut::call_mut(&mut *x, ())`.
+            // like `f: &mut FnMut()`, if there is a call `f()`, we would
+            // normally translate to `FnMut::call_mut(&mut f, ())`, but
+            // that winds up potentially requiring the user to mark their
+            // variable as `mut` which feels unnecessary and unexpected.
+            //
+            //     fn foo(f: &mut impl FnMut()) { f() }
+            //            ^ without this hack `f` would have to be declared as mutable
+            //
+            // The simplest fix by far is to just ignore this case and deref again,
+            // so we wind up with `FnMut::call_mut(&mut *f, ())`.
             ty::Ref(..) if autoderef.step_count() == 0 => {
                 return None;
             }
@@ -444,7 +448,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         // previously appeared within a `Binder<>` and hence would not
         // have been normalized before.
         let fn_sig = self.replace_bound_vars_with_fresh_vars(call_expr.span, infer::FnCall, fn_sig);
-        let fn_sig = self.normalize_associated_types_in(call_expr.span, fn_sig);
+        let fn_sig = self.normalize(call_expr.span, fn_sig);
 
         // Call the generic checker.
         let expected_arg_tys = self.expected_inputs_for_expected_output(
diff --git a/compiler/rustc_hir_typeck/src/cast.rs b/compiler/rustc_hir_typeck/src/cast.rs
index 1cea8c9dadc..890a068a7be 100644
--- a/compiler/rustc_hir_typeck/src/cast.rs
+++ b/compiler/rustc_hir_typeck/src/cast.rs
@@ -46,7 +46,6 @@ use rustc_span::def_id::{DefId, LOCAL_CRATE};
 use rustc_span::symbol::sym;
 use rustc_span::Span;
 use rustc_trait_selection::infer::InferCtxtExt;
-use rustc_trait_selection::traits::error_reporting::report_object_safety_error;
 
 /// Reifies a cast check to be checked once we have full type information for
 /// a function context.
@@ -727,9 +726,6 @@ impl<'a, 'tcx> CastCheck<'tcx> {
                     debug!(" -> CoercionCast");
                     fcx.typeck_results.borrow_mut().set_coercion_cast(self.expr.hir_id.local_id);
                 }
-                Err(ty::error::TypeError::ObjectUnsafeCoercion(did)) => {
-                    self.report_object_unsafe_cast(&fcx, did);
-                }
                 Err(_) => {
                     match self.do_check(fcx) {
                         Ok(k) => {
@@ -741,14 +737,6 @@ impl<'a, 'tcx> CastCheck<'tcx> {
             };
         }
     }
-
-    fn report_object_unsafe_cast(&self, fcx: &FnCtxt<'a, 'tcx>, did: DefId) {
-        let violations = fcx.tcx.object_safety_violations(did);
-        let mut err = report_object_safety_error(fcx.tcx, self.cast_span, did, violations);
-        err.note(&format!("required by cast to type '{}'", fcx.ty_to_string(self.cast_ty)));
-        err.emit();
-    }
-
     /// Checks a cast, and report an error if one exists. In some cases, this
     /// can return Ok and create type errors in the fcx rather than returning
     /// directly. coercion-cast is handled in check instead of here.
@@ -764,10 +752,7 @@ impl<'a, 'tcx> CastCheck<'tcx> {
                 match *self.expr_ty.kind() {
                     ty::FnDef(..) => {
                         // Attempt a coercion to a fn pointer type.
-                        let f = fcx.normalize_associated_types_in(
-                            self.expr_span,
-                            self.expr_ty.fn_sig(fcx.tcx),
-                        );
+                        let f = fcx.normalize(self.expr_span, self.expr_ty.fn_sig(fcx.tcx));
                         let res = fcx.try_coerce(
                             self.expr,
                             self.expr_ty,
diff --git a/compiler/rustc_hir_typeck/src/check.rs b/compiler/rustc_hir_typeck/src/check.rs
index 1ceb07def72..0c9a350c295 100644
--- a/compiler/rustc_hir_typeck/src/check.rs
+++ b/compiler/rustc_hir_typeck/src/check.rs
@@ -1,6 +1,6 @@
 use crate::coercion::CoerceMany;
 use crate::gather_locals::GatherLocalsVisitor;
-use crate::{FnCtxt, Inherited};
+use crate::FnCtxt;
 use crate::{GeneratorTypes, UnsafetyState};
 use rustc_hir as hir;
 use rustc_hir::def::DefKind;
@@ -20,21 +20,16 @@ use std::cell::RefCell;
 ///
 /// * ...
 /// * inherited: other fields inherited from the enclosing fn (if any)
-#[instrument(skip(inherited, body), level = "debug")]
+#[instrument(skip(fcx, body), level = "debug")]
 pub(super) fn check_fn<'a, 'tcx>(
-    inherited: &'a Inherited<'tcx>,
-    param_env: ty::ParamEnv<'tcx>,
+    fcx: &mut FnCtxt<'a, 'tcx>,
     fn_sig: ty::FnSig<'tcx>,
     decl: &'tcx hir::FnDecl<'tcx>,
     fn_def_id: LocalDefId,
     body: &'tcx hir::Body<'tcx>,
     can_be_generator: Option<hir::Movability>,
-) -> (FnCtxt<'a, 'tcx>, Option<GeneratorTypes<'tcx>>) {
-    let fn_id = inherited.tcx.hir().local_def_id_to_hir_id(fn_def_id);
-
-    // Create the function context. This is either derived from scratch or,
-    // in the case of closures, based on the outer context.
-    let mut fcx = FnCtxt::new(inherited, param_env, body.value.hir_id);
+) -> Option<GeneratorTypes<'tcx>> {
+    let fn_id = fcx.tcx.hir().local_def_id_to_hir_id(fn_def_id);
     fcx.ps.set(UnsafetyState::function(fn_sig.unsafety, fn_id));
 
     let tcx = fcx.tcx;
@@ -47,7 +42,7 @@ pub(super) fn check_fn<'a, 'tcx>(
             declared_ret_ty,
             body.value.hir_id,
             decl.output.span(),
-            param_env,
+            fcx.param_env,
         ));
 
     fcx.ret_coercion = Some(RefCell::new(CoerceMany::new(ret_ty)));
@@ -105,7 +100,7 @@ pub(super) fn check_fn<'a, 'tcx>(
         fcx.write_ty(param.hir_id, param_ty);
     }
 
-    inherited.typeck_results.borrow_mut().liberated_fn_sigs_mut().insert(fn_id, fn_sig);
+    fcx.typeck_results.borrow_mut().liberated_fn_sigs_mut().insert(fn_id, fn_sig);
 
     if let ty::Dynamic(_, _, ty::Dyn) = declared_ret_ty.kind() {
         // FIXME: We need to verify that the return type is `Sized` after the return expression has
@@ -174,7 +169,7 @@ pub(super) fn check_fn<'a, 'tcx>(
         check_panic_info_fn(tcx, panic_impl_did.expect_local(), fn_sig, decl, declared_ret_ty);
     }
 
-    (fcx, gen_ty)
+    gen_ty
 }
 
 fn check_panic_info_fn(
diff --git a/compiler/rustc_hir_typeck/src/closure.rs b/compiler/rustc_hir_typeck/src/closure.rs
index e2fefd2724f..5d3419b3b6e 100644
--- a/compiler/rustc_hir_typeck/src/closure.rs
+++ b/compiler/rustc_hir_typeck/src/closure.rs
@@ -79,16 +79,15 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
 
         debug!(?bound_sig, ?liberated_sig);
 
+        let mut fcx = FnCtxt::new(self, self.param_env.without_const(), body.value.hir_id);
         let generator_types = check_fn(
-            self,
-            self.param_env.without_const(),
+            &mut fcx,
             liberated_sig,
             closure.fn_decl,
             expr_def_id,
             body,
             closure.movability,
-        )
-        .1;
+        );
 
         let parent_substs = InternalSubsts::identity_for_item(
             self.tcx,
@@ -178,7 +177,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                 });
                 let kind = object_type
                     .principal_def_id()
-                    .and_then(|did| self.tcx.fn_trait_kind_from_lang_item(did));
+                    .and_then(|did| self.tcx.fn_trait_kind_from_def_id(did));
                 (sig, kind)
             }
             ty::Infer(ty::TyVar(vid)) => self.deduce_signature_from_predicates(
@@ -214,7 +213,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             if expected_sig.is_none()
                 && let ty::PredicateKind::Clause(ty::Clause::Projection(proj_predicate)) = bound_predicate.skip_binder()
             {
-                expected_sig = self.normalize_associated_types_in(
+                expected_sig = self.normalize(
                     obligation.cause.span,
                     self.deduce_sig_from_projection(
                     Some(obligation.cause.span),
@@ -235,7 +234,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                 _ => None,
             };
             if let Some(closure_kind) =
-                trait_def_id.and_then(|def_id| self.tcx.fn_trait_kind_from_lang_item(def_id))
+                trait_def_id.and_then(|def_id| self.tcx.fn_trait_kind_from_def_id(def_id))
             {
                 expected_kind = Some(
                     expected_kind
@@ -263,7 +262,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
 
         let trait_def_id = projection.trait_def_id(tcx);
 
-        let is_fn = tcx.fn_trait_kind_from_lang_item(trait_def_id).is_some();
+        let is_fn = tcx.is_fn_trait(trait_def_id);
         let gen_trait = tcx.require_lang_item(LangItem::Generator, cause_span);
         let is_gen = gen_trait == trait_def_id;
         if !is_fn && !is_gen {
@@ -623,7 +622,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         );
         // Astconv can't normalize inputs or outputs with escaping bound vars,
         // so normalize them here, after we've wrapped them in a binder.
-        let result = self.normalize_associated_types_in(self.tcx.hir().span(hir_id), result);
+        let result = self.normalize(self.tcx.hir().span(hir_id), result);
 
         let c_result = self.inh.infcx.canonicalize_response(result);
         self.typeck_results.borrow_mut().user_provided_sigs.insert(expr_def_id, c_result);
@@ -797,12 +796,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
     ) -> ClosureSignatures<'tcx> {
         let liberated_sig =
             self.tcx().liberate_late_bound_regions(expr_def_id.to_def_id(), bound_sig);
-        let liberated_sig = self.inh.normalize_associated_types_in(
-            body.value.span,
-            self.tcx.hir().local_def_id_to_hir_id(expr_def_id),
-            self.param_env,
-            liberated_sig,
-        );
+        let liberated_sig = self.normalize(body.value.span, liberated_sig);
         ClosureSignatures { bound_sig, liberated_sig }
     }
 }
diff --git a/compiler/rustc_hir_typeck/src/coercion.rs b/compiler/rustc_hir_typeck/src/coercion.rs
index 6b6d54db506..f0b349f0c98 100644
--- a/compiler/rustc_hir_typeck/src/coercion.rs
+++ b/compiler/rustc_hir_typeck/src/coercion.rs
@@ -62,7 +62,9 @@ use rustc_span::{self, BytePos, DesugaringKind, Span};
 use rustc_target::spec::abi::Abi;
 use rustc_trait_selection::infer::InferCtxtExt as _;
 use rustc_trait_selection::traits::error_reporting::TypeErrCtxtExt as _;
-use rustc_trait_selection::traits::{self, ObligationCause, ObligationCauseCode, ObligationCtxt};
+use rustc_trait_selection::traits::{
+    self, NormalizeExt, ObligationCause, ObligationCauseCode, ObligationCtxt,
+};
 
 use smallvec::{smallvec, SmallVec};
 use std::ops::Deref;
@@ -195,10 +197,6 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {
                 debug!("coerce: unsize successful");
                 return unsize;
             }
-            Err(TypeError::ObjectUnsafeCoercion(did)) => {
-                debug!("coerce: unsize not object safe");
-                return Err(TypeError::ObjectUnsafeCoercion(did));
-            }
             Err(error) => {
                 debug!(?error, "coerce: unsize failed");
             }
@@ -498,27 +496,9 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {
         target = self.shallow_resolve(target);
         debug!(?source, ?target);
 
-        // These 'if' statements require some explanation.
-        // The `CoerceUnsized` trait is special - it is only
-        // possible to write `impl CoerceUnsized<B> for A` where
-        // A and B have 'matching' fields. This rules out the following
-        // two types of blanket impls:
-        //
-        // `impl<T> CoerceUnsized<T> for SomeType`
-        // `impl<T> CoerceUnsized<SomeType> for T`
-        //
-        // Both of these trigger a special `CoerceUnsized`-related error (E0376)
-        //
-        // We can take advantage of this fact to avoid performing unnecessary work.
-        // If either `source` or `target` is a type variable, then any applicable impl
-        // would need to be generic over the self-type (`impl<T> CoerceUnsized<SomeType> for T`)
-        // or generic over the `CoerceUnsized` type parameter (`impl<T> CoerceUnsized<T> for
-        // SomeType`).
-        //
-        // However, these are exactly the kinds of impls which are forbidden by
-        // the compiler! Therefore, we can be sure that coercion will always fail
-        // when either the source or target type is a type variable. This allows us
-        // to skip performing any trait selection, and immediately bail out.
+        // We don't apply any coercions incase either the source or target
+        // aren't sufficiently well known but tend to instead just equate
+        // them both.
         if source.is_ty_var() {
             debug!("coerce_unsized: source is a TyVar, bailing out");
             return Err(TypeError::Mismatch);
@@ -854,7 +834,7 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {
 
         let b = self.shallow_resolve(b);
         let InferOk { value: b, mut obligations } =
-            self.normalize_associated_types_in_as_infer_ok(self.cause.span, b);
+            self.at(&self.cause, self.param_env).normalize(b);
         debug!("coerce_from_fn_item(a={:?}, b={:?})", a, b);
 
         match b.kind() {
@@ -876,7 +856,7 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {
                 }
 
                 let InferOk { value: a_sig, obligations: o1 } =
-                    self.normalize_associated_types_in_as_infer_ok(self.cause.span, a_sig);
+                    self.at(&self.cause, self.param_env).normalize(a_sig);
                 obligations.extend(o1);
 
                 let a_fn_pointer = self.tcx.mk_fn_ptr(a_sig);
@@ -1101,15 +1081,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         // Special-case that coercion alone cannot handle:
         // Function items or non-capturing closures of differing IDs or InternalSubsts.
         let (a_sig, b_sig) = {
-            #[allow(rustc::usage_of_ty_tykind)]
-            let is_capturing_closure = |ty: &ty::TyKind<'tcx>| {
-                if let &ty::Closure(closure_def_id, _substs) = ty {
+            let is_capturing_closure = |ty: Ty<'tcx>| {
+                if let &ty::Closure(closure_def_id, _substs) = ty.kind() {
                     self.tcx.upvars_mentioned(closure_def_id.expect_local()).is_some()
                 } else {
                     false
                 }
             };
-            if is_capturing_closure(prev_ty.kind()) || is_capturing_closure(new_ty.kind()) {
+            if is_capturing_closure(prev_ty) || is_capturing_closure(new_ty) {
                 (None, None)
             } else {
                 match (prev_ty.kind(), new_ty.kind()) {
@@ -1164,8 +1143,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                 return Err(TypeError::IntrinsicCast);
             }
             // The signature must match.
-            let a_sig = self.normalize_associated_types_in(new.span, a_sig);
-            let b_sig = self.normalize_associated_types_in(new.span, b_sig);
+            let (a_sig, b_sig) = self.normalize(new.span, (a_sig, b_sig));
             let sig = self
                 .at(cause, self.param_env)
                 .trace(prev_ty, new_ty)
diff --git a/compiler/rustc_hir_typeck/src/demand.rs b/compiler/rustc_hir_typeck/src/demand.rs
index 2106dce6f40..24184bdbf5c 100644
--- a/compiler/rustc_hir_typeck/src/demand.rs
+++ b/compiler/rustc_hir_typeck/src/demand.rs
@@ -57,8 +57,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         self.note_internal_mutation_in_method(err, expr, expected, expr_ty);
     }
 
-    // Requires that the two types unify, and prints an error message if
-    // they don't.
+    /// Requires that the two types unify, and prints an error message if
+    /// they don't.
     pub fn demand_suptype(&self, sp: Span, expected: Ty<'tcx>, actual: Ty<'tcx>) {
         if let Some(mut e) = self.demand_suptype_diag(sp, expected, actual) {
             e.emit();
diff --git a/compiler/rustc_hir_typeck/src/expectation.rs b/compiler/rustc_hir_typeck/src/expectation.rs
index e9e81034477..4f086cf597d 100644
--- a/compiler/rustc_hir_typeck/src/expectation.rs
+++ b/compiler/rustc_hir_typeck/src/expectation.rs
@@ -79,9 +79,9 @@ impl<'a, 'tcx> Expectation<'tcx> {
         }
     }
 
-    // Resolves `expected` by a single level if it is a variable. If
-    // there is no expected type or resolution is not possible (e.g.,
-    // no constraints yet present), just returns `self`.
+    /// Resolves `expected` by a single level if it is a variable. If
+    /// there is no expected type or resolution is not possible (e.g.,
+    /// no constraints yet present), just returns `self`.
     fn resolve(self, fcx: &FnCtxt<'a, 'tcx>) -> Expectation<'tcx> {
         match self {
             NoExpectation => NoExpectation,
diff --git a/compiler/rustc_hir_typeck/src/expr.rs b/compiler/rustc_hir_typeck/src/expr.rs
index d7f875b2857..0c5bbb3e20b 100644
--- a/compiler/rustc_hir_typeck/src/expr.rs
+++ b/compiler/rustc_hir_typeck/src/expr.rs
@@ -914,8 +914,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
     }
 
     // Check if an expression `original_expr_id` comes from the condition of a while loop,
-    // as opposed from the body of a while loop, which we can naively check by iterating
-    // parents until we find a loop...
+    /// as opposed from the body of a while loop, which we can naively check by iterating
+    /// parents until we find a loop...
     pub(super) fn comes_from_while_condition(
         &self,
         original_expr_id: HirId,
@@ -1664,7 +1664,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                         .fields
                         .iter()
                         .map(|f| {
-                            let fru_ty = self.normalize_associated_types_in(
+                            let fru_ty = self.normalize(
                                 expr_span,
                                 self.field_ty(base_expr.span, f, fresh_substs),
                             );
@@ -1748,9 +1748,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                     ty::Adt(adt, substs) if adt.is_struct() => variant
                         .fields
                         .iter()
-                        .map(|f| {
-                            self.normalize_associated_types_in(expr_span, f.ty(self.tcx, substs))
-                        })
+                        .map(|f| self.normalize(expr_span, f.ty(self.tcx, substs)))
                         .collect(),
                     _ => {
                         self.tcx
diff --git a/compiler/rustc_hir_typeck/src/expr_use_visitor.rs b/compiler/rustc_hir_typeck/src/expr_use_visitor.rs
index 275f7d12148..e5e798f4b93 100644
--- a/compiler/rustc_hir_typeck/src/expr_use_visitor.rs
+++ b/compiler/rustc_hir_typeck/src/expr_use_visitor.rs
@@ -252,11 +252,11 @@ impl<'a, 'tcx> ExprUseVisitor<'a, 'tcx> {
 
             hir::ExprKind::Match(ref discr, arms, _) => {
                 let discr_place = return_if_err!(self.mc.cat_expr(discr));
-                self.maybe_read_scrutinee(
+                return_if_err!(self.maybe_read_scrutinee(
                     discr,
                     discr_place.clone(),
                     arms.iter().map(|arm| arm.pat),
-                );
+                ));
 
                 // treatment of the discriminant is handled while walking the arms.
                 for arm in arms {
@@ -390,7 +390,7 @@ impl<'a, 'tcx> ExprUseVisitor<'a, 'tcx> {
         discr: &Expr<'_>,
         discr_place: PlaceWithHirId<'tcx>,
         pats: impl Iterator<Item = &'t hir::Pat<'t>>,
-    ) {
+    ) -> Result<(), ()> {
         // Matching should not always be considered a use of the place, hence
         // discr does not necessarily need to be borrowed.
         // We only want to borrow discr if the pattern contain something other
@@ -398,7 +398,7 @@ impl<'a, 'tcx> ExprUseVisitor<'a, 'tcx> {
         let ExprUseVisitor { ref mc, body_owner: _, delegate: _ } = *self;
         let mut needs_to_be_read = false;
         for pat in pats {
-            return_if_err!(mc.cat_pattern(discr_place.clone(), pat, |place, pat| {
+            mc.cat_pattern(discr_place.clone(), pat, |place, pat| {
                 match &pat.kind {
                     PatKind::Binding(.., opt_sub_pat) => {
                         // If the opt_sub_pat is None, than the binding does not count as
@@ -453,7 +453,7 @@ impl<'a, 'tcx> ExprUseVisitor<'a, 'tcx> {
                         // examined
                     }
                 }
-            }));
+            })?
         }
 
         if needs_to_be_read {
@@ -474,6 +474,7 @@ impl<'a, 'tcx> ExprUseVisitor<'a, 'tcx> {
             // that the discriminant has been initialized.
             self.walk_expr(discr);
         }
+        Ok(())
     }
 
     fn walk_local<F>(
@@ -490,7 +491,11 @@ impl<'a, 'tcx> ExprUseVisitor<'a, 'tcx> {
         f(self);
         if let Some(els) = els {
             // borrowing because we need to test the discriminant
-            self.maybe_read_scrutinee(expr, expr_place.clone(), from_ref(pat).iter());
+            return_if_err!(self.maybe_read_scrutinee(
+                expr,
+                expr_place.clone(),
+                from_ref(pat).iter()
+            ));
             self.walk_block(els)
         }
         self.walk_irrefutable_pat(&expr_place, &pat);
diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs
index 994fe48c9fe..952d2726259 100644
--- a/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs
+++ b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs
@@ -16,7 +16,7 @@ use rustc_hir_analysis::astconv::{
 };
 use rustc_infer::infer::canonical::{Canonical, OriginalQueryValues, QueryResponse};
 use rustc_infer::infer::error_reporting::TypeAnnotationNeeded::E0282;
-use rustc_infer::infer::{InferOk, InferResult};
+use rustc_infer::infer::InferResult;
 use rustc_middle::ty::adjustment::{Adjust, Adjustment, AutoBorrow, AutoBorrowMutability};
 use rustc_middle::ty::error::TypeError;
 use rustc_middle::ty::fold::TypeFoldable;
@@ -30,9 +30,8 @@ use rustc_span::def_id::LocalDefId;
 use rustc_span::hygiene::DesugaringKind;
 use rustc_span::symbol::{kw, sym, Ident};
 use rustc_span::{Span, DUMMY_SP};
-use rustc_trait_selection::infer::InferCtxtExt as _;
 use rustc_trait_selection::traits::error_reporting::TypeErrCtxtExt as _;
-use rustc_trait_selection::traits::{self, ObligationCause, ObligationCauseCode, ObligationCtxt};
+use rustc_trait_selection::traits::{self, NormalizeExt, ObligationCauseCode, ObligationCtxt};
 
 use std::collections::hash_map::Entry;
 use std::slice;
@@ -343,7 +342,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
     {
         debug!("instantiate_type_scheme(value={:?}, substs={:?})", value, substs);
         let value = EarlyBinder(value).subst(self.tcx, substs);
-        let result = self.normalize_associated_types_in(span, value);
+        let result = self.normalize(span, value);
         debug!("instantiate_type_scheme = {:?}", result);
         result
     }
@@ -359,7 +358,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         let bounds = self.tcx.predicates_of(def_id);
         let spans: Vec<Span> = bounds.predicates.iter().map(|(_, span)| *span).collect();
         let result = bounds.instantiate(self.tcx, substs);
-        let result = self.normalize_associated_types_in(span, result);
+        let result = self.normalize(span, result);
         debug!(
             "instantiate_bounds(bounds={:?}, substs={:?}) = {:?}, {:?}",
             bounds, substs, result, spans,
@@ -367,50 +366,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         (result, spans)
     }
 
-    pub(in super::super) fn normalize_associated_types_in<T>(&self, span: Span, value: T) -> T
+    pub(in super::super) fn normalize<T>(&self, span: Span, value: T) -> T
     where
         T: TypeFoldable<'tcx>,
     {
-        self.inh.normalize_associated_types_in(span, self.body_id, self.param_env, value)
-    }
-
-    pub(in super::super) fn normalize_associated_types_in_as_infer_ok<T>(
-        &self,
-        span: Span,
-        value: T,
-    ) -> InferOk<'tcx, T>
-    where
-        T: TypeFoldable<'tcx>,
-    {
-        self.inh.partially_normalize_associated_types_in(
-            ObligationCause::misc(span, self.body_id),
-            self.param_env,
-            value,
-        )
-    }
-
-    pub(in super::super) fn normalize_op_associated_types_in_as_infer_ok<T>(
-        &self,
-        span: Span,
-        value: T,
-        opt_input_expr: Option<&hir::Expr<'_>>,
-    ) -> InferOk<'tcx, T>
-    where
-        T: TypeFoldable<'tcx>,
-    {
-        self.inh.partially_normalize_associated_types_in(
-            ObligationCause::new(
-                span,
-                self.body_id,
-                traits::BinOp {
-                    rhs_span: opt_input_expr.map(|expr| expr.span),
-                    is_lit: opt_input_expr
-                        .map_or(false, |expr| matches!(expr.kind, ExprKind::Lit(_))),
-                    output_ty: None,
-                },
-            ),
-            self.param_env,
-            value,
+        self.register_infer_ok_obligations(
+            self.at(&self.misc(span), self.param_env).normalize(value),
         )
     }
 
@@ -490,7 +451,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                 let span = self.tcx.def_span(anon_const.def_id);
                 let c = ty::Const::from_anon_const(self.tcx, anon_const.def_id);
                 self.register_wf_obligation(c.into(), span, ObligationCauseCode::WellFormed(None));
-                self.normalize_associated_types_in(span, c)
+                self.normalize(span, c)
             }
         }
     }
@@ -583,7 +544,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         field: &'tcx ty::FieldDef,
         substs: SubstsRef<'tcx>,
     ) -> Ty<'tcx> {
-        self.normalize_associated_types_in(span, field.ty(self.tcx, substs))
+        self.normalize(span, field.ty(self.tcx, substs))
     }
 
     pub(in super::super) fn resolve_rvalue_scopes(&self, def_id: DefId) {
@@ -1110,7 +1071,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
 
         if let Res::Local(hid) = res {
             let ty = self.local_ty(span, hid).decl_ty;
-            let ty = self.normalize_associated_types_in(span, ty);
+            let ty = self.normalize(span, ty);
             self.write_ty(hir_id, ty);
             return (ty, res);
         }
diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs
index ecf6f458ca3..86384c7b93e 100644
--- a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs
+++ b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs
@@ -2089,7 +2089,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             && let maybe_trait_item_def_id = assoc_item.trait_item_def_id.unwrap_or(def_id)
             && let maybe_trait_def_id = self.tcx.parent(maybe_trait_item_def_id)
             // Just an easy way to check "trait_def_id == Fn/FnMut/FnOnce"
-            && let Some(call_kind) = ty::ClosureKind::from_def_id(self.tcx, maybe_trait_def_id)
+            && let Some(call_kind) = self.tcx.fn_trait_kind_from_def_id(maybe_trait_def_id)
             && let Some(callee_ty) = callee_ty
         {
             let callee_ty = callee_ty.peel_refs();
@@ -2115,7 +2115,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                         {
                             if let ty::PredicateKind::Clause(ty::Clause::Trait(pred)) = predicate.kind().skip_binder()
                                 && pred.self_ty().peel_refs() == callee_ty
-                                && ty::ClosureKind::from_def_id(self.tcx, pred.def_id()).is_some()
+                                && self.tcx.is_fn_trait(pred.def_id())
                             {
                                 err.span_note(span, "callable defined here");
                                 return;
diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs
index ed5f7f31764..ea141e815bf 100644
--- a/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs
+++ b/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs
@@ -288,7 +288,7 @@ impl<'a, 'tcx> AstConv<'tcx> for FnCtxt<'a, 'tcx> {
         if ty.has_escaping_bound_vars() {
             ty // FIXME: normalization and escaping regions
         } else {
-            self.normalize_associated_types_in(span, ty)
+            self.normalize(span, ty)
         }
     }
 
diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs
index 6c3526a71a3..b9a8d16311c 100644
--- a/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs
+++ b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs
@@ -20,6 +20,7 @@ use rustc_span::Span;
 use rustc_trait_selection::infer::InferCtxtExt;
 use rustc_trait_selection::traits::error_reporting::DefIdOrName;
 use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt as _;
+use rustc_trait_selection::traits::NormalizeExt;
 
 impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
     pub(crate) fn body_fn_sig(&self) -> Option<ty::FnSig<'tcx>> {
@@ -245,7 +246,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         // implied by wf, but also because that would possibly result in
         // erroneous errors later on.
         let infer::InferOk { value: output, obligations: _ } =
-            self.normalize_associated_types_in_as_infer_ok(expr.span, output);
+            self.at(&self.misc(expr.span), self.param_env).normalize(output);
 
         if output.is_ty_var() { None } else { Some((def_id_or_name, output, inputs)) }
     }
@@ -759,7 +760,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                 debug!("suggest_missing_return_type: expected type {:?}", ty);
                 let bound_vars = self.tcx.late_bound_vars(fn_id);
                 let ty = Binder::bind_with_vars(ty, bound_vars);
-                let ty = self.normalize_associated_types_in(span, ty);
+                let ty = self.normalize(span, ty);
                 let ty = self.tcx.erase_late_bound_regions(ty);
                 if self.can_coerce(expected, ty) {
                     err.subdiagnostic(ExpectedReturnTypeLabel::Other { span, expected });
@@ -920,7 +921,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             let ty = <dyn AstConv<'_>>::ast_ty_to_ty(self, ty);
             let bound_vars = self.tcx.late_bound_vars(fn_id);
             let ty = self.tcx.erase_late_bound_regions(Binder::bind_with_vars(ty, bound_vars));
-            let ty = self.normalize_associated_types_in(expr.span, ty);
+            let ty = self.normalize(expr.span, ty);
             let ty = match self.tcx.asyncness(fn_id.owner) {
                 hir::IsAsync::Async => {
                     let infcx = self.tcx.infer_ctxt().build();
diff --git a/compiler/rustc_hir_typeck/src/generator_interior/mod.rs b/compiler/rustc_hir_typeck/src/generator_interior/mod.rs
index 50722c42a6c..3b1518ff79b 100644
--- a/compiler/rustc_hir_typeck/src/generator_interior/mod.rs
+++ b/compiler/rustc_hir_typeck/src/generator_interior/mod.rs
@@ -118,7 +118,8 @@ impl<'a, 'tcx> InteriorVisitor<'a, 'tcx> {
                 } else {
                     let note = format!(
                         "the type is part of the {} because of this {}",
-                        self.kind, yield_data.source
+                        self.kind.descr(),
+                        yield_data.source
                     );
 
                     self.fcx
@@ -234,7 +235,7 @@ pub fn resolve_interior<'a, 'tcx>(
                 counter += 1;
                 ty::BoundRegion { var, kind }
             };
-            let ty = fcx.normalize_associated_types_in(cause.span, cause.ty);
+            let ty = fcx.normalize(cause.span, cause.ty);
             let ty = fcx.tcx.fold_regions(ty, |region, current_depth| {
                 let br = match region.kind() {
                     ty::ReVar(vid) => {
diff --git a/compiler/rustc_hir_typeck/src/inherited.rs b/compiler/rustc_hir_typeck/src/inherited.rs
index 0fb7651b3a1..869ad07c00d 100644
--- a/compiler/rustc_hir_typeck/src/inherited.rs
+++ b/compiler/rustc_hir_typeck/src/inherited.rs
@@ -7,12 +7,10 @@ use rustc_hir::def_id::LocalDefId;
 use rustc_hir::HirIdMap;
 use rustc_infer::infer;
 use rustc_infer::infer::{DefiningAnchor, InferCtxt, InferOk, TyCtxtInferExt};
-use rustc_middle::ty::fold::TypeFoldable;
 use rustc_middle::ty::visit::TypeVisitable;
 use rustc_middle::ty::{self, Ty, TyCtxt};
 use rustc_span::def_id::LocalDefIdMap;
 use rustc_span::{self, Span};
-use rustc_trait_selection::infer::InferCtxtExt as _;
 use rustc_trait_selection::traits::{
     self, ObligationCause, ObligationCtxt, TraitEngine, TraitEngineExt as _,
 };
@@ -38,19 +36,19 @@ pub struct Inherited<'tcx> {
 
     pub(super) fulfillment_cx: RefCell<Box<dyn TraitEngine<'tcx>>>,
 
-    // Some additional `Sized` obligations badly affect type inference.
-    // These obligations are added in a later stage of typeck.
-    // Removing these may also cause additional complications, see #101066.
+    /// Some additional `Sized` obligations badly affect type inference.
+    /// These obligations are added in a later stage of typeck.
+    /// Removing these may also cause additional complications, see #101066.
     pub(super) deferred_sized_obligations:
         RefCell<Vec<(Ty<'tcx>, Span, traits::ObligationCauseCode<'tcx>)>>,
 
-    // When we process a call like `c()` where `c` is a closure type,
-    // we may not have decided yet whether `c` is a `Fn`, `FnMut`, or
-    // `FnOnce` closure. In that case, we defer full resolution of the
-    // call until upvar inference can kick in and make the
-    // decision. We keep these deferred resolutions grouped by the
-    // def-id of the closure, so that once we decide, we can easily go
-    // back and process them.
+    /// When we process a call like `c()` where `c` is a closure type,
+    /// we may not have decided yet whether `c` is a `Fn`, `FnMut`, or
+    /// `FnOnce` closure. In that case, we defer full resolution of the
+    /// call until upvar inference can kick in and make the
+    /// decision. We keep these deferred resolutions grouped by the
+    /// def-id of the closure, so that once we decide, we can easily go
+    /// back and process them.
     pub(super) deferred_call_resolutions: RefCell<LocalDefIdMap<Vec<DeferredCallResolution<'tcx>>>>,
 
     pub(super) deferred_cast_checks: RefCell<Vec<super::cast::CastCheck<'tcx>>>,
@@ -102,7 +100,7 @@ impl<'tcx> Inherited<'tcx> {
                     infcx.probe(|_| {
                         let ocx = ObligationCtxt::new_in_snapshot(infcx);
                         let normalized_fn_sig = ocx.normalize(
-                            ObligationCause::dummy(),
+                            &ObligationCause::dummy(),
                             // FIXME(compiler-errors): This is probably not the right param-env...
                             infcx.tcx.param_env(def_id),
                             fn_sig,
@@ -179,35 +177,4 @@ impl<'tcx> Inherited<'tcx> {
         self.register_predicates(infer_ok.obligations);
         infer_ok.value
     }
-
-    pub(super) fn normalize_associated_types_in<T>(
-        &self,
-        span: Span,
-        body_id: hir::HirId,
-        param_env: ty::ParamEnv<'tcx>,
-        value: T,
-    ) -> T
-    where
-        T: TypeFoldable<'tcx>,
-    {
-        self.normalize_associated_types_in_with_cause(
-            ObligationCause::misc(span, body_id),
-            param_env,
-            value,
-        )
-    }
-
-    pub(super) fn normalize_associated_types_in_with_cause<T>(
-        &self,
-        cause: ObligationCause<'tcx>,
-        param_env: ty::ParamEnv<'tcx>,
-        value: T,
-    ) -> T
-    where
-        T: TypeFoldable<'tcx>,
-    {
-        let ok = self.partially_normalize_associated_types_in(cause, param_env, value);
-        debug!(?ok);
-        self.register_infer_ok_obligations(ok)
-    }
 }
diff --git a/compiler/rustc_hir_typeck/src/lib.rs b/compiler/rustc_hir_typeck/src/lib.rs
index 5104b448023..09bd123350d 100644
--- a/compiler/rustc_hir_typeck/src/lib.rs
+++ b/compiler/rustc_hir_typeck/src/lib.rs
@@ -233,9 +233,10 @@ fn typeck_with_fallback<'tcx>(
 
     let typeck_results = Inherited::build(tcx, def_id).enter(|inh| {
         let param_env = tcx.param_env(def_id);
-        let mut fcx = if let Some(hir::FnSig { header, decl, .. }) = fn_sig {
+        let mut fcx = FnCtxt::new(&inh, param_env, body.value.hir_id);
+
+        if let Some(hir::FnSig { header, decl, .. }) = fn_sig {
             let fn_sig = if rustc_hir_analysis::collect::get_infer_ret_ty(&decl.output).is_some() {
-                let fcx = FnCtxt::new(&inh, param_env, body.value.hir_id);
                 <dyn AstConv<'_>>::ty_of_fn(&fcx, id, header.unsafety, header.abi, decl, None, None)
             } else {
                 tcx.fn_sig(def_id)
@@ -245,15 +246,10 @@ fn typeck_with_fallback<'tcx>(
 
             // Compute the function signature from point of view of inside the fn.
             let fn_sig = tcx.liberate_late_bound_regions(def_id.to_def_id(), fn_sig);
-            let fn_sig = inh.normalize_associated_types_in(
-                body.value.span,
-                body_id.hir_id,
-                param_env,
-                fn_sig,
-            );
-            check_fn(&inh, param_env, fn_sig, decl, def_id, body, None).0
+            let fn_sig = fcx.normalize(body.value.span, fn_sig);
+
+            check_fn(&mut fcx, fn_sig, decl, def_id, body, None);
         } else {
-            let fcx = FnCtxt::new(&inh, param_env, body.value.hir_id);
             let expected_type = body_ty
                 .and_then(|ty| match ty.kind {
                     hir::TyKind::Infer => Some(<dyn AstConv<'_>>::ast_ty_to_ty(&fcx, ty)),
@@ -304,7 +300,7 @@ fn typeck_with_fallback<'tcx>(
                     _ => fallback(),
                 });
 
-            let expected_type = fcx.normalize_associated_types_in(body.value.span, expected_type);
+            let expected_type = fcx.normalize(body.value.span, expected_type);
             fcx.require_type_is_sized(expected_type, body.value.span, traits::ConstSized);
 
             // Gather locals in statics (because of block expressions).
@@ -313,8 +309,6 @@ fn typeck_with_fallback<'tcx>(
             fcx.check_expr_coercable_to_type(&body.value, expected_type, None);
 
             fcx.write_ty(id, expected_type);
-
-            fcx
         };
 
         fcx.type_inference_fallback();
diff --git a/compiler/rustc_hir_typeck/src/method/confirm.rs b/compiler/rustc_hir_typeck/src/method/confirm.rs
index f50a16dcb23..03d0e7926de 100644
--- a/compiler/rustc_hir_typeck/src/method/confirm.rs
+++ b/compiler/rustc_hir_typeck/src/method/confirm.rs
@@ -106,7 +106,7 @@ impl<'a, 'tcx> ConfirmContext<'a, 'tcx> {
         // traits, no trait system method can be called before this point because they
         // could alter our Self-type, except for normalizing the receiver from the
         // signature (which is also done during probing).
-        let method_sig_rcvr = self.normalize_associated_types_in(self.span, method_sig.inputs()[0]);
+        let method_sig_rcvr = self.normalize(self.span, method_sig.inputs()[0]);
         debug!(
             "confirm: self_ty={:?} method_sig_rcvr={:?} method_sig={:?} method_predicates={:?}",
             self_ty, method_sig_rcvr, method_sig, method_predicates
@@ -114,7 +114,7 @@ impl<'a, 'tcx> ConfirmContext<'a, 'tcx> {
         self.unify_receivers(self_ty, method_sig_rcvr, &pick, all_substs);
 
         let (method_sig, method_predicates) =
-            self.normalize_associated_types_in(self.span, (method_sig, method_predicates));
+            self.normalize(self.span, (method_sig, method_predicates));
         let method_sig = ty::Binder::dummy(method_sig);
 
         // Make sure nobody calls `drop()` explicitly.
diff --git a/compiler/rustc_hir_typeck/src/method/mod.rs b/compiler/rustc_hir_typeck/src/method/mod.rs
index 9c2de1763b0..ebbd5eb1e64 100644
--- a/compiler/rustc_hir_typeck/src/method/mod.rs
+++ b/compiler/rustc_hir_typeck/src/method/mod.rs
@@ -23,8 +23,8 @@ use rustc_middle::ty::subst::{InternalSubsts, SubstsRef};
 use rustc_middle::ty::{self, DefIdTree, GenericParamDefKind, Ty, TypeVisitable};
 use rustc_span::symbol::Ident;
 use rustc_span::Span;
-use rustc_trait_selection::traits;
 use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt;
+use rustc_trait_selection::traits::{self, NormalizeExt};
 
 use self::probe::{IsSuggestion, ProbeScope};
 
@@ -465,11 +465,22 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         let fn_sig = fn_sig.subst(self.tcx, substs);
         let fn_sig = self.replace_bound_vars_with_fresh_vars(span, infer::FnCall, fn_sig);
 
-        let InferOk { value, obligations: o } = if is_op {
-            self.normalize_op_associated_types_in_as_infer_ok(span, fn_sig, opt_input_expr)
+        let cause = if is_op {
+            ObligationCause::new(
+                span,
+                self.body_id,
+                traits::BinOp {
+                    rhs_span: opt_input_expr.map(|expr| expr.span),
+                    is_lit: opt_input_expr
+                        .map_or(false, |expr| matches!(expr.kind, hir::ExprKind::Lit(_))),
+                    output_ty: None,
+                },
+            )
         } else {
-            self.normalize_associated_types_in_as_infer_ok(span, fn_sig)
+            traits::ObligationCause::misc(span, self.body_id)
         };
+
+        let InferOk { value, obligations: o } = self.at(&cause, self.param_env).normalize(fn_sig);
         let fn_sig = {
             obligations.extend(o);
             value
@@ -485,11 +496,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         // any late-bound regions appearing in its bounds.
         let bounds = self.tcx.predicates_of(def_id).instantiate(self.tcx, substs);
 
-        let InferOk { value, obligations: o } = if is_op {
-            self.normalize_op_associated_types_in_as_infer_ok(span, bounds, opt_input_expr)
-        } else {
-            self.normalize_associated_types_in_as_infer_ok(span, bounds)
-        };
+        let InferOk { value, obligations: o } = self.at(&cause, self.param_env).normalize(bounds);
         let bounds = {
             obligations.extend(o);
             value
@@ -497,20 +504,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
 
         assert!(!bounds.has_escaping_bound_vars());
 
-        let cause = if is_op {
-            ObligationCause::new(
-                span,
-                self.body_id,
-                traits::BinOp {
-                    rhs_span: opt_input_expr.map(|expr| expr.span),
-                    is_lit: opt_input_expr
-                        .map_or(false, |expr| matches!(expr.kind, hir::ExprKind::Lit(_))),
-                    output_ty: None,
-                },
-            )
-        } else {
-            traits::ObligationCause::misc(span, self.body_id)
-        };
         let predicates_cause = cause.clone();
         obligations.extend(traits::predicates_for_generics(
             move |_, _| predicates_cause.clone(),
diff --git a/compiler/rustc_hir_typeck/src/method/probe.rs b/compiler/rustc_hir_typeck/src/method/probe.rs
index 6d858eacb45..c78a32c29dc 100644
--- a/compiler/rustc_hir_typeck/src/method/probe.rs
+++ b/compiler/rustc_hir_typeck/src/method/probe.rs
@@ -31,13 +31,13 @@ use rustc_span::lev_distance::{
 use rustc_span::symbol::sym;
 use rustc_span::{symbol::Ident, Span, Symbol, DUMMY_SP};
 use rustc_trait_selection::autoderef::{self, Autoderef};
-use rustc_trait_selection::infer::InferCtxtExt as _;
 use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt;
 use rustc_trait_selection::traits::query::method_autoderef::MethodAutoderefBadTy;
 use rustc_trait_selection::traits::query::method_autoderef::{
     CandidateStep, MethodAutoderefStepsResult,
 };
 use rustc_trait_selection::traits::query::CanonicalTyGoal;
+use rustc_trait_selection::traits::NormalizeExt;
 use rustc_trait_selection::traits::{self, ObligationCause};
 use std::cmp::max;
 use std::iter;
@@ -343,10 +343,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             &mut orig_values,
         );
 
-        let steps = if mode == Mode::MethodCall {
-            self.tcx.method_autoderef_steps(param_env_and_self_ty)
-        } else {
-            self.probe(|_| {
+        let steps = match mode {
+            Mode::MethodCall => self.tcx.method_autoderef_steps(param_env_and_self_ty),
+            Mode::Path => self.probe(|_| {
                 // Mode::Path - the deref steps is "trivial". This turns
                 // our CanonicalQuery into a "trivial" QueryResponse. This
                 // is a bit inefficient, but I don't think that writing
@@ -375,7 +374,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                     opt_bad_ty: None,
                     reached_recursion_limit: false,
                 }
-            })
+            }),
         };
 
         // If our autoderef loop had reached the recursion limit,
@@ -717,9 +716,8 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
             // maybe shouldn't include `Param`s, but rather fresh variables or be canonicalized,
             // see issue #89650
             let cause = traits::ObligationCause::misc(self.span, self.body_id);
-            let InferOk { value: xform_self_ty, obligations } = self
-                .fcx
-                .partially_normalize_associated_types_in(cause, self.param_env, xform_self_ty);
+            let InferOk { value: xform_self_ty, obligations } =
+                self.fcx.at(&cause, self.param_env).normalize(xform_self_ty);
 
             debug!(
                 "assemble_inherent_impl_probe after normalization: xform_self_ty = {:?}/{:?}",
@@ -1508,11 +1506,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
                     let InferOk {
                         value: normalized_xform_ret_ty,
                         obligations: normalization_obligations,
-                    } = self.fcx.partially_normalize_associated_types_in(
-                        cause.clone(),
-                        self.param_env,
-                        probe.xform_ret_ty,
-                    );
+                    } = self.fcx.at(&cause, self.param_env).normalize(probe.xform_ret_ty);
                     xform_ret_ty = normalized_xform_ret_ty;
                     debug!("xform_ret_ty after normalization: {:?}", xform_ret_ty);
 
@@ -1522,11 +1516,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
                     let impl_bounds = impl_bounds.instantiate(self.tcx, substs);
 
                     let InferOk { value: impl_bounds, obligations: norm_obligations } =
-                        self.fcx.partially_normalize_associated_types_in(
-                            cause.clone(),
-                            self.param_env,
-                            impl_bounds,
-                        );
+                        self.fcx.at(&cause, self.param_env).normalize(impl_bounds);
 
                     // Convert the bounds into obligations.
                     let impl_obligations = traits::predicates_for_generics(
diff --git a/compiler/rustc_hir_typeck/src/upvar.rs b/compiler/rustc_hir_typeck/src/upvar.rs
index 68f119adc7a..0f46972019e 100644
--- a/compiler/rustc_hir_typeck/src/upvar.rs
+++ b/compiler/rustc_hir_typeck/src/upvar.rs
@@ -2168,7 +2168,7 @@ fn determine_place_ancestry_relation<'tcx>(
     place_a: &Place<'tcx>,
     place_b: &Place<'tcx>,
 ) -> PlaceAncestryRelation {
-    // If Place A and Place B, don't start off from the same root variable, they are divergent.
+    // If Place A and Place B don't start off from the same root variable, they are divergent.
     if place_a.base != place_b.base {
         return PlaceAncestryRelation::Divergent;
     }
diff --git a/compiler/rustc_incremental/src/persist/dirty_clean.rs b/compiler/rustc_incremental/src/persist/dirty_clean.rs
index 79e2d371ed3..a8acaf6597a 100644
--- a/compiler/rustc_incremental/src/persist/dirty_clean.rs
+++ b/compiler/rustc_incremental/src/persist/dirty_clean.rs
@@ -438,9 +438,9 @@ fn expect_associated_value(tcx: TyCtxt<'_>, item: &NestedMetaItem) -> Symbol {
     }
 }
 
-// A visitor that collects all #[rustc_clean] attributes from
-// the HIR. It is used to verify that we really ran checks for all annotated
-// nodes.
+/// A visitor that collects all `#[rustc_clean]` attributes from
+/// the HIR. It is used to verify that we really ran checks for all annotated
+/// nodes.
 pub struct FindAllAttrs<'tcx> {
     tcx: TyCtxt<'tcx>,
     found_attrs: Vec<&'tcx Attribute>,
diff --git a/compiler/rustc_infer/src/infer/canonical/mod.rs b/compiler/rustc_infer/src/infer/canonical/mod.rs
index 0794792d8cb..e59715b706b 100644
--- a/compiler/rustc_infer/src/infer/canonical/mod.rs
+++ b/compiler/rustc_infer/src/infer/canonical/mod.rs
@@ -41,7 +41,7 @@ impl<'tcx> InferCtxt<'tcx> {
     /// inference variables and applies it to the canonical value.
     /// Returns both the instantiated result *and* the substitution S.
     ///
-    /// This is only meant to be invoked as part of constructing an
+    /// This can be invoked as part of constructing an
     /// inference context at the start of a query (see
     /// `InferCtxtBuilder::build_with_canonical`). It basically
     /// brings the canonical value "into scope" within your new infcx.
@@ -63,8 +63,11 @@ impl<'tcx> InferCtxt<'tcx> {
         // in them, so this code has no effect, but it is looking
         // forward to the day when we *do* want to carry universes
         // through into queries.
-        let universes: IndexVec<ty::UniverseIndex, _> = std::iter::once(ty::UniverseIndex::ROOT)
-            .chain((0..canonical.max_universe.as_u32()).map(|_| self.create_next_universe()))
+        //
+        // Instantiate the root-universe content into the current universe,
+        // and create fresh universes for the higher universes.
+        let universes: IndexVec<ty::UniverseIndex, _> = std::iter::once(self.universe())
+            .chain((1..=canonical.max_universe.as_u32()).map(|_| self.create_next_universe()))
             .collect();
 
         let canonical_inference_vars =
@@ -147,7 +150,7 @@ impl<'tcx> InferCtxt<'tcx> {
             CanonicalVarKind::PlaceholderConst(ty::PlaceholderConst { universe, name }, ty) => {
                 let universe_mapped = universe_map(universe);
                 let placeholder_mapped = ty::PlaceholderConst { universe: universe_mapped, name };
-                self.tcx.mk_const(ty::ConstKind::Placeholder(placeholder_mapped), ty).into()
+                self.tcx.mk_const(placeholder_mapped, ty).into()
             }
         }
     }
diff --git a/compiler/rustc_infer/src/infer/combine.rs b/compiler/rustc_infer/src/infer/combine.rs
index eec938cefbb..cf895ed0d3e 100644
--- a/compiler/rustc_infer/src/infer/combine.rs
+++ b/compiler/rustc_infer/src/infer/combine.rs
@@ -753,7 +753,7 @@ impl<'tcx> TypeRelation<'tcx> for Generalizer<'_, 'tcx> {
                                 origin: var_value.origin,
                                 val: ConstVariableValue::Unknown { universe: self.for_universe },
                             });
-                            Ok(self.tcx().mk_const_var(new_var_id, c.ty()))
+                            Ok(self.tcx().mk_const(new_var_id, c.ty()))
                         }
                     }
                 }
@@ -765,10 +765,7 @@ impl<'tcx> TypeRelation<'tcx> for Generalizer<'_, 'tcx> {
                     substs,
                     substs,
                 )?;
-                Ok(self.tcx().mk_const(
-                    ty::ConstKind::Unevaluated(ty::UnevaluatedConst { def, substs }),
-                    c.ty(),
-                ))
+                Ok(self.tcx().mk_const(ty::UnevaluatedConst { def, substs }, c.ty()))
             }
             _ => relate::super_relate_consts(self, c, c),
         }
@@ -975,7 +972,7 @@ impl<'tcx> TypeRelation<'tcx> for ConstInferUnifier<'_, 'tcx> {
                                         },
                                     },
                                 );
-                            Ok(self.tcx().mk_const_var(new_var_id, c.ty()))
+                            Ok(self.tcx().mk_const(new_var_id, c.ty()))
                         }
                     }
                 }
@@ -988,10 +985,7 @@ impl<'tcx> TypeRelation<'tcx> for ConstInferUnifier<'_, 'tcx> {
                     substs,
                 )?;
 
-                Ok(self.tcx().mk_const(
-                    ty::ConstKind::Unevaluated(ty::UnevaluatedConst { def, substs }),
-                    c.ty(),
-                ))
+                Ok(self.tcx().mk_const(ty::UnevaluatedConst { def, substs }, c.ty()))
             }
             _ => relate::super_relate_consts(self, c, c),
         }
diff --git a/compiler/rustc_infer/src/infer/error_reporting/mod.rs b/compiler/rustc_infer/src/infer/error_reporting/mod.rs
index 6b6be7359a5..e2be8fb12d0 100644
--- a/compiler/rustc_infer/src/infer/error_reporting/mod.rs
+++ b/compiler/rustc_infer/src/infer/error_reporting/mod.rs
@@ -1672,40 +1672,34 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
             }
         };
 
-        match terr {
-            // Ignore msg for object safe coercion
-            // since E0038 message will be printed
-            TypeError::ObjectUnsafeCoercion(_) => {}
-            _ => {
-                let mut label_or_note = |span: Span, msg: &str| {
-                    if (prefer_label && is_simple_error) || &[span] == diag.span.primary_spans() {
-                        diag.span_label(span, msg);
-                    } else {
-                        diag.span_note(span, msg);
-                    }
-                };
-                if let Some((sp, msg)) = secondary_span {
-                    if swap_secondary_and_primary {
-                        let terr = if let Some(infer::ValuePairs::Terms(infer::ExpectedFound {
-                            expected,
-                            ..
-                        })) = values
-                        {
-                            format!("expected this to be `{}`", expected)
-                        } else {
-                            terr.to_string()
-                        };
-                        label_or_note(sp, &terr);
-                        label_or_note(span, &msg);
-                    } else {
-                        label_or_note(span, &terr.to_string());
-                        label_or_note(sp, &msg);
-                    }
-                } else {
-                    label_or_note(span, &terr.to_string());
-                }
+        let mut label_or_note = |span: Span, msg: &str| {
+            if (prefer_label && is_simple_error) || &[span] == diag.span.primary_spans() {
+                diag.span_label(span, msg);
+            } else {
+                diag.span_note(span, msg);
             }
         };
+        if let Some((sp, msg)) = secondary_span {
+            if swap_secondary_and_primary {
+                let terr = if let Some(infer::ValuePairs::Terms(infer::ExpectedFound {
+                    expected,
+                    ..
+                })) = values
+                {
+                    format!("expected this to be `{}`", expected)
+                } else {
+                    terr.to_string()
+                };
+                label_or_note(sp, &terr);
+                label_or_note(span, &msg);
+            } else {
+                label_or_note(span, &terr.to_string());
+                label_or_note(sp, &msg);
+            }
+        } else {
+            label_or_note(span, &terr.to_string());
+        }
+
         if let Some((expected, found)) = expected_found {
             let (expected_label, found_label, exp_found) = match exp_found {
                 Mismatch::Variable(ef) => (
@@ -1875,9 +1869,6 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
                         );
                     }
                 }
-                TypeError::ObjectUnsafeCoercion(_) => {
-                    diag.note_unsuccessful_coercion(found, expected);
-                }
                 _ => {
                     debug!(
                         "note_type_err: exp_found={:?}, expected={:?} found={:?}",
@@ -3122,7 +3113,6 @@ impl<'tcx> ObligationCauseExt<'tcx> for ObligationCause<'tcx> {
                 TypeError::IntrinsicCast => {
                     Error0308("cannot coerce intrinsics to function pointers")
                 }
-                TypeError::ObjectUnsafeCoercion(did) => Error0038(did),
                 _ => Error0308("mismatched types"),
             },
         }
diff --git a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/placeholder_error.rs b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/placeholder_error.rs
index a585168294a..1f554c81eff 100644
--- a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/placeholder_error.rs
+++ b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/placeholder_error.rs
@@ -399,10 +399,7 @@ impl<'tcx> NiceRegionError<'_, 'tcx> {
                 self_ty.highlight.maybe_highlighting_region(vid, actual_has_vid);
 
                 if self_ty.value.is_closure()
-                    && self
-                        .tcx()
-                        .fn_trait_kind_from_lang_item(expected_trait_ref.value.def_id)
-                        .is_some()
+                    && self.tcx().is_fn_trait(expected_trait_ref.value.def_id)
                 {
                     let closure_sig = self_ty.map(|closure| {
                         if let ty::Closure(_, substs) = closure.kind() {
diff --git a/compiler/rustc_infer/src/infer/free_regions.rs b/compiler/rustc_infer/src/infer/free_regions.rs
index 728d691a2be..2402a7ea7c7 100644
--- a/compiler/rustc_infer/src/infer/free_regions.rs
+++ b/compiler/rustc_infer/src/infer/free_regions.rs
@@ -29,10 +29,10 @@ impl<'a, 'tcx> RegionRelations<'a, 'tcx> {
 
 #[derive(Clone, Debug)]
 pub struct FreeRegionMap<'tcx> {
-    // Stores the relation `a < b`, where `a` and `b` are regions.
-    //
-    // Invariant: only free regions like `'x` or `'static` are stored
-    // in this relation, not scopes.
+    /// Stores the relation `a < b`, where `a` and `b` are regions.
+    ///
+    /// Invariant: only free regions like `'x` or `'static` are stored
+    /// in this relation, not scopes.
     pub(crate) relation: TransitiveRelation<Region<'tcx>>,
 }
 
diff --git a/compiler/rustc_infer/src/infer/freshen.rs b/compiler/rustc_infer/src/infer/freshen.rs
index 27a94ec5e30..f6946929bd2 100644
--- a/compiler/rustc_infer/src/infer/freshen.rs
+++ b/compiler/rustc_infer/src/infer/freshen.rs
@@ -102,7 +102,7 @@ impl<'a, 'tcx> TypeFreshener<'a, 'tcx> {
             Entry::Vacant(entry) => {
                 let index = self.const_freshen_count;
                 self.const_freshen_count += 1;
-                let ct = self.infcx.tcx.mk_const_infer(freshener(index), ty);
+                let ct = self.infcx.tcx.mk_const(freshener(index), ty);
                 entry.insert(ct);
                 ct
             }
diff --git a/compiler/rustc_infer/src/infer/higher_ranked/mod.rs b/compiler/rustc_infer/src/infer/higher_ranked/mod.rs
index d739323de77..817ae10c760 100644
--- a/compiler/rustc_infer/src/infer/higher_ranked/mod.rs
+++ b/compiler/rustc_infer/src/infer/higher_ranked/mod.rs
@@ -94,13 +94,8 @@ impl<'tcx> InferCtxt<'tcx> {
                 }))
             },
             consts: &mut |bound_var: ty::BoundVar, ty| {
-                self.tcx.mk_const(
-                    ty::ConstKind::Placeholder(ty::PlaceholderConst {
-                        universe: next_universe,
-                        name: bound_var,
-                    }),
-                    ty,
-                )
+                self.tcx
+                    .mk_const(ty::PlaceholderConst { universe: next_universe, name: bound_var }, ty)
             },
         };
 
diff --git a/compiler/rustc_infer/src/infer/mod.rs b/compiler/rustc_infer/src/infer/mod.rs
index cda9299dcb6..2bcb47cc383 100644
--- a/compiler/rustc_infer/src/infer/mod.rs
+++ b/compiler/rustc_infer/src/infer/mod.rs
@@ -1065,7 +1065,7 @@ impl<'tcx> InferCtxt<'tcx> {
     }
 
     pub fn next_const_var(&self, ty: Ty<'tcx>, origin: ConstVariableOrigin) -> ty::Const<'tcx> {
-        self.tcx.mk_const_var(self.next_const_var_id(origin), ty)
+        self.tcx.mk_const(self.next_const_var_id(origin), ty)
     }
 
     pub fn next_const_var_in_universe(
@@ -1079,7 +1079,7 @@ impl<'tcx> InferCtxt<'tcx> {
             .borrow_mut()
             .const_unification_table()
             .new_key(ConstVarValue { origin, val: ConstVariableValue::Unknown { universe } });
-        self.tcx.mk_const_var(vid, ty)
+        self.tcx.mk_const(vid, ty)
     }
 
     pub fn next_const_var_id(&self, origin: ConstVariableOrigin) -> ConstVid<'tcx> {
@@ -1195,7 +1195,7 @@ impl<'tcx> InferCtxt<'tcx> {
                         origin,
                         val: ConstVariableValue::Unknown { universe: self.universe() },
                     });
-                self.tcx.mk_const_var(const_var_id, self.tcx.type_of(param.def_id)).into()
+                self.tcx.mk_const(const_var_id, self.tcx.type_of(param.def_id)).into()
             }
         }
     }
@@ -1580,7 +1580,7 @@ impl<'tcx> InferCtxt<'tcx> {
         span: Option<Span>,
     ) -> Result<ty::Const<'tcx>, ErrorHandled> {
         match self.const_eval_resolve(param_env, unevaluated, span) {
-            Ok(Some(val)) => Ok(ty::Const::from_value(self.tcx, val, ty)),
+            Ok(Some(val)) => Ok(self.tcx.mk_const(val, ty)),
             Ok(None) => {
                 let tcx = self.tcx;
                 let def_id = unevaluated.def.did;
@@ -2049,10 +2049,10 @@ fn replace_param_and_infer_substs_with_placeholder<'tcx>(
                     bug!("const `{ct}`'s type should not reference params or types");
                 }
                 tcx.mk_const(
-                    ty::ConstKind::Placeholder(ty::PlaceholderConst {
+                    ty::PlaceholderConst {
                         universe: ty::UniverseIndex::ROOT,
                         name: ty::BoundVar::from_usize(idx),
-                    }),
+                    },
                     ty,
                 )
                 .into()
diff --git a/compiler/rustc_infer/src/infer/nll_relate/mod.rs b/compiler/rustc_infer/src/infer/nll_relate/mod.rs
index 4f8460955c3..f6bc4db0d59 100644
--- a/compiler/rustc_infer/src/infer/nll_relate/mod.rs
+++ b/compiler/rustc_infer/src/infer/nll_relate/mod.rs
@@ -1087,7 +1087,7 @@ where
                             origin: var_value.origin,
                             val: ConstVariableValue::Unknown { universe: self.universe },
                         });
-                        Ok(self.tcx().mk_const_var(new_var_id, a.ty()))
+                        Ok(self.tcx().mk_const(new_var_id, a.ty()))
                     }
                 }
             }
diff --git a/compiler/rustc_infer/src/infer/opaque_types.rs b/compiler/rustc_infer/src/infer/opaque_types.rs
index 26caf82ec6a..524f7a39ebb 100644
--- a/compiler/rustc_infer/src/infer/opaque_types.rs
+++ b/compiler/rustc_infer/src/infer/opaque_types.rs
@@ -410,19 +410,19 @@ impl<'tcx> InferCtxt<'tcx> {
     }
 }
 
-// Visitor that requires that (almost) all regions in the type visited outlive
-// `least_region`. We cannot use `push_outlives_components` because regions in
-// closure signatures are not included in their outlives components. We need to
-// ensure all regions outlive the given bound so that we don't end up with,
-// say, `ReVar` appearing in a return type and causing ICEs when other
-// functions end up with region constraints involving regions from other
-// functions.
-//
-// We also cannot use `for_each_free_region` because for closures it includes
-// the regions parameters from the enclosing item.
-//
-// We ignore any type parameters because impl trait values are assumed to
-// capture all the in-scope type parameters.
+/// Visitor that requires that (almost) all regions in the type visited outlive
+/// `least_region`. We cannot use `push_outlives_components` because regions in
+/// closure signatures are not included in their outlives components. We need to
+/// ensure all regions outlive the given bound so that we don't end up with,
+/// say, `ReVar` appearing in a return type and causing ICEs when other
+/// functions end up with region constraints involving regions from other
+/// functions.
+///
+/// We also cannot use `for_each_free_region` because for closures it includes
+/// the regions parameters from the enclosing item.
+///
+/// We ignore any type parameters because impl trait values are assumed to
+/// capture all the in-scope type parameters.
 pub struct ConstrainOpaqueTypeRegionVisitor<'tcx, OP: FnMut(ty::Region<'tcx>)> {
     pub tcx: TyCtxt<'tcx>,
     pub op: OP,
diff --git a/compiler/rustc_infer/src/infer/opaque_types/table.rs b/compiler/rustc_infer/src/infer/opaque_types/table.rs
index 4d124554afb..c146902d594 100644
--- a/compiler/rustc_infer/src/infer/opaque_types/table.rs
+++ b/compiler/rustc_infer/src/infer/opaque_types/table.rs
@@ -9,10 +9,10 @@ use super::{OpaqueTypeDecl, OpaqueTypeMap};
 
 #[derive(Default, Debug, Clone)]
 pub struct OpaqueTypeStorage<'tcx> {
-    // Opaque types found in explicit return types and their
-    // associated fresh inference variable. Writeback resolves these
-    // variables to get the concrete type, which can be used to
-    // 'de-opaque' OpaqueTypeDecl, after typeck is done with all functions.
+    /// Opaque types found in explicit return types and their
+    /// associated fresh inference variable. Writeback resolves these
+    /// variables to get the concrete type, which can be used to
+    /// 'de-opaque' OpaqueTypeDecl, after typeck is done with all functions.
     pub opaque_types: OpaqueTypeMap<'tcx>,
 }
 
diff --git a/compiler/rustc_infer/src/infer/outlives/test_type_match.rs b/compiler/rustc_infer/src/infer/outlives/test_type_match.rs
index 5d204dd70ed..10b474efd5a 100644
--- a/compiler/rustc_infer/src/infer/outlives/test_type_match.rs
+++ b/compiler/rustc_infer/src/infer/outlives/test_type_match.rs
@@ -155,14 +155,17 @@ impl<'tcx> TypeRelation<'tcx> for Match<'tcx> {
         bug!()
     }
 
+    #[instrument(level = "trace", skip(self))]
     fn relate_with_variance<T: Relate<'tcx>>(
         &mut self,
-        _: ty::Variance,
+        variance: ty::Variance,
         _: ty::VarianceDiagInfo<'tcx>,
         a: T,
         b: T,
     ) -> RelateResult<'tcx, T> {
-        self.relate(a, b)
+        // Opaque types substs have lifetime parameters.
+        // We must not check them to be equal, as we never insert anything to make them so.
+        if variance != ty::Bivariant { self.relate(a, b) } else { Ok(a) }
     }
 
     #[instrument(skip(self), level = "debug")]
diff --git a/compiler/rustc_interface/src/interface.rs b/compiler/rustc_interface/src/interface.rs
index 99c934862c4..4c22ab68a56 100644
--- a/compiler/rustc_interface/src/interface.rs
+++ b/compiler/rustc_interface/src/interface.rs
@@ -194,7 +194,7 @@ pub fn parse_check_cfg(specs: Vec<String>) -> CheckCfg {
 
                                         for val in values {
                                             if let Some(LitKind::Str(s, _)) =
-                                                val.literal().map(|lit| &lit.kind)
+                                                val.lit().map(|lit| &lit.kind)
                                             {
                                                 ident_values.insert(s.to_string());
                                             } else {
diff --git a/compiler/rustc_lint/src/builtin.rs b/compiler/rustc_lint/src/builtin.rs
index 93d81125f48..825093384fb 100644
--- a/compiler/rustc_lint/src/builtin.rs
+++ b/compiler/rustc_lint/src/builtin.rs
@@ -2046,16 +2046,13 @@ declare_lint_pass!(ExplicitOutlivesRequirements => [EXPLICIT_OUTLIVES_REQUIREMEN
 
 impl ExplicitOutlivesRequirements {
     fn lifetimes_outliving_lifetime<'tcx>(
-        inferred_outlives: &'tcx [(ty::Predicate<'tcx>, Span)],
+        inferred_outlives: &'tcx [(ty::Clause<'tcx>, Span)],
         def_id: DefId,
     ) -> Vec<ty::Region<'tcx>> {
         inferred_outlives
             .iter()
-            .filter_map(|(pred, _)| match pred.kind().skip_binder() {
-                ty::PredicateKind::Clause(ty::Clause::RegionOutlives(ty::OutlivesPredicate(
-                    a,
-                    b,
-                ))) => match *a {
+            .filter_map(|(clause, _)| match *clause {
+                ty::Clause::RegionOutlives(ty::OutlivesPredicate(a, b)) => match *a {
                     ty::ReEarlyBound(ebr) if ebr.def_id == def_id => Some(b),
                     _ => None,
                 },
@@ -2065,16 +2062,15 @@ impl ExplicitOutlivesRequirements {
     }
 
     fn lifetimes_outliving_type<'tcx>(
-        inferred_outlives: &'tcx [(ty::Predicate<'tcx>, Span)],
+        inferred_outlives: &'tcx [(ty::Clause<'tcx>, Span)],
         index: u32,
     ) -> Vec<ty::Region<'tcx>> {
         inferred_outlives
             .iter()
-            .filter_map(|(pred, _)| match pred.kind().skip_binder() {
-                ty::PredicateKind::Clause(ty::Clause::TypeOutlives(ty::OutlivesPredicate(
-                    a,
-                    b,
-                ))) => a.is_param(index).then_some(b),
+            .filter_map(|(clause, _)| match *clause {
+                ty::Clause::TypeOutlives(ty::OutlivesPredicate(a, b)) => {
+                    a.is_param(index).then_some(b)
+                }
                 _ => None,
             })
             .collect()
diff --git a/compiler/rustc_lint/src/context.rs b/compiler/rustc_lint/src/context.rs
index 67cf66f4708..e6a0d7e60ca 100644
--- a/compiler/rustc_lint/src/context.rs
+++ b/compiler/rustc_lint/src/context.rs
@@ -206,7 +206,7 @@ impl LintStore {
         self.late_module_passes.push(Box::new(pass));
     }
 
-    // Helper method for register_early/late_pass
+    /// Helper method for register_early/late_pass
     pub fn register_lints(&mut self, lints: &[&'static Lint]) {
         for lint in lints {
             self.lints.push(lint);
diff --git a/compiler/rustc_lint/src/internal.rs b/compiler/rustc_lint/src/internal.rs
index 293f1c5c471..4f92661dbd3 100644
--- a/compiler/rustc_lint/src/internal.rs
+++ b/compiler/rustc_lint/src/internal.rs
@@ -117,7 +117,7 @@ impl<'tcx> LateLintPass<'tcx> for TyTyKind {
     fn check_path(
         &mut self,
         cx: &LateContext<'tcx>,
-        path: &'tcx rustc_hir::Path<'tcx>,
+        path: &rustc_hir::Path<'tcx>,
         _: rustc_hir::HirId,
     ) {
         if let Some(segment) = path.segments.iter().nth_back(1)
@@ -462,8 +462,8 @@ impl LateLintPass<'_> for BadOptAccess {
                 let Some(attr) = cx.tcx.get_attr(field.did, sym::rustc_lint_opt_deny_field_access) &&
                 let Some(items) = attr.meta_item_list()  &&
                 let Some(item) = items.first()  &&
-                let Some(literal) = item.literal()  &&
-                let ast::LitKind::Str(val, _) = literal.kind
+                let Some(lit) = item.lit()  &&
+                let ast::LitKind::Str(val, _) = lit.kind
             {
                 cx.struct_span_lint(BAD_OPT_ACCESS, expr.span, val.as_str(), |lint|
                     lint
diff --git a/compiler/rustc_lint/src/late.rs b/compiler/rustc_lint/src/late.rs
index f484e31ba15..1d0b3f34d5d 100644
--- a/compiler/rustc_lint/src/late.rs
+++ b/compiler/rustc_lint/src/late.rs
@@ -292,7 +292,7 @@ impl<'tcx, T: LateLintPass<'tcx>> hir_visit::Visitor<'tcx> for LateContextAndPas
         hir_visit::walk_lifetime(self, lt);
     }
 
-    fn visit_path(&mut self, p: &'tcx hir::Path<'tcx>, id: hir::HirId) {
+    fn visit_path(&mut self, p: &hir::Path<'tcx>, id: hir::HirId) {
         lint_callback!(self, check_path, p, id);
         hir_visit::walk_path(self, p);
     }
diff --git a/compiler/rustc_lint/src/passes.rs b/compiler/rustc_lint/src/passes.rs
index 1c6a057d1a8..fc11d092ccb 100644
--- a/compiler/rustc_lint/src/passes.rs
+++ b/compiler/rustc_lint/src/passes.rs
@@ -44,7 +44,7 @@ macro_rules! late_lint_methods {
             fn check_struct_def(a: &$hir hir::VariantData<$hir>);
             fn check_field_def(a: &$hir hir::FieldDef<$hir>);
             fn check_variant(a: &$hir hir::Variant<$hir>);
-            fn check_path(a: &$hir hir::Path<$hir>, b: hir::HirId);
+            fn check_path(a: &hir::Path<$hir>, b: hir::HirId);
             fn check_attribute(a: &$hir ast::Attribute);
 
             /// Called when entering a syntax node that can have lint attributes such
diff --git a/compiler/rustc_lint/src/unused.rs b/compiler/rustc_lint/src/unused.rs
index 43864ed45fa..a7836ea8e7a 100644
--- a/compiler/rustc_lint/src/unused.rs
+++ b/compiler/rustc_lint/src/unused.rs
@@ -322,10 +322,7 @@ impl<'tcx> LateLintPass<'tcx> for UnusedResults {
                 ty::Closure(..) => Some(MustUsePath::Closure(span)),
                 ty::Generator(def_id, ..) => {
                     // async fn should be treated as "implementor of `Future`"
-                    let must_use = if matches!(
-                        cx.tcx.generator_kind(def_id),
-                        Some(hir::GeneratorKind::Async(..))
-                    ) {
+                    let must_use = if cx.tcx.generator_is_async(def_id) {
                         let def_id = cx.tcx.lang_items().future_trait().unwrap();
                         is_def_must_use(cx, def_id, span)
                             .map(|inner| MustUsePath::Opaque(Box::new(inner)))
@@ -1267,7 +1264,7 @@ impl UnusedImportBraces {
 
             // Trigger the lint if the nested item is a non-self single item
             let node_name = match items[0].0.kind {
-                ast::UseTreeKind::Simple(rename, ..) => {
+                ast::UseTreeKind::Simple(rename) => {
                     let orig_ident = items[0].0.prefix.segments.last().unwrap().ident;
                     if orig_ident.name == kw::SelfLower {
                         return;
diff --git a/compiler/rustc_macros/src/query.rs b/compiler/rustc_macros/src/query.rs
index 7cefafef9d9..4047969724a 100644
--- a/compiler/rustc_macros/src/query.rs
+++ b/compiler/rustc_macros/src/query.rs
@@ -114,6 +114,9 @@ struct QueryModifiers {
 
     /// Always remap the ParamEnv's constness before hashing.
     remap_env_constness: Option<Ident>,
+
+    /// Generate a `feed` method to set the query's value from another query.
+    feedable: Option<Ident>,
 }
 
 fn parse_query_modifiers(input: ParseStream<'_>) -> Result<QueryModifiers> {
@@ -128,6 +131,7 @@ fn parse_query_modifiers(input: ParseStream<'_>) -> Result<QueryModifiers> {
     let mut depth_limit = None;
     let mut separate_provide_extern = None;
     let mut remap_env_constness = None;
+    let mut feedable = None;
 
     while !input.is_empty() {
         let modifier: Ident = input.parse()?;
@@ -187,6 +191,8 @@ fn parse_query_modifiers(input: ParseStream<'_>) -> Result<QueryModifiers> {
             try_insert!(separate_provide_extern = modifier);
         } else if modifier == "remap_env_constness" {
             try_insert!(remap_env_constness = modifier);
+        } else if modifier == "feedable" {
+            try_insert!(feedable = modifier);
         } else {
             return Err(Error::new(modifier.span(), "unknown query modifier"));
         }
@@ -206,6 +212,7 @@ fn parse_query_modifiers(input: ParseStream<'_>) -> Result<QueryModifiers> {
         depth_limit,
         separate_provide_extern,
         remap_env_constness,
+        feedable,
     })
 }
 
@@ -296,6 +303,7 @@ pub fn rustc_queries(input: TokenStream) -> TokenStream {
     let mut query_stream = quote! {};
     let mut query_description_stream = quote! {};
     let mut query_cached_stream = quote! {};
+    let mut feedable_queries = quote! {};
 
     for query in queries.0 {
         let Query { name, arg, modifiers, .. } = &query;
@@ -350,6 +358,22 @@ pub fn rustc_queries(input: TokenStream) -> TokenStream {
             [#attribute_stream] fn #name(#arg) #result,
         });
 
+        if modifiers.feedable.is_some() {
+            assert!(modifiers.anon.is_none(), "Query {name} cannot be both `feedable` and `anon`.");
+            assert!(
+                modifiers.eval_always.is_none(),
+                "Query {name} cannot be both `feedable` and `eval_always`."
+            );
+            assert!(
+                modifiers.no_hash.is_none(),
+                "Query {name} cannot be both `feedable` and `no_hash`."
+            );
+            feedable_queries.extend(quote! {
+                #(#doc_comments)*
+                [#attribute_stream] fn #name(#arg) #result,
+            });
+        }
+
         add_query_desc_cached_impl(&query, &mut query_description_stream, &mut query_cached_stream);
     }
 
@@ -363,7 +387,11 @@ pub fn rustc_queries(input: TokenStream) -> TokenStream {
                 }
             }
         }
-
+        macro_rules! rustc_feedable_queries {
+            ( $macro:ident! ) => {
+                $macro!(#feedable_queries);
+            }
+        }
         pub mod descs {
             use super::*;
             #query_description_stream
diff --git a/compiler/rustc_metadata/src/native_libs.rs b/compiler/rustc_metadata/src/native_libs.rs
index 20a2e78299a..1fd35adf1bd 100644
--- a/compiler/rustc_metadata/src/native_libs.rs
+++ b/compiler/rustc_metadata/src/native_libs.rs
@@ -29,11 +29,11 @@ use std::path::PathBuf;
 
 pub fn find_native_static_library(
     name: &str,
-    verbatim: Option<bool>,
+    verbatim: bool,
     search_paths: &[PathBuf],
     sess: &Session,
 ) -> PathBuf {
-    let formats = if verbatim.unwrap_or(false) {
+    let formats = if verbatim {
         vec![("".into(), "".into())]
     } else {
         let os = (sess.target.staticlib_prefix.clone(), sess.target.staticlib_suffix.clone());
@@ -52,7 +52,7 @@ pub fn find_native_static_library(
         }
     }
 
-    sess.emit_fatal(MissingNativeLibrary::new(name, verbatim.unwrap_or(false)));
+    sess.emit_fatal(MissingNativeLibrary::new(name, verbatim));
 }
 
 fn find_bundled_library(
@@ -66,7 +66,7 @@ fn find_bundled_library(
             let NativeLibKind::Static { bundle: Some(true) | None, .. } = kind {
         find_native_static_library(
             name.unwrap().as_str(),
-            verbatim,
+            verbatim.unwrap_or(false),
             &sess.target_filesearch(PathKind::Native).search_path_dirs(),
             sess,
         ).file_name().and_then(|s| s.to_str()).map(Symbol::intern)
@@ -311,10 +311,7 @@ impl<'tcx> Collector<'tcx> {
                             sess.emit_err(BundleNeedsStatic { span });
                         }
 
-                        ("verbatim", _) => {
-                            report_unstable_modifier!(native_link_modifiers_verbatim);
-                            assign_modifier(&mut verbatim)
-                        }
+                        ("verbatim", _) => assign_modifier(&mut verbatim),
 
                         ("whole-archive", Some(NativeLibKind::Static { whole_archive, .. })) => {
                             assign_modifier(whole_archive)
diff --git a/compiler/rustc_metadata/src/rmeta/decoder.rs b/compiler/rustc_metadata/src/rmeta/decoder.rs
index 08d377646d5..af7b0793a95 100644
--- a/compiler/rustc_metadata/src/rmeta/decoder.rs
+++ b/compiler/rustc_metadata/src/rmeta/decoder.rs
@@ -67,10 +67,10 @@ impl std::ops::Deref for MetadataBlob {
     }
 }
 
-// A map from external crate numbers (as decoded from some crate file) to
-// local crate numbers (as generated during this session). Each external
-// crate may refer to types in other external crates, and each has their
-// own crate numbers.
+/// A map from external crate numbers (as decoded from some crate file) to
+/// local crate numbers (as generated during this session). Each external
+/// crate may refer to types in other external crates, and each has their
+/// own crate numbers.
 pub(crate) type CrateNumMap = IndexVec<CrateNum, CrateNum>;
 
 pub(crate) struct CrateMetadata {
diff --git a/compiler/rustc_metadata/src/rmeta/mod.rs b/compiler/rustc_metadata/src/rmeta/mod.rs
index c51b8f96c71..6b60577c902 100644
--- a/compiler/rustc_metadata/src/rmeta/mod.rs
+++ b/compiler/rustc_metadata/src/rmeta/mod.rs
@@ -353,7 +353,7 @@ define_tables! {
     explicit_predicates_of: Table<DefIndex, LazyValue<ty::GenericPredicates<'static>>>,
     generics_of: Table<DefIndex, LazyValue<ty::Generics>>,
     // As an optimization, a missing entry indicates an empty `&[]`.
-    inferred_outlives_of: Table<DefIndex, LazyArray<(ty::Predicate<'static>, Span)>>,
+    inferred_outlives_of: Table<DefIndex, LazyArray<(ty::Clause<'static>, Span)>>,
     super_predicates_of: Table<DefIndex, LazyValue<ty::GenericPredicates<'static>>>,
     type_of: Table<DefIndex, LazyValue<Ty<'static>>>,
     variances_of: Table<DefIndex, LazyArray<ty::Variance>>,
diff --git a/compiler/rustc_middle/Cargo.toml b/compiler/rustc_middle/Cargo.toml
index fc1167c105a..cf1ab47de86 100644
--- a/compiler/rustc_middle/Cargo.toml
+++ b/compiler/rustc_middle/Cargo.toml
@@ -8,6 +8,7 @@ edition = "2021"
 [dependencies]
 bitflags = "1.2.1"
 chalk-ir = "0.87.0"
+derive_more = "0.99.17"
 either = "1.5.0"
 gsgdt = "0.1.2"
 polonius-engine = "0.13.0"
diff --git a/compiler/rustc_middle/src/arena.rs b/compiler/rustc_middle/src/arena.rs
index 7bd4b6c0c27..e83106b1ee5 100644
--- a/compiler/rustc_middle/src/arena.rs
+++ b/compiler/rustc_middle/src/arena.rs
@@ -89,7 +89,7 @@ macro_rules! arena_types {
 
             // Interned types
             [] tys: rustc_data_structures::intern::WithStableHash<rustc_middle::ty::TyS<'tcx>>,
-            [] predicates: rustc_middle::ty::PredicateS<'tcx>,
+            [] predicates: rustc_data_structures::intern::WithStableHash<rustc_middle::ty::PredicateS<'tcx>>,
             [] consts: rustc_middle::ty::ConstS<'tcx>,
 
             // Note that this deliberately duplicates items in the `rustc_hir::arena`,
diff --git a/compiler/rustc_middle/src/hir/mod.rs b/compiler/rustc_middle/src/hir/mod.rs
index 1c6264ad036..02fd03c0283 100644
--- a/compiler/rustc_middle/src/hir/mod.rs
+++ b/compiler/rustc_middle/src/hir/mod.rs
@@ -133,13 +133,8 @@ pub fn provide(providers: &mut Providers) {
         // Accessing the local_parent is ok since its value is hashed as part of `id`'s DefPathHash.
         tcx.opt_local_parent(id.def_id).map_or(CRATE_HIR_ID, |parent| {
             let mut parent_hir_id = tcx.hir().local_def_id_to_hir_id(parent);
-            if let Some(local_id) = tcx.hir_crate(()).owners[parent_hir_id.owner.def_id]
-                .unwrap()
-                .parenting
-                .get(&id.def_id)
-            {
-                parent_hir_id.local_id = *local_id;
-            }
+            parent_hir_id.local_id =
+                tcx.hir_crate(()).owners[parent_hir_id.owner.def_id].unwrap().parenting[&id.def_id];
             parent_hir_id
         })
     };
diff --git a/compiler/rustc_middle/src/lib.rs b/compiler/rustc_middle/src/lib.rs
index 6bdf591fdd7..7e4063c2ffd 100644
--- a/compiler/rustc_middle/src/lib.rs
+++ b/compiler/rustc_middle/src/lib.rs
@@ -30,8 +30,10 @@
 #![feature(core_intrinsics)]
 #![feature(discriminant_kind)]
 #![feature(exhaustive_patterns)]
+#![feature(generators)]
 #![feature(get_mut_unchecked)]
 #![feature(if_let_guard)]
+#![feature(iter_from_generator)]
 #![feature(negative_impls)]
 #![feature(never_type)]
 #![feature(extern_types)]
diff --git a/compiler/rustc_middle/src/middle/lang_items.rs b/compiler/rustc_middle/src/middle/lang_items.rs
index dd4332d0db6..343ea1f00f5 100644
--- a/compiler/rustc_middle/src/middle/lang_items.rs
+++ b/compiler/rustc_middle/src/middle/lang_items.rs
@@ -27,7 +27,10 @@ impl<'tcx> TyCtxt<'tcx> {
         })
     }
 
-    pub fn fn_trait_kind_from_lang_item(self, id: DefId) -> Option<ty::ClosureKind> {
+    /// Given a [`DefId`] of a [`Fn`], [`FnMut`] or [`FnOnce`] traits,
+    /// returns a corresponding [`ty::ClosureKind`].
+    /// For any other [`DefId`] return `None`.
+    pub fn fn_trait_kind_from_def_id(self, id: DefId) -> Option<ty::ClosureKind> {
         let items = self.lang_items();
         match Some(id) {
             x if x == items.fn_trait() => Some(ty::ClosureKind::Fn),
@@ -36,6 +39,11 @@ impl<'tcx> TyCtxt<'tcx> {
             _ => None,
         }
     }
+
+    /// 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()
+    }
 }
 
 /// Returns `true` if the specified `lang_item` must be present for this
diff --git a/compiler/rustc_middle/src/mir/interpret/allocation.rs b/compiler/rustc_middle/src/mir/interpret/allocation.rs
index f8a69f3c7d5..5f911d5884a 100644
--- a/compiler/rustc_middle/src/mir/interpret/allocation.rs
+++ b/compiler/rustc_middle/src/mir/interpret/allocation.rs
@@ -36,7 +36,7 @@ pub use init_mask::{InitChunk, InitChunkIter};
 /// module provides higher-level access.
 // Note: for performance reasons when interning, some of the `Allocation` fields can be partially
 // hashed. (see the `Hash` impl below for more details), so the impl is not derived.
-#[derive(Clone, Eq, PartialEq, PartialOrd, Ord, TyEncodable, TyDecodable)]
+#[derive(Clone, Eq, PartialEq, TyEncodable, TyDecodable)]
 #[derive(HashStable)]
 pub struct Allocation<Prov: Provenance = AllocId, Extra = ()> {
     /// The actual bytes of the allocation.
@@ -108,7 +108,7 @@ impl hash::Hash for Allocation {
 /// Here things are different because only const allocations are interned. This
 /// means that both the inner type (`Allocation`) and the outer type
 /// (`ConstAllocation`) are used quite a bit.
-#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, HashStable)]
+#[derive(Copy, Clone, PartialEq, Eq, Hash, HashStable)]
 #[rustc_pass_by_value]
 pub struct ConstAllocation<'tcx>(pub Interned<'tcx, Allocation>);
 
diff --git a/compiler/rustc_middle/src/mir/interpret/allocation/init_mask.rs b/compiler/rustc_middle/src/mir/interpret/allocation/init_mask.rs
index d88a0c19e59..82e9a961a2b 100644
--- a/compiler/rustc_middle/src/mir/interpret/allocation/init_mask.rs
+++ b/compiler/rustc_middle/src/mir/interpret/allocation/init_mask.rs
@@ -12,7 +12,7 @@ type Block = u64;
 /// is initialized. If it is `false` the byte is uninitialized.
 // Note: for performance reasons when interning, some of the `InitMask` fields can be partially
 // hashed. (see the `Hash` impl below for more details), so the impl is not derived.
-#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord, TyEncodable, TyDecodable)]
+#[derive(Clone, Debug, Eq, PartialEq, TyEncodable, TyDecodable)]
 #[derive(HashStable)]
 pub struct InitMask {
     blocks: Vec<Block>,
diff --git a/compiler/rustc_middle/src/mir/interpret/allocation/provenance_map.rs b/compiler/rustc_middle/src/mir/interpret/allocation/provenance_map.rs
index 19ee209e552..ddd3f394358 100644
--- a/compiler/rustc_middle/src/mir/interpret/allocation/provenance_map.rs
+++ b/compiler/rustc_middle/src/mir/interpret/allocation/provenance_map.rs
@@ -10,7 +10,7 @@ use super::{alloc_range, AllocError, AllocId, AllocRange, AllocResult, Provenanc
 use rustc_serialize::{Decodable, Decoder, Encodable, Encoder};
 
 /// Stores the provenance information of pointers stored in memory.
-#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
+#[derive(Clone, PartialEq, Eq, Hash, Debug)]
 #[derive(HashStable)]
 pub struct ProvenanceMap<Prov = AllocId> {
     /// Provenance in this map applies from the given offset for an entire pointer-size worth of
diff --git a/compiler/rustc_middle/src/mir/interpret/pointer.rs b/compiler/rustc_middle/src/mir/interpret/pointer.rs
index 4e59f1b2482..9c270ba1ec1 100644
--- a/compiler/rustc_middle/src/mir/interpret/pointer.rs
+++ b/compiler/rustc_middle/src/mir/interpret/pointer.rs
@@ -173,7 +173,7 @@ impl Provenance for AllocId {
 /// Represents a pointer in the Miri engine.
 ///
 /// Pointers are "tagged" with provenance information; typically the `AllocId` they belong to.
-#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, TyEncodable, TyDecodable, Hash)]
+#[derive(Copy, Clone, Eq, PartialEq, TyEncodable, TyDecodable, Hash)]
 #[derive(HashStable)]
 pub struct Pointer<Prov = AllocId> {
     pub(super) offset: Size, // kept private to avoid accidental misinterpretation (meaning depends on `Prov` type)
diff --git a/compiler/rustc_middle/src/mir/interpret/value.rs b/compiler/rustc_middle/src/mir/interpret/value.rs
index 770c3ed05e8..e6636e50e6e 100644
--- a/compiler/rustc_middle/src/mir/interpret/value.rs
+++ b/compiler/rustc_middle/src/mir/interpret/value.rs
@@ -20,15 +20,15 @@ use super::{
 /// Represents the result of const evaluation via the `eval_to_allocation` query.
 #[derive(Copy, Clone, HashStable, TyEncodable, TyDecodable, Debug, Hash, Eq, PartialEq)]
 pub struct ConstAlloc<'tcx> {
-    // the value lives here, at offset 0, and that allocation definitely is an `AllocKind::Memory`
-    // (so you can use `AllocMap::unwrap_memory`).
+    /// The value lives here, at offset 0, and that allocation definitely is an `AllocKind::Memory`
+    /// (so you can use `AllocMap::unwrap_memory`).
     pub alloc_id: AllocId,
     pub ty: Ty<'tcx>,
 }
 
 /// Represents a constant value in Rust. `Scalar` and `Slice` are optimizations for
 /// array length computations, enum discriminants and the pattern matching logic.
-#[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord, TyEncodable, TyDecodable, Hash)]
+#[derive(Copy, Clone, Debug, Eq, PartialEq, TyEncodable, TyDecodable, Hash)]
 #[derive(HashStable, Lift)]
 pub enum ConstValue<'tcx> {
     /// Used only for types with `layout::abi::Scalar` ABI.
@@ -110,7 +110,7 @@ impl<'tcx> ConstValue<'tcx> {
 ///
 /// These variants would be private if there was a convenient way to achieve that in Rust.
 /// Do *not* match on a `Scalar`! Use the various `to_*` methods instead.
-#[derive(Clone, Copy, Eq, PartialEq, Ord, PartialOrd, TyEncodable, TyDecodable, Hash)]
+#[derive(Clone, Copy, Eq, PartialEq, TyEncodable, TyDecodable, Hash)]
 #[derive(HashStable)]
 pub enum Scalar<Prov = AllocId> {
     /// The raw bytes of a simple value.
diff --git a/compiler/rustc_middle/src/mir/mod.rs b/compiler/rustc_middle/src/mir/mod.rs
index 364c1b375ae..20dde64e51b 100644
--- a/compiler/rustc_middle/src/mir/mod.rs
+++ b/compiler/rustc_middle/src/mir/mod.rs
@@ -2527,8 +2527,7 @@ impl<'tcx> ConstantKind<'tcx> {
                 let generics = tcx.generics_of(item_def_id);
                 let index = generics.param_def_id_to_index[&def_id];
                 let name = tcx.item_name(def_id);
-                let ty_const =
-                    tcx.mk_const(ty::ConstKind::Param(ty::ParamConst::new(index, name)), ty);
+                let ty_const = tcx.mk_const(ty::ParamConst::new(index, name), ty);
                 debug!(?ty_const);
 
                 return Self::Ty(ty_const);
diff --git a/compiler/rustc_middle/src/query/mod.rs b/compiler/rustc_middle/src/query/mod.rs
index 38b72ec9231..e1220320eea 100644
--- a/compiler/rustc_middle/src/query/mod.rs
+++ b/compiler/rustc_middle/src/query/mod.rs
@@ -562,7 +562,7 @@ rustc_queries! {
 
     /// Returns the inferred outlives predicates (e.g., for `struct
     /// Foo<'a, T> { x: &'a T }`, this would return `T: 'a`).
-    query inferred_outlives_of(key: DefId) -> &'tcx [(ty::Predicate<'tcx>, Span)] {
+    query inferred_outlives_of(key: DefId) -> &'tcx [(ty::Clause<'tcx>, Span)] {
         desc { |tcx| "computing inferred outlives predicates of `{}`", tcx.def_path_str(key) }
         cache_on_disk_if { key.is_local() }
         separate_provide_extern
diff --git a/compiler/rustc_middle/src/traits/mod.rs b/compiler/rustc_middle/src/traits/mod.rs
index 26f3052b642..143435cb2a1 100644
--- a/compiler/rustc_middle/src/traits/mod.rs
+++ b/compiler/rustc_middle/src/traits/mod.rs
@@ -471,7 +471,7 @@ pub struct ImplDerivedObligationCause<'tcx> {
 }
 
 impl<'tcx> ObligationCauseCode<'tcx> {
-    // Return the base obligation, ignoring derived obligations.
+    /// Returns the base obligation, ignoring derived obligations.
     pub fn peel_derives(&self) -> &Self {
         let mut base_cause = self;
         while let Some((parent_code, _)) = base_cause.parent() {
diff --git a/compiler/rustc_middle/src/ty/closure.rs b/compiler/rustc_middle/src/ty/closure.rs
index 273a61c966c..d00553cbad1 100644
--- a/compiler/rustc_middle/src/ty/closure.rs
+++ b/compiler/rustc_middle/src/ty/closure.rs
@@ -15,8 +15,8 @@ use super::{Ty, TyCtxt};
 
 use self::BorrowKind::*;
 
-// Captures are represented using fields inside a structure.
-// This represents accessing self in the closure structure
+/// Captures are represented using fields inside a structure.
+/// This represents accessing self in the closure structure
 pub const CAPTURE_STRUCT_LOCAL: mir::Local = mir::Local::from_u32(1);
 
 #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, TyEncodable, TyDecodable, HashStable)]
@@ -91,45 +91,18 @@ pub enum ClosureKind {
 }
 
 impl<'tcx> ClosureKind {
-    // This is the initial value used when doing upvar inference.
+    /// This is the initial value used when doing upvar inference.
     pub const LATTICE_BOTTOM: ClosureKind = ClosureKind::Fn;
 
     /// Returns `true` if a type that impls this closure kind
     /// must also implement `other`.
     pub fn extends(self, other: ty::ClosureKind) -> bool {
-        matches!(
-            (self, other),
-            (ClosureKind::Fn, ClosureKind::Fn)
-                | (ClosureKind::Fn, ClosureKind::FnMut)
-                | (ClosureKind::Fn, ClosureKind::FnOnce)
-                | (ClosureKind::FnMut, ClosureKind::FnMut)
-                | (ClosureKind::FnMut, ClosureKind::FnOnce)
-                | (ClosureKind::FnOnce, ClosureKind::FnOnce)
-        )
-    }
-
-    /// Returns the representative scalar type for this closure kind.
-    /// See `Ty::to_opt_closure_kind` for more details.
-    pub fn to_ty(self, tcx: TyCtxt<'tcx>) -> Ty<'tcx> {
-        match self {
-            ClosureKind::Fn => tcx.types.i8,
-            ClosureKind::FnMut => tcx.types.i16,
-            ClosureKind::FnOnce => tcx.types.i32,
-        }
-    }
-
-    pub fn from_def_id(tcx: TyCtxt<'_>, def_id: DefId) -> Option<ClosureKind> {
-        if Some(def_id) == tcx.lang_items().fn_once_trait() {
-            Some(ClosureKind::FnOnce)
-        } else if Some(def_id) == tcx.lang_items().fn_mut_trait() {
-            Some(ClosureKind::FnMut)
-        } else if Some(def_id) == tcx.lang_items().fn_trait() {
-            Some(ClosureKind::Fn)
-        } else {
-            None
-        }
+        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 {
@@ -140,6 +113,16 @@ impl<'tcx> ClosureKind {
             None,
         )
     }
+
+    /// Returns the representative scalar type for this closure kind.
+    /// See `Ty::to_opt_closure_kind` for more details.
+    pub fn to_ty(self, tcx: TyCtxt<'tcx>) -> Ty<'tcx> {
+        match self {
+            ClosureKind::Fn => tcx.types.i8,
+            ClosureKind::FnMut => tcx.types.i16,
+            ClosureKind::FnOnce => tcx.types.i32,
+        }
+    }
 }
 
 /// A composite describing a `Place` that is captured by a closure.
diff --git a/compiler/rustc_middle/src/ty/codec.rs b/compiler/rustc_middle/src/ty/codec.rs
index b22b3961f34..75f2d45eadb 100644
--- a/compiler/rustc_middle/src/ty/codec.rs
+++ b/compiler/rustc_middle/src/ty/codec.rs
@@ -345,6 +345,14 @@ impl<'tcx, D: TyDecoder<I = TyCtxt<'tcx>>> RefDecodable<'tcx, D>
     }
 }
 
+impl<'tcx, D: TyDecoder<I = TyCtxt<'tcx>>> RefDecodable<'tcx, D> for [(ty::Clause<'tcx>, Span)] {
+    fn decode(decoder: &mut D) -> &'tcx Self {
+        decoder.interner().arena.alloc_from_iter(
+            (0..decoder.read_usize()).map(|_| Decodable::decode(decoder)).collect::<Vec<_>>(),
+        )
+    }
+}
+
 impl<'tcx, D: TyDecoder<I = TyCtxt<'tcx>>> RefDecodable<'tcx, D>
     for ty::List<ty::BoundVariableKind>
 {
diff --git a/compiler/rustc_middle/src/ty/consts.rs b/compiler/rustc_middle/src/ty/consts.rs
index 9a58a196ed7..c2be08e497e 100644
--- a/compiler/rustc_middle/src/ty/consts.rs
+++ b/compiler/rustc_middle/src/ty/consts.rs
@@ -76,10 +76,10 @@ impl<'tcx> Const<'tcx> {
         match Self::try_eval_lit_or_param(tcx, ty, expr) {
             Some(v) => v,
             None => tcx.mk_const(
-                ty::ConstKind::Unevaluated(ty::UnevaluatedConst {
+                ty::UnevaluatedConst {
                     def: def.to_global(),
                     substs: InternalSubsts::identity_for_item(tcx, def.did.to_def_id()),
-                }),
+                },
                 ty,
             ),
         }
@@ -134,18 +134,12 @@ impl<'tcx> Const<'tcx> {
                 let generics = tcx.generics_of(item_def_id);
                 let index = generics.param_def_id_to_index[&def_id];
                 let name = tcx.item_name(def_id);
-                Some(tcx.mk_const(ty::ConstKind::Param(ty::ParamConst::new(index, name)), ty))
+                Some(tcx.mk_const(ty::ParamConst::new(index, name), ty))
             }
             _ => None,
         }
     }
 
-    /// Interns the given value as a constant.
-    #[inline]
-    pub fn from_value(tcx: TyCtxt<'tcx>, val: ty::ValTree<'tcx>, ty: Ty<'tcx>) -> Self {
-        tcx.mk_const(ConstKind::Value(val), ty)
-    }
-
     /// Panics if self.kind != ty::ConstKind::Value
     pub fn to_valtree(self) -> ty::ValTree<'tcx> {
         match self.kind() {
@@ -154,11 +148,6 @@ impl<'tcx> Const<'tcx> {
         }
     }
 
-    pub fn from_scalar_int(tcx: TyCtxt<'tcx>, i: ScalarInt, ty: Ty<'tcx>) -> Self {
-        let valtree = ty::ValTree::from_scalar_int(i);
-        Self::from_value(tcx, valtree, ty)
-    }
-
     #[inline]
     /// Creates a constant with the given integer value and interns it.
     pub fn from_bits(tcx: TyCtxt<'tcx>, bits: u128, ty: ParamEnvAnd<'tcx, Ty<'tcx>>) -> Self {
@@ -166,14 +155,16 @@ impl<'tcx> Const<'tcx> {
             .layout_of(ty)
             .unwrap_or_else(|e| panic!("could not compute layout for {:?}: {:?}", ty, e))
             .size;
-        Self::from_scalar_int(tcx, ScalarInt::try_from_uint(bits, size).unwrap(), ty.value)
+        tcx.mk_const(
+            ty::ValTree::from_scalar_int(ScalarInt::try_from_uint(bits, size).unwrap()),
+            ty.value,
+        )
     }
 
     #[inline]
     /// Creates an interned zst constant.
     pub fn zero_sized(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> Self {
-        let valtree = ty::ValTree::zst();
-        Self::from_value(tcx, valtree, ty)
+        tcx.mk_const(ty::ValTree::zst(), ty)
     }
 
     #[inline]
@@ -220,7 +211,7 @@ impl<'tcx> Const<'tcx> {
     pub fn eval(self, tcx: TyCtxt<'tcx>, param_env: ParamEnv<'tcx>) -> Const<'tcx> {
         if let Some(val) = self.kind().try_eval_for_typeck(tcx, param_env) {
             match val {
-                Ok(val) => Const::from_value(tcx, val, self.ty()),
+                Ok(val) => tcx.mk_const(val, self.ty()),
                 Err(guar) => tcx.const_error_with_guaranteed(self.ty(), guar),
             }
         } else {
diff --git a/compiler/rustc_middle/src/ty/consts/kind.rs b/compiler/rustc_middle/src/ty/consts/kind.rs
index de63dae8a3d..becc2b805dd 100644
--- a/compiler/rustc_middle/src/ty/consts/kind.rs
+++ b/compiler/rustc_middle/src/ty/consts/kind.rs
@@ -49,6 +49,7 @@ impl<'tcx> UnevaluatedConst<'tcx> {
 /// Represents a constant in Rust.
 #[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord, TyEncodable, TyDecodable)]
 #[derive(Hash, HashStable, TypeFoldable, TypeVisitable)]
+#[derive(derive_more::From)]
 pub enum ConstKind<'tcx> {
     /// A const generic parameter.
     Param(ty::ParamConst),
@@ -71,12 +72,19 @@ pub enum ConstKind<'tcx> {
 
     /// A placeholder for a const which could not be computed; this is
     /// propagated to avoid useless error messages.
+    #[from(ignore)]
     Error(ErrorGuaranteed),
 
     /// Expr which contains an expression which has partially evaluated items.
     Expr(Expr<'tcx>),
 }
 
+impl<'tcx> From<ty::ConstVid<'tcx>> for ConstKind<'tcx> {
+    fn from(const_vid: ty::ConstVid<'tcx>) -> Self {
+        InferConst::Var(const_vid).into()
+    }
+}
+
 #[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord, Hash)]
 #[derive(HashStable, TyEncodable, TyDecodable, TypeVisitable, TypeFoldable)]
 pub enum Expr<'tcx> {
diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs
index fbbf3f312e7..a9a7a2c8b01 100644
--- a/compiler/rustc_middle/src/ty/context.rs
+++ b/compiler/rustc_middle/src/ty/context.rs
@@ -17,8 +17,8 @@ use crate::traits;
 use crate::ty::query::{self, TyCtxtAt};
 use crate::ty::{
     self, AdtDef, AdtDefData, AdtKind, Binder, BindingMode, BoundVar, CanonicalPolyFnSig,
-    ClosureSizeProfileData, Const, ConstS, ConstVid, DefIdTree, FloatTy, FloatVar, FloatVid,
-    GenericParamDefKind, InferConst, InferTy, IntTy, IntVar, IntVid, List, ParamConst, ParamTy,
+    ClosureSizeProfileData, Const, ConstS, DefIdTree, FloatTy, FloatVar, FloatVid,
+    GenericParamDefKind, InferTy, IntTy, IntVar, IntVid, List, ParamConst, ParamTy,
     PolyExistentialPredicate, PolyFnSig, Predicate, PredicateKind, PredicateS, ProjectionTy,
     Region, RegionKind, ReprOptions, TraitObjectVisitor, Ty, TyKind, TyS, TyVar, TyVid, TypeAndMut,
     UintTy, Visibility,
@@ -53,6 +53,7 @@ use rustc_hir::{
 use rustc_index::vec::{Idx, IndexVec};
 use rustc_macros::HashStable;
 use rustc_middle::mir::FakeReadCause;
+use rustc_query_system::dep_graph::DepNodeIndex;
 use rustc_query_system::ich::StableHashingContext;
 use rustc_serialize::opaque::{FileEncodeResult, FileEncoder};
 use rustc_session::config::{CrateType, OutputFilenames};
@@ -142,7 +143,7 @@ pub struct CtxtInterners<'tcx> {
     canonical_var_infos: InternedSet<'tcx, List<CanonicalVarInfo<'tcx>>>,
     region: InternedSet<'tcx, RegionKind<'tcx>>,
     poly_existential_predicates: InternedSet<'tcx, List<PolyExistentialPredicate<'tcx>>>,
-    predicate: InternedSet<'tcx, PredicateS<'tcx>>,
+    predicate: InternedSet<'tcx, WithStableHash<PredicateS<'tcx>>>,
     predicates: InternedSet<'tcx, List<Predicate<'tcx>>>,
     projs: InternedSet<'tcx, List<ProjectionKind>>,
     place_elems: InternedSet<'tcx, List<PlaceElem<'tcx>>>,
@@ -190,20 +191,8 @@ impl<'tcx> CtxtInterners<'tcx> {
             self.type_
                 .intern(kind, |kind| {
                     let flags = super::flags::FlagComputation::for_kind(&kind);
-
-                    // It's impossible to hash inference variables (and will ICE), so we don't need to try to cache them.
-                    // Without incremental, we rarely stable-hash types, so let's not do it proactively.
-                    let stable_hash = if flags.flags.intersects(TypeFlags::NEEDS_INFER)
-                        || sess.opts.incremental.is_none()
-                    {
-                        Fingerprint::ZERO
-                    } else {
-                        let mut hasher = StableHasher::new();
-                        let mut hcx =
-                            StableHashingContext::new(sess, definitions, cstore, source_span);
-                        kind.hash_stable(&mut hcx, &mut hasher);
-                        hasher.finish()
-                    };
+                    let stable_hash =
+                        self.stable_hash(&flags, sess, definitions, cstore, source_span, &kind);
 
                     let ty_struct = TyS {
                         kind,
@@ -219,20 +208,54 @@ impl<'tcx> CtxtInterners<'tcx> {
         ))
     }
 
+    fn stable_hash<'a, T: HashStable<StableHashingContext<'a>>>(
+        &self,
+        flags: &ty::flags::FlagComputation,
+        sess: &'a Session,
+        definitions: &'a rustc_hir::definitions::Definitions,
+        cstore: &'a CrateStoreDyn,
+        source_span: &'a IndexVec<LocalDefId, Span>,
+        val: &T,
+    ) -> Fingerprint {
+        // It's impossible to hash inference variables (and will ICE), so we don't need to try to cache them.
+        // Without incremental, we rarely stable-hash types, so let's not do it proactively.
+        if flags.flags.intersects(TypeFlags::NEEDS_INFER) || sess.opts.incremental.is_none() {
+            Fingerprint::ZERO
+        } else {
+            let mut hasher = StableHasher::new();
+            let mut hcx = StableHashingContext::new(sess, definitions, cstore, source_span);
+            val.hash_stable(&mut hcx, &mut hasher);
+            hasher.finish()
+        }
+    }
+
     #[inline(never)]
-    fn intern_predicate(&self, kind: Binder<'tcx, PredicateKind<'tcx>>) -> Predicate<'tcx> {
+    fn intern_predicate(
+        &self,
+        kind: Binder<'tcx, PredicateKind<'tcx>>,
+        sess: &Session,
+        definitions: &rustc_hir::definitions::Definitions,
+        cstore: &CrateStoreDyn,
+        source_span: &IndexVec<LocalDefId, Span>,
+    ) -> Predicate<'tcx> {
         Predicate(Interned::new_unchecked(
             self.predicate
                 .intern(kind, |kind| {
                     let flags = super::flags::FlagComputation::for_predicate(kind);
 
+                    let stable_hash =
+                        self.stable_hash(&flags, sess, definitions, cstore, source_span, &kind);
+
                     let predicate_struct = PredicateS {
                         kind,
                         flags: flags.flags,
                         outer_exclusive_binder: flags.outer_exclusive_binder,
                     };
 
-                    InternedInSet(self.arena.alloc(predicate_struct))
+                    InternedInSet(
+                        self.arena
+                            .alloc(WithStableHash { internee: predicate_struct, stable_hash }),
+                    )
                 })
                 .0,
         ))
@@ -713,22 +736,24 @@ impl<'tcx> TypeckResults<'tcx> {
         self.node_substs.get(&id.local_id).cloned()
     }
 
-    // Returns the type of a pattern as a monotype. Like @expr_ty, this function
-    // doesn't provide type parameter substitutions.
+    /// Returns the type of a pattern as a monotype. Like [`expr_ty`], this function
+    /// doesn't provide type parameter substitutions.
+    ///
+    /// [`expr_ty`]: TypeckResults::expr_ty
     pub fn pat_ty(&self, pat: &hir::Pat<'_>) -> Ty<'tcx> {
         self.node_type(pat.hir_id)
     }
 
-    // Returns the type of an expression as a monotype.
-    //
-    // NB (1): This is the PRE-ADJUSTMENT TYPE for the expression.  That is, in
-    // some cases, we insert `Adjustment` annotations such as auto-deref or
-    // auto-ref.  The type returned by this function does not consider such
-    // adjustments.  See `expr_ty_adjusted()` instead.
-    //
-    // NB (2): This type doesn't provide type parameter substitutions; e.g., if you
-    // ask for the type of "id" in "id(3)", it will return "fn(&isize) -> isize"
-    // instead of "fn(ty) -> T with T = isize".
+    /// Returns the type of an expression as a monotype.
+    ///
+    /// NB (1): This is the PRE-ADJUSTMENT TYPE for the expression.  That is, in
+    /// some cases, we insert `Adjustment` annotations such as auto-deref or
+    /// auto-ref.  The type returned by this function does not consider such
+    /// adjustments.  See `expr_ty_adjusted()` instead.
+    ///
+    /// NB (2): This type doesn't provide type parameter substitutions; e.g., if you
+    /// ask for the type of `id` in `id(3)`, it will return `fn(&isize) -> isize`
+    /// instead of `fn(ty) -> T with T = isize`.
     pub fn expr_ty(&self, expr: &hir::Expr<'_>) -> Ty<'tcx> {
         self.node_type(expr.hir_id)
     }
@@ -995,18 +1020,33 @@ impl<'tcx> CommonConsts<'tcx> {
     }
 }
 
-// This struct contains information regarding the `ReFree(FreeRegion)` corresponding to a lifetime
-// conflict.
+/// This struct contains information regarding the `ReFree(FreeRegion)` corresponding to a lifetime
+/// conflict.
 #[derive(Debug)]
 pub struct FreeRegionInfo {
-    // `LocalDefId` corresponding to FreeRegion
+    /// `LocalDefId` corresponding to FreeRegion
     pub def_id: LocalDefId,
-    // the bound region corresponding to FreeRegion
+    /// the bound region corresponding to FreeRegion
     pub boundregion: ty::BoundRegionKind,
-    // checks if bound region is in Impl Item
+    /// checks if bound region is in Impl Item
     pub is_impl_item: bool,
 }
 
+/// This struct should only be created by `create_def`.
+#[derive(Copy, Clone)]
+pub struct TyCtxtFeed<'tcx> {
+    pub tcx: TyCtxt<'tcx>,
+    // Do not allow direct access, as downstream code must not mutate this field.
+    def_id: LocalDefId,
+}
+
+impl<'tcx> TyCtxtFeed<'tcx> {
+    #[inline(always)]
+    pub fn def_id(&self) -> LocalDefId {
+        self.def_id
+    }
+}
+
 /// The central data structure of the compiler. It stores references
 /// to the various **arenas** and also houses the results of the
 /// various **compiler queries** that have been performed. See the
@@ -1189,8 +1229,9 @@ impl<'tcx> TyCtxt<'tcx> {
             debug!("layout_scalar_valid_range: attr={:?}", attr);
             if let Some(
                 &[
-                    ast::NestedMetaItem::Literal(ast::Lit {
-                        kind: ast::LitKind::Int(a, _), ..
+                    ast::NestedMetaItem::Lit(ast::MetaItemLit {
+                        kind: ast::LitKind::Int(a, _),
+                        ..
                     }),
                 ],
             ) = attr.meta_item_list().as_deref()
@@ -1358,6 +1399,11 @@ impl<'tcx> TyCtxt<'tcx> {
         self.diagnostic_items(did.krate).name_to_id.get(&name) == Some(&did)
     }
 
+    /// Returns `true` if the node pointed to by `def_id` is a generator for an async construct.
+    pub fn generator_is_async(self, def_id: DefId) -> bool {
+        matches!(self.generator_kind(def_id), Some(hir::GeneratorKind::Async(_)))
+    }
+
     pub fn stability(self) -> &'tcx stability::Index {
         self.stability_index(())
     }
@@ -1463,12 +1509,15 @@ impl<'tcx> TyCtxt<'tcx> {
     }
 
     /// Create a new definition within the incr. comp. engine.
-    pub fn create_def(self, parent: LocalDefId, data: hir::definitions::DefPathData) -> LocalDefId {
+    pub fn create_def(
+        self,
+        parent: LocalDefId,
+        data: hir::definitions::DefPathData,
+    ) -> TyCtxtFeed<'tcx> {
         // This function modifies `self.definitions` using a side-effect.
         // We need to ensure that these side effects are re-run by the incr. comp. engine.
         // Depending on the forever-red node will tell the graph that the calling query
         // needs to be re-evaluated.
-        use rustc_query_system::dep_graph::DepNodeIndex;
         self.dep_graph.read_index(DepNodeIndex::FOREVER_RED_NODE);
 
         // The following call has the side effect of modifying the tables inside `definitions`.
@@ -1485,23 +1534,38 @@ impl<'tcx> TyCtxt<'tcx> {
         // This is fine because:
         // - those queries are `eval_always` so we won't miss their result changing;
         // - this write will have happened before these queries are called.
-        self.definitions.write().create_def(parent, data)
+        let def_id = self.definitions.write().create_def(parent, data);
+
+        TyCtxtFeed { tcx: self, def_id }
     }
 
     pub fn iter_local_def_id(self) -> impl Iterator<Item = LocalDefId> + 'tcx {
-        // Create a dependency to the crate to be sure we re-execute this when the amount of
+        // Create a dependency to the red node to be sure we re-execute this when the amount of
         // definitions change.
-        self.ensure().hir_crate(());
-        // Leak a read lock once we start iterating on definitions, to prevent adding new ones
-        // while iterating.  If some query needs to add definitions, it should be `ensure`d above.
-        let definitions = self.definitions.leak();
-        definitions.iter_local_def_id()
+        self.dep_graph.read_index(DepNodeIndex::FOREVER_RED_NODE);
+
+        let definitions = &self.definitions;
+        std::iter::from_generator(|| {
+            let mut i = 0;
+
+            // Recompute the number of definitions each time, because our caller may be creating
+            // new ones.
+            while i < { definitions.read().num_definitions() } {
+                let local_def_index = rustc_span::def_id::DefIndex::from_usize(i);
+                yield LocalDefId { local_def_index };
+                i += 1;
+            }
+
+            // Leak a read lock once we finish iterating on definitions, to prevent adding new ones.
+            definitions.leak();
+        })
     }
 
     pub fn def_path_table(self) -> &'tcx rustc_hir::definitions::DefPathTable {
         // Create a dependency to the crate to be sure we re-execute this when the amount of
         // definitions change.
-        self.ensure().hir_crate(());
+        self.dep_graph.read_index(DepNodeIndex::FOREVER_RED_NODE);
+
         // Leak a read lock once we start iterating on definitions, to prevent adding new ones
         // while iterating.  If some query needs to add definitions, it should be `ensure`d above.
         let definitions = self.definitions.leak();
@@ -1660,7 +1724,7 @@ impl<'tcx> TyCtxt<'tcx> {
         }
     }
 
-    // Checks if the bound region is in Impl Item.
+    /// Checks if the bound region is in Impl Item.
     pub fn is_bound_region_in_impl_item(self, suitable_region_binding_scope: LocalDefId) -> bool {
         let container_id = self.parent(suitable_region_binding_scope.to_def_id());
         if self.impl_trait_ref(container_id).is_some() {
@@ -2160,23 +2224,25 @@ impl<'tcx> Hash for InternedInSet<'tcx, WithStableHash<TyS<'tcx>>> {
     }
 }
 
-impl<'tcx> Borrow<Binder<'tcx, PredicateKind<'tcx>>> for InternedInSet<'tcx, PredicateS<'tcx>> {
+impl<'tcx> Borrow<Binder<'tcx, PredicateKind<'tcx>>>
+    for InternedInSet<'tcx, WithStableHash<PredicateS<'tcx>>>
+{
     fn borrow<'a>(&'a self) -> &'a Binder<'tcx, PredicateKind<'tcx>> {
         &self.0.kind
     }
 }
 
-impl<'tcx> PartialEq for InternedInSet<'tcx, PredicateS<'tcx>> {
-    fn eq(&self, other: &InternedInSet<'tcx, PredicateS<'tcx>>) -> bool {
+impl<'tcx> PartialEq for InternedInSet<'tcx, WithStableHash<PredicateS<'tcx>>> {
+    fn eq(&self, other: &InternedInSet<'tcx, WithStableHash<PredicateS<'tcx>>>) -> bool {
         // The `Borrow` trait requires that `x.borrow() == y.borrow()` equals
         // `x == y`.
         self.0.kind == other.0.kind
     }
 }
 
-impl<'tcx> Eq for InternedInSet<'tcx, PredicateS<'tcx>> {}
+impl<'tcx> Eq for InternedInSet<'tcx, WithStableHash<PredicateS<'tcx>>> {}
 
-impl<'tcx> Hash for InternedInSet<'tcx, PredicateS<'tcx>> {
+impl<'tcx> Hash for InternedInSet<'tcx, WithStableHash<PredicateS<'tcx>>> {
     fn hash<H: Hasher>(&self, s: &mut H) {
         // The `Borrow` trait requires that `x.borrow().hash(s) == x.hash(s)`.
         self.0.kind.hash(s)
@@ -2378,7 +2444,14 @@ impl<'tcx> TyCtxt<'tcx> {
 
     #[inline]
     pub fn mk_predicate(self, binder: Binder<'tcx, PredicateKind<'tcx>>) -> Predicate<'tcx> {
-        self.interners.intern_predicate(binder)
+        self.interners.intern_predicate(
+            binder,
+            self.sess,
+            &self.definitions.read(),
+            &*self.untracked_resolutions.cstore,
+            // This is only used to create a stable hashing context.
+            &self.untracked_resolutions.source_span,
+        )
     }
 
     #[inline]
@@ -2596,13 +2669,8 @@ impl<'tcx> TyCtxt<'tcx> {
     }
 
     #[inline]
-    pub fn mk_const(self, kind: ty::ConstKind<'tcx>, ty: Ty<'tcx>) -> Const<'tcx> {
-        self.mk_const_internal(ty::ConstS { kind, ty })
-    }
-
-    #[inline]
-    pub fn mk_const_var(self, v: ConstVid<'tcx>, ty: Ty<'tcx>) -> Const<'tcx> {
-        self.mk_const(ty::ConstKind::Infer(InferConst::Var(v)), ty)
+    pub fn mk_const(self, kind: impl Into<ty::ConstKind<'tcx>>, ty: Ty<'tcx>) -> Const<'tcx> {
+        self.mk_const_internal(ty::ConstS { kind: kind.into(), ty })
     }
 
     #[inline]
@@ -2621,29 +2689,22 @@ impl<'tcx> TyCtxt<'tcx> {
     }
 
     #[inline]
-    pub fn mk_const_infer(self, ic: InferConst<'tcx>, ty: Ty<'tcx>) -> ty::Const<'tcx> {
-        self.mk_const(ty::ConstKind::Infer(ic), ty)
-    }
-
-    #[inline]
     pub fn mk_ty_param(self, index: u32, name: Symbol) -> Ty<'tcx> {
         self.mk_ty(Param(ParamTy { index, name }))
     }
 
-    #[inline]
-    pub fn mk_const_param(self, index: u32, name: Symbol, ty: Ty<'tcx>) -> Const<'tcx> {
-        self.mk_const(ty::ConstKind::Param(ParamConst { index, name }), ty)
-    }
-
     pub fn mk_param_from_def(self, param: &ty::GenericParamDef) -> GenericArg<'tcx> {
         match param.kind {
             GenericParamDefKind::Lifetime => {
                 self.mk_region(ty::ReEarlyBound(param.to_early_bound_region_data())).into()
             }
             GenericParamDefKind::Type { .. } => self.mk_ty_param(param.index, param.name).into(),
-            GenericParamDefKind::Const { .. } => {
-                self.mk_const_param(param.index, param.name, self.type_of(param.def_id)).into()
-            }
+            GenericParamDefKind::Const { .. } => self
+                .mk_const(
+                    ParamConst { index: param.index, name: param.name },
+                    self.type_of(param.def_id),
+                )
+                .into(),
         }
     }
 
diff --git a/compiler/rustc_middle/src/ty/diagnostics.rs b/compiler/rustc_middle/src/ty/diagnostics.rs
index b087ff4bf53..511d51cd670 100644
--- a/compiler/rustc_middle/src/ty/diagnostics.rs
+++ b/compiler/rustc_middle/src/ty/diagnostics.rs
@@ -355,6 +355,12 @@ pub fn suggest_constraining_type_params<'a>(
         ));
     }
 
+    // FIXME: remove the suggestions that are from derive, as the span is not correct
+    suggestions = suggestions
+        .into_iter()
+        .filter(|(span, _, _)| !span.in_derive_expansion())
+        .collect::<Vec<_>>();
+
     if suggestions.len() == 1 {
         let (span, suggestion, msg) = suggestions.pop().unwrap();
 
diff --git a/compiler/rustc_middle/src/ty/error.rs b/compiler/rustc_middle/src/ty/error.rs
index a61f41b9c58..d83e17574a0 100644
--- a/compiler/rustc_middle/src/ty/error.rs
+++ b/compiler/rustc_middle/src/ty/error.rs
@@ -70,7 +70,6 @@ pub enum TypeError<'tcx> {
     CyclicConst(ty::Const<'tcx>),
     ProjectionMismatched(ExpectedFound<DefId>),
     ExistentialMismatch(ExpectedFound<&'tcx ty::List<ty::PolyExistentialPredicate<'tcx>>>),
-    ObjectUnsafeCoercion(DefId),
     ConstMismatch(ExpectedFound<ty::Const<'tcx>>),
 
     IntrinsicCast,
@@ -222,7 +221,6 @@ impl<'tcx> fmt::Display for TypeError<'tcx> {
                 f,
                 "cannot coerce functions with `#[target_feature]` to safe function pointers"
             ),
-            ObjectUnsafeCoercion(_) => write!(f, "coercion to object-unsafe trait object"),
         }
     }
 }
@@ -249,8 +247,7 @@ impl<'tcx> TypeError<'tcx> {
             | ProjectionMismatched(_)
             | ExistentialMismatch(_)
             | ConstMismatch(_)
-            | IntrinsicCast
-            | ObjectUnsafeCoercion(_) => true,
+            | IntrinsicCast => true,
         }
     }
 }
diff --git a/compiler/rustc_middle/src/ty/flags.rs b/compiler/rustc_middle/src/ty/flags.rs
index 1e9fe779b76..046a2660a1f 100644
--- a/compiler/rustc_middle/src/ty/flags.rs
+++ b/compiler/rustc_middle/src/ty/flags.rs
@@ -6,7 +6,7 @@ use std::slice;
 pub struct FlagComputation {
     pub flags: TypeFlags,
 
-    // see `Ty::outer_exclusive_binder` for details
+    /// see `Ty::outer_exclusive_binder` for details
     pub outer_exclusive_binder: ty::DebruijnIndex,
 }
 
diff --git a/compiler/rustc_middle/src/ty/fold.rs b/compiler/rustc_middle/src/ty/fold.rs
index 2842b3c3102..d431d008ddf 100644
--- a/compiler/rustc_middle/src/ty/fold.rs
+++ b/compiler/rustc_middle/src/ty/fold.rs
@@ -407,6 +407,7 @@ where
         match *t.kind() {
             ty::Bound(debruijn, bound_ty) if debruijn == self.current_index => {
                 let ty = self.delegate.replace_ty(bound_ty);
+                debug_assert!(!ty.has_vars_bound_above(ty::INNERMOST));
                 ty::fold::shift_vars(self.tcx, ty, self.current_index.as_u32())
             }
             _ if t.has_vars_bound_at_or_above(self.current_index) => t.super_fold_with(self),
@@ -437,6 +438,7 @@ where
         match ct.kind() {
             ty::ConstKind::Bound(debruijn, bound_const) if debruijn == self.current_index => {
                 let ct = self.delegate.replace_const(bound_const, ct.ty());
+                debug_assert!(!ct.has_vars_bound_above(ty::INNERMOST));
                 ty::fold::shift_vars(self.tcx, ct, self.current_index.as_u32())
             }
             _ => ct.super_fold_with(self),
@@ -697,14 +699,10 @@ impl<'tcx> TypeFolder<'tcx> for Shifter<'tcx> {
 
     fn fold_region(&mut self, r: ty::Region<'tcx>) -> ty::Region<'tcx> {
         match *r {
-            ty::ReLateBound(debruijn, br) => {
-                if self.amount == 0 || debruijn < self.current_index {
-                    r
-                } else {
-                    let debruijn = debruijn.shifted_in(self.amount);
-                    let shifted = ty::ReLateBound(debruijn, br);
-                    self.tcx.mk_region(shifted)
-                }
+            ty::ReLateBound(debruijn, br) if debruijn >= self.current_index => {
+                let debruijn = debruijn.shifted_in(self.amount);
+                let shifted = ty::ReLateBound(debruijn, br);
+                self.tcx.mk_region(shifted)
             }
             _ => r,
         }
@@ -712,31 +710,30 @@ impl<'tcx> TypeFolder<'tcx> for Shifter<'tcx> {
 
     fn fold_ty(&mut self, ty: Ty<'tcx>) -> Ty<'tcx> {
         match *ty.kind() {
-            ty::Bound(debruijn, bound_ty) => {
-                if self.amount == 0 || debruijn < self.current_index {
-                    ty
-                } else {
-                    let debruijn = debruijn.shifted_in(self.amount);
-                    self.tcx.mk_ty(ty::Bound(debruijn, bound_ty))
-                }
+            ty::Bound(debruijn, bound_ty) if debruijn >= self.current_index => {
+                let debruijn = debruijn.shifted_in(self.amount);
+                self.tcx.mk_ty(ty::Bound(debruijn, bound_ty))
             }
 
-            _ => ty.super_fold_with(self),
+            _ if ty.has_vars_bound_at_or_above(self.current_index) => ty.super_fold_with(self),
+            _ => ty,
         }
     }
 
     fn fold_const(&mut self, ct: ty::Const<'tcx>) -> ty::Const<'tcx> {
-        if let ty::ConstKind::Bound(debruijn, bound_ct) = ct.kind() {
-            if self.amount == 0 || debruijn < self.current_index {
-                ct
-            } else {
-                let debruijn = debruijn.shifted_in(self.amount);
-                self.tcx.mk_const(ty::ConstKind::Bound(debruijn, bound_ct), ct.ty())
-            }
+        if let ty::ConstKind::Bound(debruijn, bound_ct) = ct.kind()
+            && debruijn >= self.current_index
+        {
+            let debruijn = debruijn.shifted_in(self.amount);
+            self.tcx.mk_const(ty::ConstKind::Bound(debruijn, bound_ct), ct.ty())
         } else {
             ct.super_fold_with(self)
         }
     }
+
+    fn fold_predicate(&mut self, p: ty::Predicate<'tcx>) -> ty::Predicate<'tcx> {
+        if p.has_vars_bound_at_or_above(self.current_index) { p.super_fold_with(self) } else { p }
+    }
 }
 
 pub fn shift_region<'tcx>(
@@ -758,5 +755,9 @@ where
 {
     debug!("shift_vars(value={:?}, amount={})", value, amount);
 
+    if amount == 0 || !value.has_escaping_bound_vars() {
+        return value;
+    }
+
     value.fold_with(&mut Shifter::new(tcx, amount))
 }
diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs
index d49f45744ae..dd4ab3e8d30 100644
--- a/compiler/rustc_middle/src/ty/mod.rs
+++ b/compiler/rustc_middle/src/ty/mod.rs
@@ -414,7 +414,7 @@ impl Visibility<DefId> {
         self.map_id(|id| id.expect_local())
     }
 
-    // Returns `true` if this item is visible anywhere in the local crate.
+    /// Returns `true` if this item is visible anywhere in the local crate.
     pub fn is_visible_locally(self) -> bool {
         match self {
             Visibility::Public => true,
@@ -548,9 +548,9 @@ pub(crate) struct PredicateS<'tcx> {
 }
 
 /// Use this rather than `PredicateS`, whenever possible.
-#[derive(Clone, Copy, PartialEq, Eq, Hash)]
+#[derive(Clone, Copy, PartialEq, Eq, Hash, HashStable)]
 #[rustc_pass_by_value]
-pub struct Predicate<'tcx>(Interned<'tcx, PredicateS<'tcx>>);
+pub struct Predicate<'tcx>(Interned<'tcx, WithStableHash<PredicateS<'tcx>>>);
 
 impl<'tcx> Predicate<'tcx> {
     /// Gets the inner `Binder<'tcx, PredicateKind<'tcx>>`.
@@ -631,7 +631,7 @@ impl<'tcx> Predicate<'tcx> {
     }
 }
 
-impl<'a, 'tcx> HashStable<StableHashingContext<'a>> for Predicate<'tcx> {
+impl<'a, 'tcx> HashStable<StableHashingContext<'a>> for PredicateS<'tcx> {
     fn hash_stable(&self, hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) {
         let PredicateS {
             ref kind,
@@ -640,7 +640,7 @@ impl<'a, 'tcx> HashStable<StableHashingContext<'a>> for Predicate<'tcx> {
             // also contained in `kind`, so no need to hash them.
             flags: _,
             outer_exclusive_binder: _,
-        } = self.0.0;
+        } = self;
 
         kind.hash_stable(hcx, hasher);
     }
@@ -734,7 +734,7 @@ pub struct CratePredicatesMap<'tcx> {
     /// For each struct with outlive bounds, maps to a vector of the
     /// predicate of its outlive bounds. If an item has no outlives
     /// bounds, it will have no entry.
-    pub predicates: FxHashMap<DefId, &'tcx [(Predicate<'tcx>, Span)]>,
+    pub predicates: FxHashMap<DefId, &'tcx [(Clause<'tcx>, Span)]>,
 }
 
 impl<'tcx> Predicate<'tcx> {
@@ -926,9 +926,10 @@ impl<'tcx> PolyTraitPredicate<'tcx> {
     }
 }
 
+/// `A: B`
 #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, TyEncodable, TyDecodable)]
 #[derive(HashStable, TypeFoldable, TypeVisitable, Lift)]
-pub struct OutlivesPredicate<A, B>(pub A, pub B); // `A: B`
+pub struct OutlivesPredicate<A, B>(pub A, pub B);
 pub type RegionOutlivesPredicate<'tcx> = OutlivesPredicate<ty::Region<'tcx>, ty::Region<'tcx>>;
 pub type TypeOutlivesPredicate<'tcx> = OutlivesPredicate<Ty<'tcx>, ty::Region<'tcx>>;
 pub type PolyRegionOutlivesPredicate<'tcx> = ty::Binder<'tcx, RegionOutlivesPredicate<'tcx>>;
@@ -1166,6 +1167,13 @@ impl<'tcx> ToPredicate<'tcx, Predicate<'tcx>> for Binder<'tcx, PredicateKind<'tc
     }
 }
 
+impl<'tcx> ToPredicate<'tcx, Predicate<'tcx>> for Clause<'tcx> {
+    #[inline(always)]
+    fn to_predicate(self, tcx: TyCtxt<'tcx>) -> Predicate<'tcx> {
+        tcx.mk_predicate(ty::Binder::dummy(ty::PredicateKind::Clause(self)))
+    }
+}
+
 impl<'tcx> ToPredicate<'tcx, Predicate<'tcx>> for Binder<'tcx, TraitRef<'tcx>> {
     #[inline(always)]
     fn to_predicate(self, tcx: TyCtxt<'tcx>) -> Predicate<'tcx> {
diff --git a/compiler/rustc_middle/src/ty/parameterized.rs b/compiler/rustc_middle/src/ty/parameterized.rs
index b2bcf0e29cd..c7d6c6abd1c 100644
--- a/compiler/rustc_middle/src/ty/parameterized.rs
+++ b/compiler/rustc_middle/src/ty/parameterized.rs
@@ -5,7 +5,7 @@ use rustc_index::vec::{Idx, IndexVec};
 use crate::middle::exported_symbols::ExportedSymbol;
 use crate::mir::Body;
 use crate::ty::{
-    self, Const, FnSig, GeneratorDiagnosticData, GenericPredicates, Predicate, TraitRef, Ty,
+    self, Clause, Const, FnSig, GeneratorDiagnosticData, GenericPredicates, Predicate, TraitRef, Ty,
 };
 
 pub trait ParameterizedOverTcx: 'static {
@@ -121,6 +121,7 @@ parameterized_over_tcx! {
     TraitRef,
     Const,
     Predicate,
+    Clause,
     GeneratorDiagnosticData,
     Body,
     ExportedSymbol,
diff --git a/compiler/rustc_middle/src/ty/print/pretty.rs b/compiler/rustc_middle/src/ty/print/pretty.rs
index f93dc15068e..5303341ba44 100644
--- a/compiler/rustc_middle/src/ty/print/pretty.rs
+++ b/compiler/rustc_middle/src/ty/print/pretty.rs
@@ -681,25 +681,20 @@ pub trait PrettyPrinter<'tcx>:
             }
             ty::Str => p!("str"),
             ty::Generator(did, substs, movability) => {
-                // FIXME(swatinem): async constructs used to be pretty printed
-                // as `impl Future` previously due to the `from_generator` wrapping.
-                // lets special case this here for now to avoid churn in diagnostics.
-                let generator_kind = self.tcx().generator_kind(did);
-                if matches!(generator_kind, Some(hir::GeneratorKind::Async(..))) {
-                    let return_ty = substs.as_generator().return_ty();
-                    p!(write("impl Future<Output = {}>", return_ty));
-
-                    return Ok(self);
-                }
-
                 p!(write("["));
-                match movability {
-                    hir::Movability::Movable => {}
-                    hir::Movability::Static => p!("static "),
+                let generator_kind = self.tcx().generator_kind(did).unwrap();
+                let should_print_movability =
+                    self.should_print_verbose() || generator_kind == hir::GeneratorKind::Gen;
+
+                if should_print_movability {
+                    match movability {
+                        hir::Movability::Movable => {}
+                        hir::Movability::Static => p!("static "),
+                    }
                 }
 
                 if !self.should_print_verbose() {
-                    p!("generator");
+                    p!(write("{}", generator_kind));
                     // FIXME(eddyb) should use `def_span`.
                     if let Some(did) = did.as_local() {
                         let span = self.tcx().def_span(did);
@@ -1085,7 +1080,7 @@ pub trait PrettyPrinter<'tcx>:
                 let mut resugared = false;
 
                 // Special-case `Fn(...) -> ...` and re-sugar it.
-                let fn_trait_kind = cx.tcx().fn_trait_kind_from_lang_item(principal.def_id);
+                let fn_trait_kind = cx.tcx().fn_trait_kind_from_def_id(principal.def_id);
                 if !cx.should_print_verbose() && fn_trait_kind.is_some() {
                     if let ty::Tuple(tys) = principal.substs.type_at(0).kind() {
                         let mut projections = predicates.projection_bounds();
@@ -1473,8 +1468,7 @@ pub trait PrettyPrinter<'tcx>:
             }
             // Aggregates, printed as array/tuple/struct/variant construction syntax.
             (ty::ValTree::Branch(_), ty::Array(..) | ty::Tuple(..) | ty::Adt(..)) => {
-                let contents =
-                    self.tcx().destructure_const(ty::Const::from_value(self.tcx(), valtree, ty));
+                let contents = self.tcx().destructure_const(self.tcx().mk_const(valtree, ty));
                 let fields = contents.fields.iter().copied();
                 match *ty.kind() {
                     ty::Array(..) => {
diff --git a/compiler/rustc_middle/src/ty/query.rs b/compiler/rustc_middle/src/ty/query.rs
index afbc9eb0512..47c1379b308 100644
--- a/compiler/rustc_middle/src/ty/query.rs
+++ b/compiler/rustc_middle/src/ty/query.rs
@@ -28,6 +28,7 @@ use crate::traits::query::{
 };
 use crate::traits::specialization_graph;
 use crate::traits::{self, ImplSource};
+use crate::ty::context::TyCtxtFeed;
 use crate::ty::fast_reject::SimplifiedType;
 use crate::ty::layout::TyAndLayout;
 use crate::ty::subst::{GenericArg, SubstsRef};
@@ -327,6 +328,46 @@ macro_rules! define_callbacks {
     };
 }
 
+macro_rules! define_feedable {
+    ($($(#[$attr:meta])* [$($modifiers:tt)*] fn $name:ident($($K:tt)*) -> $V:ty,)*) => {
+        impl<'tcx> TyCtxtFeed<'tcx> {
+            $($(#[$attr])*
+            #[inline(always)]
+            pub fn $name(self, value: $V) -> query_stored::$name<'tcx> {
+                let key = self.def_id().into_query_param();
+                opt_remap_env_constness!([$($modifiers)*][key]);
+
+                let tcx = self.tcx;
+                let cache = &tcx.query_caches.$name;
+
+                let cached = try_get_cached(tcx, cache, &key, copy);
+
+                match cached {
+                    Ok(old) => {
+                        assert_eq!(
+                            value, old,
+                            "Trying to feed an already recorded value for query {} key={key:?}",
+                            stringify!($name),
+                        );
+                        return old;
+                    }
+                    Err(()) => (),
+                }
+
+                let dep_node = dep_graph::DepNode::construct(tcx, dep_graph::DepKind::$name, &key);
+                let dep_node_index = tcx.dep_graph.with_feed_task(
+                    dep_node,
+                    tcx,
+                    key,
+                    &value,
+                    dep_graph::hash_result,
+                );
+                cache.complete(key, value, dep_node_index)
+            })*
+        }
+    }
+}
+
 // Each of these queries corresponds to a function pointer field in the
 // `Providers` struct for requesting a value of that type, and a method
 // on `tcx: TyCtxt` (and `tcx.at(span)`) for doing that request in a way
@@ -340,6 +381,7 @@ macro_rules! define_callbacks {
 // as they will raise an fatal error on query cycles instead.
 
 rustc_query_append! { define_callbacks! }
+rustc_feedable_queries! { define_feedable! }
 
 mod sealed {
     use super::{DefId, LocalDefId, OwnerId};
diff --git a/compiler/rustc_middle/src/ty/relate.rs b/compiler/rustc_middle/src/ty/relate.rs
index e6340040e9c..c759fb6d5e4 100644
--- a/compiler/rustc_middle/src/ty/relate.rs
+++ b/compiler/rustc_middle/src/ty/relate.rs
@@ -663,10 +663,7 @@ pub fn super_relate_consts<'tcx, R: TypeRelation<'tcx>>(
                 au.substs,
                 bu.substs,
             )?;
-            return Ok(tcx.mk_const(
-                ty::ConstKind::Unevaluated(ty::UnevaluatedConst { def: au.def, substs }),
-                a.ty(),
-            ));
+            return Ok(tcx.mk_const(ty::UnevaluatedConst { def: au.def, substs }, a.ty()));
         }
         // Before calling relate on exprs, it is necessary to ensure that the nested consts
         // have identical types.
diff --git a/compiler/rustc_middle/src/ty/sty.rs b/compiler/rustc_middle/src/ty/sty.rs
index ac648152a42..5984686044b 100644
--- a/compiler/rustc_middle/src/ty/sty.rs
+++ b/compiler/rustc_middle/src/ty/sty.rs
@@ -2116,8 +2116,7 @@ impl<'tcx> Ty<'tcx> {
     /// parameter. This is kind of a phantom type, except that the
     /// most convenient thing for us to are the integral types. This
     /// function converts such a special type into the closure
-    /// kind. To go the other way, use
-    /// `tcx.closure_kind_ty(closure_kind)`.
+    /// kind. To go the other way, use `closure_kind.to_ty(tcx)`.
     ///
     /// Note that during type checking, we use an inference variable
     /// to represent the closure kind, because it has not yet been
@@ -2243,7 +2242,7 @@ impl<'tcx> Ty<'tcx> {
         }
     }
 
-    // If `self` is a primitive, return its [`Symbol`].
+    /// If `self` is a primitive, return its [`Symbol`].
     pub fn primitive_symbol(self) -> Option<Symbol> {
         match self.kind() {
             ty::Bool => Some(sym::bool),
diff --git a/compiler/rustc_middle/src/ty/trait_def.rs b/compiler/rustc_middle/src/ty/trait_def.rs
index ac79949fca5..b38a5fbf20f 100644
--- a/compiler/rustc_middle/src/ty/trait_def.rs
+++ b/compiler/rustc_middle/src/ty/trait_def.rs
@@ -211,7 +211,7 @@ impl<'tcx> TyCtxt<'tcx> {
     }
 }
 
-// Query provider for `trait_impls_of`.
+/// Query provider for `trait_impls_of`.
 pub(super) fn trait_impls_of_provider(tcx: TyCtxt<'_>, trait_id: DefId) -> TraitImpls {
     let mut impls = TraitImpls::default();
 
@@ -255,7 +255,7 @@ pub(super) fn trait_impls_of_provider(tcx: TyCtxt<'_>, trait_id: DefId) -> Trait
     impls
 }
 
-// Query provider for `incoherent_impls`.
+/// Query provider for `incoherent_impls`.
 pub(super) fn incoherent_impls_provider(tcx: TyCtxt<'_>, simp: SimplifiedType) -> &[DefId] {
     let mut impls = Vec::new();
 
diff --git a/compiler/rustc_middle/src/ty/util.rs b/compiler/rustc_middle/src/ty/util.rs
index 717e5136e96..47c1ce80756 100644
--- a/compiler/rustc_middle/src/ty/util.rs
+++ b/compiler/rustc_middle/src/ty/util.rs
@@ -1208,11 +1208,11 @@ pub fn is_trivially_const_drop<'tcx>(ty: Ty<'tcx>) -> bool {
     }
 }
 
-// Does the equivalent of
-// ```
-// let v = self.iter().map(|p| p.fold_with(folder)).collect::<SmallVec<[_; 8]>>();
-// folder.tcx().intern_*(&v)
-// ```
+/// Does the equivalent of
+/// ```ignore (ilustrative)
+/// let v = self.iter().map(|p| p.fold_with(folder)).collect::<SmallVec<[_; 8]>>();
+/// folder.tcx().intern_*(&v)
+/// ```
 pub fn fold_list<'tcx, F, T>(
     list: &'tcx ty::List<T>,
     folder: &mut F,
diff --git a/compiler/rustc_middle/src/ty/visit.rs b/compiler/rustc_middle/src/ty/visit.rs
index b04afe549ac..4cdfd9e5940 100644
--- a/compiler/rustc_middle/src/ty/visit.rs
+++ b/compiler/rustc_middle/src/ty/visit.rs
@@ -72,12 +72,18 @@ pub trait TypeVisitable<'tcx>: fmt::Debug + Clone {
         self.visit_with(&mut HasEscapingVarsVisitor { outer_index: binder }).is_break()
     }
 
-    /// Returns `true` if this `self` has any regions that escape `binder` (and
+    /// Returns `true` if this type has any regions that escape `binder` (and
     /// hence are not bound by it).
     fn has_vars_bound_above(&self, binder: ty::DebruijnIndex) -> bool {
         self.has_vars_bound_at_or_above(binder.shifted_in(1))
     }
 
+    /// Return `true` if this type has regions that are not a part of the type.
+    /// For example, `for<'a> fn(&'a i32)` return `false`, while `fn(&'a i32)`
+    /// would return `true`. The latter can occur when traversing through the
+    /// former.
+    ///
+    /// See [`HasEscapingVarsVisitor`] for more information.
     fn has_escaping_bound_vars(&self) -> bool {
         self.has_vars_bound_at_or_above(ty::INNERMOST)
     }
diff --git a/compiler/rustc_mir_build/src/build/custom/mod.rs b/compiler/rustc_mir_build/src/build/custom/mod.rs
index 68d8766c907..2412824efeb 100644
--- a/compiler/rustc_mir_build/src/build/custom/mod.rs
+++ b/compiler/rustc_mir_build/src/build/custom/mod.rs
@@ -74,7 +74,7 @@ pub(super) fn build_custom_mir<'tcx>(
     let mut pctxt = ParseCtxt {
         tcx,
         thir,
-        source_info: SourceInfo { span, scope: OUTERMOST_SOURCE_SCOPE },
+        source_scope: OUTERMOST_SOURCE_SCOPE,
         body: &mut body,
         local_map: FxHashMap::default(),
         block_map: FxHashMap::default(),
@@ -128,7 +128,7 @@ fn parse_attribute(attr: &Attribute) -> MirPhase {
 struct ParseCtxt<'tcx, 'body> {
     tcx: TyCtxt<'tcx>,
     thir: &'body Thir<'tcx>,
-    source_info: SourceInfo,
+    source_scope: SourceScope,
 
     body: &'body mut Body<'tcx>,
     local_map: FxHashMap<LocalVarId, Local>,
diff --git a/compiler/rustc_mir_build/src/build/custom/parse.rs b/compiler/rustc_mir_build/src/build/custom/parse.rs
index 52cb0a4826d..d72770e70c7 100644
--- a/compiler/rustc_mir_build/src/build/custom/parse.rs
+++ b/compiler/rustc_mir_build/src/build/custom/parse.rs
@@ -23,6 +23,7 @@ macro_rules! parse_by_kind {
     (
         $self:ident,
         $expr_id:expr,
+        $expr_name:pat,
         $expected:literal,
         $(
             @call($name:literal, $args:ident) => $call_expr:expr,
@@ -33,6 +34,8 @@ macro_rules! parse_by_kind {
     ) => {{
         let expr_id = $self.preparse($expr_id);
         let expr = &$self.thir[expr_id];
+        debug!("Trying to parse {:?} as {}", expr.kind, $expected);
+        let $expr_name = expr;
         match &expr.kind {
             $(
                 ExprKind::Call { ty, fun: _, args: $args, .. } if {
@@ -137,10 +140,10 @@ impl<'tcx, 'body> ParseCtxt<'tcx, 'body> {
     /// This allows us to easily parse the basic blocks declarations, local declarations, and
     /// basic block definitions in order.
     pub fn parse_body(&mut self, expr_id: ExprId) -> PResult<()> {
-        let body = parse_by_kind!(self, expr_id, "whole body",
+        let body = parse_by_kind!(self, expr_id, _, "whole body",
             ExprKind::Block { block } => self.thir[*block].expr.unwrap(),
         );
-        let (block_decls, rest) = parse_by_kind!(self, body, "body with block decls",
+        let (block_decls, rest) = parse_by_kind!(self, body, _, "body with block decls",
             ExprKind::Block { block } => {
                 let block = &self.thir[*block];
                 (&block.stmts, block.expr.unwrap())
@@ -148,7 +151,7 @@ impl<'tcx, 'body> ParseCtxt<'tcx, 'body> {
         );
         self.parse_block_decls(block_decls.iter().copied())?;
 
-        let (local_decls, rest) = parse_by_kind!(self, rest, "body with local decls",
+        let (local_decls, rest) = parse_by_kind!(self, rest, _, "body with local decls",
             ExprKind::Block { block } => {
                 let block = &self.thir[*block];
                 (&block.stmts, block.expr.unwrap())
@@ -156,7 +159,7 @@ impl<'tcx, 'body> ParseCtxt<'tcx, 'body> {
         );
         self.parse_local_decls(local_decls.iter().copied())?;
 
-        let block_defs = parse_by_kind!(self, rest, "body with block defs",
+        let block_defs = parse_by_kind!(self, rest, _, "body with block defs",
             ExprKind::Block { block } => &self.thir[*block].stmts,
         );
         for (i, block_def) in block_defs.iter().enumerate() {
@@ -223,22 +226,30 @@ impl<'tcx, 'body> ParseCtxt<'tcx, 'body> {
     }
 
     fn parse_block_def(&self, expr_id: ExprId) -> PResult<BasicBlockData<'tcx>> {
-        let block = parse_by_kind!(self, expr_id, "basic block",
+        let block = parse_by_kind!(self, expr_id, _, "basic block",
             ExprKind::Block { block } => &self.thir[*block],
         );
 
         let mut data = BasicBlockData::new(None);
         for stmt_id in &*block.stmts {
             let stmt = self.statement_as_expr(*stmt_id)?;
+            let span = self.thir[stmt].span;
             let statement = self.parse_statement(stmt)?;
-            data.statements.push(Statement { source_info: self.source_info, kind: statement });
+            data.statements.push(Statement {
+                source_info: SourceInfo { span, scope: self.source_scope },
+                kind: statement,
+            });
         }
 
         let Some(trailing) = block.expr else {
             return Err(self.expr_error(expr_id, "terminator"))
         };
+        let span = self.thir[trailing].span;
         let terminator = self.parse_terminator(trailing)?;
-        data.terminator = Some(Terminator { source_info: self.source_info, kind: terminator });
+        data.terminator = Some(Terminator {
+            source_info: SourceInfo { span, scope: self.source_scope },
+            kind: terminator,
+        });
 
         Ok(data)
     }
diff --git a/compiler/rustc_mir_build/src/build/custom/parse/instruction.rs b/compiler/rustc_mir_build/src/build/custom/parse/instruction.rs
index 6d6176584f5..03206af33bf 100644
--- a/compiler/rustc_mir_build/src/build/custom/parse/instruction.rs
+++ b/compiler/rustc_mir_build/src/build/custom/parse/instruction.rs
@@ -1,10 +1,11 @@
+use rustc_middle::mir::interpret::{ConstValue, Scalar};
 use rustc_middle::{mir::*, thir::*, ty};
 
 use super::{parse_by_kind, PResult, ParseCtxt};
 
 impl<'tcx, 'body> ParseCtxt<'tcx, 'body> {
     pub fn parse_statement(&self, expr_id: ExprId) -> PResult<StatementKind<'tcx>> {
-        parse_by_kind!(self, expr_id, "statement",
+        parse_by_kind!(self, expr_id, _, "statement",
             @call("mir_retag", args) => {
                 Ok(StatementKind::Retag(RetagKind::Default, Box::new(self.parse_place(args[0])?)))
             },
@@ -20,7 +21,7 @@ impl<'tcx, 'body> ParseCtxt<'tcx, 'body> {
     }
 
     pub fn parse_terminator(&self, expr_id: ExprId) -> PResult<TerminatorKind<'tcx>> {
-        parse_by_kind!(self, expr_id, "terminator",
+        parse_by_kind!(self, expr_id, _, "terminator",
             @call("mir_return", _args) => {
                 Ok(TerminatorKind::Return)
             },
@@ -31,7 +32,7 @@ impl<'tcx, 'body> ParseCtxt<'tcx, 'body> {
     }
 
     fn parse_rvalue(&self, expr_id: ExprId) -> PResult<Rvalue<'tcx>> {
-        parse_by_kind!(self, expr_id, "rvalue",
+        parse_by_kind!(self, expr_id, _, "rvalue",
             ExprKind::Borrow { borrow_kind, arg } => Ok(
                 Rvalue::Ref(self.tcx.lifetimes.re_erased, *borrow_kind, self.parse_place(*arg)?)
             ),
@@ -43,14 +44,26 @@ impl<'tcx, 'body> ParseCtxt<'tcx, 'body> {
     }
 
     fn parse_operand(&self, expr_id: ExprId) -> PResult<Operand<'tcx>> {
-        parse_by_kind!(self, expr_id, "operand",
+        parse_by_kind!(self, expr_id, expr, "operand",
             @call("mir_move", args) => self.parse_place(args[0]).map(Operand::Move),
+            @call("mir_static", args) => self.parse_static(args[0]),
+            @call("mir_static_mut", args) => self.parse_static(args[0]),
+            ExprKind::Literal { .. }
+            | ExprKind::NamedConst { .. }
+            | ExprKind::NonHirLiteral { .. }
+            | ExprKind::ZstLiteral { .. }
+            | ExprKind::ConstParam { .. }
+            | ExprKind::ConstBlock { .. } => {
+                Ok(Operand::Constant(Box::new(
+                    crate::build::expr::as_constant::as_constant_inner(expr, |_| None, self.tcx)
+                )))
+            },
             _ => self.parse_place(expr_id).map(Operand::Copy),
         )
     }
 
     fn parse_place(&self, expr_id: ExprId) -> PResult<Place<'tcx>> {
-        parse_by_kind!(self, expr_id, "place",
+        parse_by_kind!(self, expr_id, _, "place",
             ExprKind::Deref { arg } => Ok(
                 self.parse_place(*arg)?.project_deeper(&[PlaceElem::Deref], self.tcx)
             ),
@@ -59,14 +72,34 @@ impl<'tcx, 'body> ParseCtxt<'tcx, 'body> {
     }
 
     fn parse_local(&self, expr_id: ExprId) -> PResult<Local> {
-        parse_by_kind!(self, expr_id, "local",
+        parse_by_kind!(self, expr_id, _, "local",
             ExprKind::VarRef { id } => Ok(self.local_map[id]),
         )
     }
 
     fn parse_block(&self, expr_id: ExprId) -> PResult<BasicBlock> {
-        parse_by_kind!(self, expr_id, "basic block",
+        parse_by_kind!(self, expr_id, _, "basic block",
             ExprKind::VarRef { id } => Ok(self.block_map[id]),
         )
     }
+
+    fn parse_static(&self, expr_id: ExprId) -> PResult<Operand<'tcx>> {
+        let expr_id = parse_by_kind!(self, expr_id, _, "static",
+            ExprKind::Deref { arg } => *arg,
+        );
+
+        parse_by_kind!(self, expr_id, expr, "static",
+            ExprKind::StaticRef { alloc_id, ty, .. } => {
+                let const_val =
+                    ConstValue::Scalar(Scalar::from_pointer((*alloc_id).into(), &self.tcx));
+                let literal = ConstantKind::Val(const_val, *ty);
+
+                Ok(Operand::Constant(Box::new(Constant {
+                    span: expr.span,
+                    user_ty: None,
+                    literal
+                })))
+            },
+        )
+    }
 }
diff --git a/compiler/rustc_mir_build/src/build/expr/as_constant.rs b/compiler/rustc_mir_build/src/build/expr/as_constant.rs
index 7d8a940bde5..717c6231574 100644
--- a/compiler/rustc_mir_build/src/build/expr/as_constant.rs
+++ b/compiler/rustc_mir_build/src/build/expr/as_constant.rs
@@ -8,7 +8,9 @@ use rustc_middle::mir::interpret::{
 };
 use rustc_middle::mir::*;
 use rustc_middle::thir::*;
-use rustc_middle::ty::{self, CanonicalUserTypeAnnotation, TyCtxt};
+use rustc_middle::ty::{
+    self, CanonicalUserType, CanonicalUserTypeAnnotation, TyCtxt, UserTypeAnnotationIndex,
+};
 use rustc_span::DUMMY_SP;
 use rustc_target::abi::Size;
 
@@ -19,84 +21,87 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
         let this = self;
         let tcx = this.tcx;
         let Expr { ty, temp_lifetime: _, span, ref kind } = *expr;
-        match *kind {
+        match kind {
             ExprKind::Scope { region_scope: _, lint_level: _, value } => {
-                this.as_constant(&this.thir[value])
-            }
-            ExprKind::Literal { lit, neg } => {
-                let literal =
-                    match lit_to_mir_constant(tcx, LitToConstInput { lit: &lit.node, ty, neg }) {
-                        Ok(c) => c,
-                        Err(LitToConstError::Reported(guar)) => {
-                            ConstantKind::Ty(tcx.const_error_with_guaranteed(ty, guar))
-                        }
-                        Err(LitToConstError::TypeError) => {
-                            bug!("encountered type error in `lit_to_mir_constant")
-                        }
-                    };
-
-                Constant { span, user_ty: None, literal }
+                this.as_constant(&this.thir[*value])
             }
-            ExprKind::NonHirLiteral { lit, ref user_ty } => {
-                let user_ty = user_ty.as_ref().map(|user_ty| {
-                    this.canonical_user_type_annotations.push(CanonicalUserTypeAnnotation {
+            _ => as_constant_inner(
+                expr,
+                |user_ty| {
+                    Some(this.canonical_user_type_annotations.push(CanonicalUserTypeAnnotation {
                         span,
                         user_ty: user_ty.clone(),
                         inferred_ty: ty,
-                    })
-                });
-                let literal = ConstantKind::Val(ConstValue::Scalar(Scalar::Int(lit)), ty);
+                    }))
+                },
+                tcx,
+            ),
+        }
+    }
+}
 
-                Constant { span, user_ty: user_ty, literal }
-            }
-            ExprKind::ZstLiteral { ref user_ty } => {
-                let user_ty = user_ty.as_ref().map(|user_ty| {
-                    this.canonical_user_type_annotations.push(CanonicalUserTypeAnnotation {
-                        span,
-                        user_ty: user_ty.clone(),
-                        inferred_ty: ty,
-                    })
-                });
-                let literal = ConstantKind::Val(ConstValue::ZeroSized, ty);
+pub fn as_constant_inner<'tcx>(
+    expr: &Expr<'tcx>,
+    push_cuta: impl FnMut(&Box<CanonicalUserType<'tcx>>) -> Option<UserTypeAnnotationIndex>,
+    tcx: TyCtxt<'tcx>,
+) -> Constant<'tcx> {
+    let Expr { ty, temp_lifetime: _, span, ref kind } = *expr;
+    match *kind {
+        ExprKind::Literal { lit, neg } => {
+            let literal =
+                match lit_to_mir_constant(tcx, LitToConstInput { lit: &lit.node, ty, neg }) {
+                    Ok(c) => c,
+                    Err(LitToConstError::Reported(guar)) => {
+                        ConstantKind::Ty(tcx.const_error_with_guaranteed(ty, guar))
+                    }
+                    Err(LitToConstError::TypeError) => {
+                        bug!("encountered type error in `lit_to_mir_constant")
+                    }
+                };
 
-                Constant { span, user_ty: user_ty, literal }
-            }
-            ExprKind::NamedConst { def_id, substs, ref user_ty } => {
-                let user_ty = user_ty.as_ref().map(|user_ty| {
-                    this.canonical_user_type_annotations.push(CanonicalUserTypeAnnotation {
-                        span,
-                        user_ty: user_ty.clone(),
-                        inferred_ty: ty,
-                    })
-                });
+            Constant { span, user_ty: None, literal }
+        }
+        ExprKind::NonHirLiteral { lit, ref user_ty } => {
+            let user_ty = user_ty.as_ref().map(push_cuta).flatten();
 
-                let uneval =
-                    mir::UnevaluatedConst::new(ty::WithOptConstParam::unknown(def_id), substs);
-                let literal = ConstantKind::Unevaluated(uneval, ty);
+            let literal = ConstantKind::Val(ConstValue::Scalar(Scalar::Int(lit)), ty);
 
-                Constant { user_ty, span, literal }
-            }
-            ExprKind::ConstParam { param, def_id: _ } => {
-                let const_param = tcx.mk_const(ty::ConstKind::Param(param), expr.ty);
-                let literal = ConstantKind::Ty(const_param);
+            Constant { span, user_ty: user_ty, literal }
+        }
+        ExprKind::ZstLiteral { ref user_ty } => {
+            let user_ty = user_ty.as_ref().map(push_cuta).flatten();
 
-                Constant { user_ty: None, span, literal }
-            }
-            ExprKind::ConstBlock { did: def_id, substs } => {
-                let uneval =
-                    mir::UnevaluatedConst::new(ty::WithOptConstParam::unknown(def_id), substs);
-                let literal = ConstantKind::Unevaluated(uneval, ty);
+            let literal = ConstantKind::Val(ConstValue::ZeroSized, ty);
 
-                Constant { user_ty: None, span, literal }
-            }
-            ExprKind::StaticRef { alloc_id, ty, .. } => {
-                let const_val = ConstValue::Scalar(Scalar::from_pointer(alloc_id.into(), &tcx));
-                let literal = ConstantKind::Val(const_val, ty);
+            Constant { span, user_ty: user_ty, literal }
+        }
+        ExprKind::NamedConst { def_id, substs, ref user_ty } => {
+            let user_ty = user_ty.as_ref().map(push_cuta).flatten();
 
-                Constant { span, user_ty: None, literal }
-            }
-            _ => span_bug!(span, "expression is not a valid constant {:?}", kind),
+            let uneval = mir::UnevaluatedConst::new(ty::WithOptConstParam::unknown(def_id), substs);
+            let literal = ConstantKind::Unevaluated(uneval, ty);
+
+            Constant { user_ty, span, literal }
+        }
+        ExprKind::ConstParam { param, def_id: _ } => {
+            let const_param = tcx.mk_const(ty::ConstKind::Param(param), expr.ty);
+            let literal = ConstantKind::Ty(const_param);
+
+            Constant { user_ty: None, span, literal }
+        }
+        ExprKind::ConstBlock { did: def_id, substs } => {
+            let uneval = mir::UnevaluatedConst::new(ty::WithOptConstParam::unknown(def_id), substs);
+            let literal = ConstantKind::Unevaluated(uneval, ty);
+
+            Constant { user_ty: None, span, literal }
+        }
+        ExprKind::StaticRef { alloc_id, ty, .. } => {
+            let const_val = ConstValue::Scalar(Scalar::from_pointer(alloc_id.into(), &tcx));
+            let literal = ConstantKind::Val(const_val, ty);
+
+            Constant { span, user_ty: None, literal }
         }
+        _ => span_bug!(span, "expression is not a valid constant {:?}", kind),
     }
 }
 
diff --git a/compiler/rustc_mir_build/src/build/expr/category.rs b/compiler/rustc_mir_build/src/build/expr/category.rs
index a4386319dc1..d33401f0764 100644
--- a/compiler/rustc_mir_build/src/build/expr/category.rs
+++ b/compiler/rustc_mir_build/src/build/expr/category.rs
@@ -2,35 +2,35 @@ use rustc_middle::thir::*;
 
 #[derive(Debug, PartialEq)]
 pub(crate) enum Category {
-    // An assignable memory location like `x`, `x.f`, `foo()[3]`, that
-    // sort of thing. Something that could appear on the LHS of an `=`
-    // sign.
+    /// An assignable memory location like `x`, `x.f`, `foo()[3]`, that
+    /// sort of thing. Something that could appear on the LHS of an `=`
+    /// sign.
     Place,
 
-    // A literal like `23` or `"foo"`. Does not include constant
-    // expressions like `3 + 5`.
+    /// A literal like `23` or `"foo"`. Does not include constant
+    /// expressions like `3 + 5`.
     Constant,
 
-    // Something that generates a new value at runtime, like `x + y`
-    // or `foo()`.
+    /// Something that generates a new value at runtime, like `x + y`
+    /// or `foo()`.
     Rvalue(RvalueFunc),
 }
 
-// Rvalues fall into different "styles" that will determine which fn
-// is best suited to generate them.
+/// Rvalues fall into different "styles" that will determine which fn
+/// is best suited to generate them.
 #[derive(Debug, PartialEq)]
 pub(crate) enum RvalueFunc {
-    // Best generated by `into`. This is generally exprs that
-    // cause branching, like `match`, but also includes calls.
+    /// Best generated by `into`. This is generally exprs that
+    /// cause branching, like `match`, but also includes calls.
     Into,
 
-    // Best generated by `as_rvalue`. This is usually the case.
+    /// Best generated by `as_rvalue`. This is usually the case.
     AsRvalue,
 }
 
-/// Determines the category for a given expression. Note that scope
-/// and paren expressions have no category.
 impl Category {
+    /// Determines the category for a given expression. Note that scope
+    /// and paren expressions have no category.
     pub(crate) fn of(ek: &ExprKind<'_>) -> Option<Category> {
         match *ek {
             ExprKind::Scope { .. } => None,
diff --git a/compiler/rustc_mir_build/src/build/misc.rs b/compiler/rustc_mir_build/src/build/misc.rs
index 86f466ff767..baeb2718cae 100644
--- a/compiler/rustc_mir_build/src/build/misc.rs
+++ b/compiler/rustc_mir_build/src/build/misc.rs
@@ -34,8 +34,8 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
         Operand::Constant(constant)
     }
 
-    // Returns a zero literal operand for the appropriate type, works for
-    // bool, char and integers.
+    /// Returns a zero literal operand for the appropriate type, works for
+    /// bool, char and integers.
     pub(crate) fn zero_literal(&mut self, span: Span, ty: Ty<'tcx>) -> Operand<'tcx> {
         let literal = ConstantKind::from_bits(self.tcx, 0, ty::ParamEnv::empty().and(ty));
 
diff --git a/compiler/rustc_mir_build/src/build/mod.rs b/compiler/rustc_mir_build/src/build/mod.rs
index 0b76122913e..b456e2aa37a 100644
--- a/compiler/rustc_mir_build/src/build/mod.rs
+++ b/compiler/rustc_mir_build/src/build/mod.rs
@@ -492,7 +492,7 @@ fn construct_fn<'tcx>(
             arguments,
             return_ty,
             return_ty_span,
-            span,
+            span_with_body,
             custom_mir_attr,
         );
     }
diff --git a/compiler/rustc_mir_build/src/build/scope.rs b/compiler/rustc_mir_build/src/build/scope.rs
index 3cebd5ebed6..5ddae5f5300 100644
--- a/compiler/rustc_mir_build/src/build/scope.rs
+++ b/compiler/rustc_mir_build/src/build/scope.rs
@@ -443,8 +443,9 @@ impl<'tcx> Scopes<'tcx> {
 impl<'a, 'tcx> Builder<'a, 'tcx> {
     // Adding and removing scopes
     // ==========================
-    //  Start a breakable scope, which tracks where `continue`, `break` and
-    //  `return` should branch to.
+
+    ///  Start a breakable scope, which tracks where `continue`, `break` and
+    ///  `return` should branch to.
     pub(crate) fn in_breakable_scope<F>(
         &mut self,
         loop_block: Option<BasicBlock>,
@@ -799,6 +800,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
 
     // Finding scopes
     // ==============
+
     /// Returns the scope that we should use as the lifetime of an
     /// operand. Basically, an operand must live until it is consumed.
     /// This is similar to, but not quite the same as, the temporary
@@ -824,6 +826,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
 
     // Scheduling drops
     // ================
+
     pub(crate) fn schedule_drop_storage_and_value(
         &mut self,
         span: Span,
@@ -996,6 +999,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
 
     // Other
     // =====
+
     /// Returns the [DropIdx] for the innermost drop if the function unwound at
     /// this point. The `DropIdx` will be created if it doesn't already exist.
     fn diverge_cleanup(&mut self) -> DropIdx {
diff --git a/compiler/rustc_mir_build/src/thir/constant.rs b/compiler/rustc_mir_build/src/thir/constant.rs
index 85e8801bda3..a9ed945d4a1 100644
--- a/compiler/rustc_mir_build/src/thir/constant.rs
+++ b/compiler/rustc_mir_build/src/thir/constant.rs
@@ -61,5 +61,5 @@ pub(crate) fn lit_to_const<'tcx>(
         _ => return Err(LitToConstError::TypeError),
     };
 
-    Ok(ty::Const::from_value(tcx, valtree, ty))
+    Ok(tcx.mk_const(valtree, ty))
 }
diff --git a/compiler/rustc_mir_transform/src/add_moves_for_packed_drops.rs b/compiler/rustc_mir_transform/src/add_moves_for_packed_drops.rs
index ffb5d8c6d95..9b2260f6825 100644
--- a/compiler/rustc_mir_transform/src/add_moves_for_packed_drops.rs
+++ b/compiler/rustc_mir_transform/src/add_moves_for_packed_drops.rs
@@ -5,37 +5,36 @@ use crate::util;
 use crate::MirPass;
 use rustc_middle::mir::patch::MirPatch;
 
-// This pass moves values being dropped that are within a packed
-// struct to a separate local before dropping them, to ensure that
-// they are dropped from an aligned address.
-//
-// For example, if we have something like
-// ```Rust
-//     #[repr(packed)]
-//     struct Foo {
-//         dealign: u8,
-//         data: Vec<u8>
-//     }
-//
-//     let foo = ...;
-// ```
-//
-// We want to call `drop_in_place::<Vec<u8>>` on `data` from an aligned
-// address. This means we can't simply drop `foo.data` directly, because
-// its address is not aligned.
-//
-// Instead, we move `foo.data` to a local and drop that:
-// ```
-//     storage.live(drop_temp)
-//     drop_temp = foo.data;
-//     drop(drop_temp) -> next
-// next:
-//     storage.dead(drop_temp)
-// ```
-//
-// The storage instructions are required to avoid stack space
-// blowup.
-
+/// This pass moves values being dropped that are within a packed
+/// struct to a separate local before dropping them, to ensure that
+/// they are dropped from an aligned address.
+///
+/// For example, if we have something like
+/// ```ignore (ilustrative)
+/// #[repr(packed)]
+/// struct Foo {
+///     dealign: u8,
+///     data: Vec<u8>
+/// }
+///
+/// let foo = ...;
+/// ```
+///
+/// We want to call `drop_in_place::<Vec<u8>>` on `data` from an aligned
+/// address. This means we can't simply drop `foo.data` directly, because
+/// its address is not aligned.
+///
+/// Instead, we move `foo.data` to a local and drop that:
+/// ```ignore (ilustrative)
+///     storage.live(drop_temp)
+///     drop_temp = foo.data;
+///     drop(drop_temp) -> next
+/// next:
+///     storage.dead(drop_temp)
+/// ```
+///
+/// The storage instructions are required to avoid stack space
+/// blowup.
 pub struct AddMovesForPackedDrops;
 
 impl<'tcx> MirPass<'tcx> for AddMovesForPackedDrops {
diff --git a/compiler/rustc_mir_transform/src/elaborate_box_derefs.rs b/compiler/rustc_mir_transform/src/elaborate_box_derefs.rs
index ef8d6bb6559..932134bd631 100644
--- a/compiler/rustc_mir_transform/src/elaborate_box_derefs.rs
+++ b/compiler/rustc_mir_transform/src/elaborate_box_derefs.rs
@@ -25,7 +25,7 @@ pub fn build_ptr_tys<'tcx>(
     (unique_ty, nonnull_ty, ptr_ty)
 }
 
-// Constructs the projection needed to access a Box's pointer
+/// Constructs the projection needed to access a Box's pointer
 pub fn build_projection<'tcx>(
     unique_ty: Ty<'tcx>,
     nonnull_ty: Ty<'tcx>,
diff --git a/compiler/rustc_mir_transform/src/shim.rs b/compiler/rustc_mir_transform/src/shim.rs
index 68703eb0a23..a115bb2831a 100644
--- a/compiler/rustc_mir_transform/src/shim.rs
+++ b/compiler/rustc_mir_transform/src/shim.rs
@@ -37,7 +37,7 @@ fn make_shim<'tcx>(tcx: TyCtxt<'tcx>, instance: ty::InstanceDef<'tcx>) -> Body<'
         }
         ty::InstanceDef::FnPtrShim(def_id, ty) => {
             let trait_ = tcx.trait_of_item(def_id).unwrap();
-            let adjustment = match tcx.fn_trait_kind_from_lang_item(trait_) {
+            let adjustment = match tcx.fn_trait_kind_from_def_id(trait_) {
                 Some(ty::ClosureKind::FnOnce) => Adjustment::Identity,
                 Some(ty::ClosureKind::FnMut | ty::ClosureKind::Fn) => Adjustment::Deref,
                 None => bug!("fn pointer {:?} is not an fn", ty),
diff --git a/compiler/rustc_monomorphize/src/collector.rs b/compiler/rustc_monomorphize/src/collector.rs
index 559ce227454..cf7226a129c 100644
--- a/compiler/rustc_monomorphize/src/collector.rs
+++ b/compiler/rustc_monomorphize/src/collector.rs
@@ -295,8 +295,8 @@ impl<'tcx> InliningMap<'tcx> {
         assert!(self.index.insert(source, start_index..end_index).is_none());
     }
 
-    // Internally iterate over all items referenced by `source` which will be
-    // made available for inlining.
+    /// Internally iterate over all items referenced by `source` which will be
+    /// made available for inlining.
     pub fn with_inlining_candidates<F>(&self, source: MonoItem<'tcx>, mut f: F)
     where
         F: FnMut(MonoItem<'tcx>),
@@ -310,7 +310,7 @@ impl<'tcx> InliningMap<'tcx> {
         }
     }
 
-    // Internally iterate over all items and the things each accesses.
+    /// Internally iterate over all items and the things each accesses.
     pub fn iter_accesses<F>(&self, mut f: F)
     where
         F: FnMut(MonoItem<'tcx>, &[MonoItem<'tcx>]),
diff --git a/compiler/rustc_parse/src/lexer/unicode_chars.rs b/compiler/rustc_parse/src/lexer/unicode_chars.rs
index 2c68cc5895c..f1b50296e25 100644
--- a/compiler/rustc_parse/src/lexer/unicode_chars.rs
+++ b/compiler/rustc_parse/src/lexer/unicode_chars.rs
@@ -1,5 +1,5 @@
-// Characters and their corresponding confusables were collected from
-// https://www.unicode.org/Public/security/10.0.0/confusables.txt
+//! Characters and their corresponding confusables were collected from
+//! <https://www.unicode.org/Public/security/10.0.0/confusables.txt>
 
 use super::StringReader;
 use crate::token::{self, Delimiter};
diff --git a/compiler/rustc_parse/src/parser/attr.rs b/compiler/rustc_parse/src/parser/attr.rs
index 0ed24fe849c..c7d239b647f 100644
--- a/compiler/rustc_parse/src/parser/attr.rs
+++ b/compiler/rustc_parse/src/parser/attr.rs
@@ -315,8 +315,9 @@ impl<'a> Parser<'a> {
         Ok(attrs)
     }
 
-    pub(crate) fn parse_unsuffixed_lit(&mut self) -> PResult<'a, ast::Lit> {
-        let lit = self.parse_ast_lit()?;
+    // Note: must be unsuffixed.
+    pub(crate) fn parse_unsuffixed_meta_item_lit(&mut self) -> PResult<'a, ast::MetaItemLit> {
+        let lit = self.parse_meta_item_lit()?;
         debug!("checking if {:?} is unsuffixed", lit);
 
         if !lit.kind.is_unsuffixed() {
@@ -391,7 +392,7 @@ impl<'a> Parser<'a> {
 
     pub(crate) fn parse_meta_item_kind(&mut self) -> PResult<'a, ast::MetaItemKind> {
         Ok(if self.eat(&token::Eq) {
-            ast::MetaItemKind::NameValue(self.parse_unsuffixed_lit()?)
+            ast::MetaItemKind::NameValue(self.parse_unsuffixed_meta_item_lit()?)
         } else if self.check(&token::OpenDelim(Delimiter::Parenthesis)) {
             // Matches `meta_seq = ( COMMASEP(meta_item_inner) )`.
             let (list, _) = self.parse_paren_comma_seq(|p| p.parse_meta_item_inner())?;
@@ -403,8 +404,8 @@ impl<'a> Parser<'a> {
 
     /// Matches `meta_item_inner : (meta_item | UNSUFFIXED_LIT) ;`.
     fn parse_meta_item_inner(&mut self) -> PResult<'a, ast::NestedMetaItem> {
-        match self.parse_unsuffixed_lit() {
-            Ok(lit) => return Ok(ast::NestedMetaItem::Literal(lit)),
+        match self.parse_unsuffixed_meta_item_lit() {
+            Ok(lit) => return Ok(ast::NestedMetaItem::Lit(lit)),
             Err(err) => err.cancel(),
         }
 
diff --git a/compiler/rustc_parse/src/parser/attr_wrapper.rs b/compiler/rustc_parse/src/parser/attr_wrapper.rs
index c8160548763..a084a701088 100644
--- a/compiler/rustc_parse/src/parser/attr_wrapper.rs
+++ b/compiler/rustc_parse/src/parser/attr_wrapper.rs
@@ -50,7 +50,7 @@ impl AttrWrapper {
         self.attrs
     }
 
-    // Prepend `self.attrs` to `attrs`.
+    /// Prepend `self.attrs` to `attrs`.
     // FIXME: require passing an NT to prevent misuse of this method
     pub(crate) fn prepend_to_nt_inner(self, attrs: &mut AttrVec) {
         let mut self_attrs = self.attrs;
diff --git a/compiler/rustc_parse/src/parser/diagnostics.rs b/compiler/rustc_parse/src/parser/diagnostics.rs
index 3f5baf343c9..c316a4dd6b4 100644
--- a/compiler/rustc_parse/src/parser/diagnostics.rs
+++ b/compiler/rustc_parse/src/parser/diagnostics.rs
@@ -224,9 +224,9 @@ impl MultiSugg {
     }
 }
 
-// SnapshotParser is used to create a snapshot of the parser
-// without causing duplicate errors being emitted when the `Parser`
-// is dropped.
+/// SnapshotParser is used to create a snapshot of the parser
+/// without causing duplicate errors being emitted when the `Parser`
+/// is dropped.
 pub struct SnapshotParser<'a> {
     parser: Parser<'a>,
     unclosed_delims: Vec<UnmatchedBrace>,
diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs
index 9f2267efb82..e0443a697b5 100644
--- a/compiler/rustc_parse/src/parser/expr.rs
+++ b/compiler/rustc_parse/src/parser/expr.rs
@@ -33,10 +33,10 @@ use rustc_ast::util::case::Case;
 use rustc_ast::util::classify;
 use rustc_ast::util::parser::{prec_let_scrutinee_needs_par, AssocOp, Fixity};
 use rustc_ast::visit::Visitor;
-use rustc_ast::{self as ast, AttrStyle, AttrVec, CaptureBy, ExprField, Lit, UnOp, DUMMY_NODE_ID};
+use rustc_ast::{self as ast, AttrStyle, AttrVec, CaptureBy, ExprField, UnOp, DUMMY_NODE_ID};
 use rustc_ast::{AnonConst, BinOp, BinOpKind, FnDecl, FnRetTy, MacCall, Param, Ty, TyKind};
 use rustc_ast::{Arm, Async, BlockCheckMode, Expr, ExprKind, Label, Movability, RangeLimits};
-use rustc_ast::{ClosureBinder, StmtKind};
+use rustc_ast::{ClosureBinder, MetaItemLit, StmtKind};
 use rustc_ast_pretty::pprust;
 use rustc_errors::{
     Applicability, Diagnostic, DiagnosticBuilder, ErrorGuaranteed, IntoDiagnostic, PResult,
@@ -1631,7 +1631,7 @@ impl<'a> Parser<'a> {
         &self,
         lifetime: Ident,
         err: impl FnOnce(&Self) -> DiagnosticBuilder<'a, ErrorGuaranteed>,
-    ) -> ast::Lit {
+    ) -> ast::MetaItemLit {
         if let Some(mut diag) =
             self.sess.span_diagnostic.steal_diagnostic(lifetime.span, StashKey::LifetimeIsChar)
         {
@@ -1653,7 +1653,7 @@ impl<'a> Parser<'a> {
                 .emit();
         }
         let name = lifetime.without_first_quote().name;
-        ast::Lit {
+        ast::MetaItemLit {
             token_lit: token::Lit::new(token::LitKind::Char, name, None),
             kind: ast::LitKind::Char(name.as_str().chars().next().unwrap_or('_')),
             span: lifetime.span,
@@ -1768,8 +1768,8 @@ impl<'a> Parser<'a> {
     /// Returns a string literal if the next token is a string literal.
     /// In case of error returns `Some(lit)` if the next token is a literal with a wrong kind,
     /// and returns `None` if the next token is not literal at all.
-    pub fn parse_str_lit(&mut self) -> Result<ast::StrLit, Option<Lit>> {
-        match self.parse_opt_ast_lit() {
+    pub fn parse_str_lit(&mut self) -> Result<ast::StrLit, Option<MetaItemLit>> {
+        match self.parse_opt_meta_item_lit() {
             Some(lit) => match lit.kind {
                 ast::LitKind::Str(symbol_unescaped, style) => Ok(ast::StrLit {
                     style,
@@ -1784,7 +1784,7 @@ impl<'a> Parser<'a> {
         }
     }
 
-    fn handle_missing_lit(&mut self) -> PResult<'a, Lit> {
+    fn handle_missing_lit(&mut self) -> PResult<'a, MetaItemLit> {
         if let token::Interpolated(inner) = &self.token.kind {
             let expr = match inner.as_ref() {
                 token::NtExpr(expr) => Some(expr),
@@ -1820,8 +1820,8 @@ impl<'a> Parser<'a> {
             .or_else(|()| self.handle_missing_lit().map(|lit| (lit.token_lit, lit.span)))
     }
 
-    pub(super) fn parse_ast_lit(&mut self) -> PResult<'a, Lit> {
-        self.parse_opt_ast_lit().ok_or(()).or_else(|()| self.handle_missing_lit())
+    pub(super) fn parse_meta_item_lit(&mut self) -> PResult<'a, MetaItemLit> {
+        self.parse_opt_meta_item_lit().ok_or(()).or_else(|()| self.handle_missing_lit())
     }
 
     fn recover_after_dot(&mut self) -> Option<Token> {
@@ -1867,12 +1867,12 @@ impl<'a> Parser<'a> {
 
     /// Matches `lit = true | false | token_lit`.
     /// Returns `None` if the next token is not a literal.
-    pub(super) fn parse_opt_ast_lit(&mut self) -> Option<Lit> {
+    pub(super) fn parse_opt_meta_item_lit(&mut self) -> Option<MetaItemLit> {
         let recovered = self.recover_after_dot();
         let token = recovered.as_ref().unwrap_or(&self.token);
         match token::Lit::from_token(token) {
             Some(token_lit) => {
-                match Lit::from_token_lit(token_lit, token.span) {
+                match MetaItemLit::from_token_lit(token_lit, token.span) {
                     Ok(lit) => {
                         self.bump();
                         Some(lit)
@@ -1889,7 +1889,10 @@ impl<'a> Parser<'a> {
                         let suffixless_lit = token::Lit::new(lit.kind, lit.symbol, None);
                         let symbol = Symbol::intern(&suffixless_lit.to_string());
                         let lit = token::Lit::new(token::Err, symbol, lit.suffix);
-                        Some(Lit::from_token_lit(lit, span).unwrap_or_else(|_| unreachable!()))
+                        Some(
+                            MetaItemLit::from_token_lit(lit, span)
+                                .unwrap_or_else(|_| unreachable!()),
+                        )
                     }
                 }
             }
diff --git a/compiler/rustc_parse/src/parser/item.rs b/compiler/rustc_parse/src/parser/item.rs
index 767fb9378be..84c63219920 100644
--- a/compiler/rustc_parse/src/parser/item.rs
+++ b/compiler/rustc_parse/src/parser/item.rs
@@ -1012,7 +1012,7 @@ impl<'a> Parser<'a> {
                     prefix.span = lo.to(self.prev_token.span);
                 }
 
-                UseTreeKind::Simple(self.parse_rename()?, DUMMY_NODE_ID, DUMMY_NODE_ID)
+                UseTreeKind::Simple(self.parse_rename()?)
             }
         };
 
diff --git a/compiler/rustc_parse/src/parser/stmt.rs b/compiler/rustc_parse/src/parser/stmt.rs
index 1b56cd72db0..ff1ddfd97df 100644
--- a/compiler/rustc_parse/src/parser/stmt.rs
+++ b/compiler/rustc_parse/src/parser/stmt.rs
@@ -358,7 +358,7 @@ impl<'a> Parser<'a> {
     /// report error for `let 1x = 123`
     pub fn report_invalid_identifier_error(&mut self) -> PResult<'a, ()> {
         if let token::Literal(lit) = self.token.uninterpolate().kind &&
-            rustc_ast::Lit::from_token(&self.token).is_none() &&
+            rustc_ast::MetaItemLit::from_token(&self.token).is_none() &&
             (lit.kind == token::LitKind::Integer || lit.kind == token::LitKind::Float) &&
             self.look_ahead(1, |t| matches!(t.kind, token::Eq) || matches!(t.kind, token::Colon ) ) {
                 return Err(self.sess.create_err(InvalidIdentiferStartsWithNumber { span: self.token.span }));
diff --git a/compiler/rustc_parse/src/validate_attr.rs b/compiler/rustc_parse/src/validate_attr.rs
index e2f95d74a3d..72402a20090 100644
--- a/compiler/rustc_parse/src/validate_attr.rs
+++ b/compiler/rustc_parse/src/validate_attr.rs
@@ -10,9 +10,9 @@ use rustc_errors::{Applicability, FatalError, PResult};
 use rustc_feature::{AttributeTemplate, BuiltinAttribute, BUILTIN_ATTRIBUTE_MAP};
 use rustc_session::lint::builtin::ILL_FORMED_ATTRIBUTE_INPUT;
 use rustc_session::parse::ParseSess;
-use rustc_span::{sym, Symbol};
+use rustc_span::{sym, Span, Symbol};
 
-pub fn check_meta(sess: &ParseSess, attr: &Attribute) {
+pub fn check_attr(sess: &ParseSess, attr: &Attribute) {
     if attr.is_doc_comment() {
         return;
     }
@@ -51,7 +51,7 @@ pub fn parse_meta<'a>(sess: &'a ParseSess, attr: &Attribute) -> PResult<'a, Meta
             }
             AttrArgs::Eq(_, AttrArgsEq::Ast(expr)) => {
                 if let ast::ExprKind::Lit(token_lit) = expr.kind
-                    && let Ok(lit) = ast::Lit::from_token_lit(token_lit, expr.span)
+                    && let Ok(lit) = ast::MetaItemLit::from_token_lit(token_lit, expr.span)
                 {
                     if token_lit.suffix.is_some() {
                         let mut err = sess.span_diagnostic.struct_span_err(
@@ -115,25 +115,34 @@ pub fn check_builtin_attribute(
     name: Symbol,
     template: AttributeTemplate,
 ) {
-    // Some special attributes like `cfg` must be checked
-    // before the generic check, so we skip them here.
-    let should_skip = |name| name == sym::cfg;
-
     match parse_meta(sess, attr) {
-        Ok(meta) => {
-            if !should_skip(name) && !is_attr_template_compatible(&template, &meta.kind) {
-                emit_malformed_attribute(sess, attr, name, template);
-            }
-        }
+        Ok(meta) => check_builtin_meta_item(sess, &meta, attr.style, name, template),
         Err(mut err) => {
             err.emit();
         }
     }
 }
 
+pub fn check_builtin_meta_item(
+    sess: &ParseSess,
+    meta: &MetaItem,
+    style: ast::AttrStyle,
+    name: Symbol,
+    template: AttributeTemplate,
+) {
+    // Some special attributes like `cfg` must be checked
+    // before the generic check, so we skip them here.
+    let should_skip = |name| name == sym::cfg;
+
+    if !should_skip(name) && !is_attr_template_compatible(&template, &meta.kind) {
+        emit_malformed_attribute(sess, style, meta.span, name, template);
+    }
+}
+
 fn emit_malformed_attribute(
     sess: &ParseSess,
-    attr: &Attribute,
+    style: ast::AttrStyle,
+    span: Span,
     name: Symbol,
     template: AttributeTemplate,
 ) {
@@ -147,7 +156,7 @@ fn emit_malformed_attribute(
     let mut msg = "attribute must be of the form ".to_owned();
     let mut suggestions = vec![];
     let mut first = true;
-    let inner = if attr.style == ast::AttrStyle::Inner { "!" } else { "" };
+    let inner = if style == ast::AttrStyle::Inner { "!" } else { "" };
     if template.word {
         first = false;
         let code = format!("#{}[{}]", inner, name);
@@ -172,12 +181,12 @@ fn emit_malformed_attribute(
         suggestions.push(code);
     }
     if should_warn(name) {
-        sess.buffer_lint(&ILL_FORMED_ATTRIBUTE_INPUT, attr.span, ast::CRATE_NODE_ID, &msg);
+        sess.buffer_lint(&ILL_FORMED_ATTRIBUTE_INPUT, span, ast::CRATE_NODE_ID, &msg);
     } else {
         sess.span_diagnostic
-            .struct_span_err(attr.span, &error_msg)
+            .struct_span_err(span, &error_msg)
             .span_suggestions(
-                attr.span,
+                span,
                 if suggestions.len() == 1 {
                     "must be of the form"
                 } else {
@@ -196,7 +205,7 @@ pub fn emit_fatal_malformed_builtin_attribute(
     name: Symbol,
 ) -> ! {
     let template = BUILTIN_ATTRIBUTE_MAP.get(&name).expect("builtin attr defined").template;
-    emit_malformed_attribute(sess, attr, name, template);
+    emit_malformed_attribute(sess, attr.style, attr.span, name, template);
     // This is fatal, otherwise it will likely cause a cascade of other errors
     // (and an error here is expected to be very rare).
     FatalError.raise()
diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs
index acb9bd8e78a..2e2874dbccb 100644
--- a/compiler/rustc_passes/src/check_attr.rs
+++ b/compiler/rustc_passes/src/check_attr.rs
@@ -8,7 +8,7 @@ use crate::errors::{
     self, AttrApplication, DebugVisualizerUnreadable, InvalidAttrAtCrateLevel, ObjectLifetimeErr,
     OnlyHasEffectOn, TransparentIncompatible, UnrecognizedReprHint,
 };
-use rustc_ast::{ast, AttrStyle, Attribute, Lit, LitKind, MetaItemKind, NestedMetaItem};
+use rustc_ast::{ast, AttrStyle, Attribute, LitKind, MetaItemKind, MetaItemLit, NestedMetaItem};
 use rustc_data_structures::fx::FxHashMap;
 use rustc_errors::{fluent, Applicability, MultiSpan};
 use rustc_expand::base::resolve_path;
@@ -715,7 +715,7 @@ impl CheckAttrVisitor<'_> {
         if let Some(values) = meta.meta_item_list() {
             let mut errors = 0;
             for v in values {
-                match v.literal() {
+                match v.lit() {
                     Some(l) => match l.kind {
                         LitKind::Str(s, _) => {
                             if !self.check_doc_alias_value(v, s, hir_id, target, true, aliases) {
@@ -1355,7 +1355,7 @@ impl CheckAttrVisitor<'_> {
             return false;
         };
 
-        if matches!(&list[..], &[NestedMetaItem::Literal(Lit { kind: LitKind::Int(..), .. })]) {
+        if matches!(&list[..], &[NestedMetaItem::Lit(MetaItemLit { kind: LitKind::Int(..), .. })]) {
             true
         } else {
             self.tcx.sess.emit_err(errors::RustcLayoutScalarValidRangeArg { attr_span: attr.span });
@@ -1418,7 +1418,7 @@ impl CheckAttrVisitor<'_> {
         let arg_count = decl.inputs.len() as u128 + generics.params.len() as u128;
         let mut invalid_args = vec![];
         for meta in list {
-            if let Some(LitKind::Int(val, _)) = meta.literal().map(|lit| &lit.kind) {
+            if let Some(LitKind::Int(val, _)) = meta.lit().map(|lit| &lit.kind) {
                 if *val >= arg_count {
                     let span = meta.span();
                     self.tcx.sess.emit_err(errors::RustcLegacyConstGenericsIndexExceed {
diff --git a/compiler/rustc_passes/src/dead.rs b/compiler/rustc_passes/src/dead.rs
index 5d0224c35f3..da023fcf4c3 100644
--- a/compiler/rustc_passes/src/dead.rs
+++ b/compiler/rustc_passes/src/dead.rs
@@ -433,7 +433,7 @@ impl<'tcx> Visitor<'tcx> for MarkSymbolVisitor<'tcx> {
         self.in_pat = false;
     }
 
-    fn visit_path(&mut self, path: &'tcx hir::Path<'tcx>, _: hir::HirId) {
+    fn visit_path(&mut self, path: &hir::Path<'tcx>, _: hir::HirId) {
         self.handle_res(path.res);
         intravisit::walk_path(self, path);
     }
diff --git a/compiler/rustc_passes/src/hir_id_validator.rs b/compiler/rustc_passes/src/hir_id_validator.rs
index 88bb39debb1..d143adb2eb9 100644
--- a/compiler/rustc_passes/src/hir_id_validator.rs
+++ b/compiler/rustc_passes/src/hir_id_validator.rs
@@ -1,11 +1,11 @@
 use rustc_data_structures::sync::Lock;
 use rustc_hir as hir;
+use rustc_hir::def_id::LocalDefId;
 use rustc_hir::intravisit;
 use rustc_hir::{HirId, ItemLocalId};
 use rustc_index::bit_set::GrowableBitSet;
-use rustc_middle::hir::map::Map;
 use rustc_middle::hir::nested_filter;
-use rustc_middle::ty::TyCtxt;
+use rustc_middle::ty::{DefIdTree, TyCtxt};
 
 pub fn check_crate(tcx: TyCtxt<'_>) {
     tcx.dep_graph.assert_ignored();
@@ -17,11 +17,10 @@ pub fn check_crate(tcx: TyCtxt<'_>) {
     #[cfg(debug_assertions)]
     {
         let errors = Lock::new(Vec::new());
-        let hir_map = tcx.hir();
 
-        hir_map.par_for_each_module(|module_id| {
+        tcx.hir().par_for_each_module(|module_id| {
             let mut v = HirIdValidator {
-                hir_map,
+                tcx,
                 owner: None,
                 hir_ids_seen: Default::default(),
                 errors: &errors,
@@ -40,20 +39,15 @@ pub fn check_crate(tcx: TyCtxt<'_>) {
 }
 
 struct HirIdValidator<'a, 'hir> {
-    hir_map: Map<'hir>,
+    tcx: TyCtxt<'hir>,
     owner: Option<hir::OwnerId>,
     hir_ids_seen: GrowableBitSet<ItemLocalId>,
     errors: &'a Lock<Vec<String>>,
 }
 
 impl<'a, 'hir> HirIdValidator<'a, 'hir> {
-    fn new_visitor(&self, hir_map: Map<'hir>) -> HirIdValidator<'a, 'hir> {
-        HirIdValidator {
-            hir_map,
-            owner: None,
-            hir_ids_seen: Default::default(),
-            errors: self.errors,
-        }
+    fn new_visitor(&self, tcx: TyCtxt<'hir>) -> HirIdValidator<'a, 'hir> {
+        HirIdValidator { tcx, owner: None, hir_ids_seen: Default::default(), errors: self.errors }
     }
 
     #[cold]
@@ -96,36 +90,69 @@ impl<'a, 'hir> HirIdValidator<'a, 'hir> {
                 missing_items.push(format!(
                     "[local_id: {}, owner: {}]",
                     local_id,
-                    self.hir_map.def_path(owner.def_id).to_string_no_crate_verbose()
+                    self.tcx.hir().def_path(owner.def_id).to_string_no_crate_verbose()
                 ));
             }
             self.error(|| {
                 format!(
                     "ItemLocalIds not assigned densely in {}. \
                 Max ItemLocalId = {}, missing IDs = {:#?}; seens IDs = {:#?}",
-                    self.hir_map.def_path(owner.def_id).to_string_no_crate_verbose(),
+                    self.tcx.hir().def_path(owner.def_id).to_string_no_crate_verbose(),
                     max,
                     missing_items,
                     self.hir_ids_seen
                         .iter()
                         .map(|local_id| HirId { owner, local_id })
-                        .map(|h| format!("({:?} {})", h, self.hir_map.node_to_string(h)))
+                        .map(|h| format!("({:?} {})", h, self.tcx.hir().node_to_string(h)))
                         .collect::<Vec<_>>()
                 )
             });
         }
     }
+
+    fn check_nested_id(&mut self, id: LocalDefId) {
+        let Some(owner) = self.owner else { return };
+        let def_parent = self.tcx.local_parent(id);
+        let def_parent_hir_id = self.tcx.local_def_id_to_hir_id(def_parent);
+        if def_parent_hir_id.owner != owner {
+            self.error(|| {
+                format!(
+                    "inconsistent Def parent at `{:?}` for `{:?}`:\nexpected={:?}\nfound={:?}",
+                    self.tcx.def_span(id),
+                    id,
+                    owner,
+                    def_parent_hir_id
+                )
+            });
+        }
+    }
 }
 
 impl<'a, 'hir> intravisit::Visitor<'hir> for HirIdValidator<'a, 'hir> {
     type NestedFilter = nested_filter::OnlyBodies;
 
     fn nested_visit_map(&mut self) -> Self::Map {
-        self.hir_map
+        self.tcx.hir()
+    }
+
+    fn visit_nested_item(&mut self, id: hir::ItemId) {
+        self.check_nested_id(id.owner_id.def_id);
+    }
+
+    fn visit_nested_trait_item(&mut self, id: hir::TraitItemId) {
+        self.check_nested_id(id.owner_id.def_id);
+    }
+
+    fn visit_nested_impl_item(&mut self, id: hir::ImplItemId) {
+        self.check_nested_id(id.owner_id.def_id);
+    }
+
+    fn visit_nested_foreign_item(&mut self, id: hir::ForeignItemId) {
+        self.check_nested_id(id.owner_id.def_id);
     }
 
     fn visit_item(&mut self, i: &'hir hir::Item<'hir>) {
-        let mut inner_visitor = self.new_visitor(self.hir_map);
+        let mut inner_visitor = self.new_visitor(self.tcx);
         inner_visitor.check(i.owner_id, |this| intravisit::walk_item(this, i));
     }
 
@@ -136,9 +163,9 @@ impl<'a, 'hir> intravisit::Visitor<'hir> for HirIdValidator<'a, 'hir> {
             self.error(|| {
                 format!(
                     "HirIdValidator: The recorded owner of {} is {} instead of {}",
-                    self.hir_map.node_to_string(hir_id),
-                    self.hir_map.def_path(hir_id.owner.def_id).to_string_no_crate_verbose(),
-                    self.hir_map.def_path(owner.def_id).to_string_no_crate_verbose()
+                    self.tcx.hir().node_to_string(hir_id),
+                    self.tcx.hir().def_path(hir_id.owner.def_id).to_string_no_crate_verbose(),
+                    self.tcx.hir().def_path(owner.def_id).to_string_no_crate_verbose()
                 )
             });
         }
@@ -147,17 +174,17 @@ impl<'a, 'hir> intravisit::Visitor<'hir> for HirIdValidator<'a, 'hir> {
     }
 
     fn visit_foreign_item(&mut self, i: &'hir hir::ForeignItem<'hir>) {
-        let mut inner_visitor = self.new_visitor(self.hir_map);
+        let mut inner_visitor = self.new_visitor(self.tcx);
         inner_visitor.check(i.owner_id, |this| intravisit::walk_foreign_item(this, i));
     }
 
     fn visit_trait_item(&mut self, i: &'hir hir::TraitItem<'hir>) {
-        let mut inner_visitor = self.new_visitor(self.hir_map);
+        let mut inner_visitor = self.new_visitor(self.tcx);
         inner_visitor.check(i.owner_id, |this| intravisit::walk_trait_item(this, i));
     }
 
     fn visit_impl_item(&mut self, i: &'hir hir::ImplItem<'hir>) {
-        let mut inner_visitor = self.new_visitor(self.hir_map);
+        let mut inner_visitor = self.new_visitor(self.tcx);
         inner_visitor.check(i.owner_id, |this| intravisit::walk_impl_item(this, i));
     }
 }
diff --git a/compiler/rustc_passes/src/hir_stats.rs b/compiler/rustc_passes/src/hir_stats.rs
index 140f02c046a..a7854cd4998 100644
--- a/compiler/rustc_passes/src/hir_stats.rs
+++ b/compiler/rustc_passes/src/hir_stats.rs
@@ -369,7 +369,7 @@ impl<'v> hir_visit::Visitor<'v> for StatCollector<'v> {
         hir_visit::walk_fn(self, fk, fd, b, id)
     }
 
-    fn visit_use(&mut self, p: &'v hir::Path<'v>, hir_id: hir::HirId) {
+    fn visit_use(&mut self, p: &'v hir::UsePath<'v>, hir_id: hir::HirId) {
         // This is `visit_use`, but the type is `Path` so record it that way.
         self.record("Path", Id::None, p);
         hir_visit::walk_use(self, p, hir_id)
@@ -442,7 +442,7 @@ impl<'v> hir_visit::Visitor<'v> for StatCollector<'v> {
         hir_visit::walk_lifetime(self, lifetime)
     }
 
-    fn visit_path(&mut self, path: &'v hir::Path<'v>, _id: hir::HirId) {
+    fn visit_path(&mut self, path: &hir::Path<'v>, _id: hir::HirId) {
         self.record("Path", Id::None, path);
         hir_visit::walk_path(self, path)
     }
diff --git a/compiler/rustc_passes/src/reachable.rs b/compiler/rustc_passes/src/reachable.rs
index 73ea06a6370..e7c3c712852 100644
--- a/compiler/rustc_passes/src/reachable.rs
+++ b/compiler/rustc_passes/src/reachable.rs
@@ -116,6 +116,17 @@ impl<'tcx> Visitor<'tcx> for ReachableContext<'tcx> {
 
         intravisit::walk_expr(self, expr)
     }
+
+    fn visit_inline_asm(&mut self, asm: &'tcx hir::InlineAsm<'tcx>, id: hir::HirId) {
+        for (op, _) in asm.operands {
+            if let hir::InlineAsmOperand::SymStatic { def_id, .. } = op {
+                if let Some(def_id) = def_id.as_local() {
+                    self.reachable_symbols.insert(def_id);
+                }
+            }
+        }
+        intravisit::walk_inline_asm(self, asm, id);
+    }
 }
 
 impl<'tcx> ReachableContext<'tcx> {
diff --git a/compiler/rustc_passes/src/stability.rs b/compiler/rustc_passes/src/stability.rs
index 88bd655d8d3..da715523474 100644
--- a/compiler/rustc_passes/src/stability.rs
+++ b/compiler/rustc_passes/src/stability.rs
@@ -787,7 +787,7 @@ impl<'tcx> Visitor<'tcx> for Checker<'tcx> {
         intravisit::walk_item(self, item);
     }
 
-    fn visit_path(&mut self, path: &'tcx hir::Path<'tcx>, id: hir::HirId) {
+    fn visit_path(&mut self, path: &hir::Path<'tcx>, id: hir::HirId) {
         if let Some(def_id) = path.res.opt_def_id() {
             let method_span = path.segments.last().map(|s| s.ident.span);
             let item_is_allowed = self.tcx.check_stability_allow_unstable(
@@ -880,7 +880,7 @@ struct CheckTraitImplStable<'tcx> {
 }
 
 impl<'tcx> Visitor<'tcx> for CheckTraitImplStable<'tcx> {
-    fn visit_path(&mut self, path: &'tcx hir::Path<'tcx>, _id: hir::HirId) {
+    fn visit_path(&mut self, path: &hir::Path<'tcx>, _id: hir::HirId) {
         if let Some(def_id) = path.res.opt_def_id() {
             if let Some(stab) = self.tcx.lookup_stability(def_id) {
                 self.fully_stable &= stab.level.is_stable();
diff --git a/compiler/rustc_passes/src/upvars.rs b/compiler/rustc_passes/src/upvars.rs
index 9e41efce9ce..605cf0a93b8 100644
--- a/compiler/rustc_passes/src/upvars.rs
+++ b/compiler/rustc_passes/src/upvars.rs
@@ -66,7 +66,7 @@ impl CaptureCollector<'_, '_> {
 }
 
 impl<'tcx> Visitor<'tcx> for CaptureCollector<'_, 'tcx> {
-    fn visit_path(&mut self, path: &'tcx hir::Path<'tcx>, _: hir::HirId) {
+    fn visit_path(&mut self, path: &hir::Path<'tcx>, _: hir::HirId) {
         if let Res::Local(var_id) = path.res {
             self.visit_local_use(var_id, path.span);
         }
diff --git a/compiler/rustc_query_impl/src/on_disk_cache.rs b/compiler/rustc_query_impl/src/on_disk_cache.rs
index c61d2a9c2d0..ac9653b9007 100644
--- a/compiler/rustc_query_impl/src/on_disk_cache.rs
+++ b/compiler/rustc_query_impl/src/on_disk_cache.rs
@@ -818,6 +818,12 @@ impl<'a, 'tcx> Decodable<CacheDecoder<'a, 'tcx>> for &'tcx [(ty::Predicate<'tcx>
     }
 }
 
+impl<'a, 'tcx> Decodable<CacheDecoder<'a, 'tcx>> for &'tcx [(ty::Clause<'tcx>, Span)] {
+    fn decode(d: &mut CacheDecoder<'a, 'tcx>) -> Self {
+        RefDecodable::decode(d)
+    }
+}
+
 impl<'a, 'tcx> Decodable<CacheDecoder<'a, 'tcx>> for &'tcx [rustc_ast::InlineAsmTemplatePiece] {
     fn decode(d: &mut CacheDecoder<'a, 'tcx>) -> Self {
         RefDecodable::decode(d)
diff --git a/compiler/rustc_query_impl/src/plumbing.rs b/compiler/rustc_query_impl/src/plumbing.rs
index 99edaa04162..8d5d84c5db4 100644
--- a/compiler/rustc_query_impl/src/plumbing.rs
+++ b/compiler/rustc_query_impl/src/plumbing.rs
@@ -252,6 +252,18 @@ macro_rules! depth_limit {
     };
 }
 
+macro_rules! feedable {
+    ([]) => {{
+        false
+    }};
+    ([(feedable) $($rest:tt)*]) => {{
+        true
+    }};
+    ([$other:tt $($modifiers:tt)*]) => {
+        feedable!([$($modifiers)*])
+    };
+}
+
 macro_rules! hash_result {
     ([]) => {{
         Some(dep_graph::hash_result)
@@ -309,7 +321,7 @@ pub(crate) fn create_query_frame<
         ty::print::with_forced_impl_filename_line!(do_describe(tcx.tcx, key))
     );
     let description =
-        if tcx.sess.verbose() { format!("{} [{}]", description, name) } else { description };
+        if tcx.sess.verbose() { format!("{} [{:?}]", description, name) } else { description };
     let span = if kind == dep_graph::DepKind::def_span {
         // The `def_span` query is used to calculate `default_span`,
         // so exit to avoid infinite recursion.
@@ -491,6 +503,7 @@ macro_rules! define_queries {
                     anon: is_anon!([$($modifiers)*]),
                     eval_always: is_eval_always!([$($modifiers)*]),
                     depth_limit: depth_limit!([$($modifiers)*]),
+                    feedable: feedable!([$($modifiers)*]),
                     dep_kind: dep_graph::DepKind::$name,
                     hash_result: hash_result!([$($modifiers)*]),
                     handle_cycle_error: handle_cycle_error!([$($modifiers)*]),
diff --git a/compiler/rustc_query_system/src/dep_graph/graph.rs b/compiler/rustc_query_system/src/dep_graph/graph.rs
index e90afc591b5..e44857a0238 100644
--- a/compiler/rustc_query_system/src/dep_graph/graph.rs
+++ b/compiler/rustc_query_system/src/dep_graph/graph.rs
@@ -489,6 +489,91 @@ impl<K: DepKind> DepGraph<K> {
         }
     }
 
+    /// Create a node when we force-feed a value into the query cache.
+    /// This is used to remove cycles during type-checking const generic parameters.
+    ///
+    /// As usual in the query system, we consider the current state of the calling query
+    /// only depends on the list of dependencies up to now.  As a consequence, the value
+    /// that this query gives us can only depend on those dependencies too.  Therefore,
+    /// it is sound to use the current dependency set for the created node.
+    ///
+    /// During replay, the order of the nodes is relevant in the dependency graph.
+    /// So the unchanged replay will mark the caller query before trying to mark this one.
+    /// If there is a change to report, the caller query will be re-executed before this one.
+    ///
+    /// FIXME: If the code is changed enough for this node to be marked before requiring the
+    /// caller's node, we suppose that those changes will be enough to mark this node red and
+    /// force a recomputation using the "normal" way.
+    pub fn with_feed_task<Ctxt: DepContext<DepKind = K>, A: Debug, R: Debug>(
+        &self,
+        node: DepNode<K>,
+        cx: Ctxt,
+        key: A,
+        result: &R,
+        hash_result: fn(&mut StableHashingContext<'_>, &R) -> Fingerprint,
+    ) -> DepNodeIndex {
+        if let Some(data) = self.data.as_ref() {
+            // The caller query has more dependencies than the node we are creating.  We may
+            // encounter a case where this created node is marked as green, but the caller query is
+            // subsequently marked as red or recomputed.  In this case, we will end up feeding a
+            // value to an existing node.
+            //
+            // For sanity, we still check that the loaded stable hash and the new one match.
+            if let Some(dep_node_index) = self.dep_node_index_of_opt(&node) {
+                let _current_fingerprint =
+                    crate::query::incremental_verify_ich(cx, result, &node, Some(hash_result));
+
+                #[cfg(debug_assertions)]
+                data.current.record_edge(dep_node_index, node, _current_fingerprint);
+
+                return dep_node_index;
+            }
+
+            let mut edges = SmallVec::new();
+            K::read_deps(|task_deps| match task_deps {
+                TaskDepsRef::Allow(deps) => edges.extend(deps.lock().reads.iter().copied()),
+                TaskDepsRef::Ignore | TaskDepsRef::Forbid => {
+                    panic!("Cannot summarize when dependencies are not recorded.")
+                }
+            });
+
+            let hashing_timer = cx.profiler().incr_result_hashing();
+            let current_fingerprint =
+                cx.with_stable_hashing_context(|mut hcx| hash_result(&mut hcx, result));
+
+            let print_status = cfg!(debug_assertions) && cx.sess().opts.unstable_opts.dep_tasks;
+
+            // Intern the new `DepNode` with the dependencies up-to-now.
+            let (dep_node_index, prev_and_color) = data.current.intern_node(
+                cx.profiler(),
+                &data.previous,
+                node,
+                edges,
+                Some(current_fingerprint),
+                print_status,
+            );
+
+            hashing_timer.finish_with_query_invocation_id(dep_node_index.into());
+
+            if let Some((prev_index, color)) = prev_and_color {
+                debug_assert!(
+                    data.colors.get(prev_index).is_none(),
+                    "DepGraph::with_task() - Duplicate DepNodeColor insertion for {key:?}",
+                );
+
+                data.colors.insert(prev_index, color);
+            }
+
+            dep_node_index
+        } else {
+            // Incremental compilation is turned off. We just execute the task
+            // without tracking. We still provide a dep-node index that uniquely
+            // identifies the task so that we have a cheap way of referring to
+            // the query for self-profiling.
+            self.next_virtual_depnode_index()
+        }
+    }
+
     #[inline]
     pub fn dep_node_index_of(&self, dep_node: &DepNode<K>) -> DepNodeIndex {
         self.dep_node_index_of_opt(dep_node).unwrap()
@@ -779,26 +864,26 @@ impl<K: DepKind> DepGraph<K> {
         }
     }
 
-    // Returns true if the given node has been marked as red during the
-    // current compilation session. Used in various assertions
+    /// Returns true if the given node has been marked as red during the
+    /// current compilation session. Used in various assertions
     pub fn is_red(&self, dep_node: &DepNode<K>) -> bool {
         self.node_color(dep_node) == Some(DepNodeColor::Red)
     }
 
-    // Returns true if the given node has been marked as green during the
-    // current compilation session. Used in various assertions
+    /// Returns true if the given node has been marked as green during the
+    /// current compilation session. Used in various assertions
     pub fn is_green(&self, dep_node: &DepNode<K>) -> bool {
         self.node_color(dep_node).map_or(false, |c| c.is_green())
     }
 
-    // This method loads all on-disk cacheable query results into memory, so
-    // they can be written out to the new cache file again. Most query results
-    // will already be in memory but in the case where we marked something as
-    // green but then did not need the value, that value will never have been
-    // loaded from disk.
-    //
-    // This method will only load queries that will end up in the disk cache.
-    // Other queries will not be executed.
+    /// This method loads all on-disk cacheable query results into memory, so
+    /// they can be written out to the new cache file again. Most query results
+    /// will already be in memory but in the case where we marked something as
+    /// green but then did not need the value, that value will never have been
+    /// loaded from disk.
+    ///
+    /// This method will only load queries that will end up in the disk cache.
+    /// Other queries will not be executed.
     pub fn exec_cache_promotions<Tcx: DepContext<DepKind = K>>(&self, tcx: Tcx) {
         let _prof_timer = tcx.profiler().generic_activity("incr_comp_query_cache_promotion");
 
@@ -916,6 +1001,11 @@ pub(super) struct CurrentDepGraph<K: DepKind> {
     new_node_to_index: Sharded<FxHashMap<DepNode<K>, DepNodeIndex>>,
     prev_index_to_index: Lock<IndexVec<SerializedDepNodeIndex, Option<DepNodeIndex>>>,
 
+    /// This is used to verify that fingerprints do not change between the creation of a node
+    /// and its recomputation.
+    #[cfg(debug_assertions)]
+    fingerprints: Lock<FxHashMap<DepNode<K>, Fingerprint>>,
+
     /// Used to trap when a specific edge is added to the graph.
     /// This is used for debug purposes and is only active with `debug_assertions`.
     #[cfg(debug_assertions)]
@@ -999,6 +1089,8 @@ impl<K: DepKind> CurrentDepGraph<K> {
             anon_id_seed,
             #[cfg(debug_assertions)]
             forbidden_edge,
+            #[cfg(debug_assertions)]
+            fingerprints: Lock::new(Default::default()),
             total_read_count: AtomicU64::new(0),
             total_duplicate_read_count: AtomicU64::new(0),
             node_intern_event_id,
@@ -1006,10 +1098,18 @@ impl<K: DepKind> CurrentDepGraph<K> {
     }
 
     #[cfg(debug_assertions)]
-    fn record_edge(&self, dep_node_index: DepNodeIndex, key: DepNode<K>) {
+    fn record_edge(&self, dep_node_index: DepNodeIndex, key: DepNode<K>, fingerprint: Fingerprint) {
         if let Some(forbidden_edge) = &self.forbidden_edge {
             forbidden_edge.index_to_node.lock().insert(dep_node_index, key);
         }
+        match self.fingerprints.lock().entry(key) {
+            Entry::Vacant(v) => {
+                v.insert(fingerprint);
+            }
+            Entry::Occupied(o) => {
+                assert_eq!(*o.get(), fingerprint, "Unstable fingerprints for {:?}", key);
+            }
+        }
     }
 
     /// Writes the node to the current dep-graph and allocates a `DepNodeIndex` for it.
@@ -1021,17 +1121,21 @@ impl<K: DepKind> CurrentDepGraph<K> {
         edges: EdgesVec,
         current_fingerprint: Fingerprint,
     ) -> DepNodeIndex {
-        match self.new_node_to_index.get_shard_by_value(&key).lock().entry(key) {
+        let dep_node_index = match self.new_node_to_index.get_shard_by_value(&key).lock().entry(key)
+        {
             Entry::Occupied(entry) => *entry.get(),
             Entry::Vacant(entry) => {
                 let dep_node_index =
                     self.encoder.borrow().send(profiler, key, current_fingerprint, edges);
                 entry.insert(dep_node_index);
-                #[cfg(debug_assertions)]
-                self.record_edge(dep_node_index, key);
                 dep_node_index
             }
-        }
+        };
+
+        #[cfg(debug_assertions)]
+        self.record_edge(dep_node_index, key, current_fingerprint);
+
+        dep_node_index
     }
 
     fn intern_node(
@@ -1072,7 +1176,7 @@ impl<K: DepKind> CurrentDepGraph<K> {
                     };
 
                     #[cfg(debug_assertions)]
-                    self.record_edge(dep_node_index, key);
+                    self.record_edge(dep_node_index, key, fingerprint);
                     (dep_node_index, Some((prev_index, DepNodeColor::Green(dep_node_index))))
                 } else {
                     if print_status {
@@ -1094,7 +1198,7 @@ impl<K: DepKind> CurrentDepGraph<K> {
                     };
 
                     #[cfg(debug_assertions)]
-                    self.record_edge(dep_node_index, key);
+                    self.record_edge(dep_node_index, key, fingerprint);
                     (dep_node_index, Some((prev_index, DepNodeColor::Red)))
                 }
             } else {
@@ -1119,7 +1223,7 @@ impl<K: DepKind> CurrentDepGraph<K> {
                 };
 
                 #[cfg(debug_assertions)]
-                self.record_edge(dep_node_index, key);
+                self.record_edge(dep_node_index, key, Fingerprint::ZERO);
                 (dep_node_index, Some((prev_index, DepNodeColor::Red)))
             }
         } else {
@@ -1150,19 +1254,16 @@ impl<K: DepKind> CurrentDepGraph<K> {
             Some(dep_node_index) => dep_node_index,
             None => {
                 let key = prev_graph.index_to_node(prev_index);
-                let dep_node_index = self.encoder.borrow().send(
-                    profiler,
-                    key,
-                    prev_graph.fingerprint_by_index(prev_index),
-                    prev_graph
-                        .edge_targets_from(prev_index)
-                        .iter()
-                        .map(|i| prev_index_to_index[*i].unwrap())
-                        .collect(),
-                );
+                let edges = prev_graph
+                    .edge_targets_from(prev_index)
+                    .iter()
+                    .map(|i| prev_index_to_index[*i].unwrap())
+                    .collect();
+                let fingerprint = prev_graph.fingerprint_by_index(prev_index);
+                let dep_node_index = self.encoder.borrow().send(profiler, key, fingerprint, edges);
                 prev_index_to_index[prev_index] = Some(dep_node_index);
                 #[cfg(debug_assertions)]
-                self.record_edge(dep_node_index, key);
+                self.record_edge(dep_node_index, key, fingerprint);
                 dep_node_index
             }
         }
diff --git a/compiler/rustc_query_system/src/query/caches.rs b/compiler/rustc_query_system/src/query/caches.rs
index cdd43572422..4c4680b5d8e 100644
--- a/compiler/rustc_query_system/src/query/caches.rs
+++ b/compiler/rustc_query_system/src/query/caches.rs
@@ -117,6 +117,8 @@ where
         let mut lock = self.cache.get_shard_by_value(&key).lock();
         #[cfg(not(parallel_compiler))]
         let mut lock = self.cache.lock();
+        // We may be overwriting another value.  This is all right, since the dep-graph
+        // will check that the fingerprint matches.
         lock.insert(key, (value.clone(), index));
         value
     }
@@ -202,6 +204,8 @@ where
         let mut lock = self.cache.get_shard_by_value(&key).lock();
         #[cfg(not(parallel_compiler))]
         let mut lock = self.cache.lock();
+        // We may be overwriting another value.  This is all right, since the dep-graph
+        // will check that the fingerprint matches.
         lock.insert(key, value);
         &value.0
     }
diff --git a/compiler/rustc_query_system/src/query/config.rs b/compiler/rustc_query_system/src/query/config.rs
index f40e174b7e7..7d1b62ab102 100644
--- a/compiler/rustc_query_system/src/query/config.rs
+++ b/compiler/rustc_query_system/src/query/config.rs
@@ -15,8 +15,8 @@ pub trait QueryConfig<Qcx: QueryContext> {
     const NAME: &'static str;
 
     type Key: Eq + Hash + Clone + Debug;
-    type Value;
-    type Stored: Clone;
+    type Value: Debug;
+    type Stored: Debug + Clone + std::borrow::Borrow<Self::Value>;
 
     type Cache: QueryCache<Key = Self::Key, Stored = Self::Stored, Value = Self::Value>;
 
@@ -45,6 +45,7 @@ pub struct QueryVTable<Qcx: QueryContext, K, V> {
     pub dep_kind: Qcx::DepKind,
     pub eval_always: bool,
     pub depth_limit: bool,
+    pub feedable: bool,
 
     pub compute: fn(Qcx::DepContext, K) -> V,
     pub hash_result: Option<fn(&mut StableHashingContext<'_>, &V) -> Fingerprint>,
diff --git a/compiler/rustc_query_system/src/query/plumbing.rs b/compiler/rustc_query_system/src/query/plumbing.rs
index f8d93a27d1c..848fa67e3df 100644
--- a/compiler/rustc_query_system/src/query/plumbing.rs
+++ b/compiler/rustc_query_system/src/query/plumbing.rs
@@ -3,6 +3,7 @@
 //! manage the caches, and so forth.
 
 use crate::dep_graph::{DepContext, DepNode, DepNodeIndex, DepNodeParams};
+use crate::ich::StableHashingContext;
 use crate::query::caches::QueryCache;
 use crate::query::config::QueryVTable;
 use crate::query::job::{report_cycle, QueryInfo, QueryJob, QueryJobId, QueryJobInfo};
@@ -19,6 +20,7 @@ use rustc_data_structures::sync::Lock;
 use rustc_errors::{DiagnosticBuilder, ErrorGuaranteed, FatalError};
 use rustc_session::Session;
 use rustc_span::{Span, DUMMY_SP};
+use std::borrow::Borrow;
 use std::cell::Cell;
 use std::collections::hash_map::Entry;
 use std::fmt::Debug;
@@ -369,11 +371,26 @@ where
     C: QueryCache,
     C::Key: Clone + DepNodeParams<Qcx::DepContext>,
     C::Value: Value<Qcx::DepContext>,
+    C::Stored: Debug + std::borrow::Borrow<C::Value>,
     Qcx: QueryContext,
 {
     match JobOwner::<'_, C::Key>::try_start(&qcx, state, span, key.clone()) {
         TryGetJob::NotYetStarted(job) => {
-            let (result, dep_node_index) = execute_job(qcx, key, dep_node, query, job.id);
+            let (result, dep_node_index) = execute_job(qcx, key.clone(), dep_node, query, job.id);
+            if query.feedable {
+                // We may have put a value inside the cache from inside the execution.
+                // Verify that it has the same hash as what we have now, to ensure consistency.
+                let _ = cache.lookup(&key, |cached_result, _| {
+                    let hasher = query.hash_result.expect("feedable forbids no_hash");
+                    let old_hash = qcx.dep_context().with_stable_hashing_context(|mut hcx| hasher(&mut hcx, cached_result.borrow()));
+                    let new_hash = qcx.dep_context().with_stable_hashing_context(|mut hcx| hasher(&mut hcx, &result));
+                    debug_assert_eq!(
+                        old_hash, new_hash,
+                        "Computed query value for {:?}({:?}) is inconsistent with fed value,\ncomputed={:#?}\nfed={:#?}",
+                        query.dep_kind, key, result, cached_result,
+                    );
+                });
+            }
             let result = job.complete(cache, result, dep_node_index);
             (result, Some(dep_node_index))
         }
@@ -525,7 +542,7 @@ where
             if std::intrinsics::unlikely(
                 try_verify || qcx.dep_context().sess().opts.unstable_opts.incremental_verify_ich,
             ) {
-                incremental_verify_ich(*qcx.dep_context(), &result, dep_node, query);
+                incremental_verify_ich(*qcx.dep_context(), &result, dep_node, query.hash_result);
             }
 
             return Some((result, dep_node_index));
@@ -558,39 +575,42 @@ where
     //
     // See issue #82920 for an example of a miscompilation that would get turned into
     // an ICE by this check
-    incremental_verify_ich(*qcx.dep_context(), &result, dep_node, query);
+    incremental_verify_ich(*qcx.dep_context(), &result, dep_node, query.hash_result);
 
     Some((result, dep_node_index))
 }
 
-#[instrument(skip(qcx, result, query), level = "debug")]
-fn incremental_verify_ich<Qcx, K, V: Debug>(
-    qcx: Qcx::DepContext,
+#[instrument(skip(tcx, result, hash_result), level = "debug")]
+pub(crate) fn incremental_verify_ich<Tcx, V: Debug>(
+    tcx: Tcx,
     result: &V,
-    dep_node: &DepNode<Qcx::DepKind>,
-    query: &QueryVTable<Qcx, K, V>,
-) where
-    Qcx: QueryContext,
+    dep_node: &DepNode<Tcx::DepKind>,
+    hash_result: Option<fn(&mut StableHashingContext<'_>, &V) -> Fingerprint>,
+) -> Fingerprint
+where
+    Tcx: DepContext,
 {
     assert!(
-        qcx.dep_graph().is_green(dep_node),
+        tcx.dep_graph().is_green(dep_node),
         "fingerprint for green query instance not loaded from cache: {:?}",
         dep_node,
     );
 
-    let new_hash = query.hash_result.map_or(Fingerprint::ZERO, |f| {
-        qcx.with_stable_hashing_context(|mut hcx| f(&mut hcx, result))
+    let new_hash = hash_result.map_or(Fingerprint::ZERO, |f| {
+        tcx.with_stable_hashing_context(|mut hcx| f(&mut hcx, result))
     });
 
-    let old_hash = qcx.dep_graph().prev_fingerprint_of(dep_node);
+    let old_hash = tcx.dep_graph().prev_fingerprint_of(dep_node);
 
     if Some(new_hash) != old_hash {
         incremental_verify_ich_failed(
-            qcx.sess(),
+            tcx.sess(),
             DebugArg::from(&dep_node),
             DebugArg::from(&result),
         );
     }
+
+    new_hash
 }
 
 // This DebugArg business is largely a mirror of std::fmt::ArgumentV1, which is
diff --git a/compiler/rustc_resolve/src/build_reduced_graph.rs b/compiler/rustc_resolve/src/build_reduced_graph.rs
index 91ac442431d..9c90d67aadf 100644
--- a/compiler/rustc_resolve/src/build_reduced_graph.rs
+++ b/compiler/rustc_resolve/src/build_reduced_graph.rs
@@ -445,19 +445,13 @@ impl<'a, 'b> BuildReducedGraphVisitor<'a, 'b> {
             prefix.is_empty() || prefix.len() == 1 && prefix[0].ident.name == kw::PathRoot
         };
         match use_tree.kind {
-            ast::UseTreeKind::Simple(rename, id1, id2) => {
+            ast::UseTreeKind::Simple(rename) => {
                 let mut ident = use_tree.ident();
                 let mut module_path = prefix;
                 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 id1 != ast::DUMMY_NODE_ID {
-                    self.r.visibilities.insert(self.r.local_def_id(id1), vis);
-                }
-                if id2 != ast::DUMMY_NODE_ID {
-                    self.r.visibilities.insert(self.r.local_def_id(id2), vis);
-                }
 
                 if nested {
                     // Correctly handle `self`
@@ -565,7 +559,6 @@ impl<'a, 'b> BuildReducedGraphVisitor<'a, 'b> {
                     type_ns_only,
                     nested,
                     id,
-                    additional_ids: (id1, id2),
                 };
 
                 self.add_import(module_path, kind, use_tree.span, item, root_span, item.id, vis);
@@ -621,11 +614,7 @@ impl<'a, 'b> BuildReducedGraphVisitor<'a, 'b> {
                     let new_span = prefix[prefix.len() - 1].ident.span;
                     let tree = ast::UseTree {
                         prefix: ast::Path::from_ident(Ident::new(kw::SelfLower, new_span)),
-                        kind: ast::UseTreeKind::Simple(
-                            Some(Ident::new(kw::Underscore, new_span)),
-                            ast::DUMMY_NODE_ID,
-                            ast::DUMMY_NODE_ID,
-                        ),
+                        kind: ast::UseTreeKind::Simple(Some(Ident::new(kw::Underscore, new_span))),
                         span: use_tree.span,
                     };
                     self.build_reduced_graph_for_use_tree(
diff --git a/compiler/rustc_resolve/src/def_collector.rs b/compiler/rustc_resolve/src/def_collector.rs
index bf2428e1731..2764a6c28a5 100644
--- a/compiler/rustc_resolve/src/def_collector.rs
+++ b/compiler/rustc_resolve/src/def_collector.rs
@@ -158,14 +158,6 @@ impl<'a, 'b> visit::Visitor<'a> for DefCollector<'a, 'b> {
 
     fn visit_use_tree(&mut self, use_tree: &'a UseTree, id: NodeId, _nested: bool) {
         self.create_def(id, DefPathData::Use, use_tree.span);
-        match use_tree.kind {
-            UseTreeKind::Simple(_, id1, id2) => {
-                self.create_def(id1, DefPathData::Use, use_tree.prefix.span);
-                self.create_def(id2, DefPathData::Use, use_tree.prefix.span);
-            }
-            UseTreeKind::Glob => (),
-            UseTreeKind::Nested(..) => {}
-        }
         visit::walk_use_tree(self, use_tree, id);
     }
 
diff --git a/compiler/rustc_resolve/src/effective_visibilities.rs b/compiler/rustc_resolve/src/effective_visibilities.rs
index 3aa8d52db03..85399385d1f 100644
--- a/compiler/rustc_resolve/src/effective_visibilities.rs
+++ b/compiler/rustc_resolve/src/effective_visibilities.rs
@@ -1,4 +1,4 @@
-use crate::{ImportKind, NameBinding, NameBindingKind, Resolver, ResolverTree};
+use crate::{NameBinding, NameBindingKind, Resolver, ResolverTree};
 use rustc_ast::ast;
 use rustc_ast::visit;
 use rustc_ast::visit::Visitor;
@@ -104,28 +104,11 @@ impl<'r, 'a> EffectiveVisibilitiesVisitor<'r, 'a> {
         for (binding, eff_vis) in visitor.import_effective_visibilities.iter() {
             let NameBindingKind::Import { import, .. } = binding.kind else { unreachable!() };
             if let Some(node_id) = import.id() {
-                let mut update = |node_id| {
-                    r.effective_visibilities.update_eff_vis(
-                        r.local_def_id(node_id),
-                        eff_vis,
-                        ResolverTree(&r.definitions, &r.crate_loader),
-                    )
-                };
-                update(node_id);
-                if let ImportKind::Single { additional_ids: (id1, id2), .. } = import.kind {
-                    // In theory all the single import IDs have individual visibilities and
-                    // effective visibilities, but in practice these IDs go straight to HIR
-                    // where all their few uses assume that their (effective) visibility
-                    // applies to the whole syntactic `use` item. So they all get the same
-                    // value which is the maximum of all bindings. Maybe HIR for imports
-                    // shouldn't use three IDs at all.
-                    if id1 != ast::DUMMY_NODE_ID {
-                        update(id1);
-                    }
-                    if id2 != ast::DUMMY_NODE_ID {
-                        update(id2);
-                    }
-                }
+                r.effective_visibilities.update_eff_vis(
+                    r.local_def_id(node_id),
+                    eff_vis,
+                    ResolverTree(&r.definitions, &r.crate_loader),
+                )
             }
         }
 
diff --git a/compiler/rustc_resolve/src/imports.rs b/compiler/rustc_resolve/src/imports.rs
index 4c899a5ff2d..e6f4d7fcfcf 100644
--- a/compiler/rustc_resolve/src/imports.rs
+++ b/compiler/rustc_resolve/src/imports.rs
@@ -56,9 +56,6 @@ pub enum ImportKind<'a> {
         /// If this is the import for `foo::bar::a`, we would have the ID of the `UseTree`
         /// for `a` in this field.
         id: NodeId,
-        /// Additional `NodeId`s allocated to a `ast::UseTree` for automatically generated `use` statement
-        /// (eg. implicit struct constructors)
-        additional_ids: (NodeId, NodeId),
     },
     Glob {
         is_prelude: bool,
@@ -88,7 +85,6 @@ impl<'a> std::fmt::Debug for ImportKind<'a> {
                 ref type_ns_only,
                 ref nested,
                 ref id,
-                ref additional_ids,
                 // Ignore the following to avoid an infinite loop while printing.
                 source_bindings: _,
                 target_bindings: _,
@@ -99,7 +95,6 @@ impl<'a> std::fmt::Debug for ImportKind<'a> {
                 .field("type_ns_only", type_ns_only)
                 .field("nested", nested)
                 .field("id", id)
-                .field("additional_ids", additional_ids)
                 .finish_non_exhaustive(),
             Glob { ref is_prelude, ref max_vis, ref id } => f
                 .debug_struct("Glob")
@@ -196,7 +191,7 @@ pub(crate) struct NameResolution<'a> {
 }
 
 impl<'a> NameResolution<'a> {
-    // Returns the binding for the name if it is known or None if it not known.
+    /// Returns the binding for the name if it is known or None if it not known.
     pub(crate) fn binding(&self) -> Option<&'a NameBinding<'a>> {
         self.binding.and_then(|binding| {
             if !binding.is_glob_import() || self.single_imports.is_empty() {
@@ -228,8 +223,8 @@ fn pub_use_of_private_extern_crate_hack(import: &Import<'_>, binding: &NameBindi
 }
 
 impl<'a> Resolver<'a> {
-    // Given a binding and an import that resolves to it,
-    // return the corresponding binding defined by the import.
+    /// Given a binding and an import that resolves to it,
+    /// return the corresponding binding defined by the import.
     pub(crate) fn import(
         &self,
         binding: &'a NameBinding<'a>,
@@ -261,7 +256,7 @@ impl<'a> Resolver<'a> {
         })
     }
 
-    // Define the name or return the existing binding if there is a collision.
+    /// Define the name or return the existing binding if there is a collision.
     pub(crate) fn try_define(
         &mut self,
         module: Module<'a>,
diff --git a/compiler/rustc_resolve/src/late/diagnostics.rs b/compiler/rustc_resolve/src/late/diagnostics.rs
index 9c95adc628b..df59a350ea7 100644
--- a/compiler/rustc_resolve/src/late/diagnostics.rs
+++ b/compiler/rustc_resolve/src/late/diagnostics.rs
@@ -282,6 +282,14 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> {
                             "you may want to use a bool value instead",
                             format!("{}", item_typo),
                         ))
+                    // FIXME(vincenzopalazzo): make the check smarter,
+                    // and maybe expand with levenshtein distance checks
+                    } else if item_str.as_str() == "printf" {
+                        Some((
+                            item_span,
+                            "you may have meant to use the `print` macro",
+                            "print!".to_owned(),
+                        ))
                     } else {
                         suggestion
                     };
diff --git a/compiler/rustc_resolve/src/lib.rs b/compiler/rustc_resolve/src/lib.rs
index 82214d4c3c4..4ef89cfb255 100644
--- a/compiler/rustc_resolve/src/lib.rs
+++ b/compiler/rustc_resolve/src/lib.rs
@@ -1989,7 +1989,7 @@ impl<'a> Resolver<'a> {
                     .find(|a| a.has_name(sym::rustc_legacy_const_generics))?;
                 let mut ret = Vec::new();
                 for meta in attr.meta_item_list()? {
-                    match meta.literal()?.kind {
+                    match meta.lit()?.kind {
                         LitKind::Int(a, _) => ret.push(a as usize),
                         _ => panic!("invalid arg index"),
                     }
diff --git a/compiler/rustc_save_analysis/src/dump_visitor.rs b/compiler/rustc_save_analysis/src/dump_visitor.rs
index fae20c2ba5f..b4528853825 100644
--- a/compiler/rustc_save_analysis/src/dump_visitor.rs
+++ b/compiler/rustc_save_analysis/src/dump_visitor.rs
@@ -185,13 +185,13 @@ impl<'tcx> DumpVisitor<'tcx> {
         }
     }
 
-    fn write_sub_paths(&mut self, path: &'tcx hir::Path<'tcx>) {
+    fn write_sub_paths<R>(&mut self, path: &'tcx hir::Path<'tcx, R>) {
         self.write_segments(path.segments)
     }
 
     // As write_sub_paths, but does not process the last ident in the path (assuming it
     // will be processed elsewhere). See note on write_sub_paths about global.
-    fn write_sub_paths_truncated(&mut self, path: &'tcx hir::Path<'tcx>) {
+    fn write_sub_paths_truncated<R>(&mut self, path: &'tcx hir::Path<'tcx, R>) {
         if let [segments @ .., _] = path.segments {
             self.write_segments(segments)
         }
diff --git a/compiler/rustc_save_analysis/src/lib.rs b/compiler/rustc_save_analysis/src/lib.rs
index ffe8edf69b7..f05eb2b7432 100644
--- a/compiler/rustc_save_analysis/src/lib.rs
+++ b/compiler/rustc_save_analysis/src/lib.rs
@@ -94,7 +94,7 @@ impl<'tcx> SaveContext<'tcx> {
         }
     }
 
-    // Returns path to the compilation output (e.g., libfoo-12345678.rmeta)
+    /// Returns path to the compilation output (e.g., libfoo-12345678.rmeta)
     pub fn compilation_output(&self, crate_name: &str) -> PathBuf {
         let sess = &self.tcx.sess;
         // Save-analysis is emitted per whole session, not per each crate type
@@ -112,7 +112,7 @@ impl<'tcx> SaveContext<'tcx> {
         }
     }
 
-    // List external crates used by the current crate.
+    /// List external crates used by the current crate.
     pub fn get_external_crates(&self) -> Vec<ExternalCrateData> {
         let mut result = Vec::with_capacity(self.tcx.crates(()).len());
 
@@ -594,7 +594,9 @@ impl<'tcx> SaveContext<'tcx> {
         match self.tcx.hir().get(hir_id) {
             Node::TraitRef(tr) => tr.path.res,
 
-            Node::Item(&hir::Item { kind: hir::ItemKind::Use(path, _), .. }) => path.res,
+            Node::Item(&hir::Item { kind: hir::ItemKind::Use(path, _), .. }) => {
+                path.res.get(0).copied().unwrap_or(Res::Err)
+            }
             Node::PathSegment(seg) => {
                 if seg.res != Res::Err {
                     seg.res
diff --git a/compiler/rustc_serialize/src/leb128.rs b/compiler/rustc_serialize/src/leb128.rs
index 08b3c054200..7dad9aa01fa 100644
--- a/compiler/rustc_serialize/src/leb128.rs
+++ b/compiler/rustc_serialize/src/leb128.rs
@@ -1,22 +1,19 @@
-#![macro_use]
-
-macro_rules! max_leb128_len {
-    ($int_ty:ty) => {
-        // The longest LEB128 encoding for an integer uses 7 bits per byte.
-        (std::mem::size_of::<$int_ty>() * 8 + 6) / 7
-    };
+/// Returns the length of the longest LEB128 encoding for `T`, assuming `T` is an integer type
+pub const fn max_leb128_len<T>() -> usize {
+    // The longest LEB128 encoding for an integer uses 7 bits per byte.
+    (std::mem::size_of::<T>() * 8 + 6) / 7
 }
 
-// Returns the longest LEB128 encoding of all supported integer types.
-pub const fn max_leb128_len() -> usize {
-    max_leb128_len!(u128)
+/// Returns the length of the longest LEB128 encoding of all supported integer types.
+pub const fn largest_max_leb128_len() -> usize {
+    max_leb128_len::<u128>()
 }
 
 macro_rules! impl_write_unsigned_leb128 {
     ($fn_name:ident, $int_ty:ty) => {
         #[inline]
         pub fn $fn_name(
-            out: &mut [::std::mem::MaybeUninit<u8>; max_leb128_len!($int_ty)],
+            out: &mut [::std::mem::MaybeUninit<u8>; max_leb128_len::<$int_ty>()],
             mut value: $int_ty,
         ) -> &[u8] {
             let mut i = 0;
@@ -90,7 +87,7 @@ macro_rules! impl_write_signed_leb128 {
     ($fn_name:ident, $int_ty:ty) => {
         #[inline]
         pub fn $fn_name(
-            out: &mut [::std::mem::MaybeUninit<u8>; max_leb128_len!($int_ty)],
+            out: &mut [::std::mem::MaybeUninit<u8>; max_leb128_len::<$int_ty>()],
             mut value: $int_ty,
         ) -> &[u8] {
             let mut i = 0;
diff --git a/compiler/rustc_serialize/src/opaque.rs b/compiler/rustc_serialize/src/opaque.rs
index f35cc08f4fb..0afeb86fceb 100644
--- a/compiler/rustc_serialize/src/opaque.rs
+++ b/compiler/rustc_serialize/src/opaque.rs
@@ -1,4 +1,4 @@
-use crate::leb128::{self, max_leb128_len};
+use crate::leb128::{self, largest_max_leb128_len};
 use crate::serialize::{Decodable, Decoder, Encodable, Encoder};
 use std::convert::TryInto;
 use std::fs::File;
@@ -32,7 +32,7 @@ impl MemEncoder {
 
 macro_rules! write_leb128 {
     ($enc:expr, $value:expr, $int_ty:ty, $fun:ident) => {{
-        const MAX_ENCODED_LEN: usize = max_leb128_len!($int_ty);
+        const MAX_ENCODED_LEN: usize = $crate::leb128::max_leb128_len::<$int_ty>();
         let old_len = $enc.data.len();
 
         if MAX_ENCODED_LEN > $enc.data.capacity() - old_len {
@@ -155,19 +155,19 @@ impl Encoder for MemEncoder {
 
 pub type FileEncodeResult = Result<usize, io::Error>;
 
-// `FileEncoder` encodes data to file via fixed-size buffer.
-//
-// When encoding large amounts of data to a file, using `FileEncoder` may be
-// preferred over using `MemEncoder` to encode to a `Vec`, and then writing the
-// `Vec` to file, as the latter uses as much memory as there is encoded data,
-// while the former uses the fixed amount of memory allocated to the buffer.
-// `FileEncoder` also has the advantage of not needing to reallocate as data
-// is appended to it, but the disadvantage of requiring more error handling,
-// which has some runtime overhead.
+/// `FileEncoder` encodes data to file via fixed-size buffer.
+///
+/// When encoding large amounts of data to a file, using `FileEncoder` may be
+/// preferred over using `MemEncoder` to encode to a `Vec`, and then writing the
+/// `Vec` to file, as the latter uses as much memory as there is encoded data,
+/// while the former uses the fixed amount of memory allocated to the buffer.
+/// `FileEncoder` also has the advantage of not needing to reallocate as data
+/// is appended to it, but the disadvantage of requiring more error handling,
+/// which has some runtime overhead.
 pub struct FileEncoder {
-    // The input buffer. For adequate performance, we need more control over
-    // buffering than `BufWriter` offers. If `BufWriter` ever offers a raw
-    // buffer access API, we can use it, and remove `buf` and `buffered`.
+    /// The input buffer. For adequate performance, we need more control over
+    /// buffering than `BufWriter` offers. If `BufWriter` ever offers a raw
+    /// buffer access API, we can use it, and remove `buf` and `buffered`.
     buf: Box<[MaybeUninit<u8>]>,
     buffered: usize,
     flushed: usize,
@@ -186,12 +186,12 @@ impl FileEncoder {
     pub fn with_capacity<P: AsRef<Path>>(path: P, capacity: usize) -> io::Result<Self> {
         // Require capacity at least as large as the largest LEB128 encoding
         // here, so that we don't have to check or handle this on every write.
-        assert!(capacity >= max_leb128_len());
+        assert!(capacity >= largest_max_leb128_len());
 
         // Require capacity small enough such that some capacity checks can be
         // done using guaranteed non-overflowing add rather than sub, which
         // shaves an instruction off those code paths (on x86 at least).
-        assert!(capacity <= usize::MAX - max_leb128_len());
+        assert!(capacity <= usize::MAX - largest_max_leb128_len());
 
         // Create the file for reading and writing, because some encoders do both
         // (e.g. the metadata encoder when -Zmeta-stats is enabled)
@@ -411,7 +411,7 @@ impl Drop for FileEncoder {
 
 macro_rules! file_encoder_write_leb128 {
     ($enc:expr, $value:expr, $int_ty:ty, $fun:ident) => {{
-        const MAX_ENCODED_LEN: usize = max_leb128_len!($int_ty);
+        const MAX_ENCODED_LEN: usize = $crate::leb128::max_leb128_len::<$int_ty>();
 
         // We ensure this during `FileEncoder` construction.
         debug_assert!($enc.capacity() >= MAX_ENCODED_LEN);
@@ -711,7 +711,7 @@ impl<'a> Decodable<MemDecoder<'a>> for Vec<u8> {
     }
 }
 
-// An integer that will always encode to 8 bytes.
+/// An integer that will always encode to 8 bytes.
 pub struct IntEncodedWithFixedSize(pub u64);
 
 impl IntEncodedWithFixedSize {
diff --git a/compiler/rustc_session/src/config.rs b/compiler/rustc_session/src/config.rs
index 3b1b33aa095..927810351e9 100644
--- a/compiler/rustc_session/src/config.rs
+++ b/compiler/rustc_session/src/config.rs
@@ -2029,10 +2029,7 @@ fn parse_native_lib_modifiers(
                 "linking modifier `bundle` is only compatible with `static` linking kind",
             ),
 
-            ("verbatim", _) => {
-                report_unstable_modifier();
-                assign_modifier(&mut verbatim)
-            }
+            ("verbatim", _) => assign_modifier(&mut verbatim),
 
             ("whole-archive", NativeLibKind::Static { whole_archive, .. }) => {
                 assign_modifier(whole_archive)
diff --git a/compiler/rustc_session/src/errors.rs b/compiler/rustc_session/src/errors.rs
index 2f7055e3cc5..9aa8a06c6d3 100644
--- a/compiler/rustc_session/src/errors.rs
+++ b/compiler/rustc_session/src/errors.rs
@@ -197,12 +197,12 @@ pub enum UnleashedFeatureHelp {
 
 #[derive(Diagnostic)]
 #[diag(session_invalid_literal_suffix)]
-pub(crate) struct InvalidLiteralSuffix {
+pub(crate) struct InvalidLiteralSuffix<'a> {
     #[primary_span]
     #[label]
     pub span: Span,
     // FIXME(#100717)
-    pub kind: String,
+    pub kind: &'a str,
     pub suffix: Symbol,
 }
 
@@ -311,11 +311,7 @@ pub fn report_lit_error(sess: &ParseSess, err: LitError, lit: token::Lit, span:
         LitError::LexerError => {}
         LitError::InvalidSuffix => {
             if let Some(suffix) = suffix {
-                sess.emit_err(InvalidLiteralSuffix {
-                    span,
-                    kind: format!("{}", kind.descr()),
-                    suffix,
-                });
+                sess.emit_err(InvalidLiteralSuffix { span, kind: kind.descr(), suffix });
             }
         }
         LitError::InvalidIntSuffix => {
diff --git a/compiler/rustc_span/src/analyze_source_file.rs b/compiler/rustc_span/src/analyze_source_file.rs
index 5987fb2a198..47aa4dfba42 100644
--- a/compiler/rustc_span/src/analyze_source_file.rs
+++ b/compiler/rustc_span/src/analyze_source_file.rs
@@ -41,7 +41,7 @@ pub fn analyze_source_file(
 }
 
 cfg_if::cfg_if! {
-    if #[cfg(all(any(target_arch = "x86", target_arch = "x86_64")))] {
+    if #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] {
         fn analyze_source_file_dispatch(src: &str,
                                     source_file_start_pos: BytePos,
                                     lines: &mut Vec<BytePos>,
diff --git a/compiler/rustc_span/src/lib.rs b/compiler/rustc_span/src/lib.rs
index 7ccfa600ec3..1065cd384a9 100644
--- a/compiler/rustc_span/src/lib.rs
+++ b/compiler/rustc_span/src/lib.rs
@@ -78,10 +78,10 @@ use sha2::Sha256;
 #[cfg(test)]
 mod tests;
 
-// Per-session global variables: this struct is stored in thread-local storage
-// in such a way that it is accessible without any kind of handle to all
-// threads within the compilation session, but is not accessible outside the
-// session.
+/// Per-session global variables: this struct is stored in thread-local storage
+/// in such a way that it is accessible without any kind of handle to all
+/// threads within the compilation session, but is not accessible outside the
+/// session.
 pub struct SessionGlobals {
     symbol_interner: symbol::Interner,
     span_interner: Lock<span_encoding::SpanInterner>,
@@ -359,8 +359,8 @@ impl FileName {
         FileNameDisplay { inner: self, display_pref: FileNameDisplayPreference::Remapped }
     }
 
-    // This may include transient local filesystem information.
-    // Must not be embedded in build outputs.
+    /// This may include transient local filesystem information.
+    /// Must not be embedded in build outputs.
     pub fn prefer_local(&self) -> FileNameDisplay<'_> {
         FileNameDisplay { inner: self, display_pref: FileNameDisplayPreference::Local }
     }
@@ -751,7 +751,7 @@ impl Span {
 
     /// Checks if a span is "internal" to a macro in which `unsafe`
     /// can be used without triggering the `unsafe_code` lint.
-    //  (that is, a macro marked with `#[allow_internal_unsafe]`).
+    /// (that is, a macro marked with `#[allow_internal_unsafe]`).
     pub fn allows_unsafe(self) -> bool {
         self.ctxt().outer_expn_data().allow_internal_unsafe
     }
diff --git a/compiler/rustc_span/src/source_map.rs b/compiler/rustc_span/src/source_map.rs
index e8d129d733c..2ae57d9e56d 100644
--- a/compiler/rustc_span/src/source_map.rs
+++ b/compiler/rustc_span/src/source_map.rs
@@ -130,14 +130,14 @@ impl FileLoader for RealFileLoader {
 /// different has no real downsides.
 #[derive(Copy, Clone, PartialEq, Eq, Hash, Encodable, Decodable, Debug)]
 pub struct StableSourceFileId {
-    // A hash of the source file's FileName. This is hash so that it's size
-    // is more predictable than if we included the actual FileName value.
+    /// A hash of the source file's [`FileName`]. This is hash so that it's size
+    /// is more predictable than if we included the actual [`FileName`] value.
     pub file_name_hash: u64,
 
-    // The CrateNum of the crate this source file was originally parsed for.
-    // We cannot include this information in the hash because at the time
-    // of hashing we don't have the context to map from the CrateNum's numeric
-    // value to a StableCrateId.
+    /// The [`CrateNum`] of the crate this source file was originally parsed for.
+    /// We cannot include this information in the hash because at the time
+    /// of hashing we don't have the context to map from the [`CrateNum`]'s numeric
+    /// value to a `StableCrateId`.
     pub cnum: CrateNum,
 }
 
@@ -402,7 +402,7 @@ impl SourceMap {
         source_file
     }
 
-    // If there is a doctest offset, applies it to the line.
+    /// If there is a doctest offset, applies it to the line.
     pub fn doctest_offset_line(&self, file: &FileName, orig: usize) -> usize {
         match file {
             FileName::DocTest(_, offset) => {
@@ -429,7 +429,7 @@ impl SourceMap {
         Loc { file: sf, line, col, col_display }
     }
 
-    // If the corresponding `SourceFile` is empty, does not return a line number.
+    /// If the corresponding `SourceFile` is empty, does not return a line number.
     pub fn lookup_line(&self, pos: BytePos) -> Result<SourceFileAndLine, Lrc<SourceFile>> {
         let f = self.lookup_source_file(pos);
 
@@ -1053,9 +1053,9 @@ impl SourceMap {
         SourceFileAndBytePos { sf, pos: offset }
     }
 
-    // Returns the index of the `SourceFile` (in `self.files`) that contains `pos`.
-    // This index is guaranteed to be valid for the lifetime of this `SourceMap`,
-    // since `source_files` is a `MonotonicVec`
+    /// Returns the index of the [`SourceFile`] (in `self.files`) that contains `pos`.
+    /// This index is guaranteed to be valid for the lifetime of this `SourceMap`,
+    /// since `source_files` is a `MonotonicVec`
     pub fn lookup_source_file_idx(&self, pos: BytePos) -> usize {
         self.files
             .borrow()
diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs
index aba301dce10..663cf65d1a5 100644
--- a/compiler/rustc_span/src/symbol.rs
+++ b/compiler/rustc_span/src/symbol.rs
@@ -1488,6 +1488,7 @@ symbols! {
         ty,
         type_alias_enum_variants,
         type_alias_impl_trait,
+        type_ascribe,
         type_ascription,
         type_changing_struct_update,
         type_id,
@@ -2051,8 +2052,8 @@ impl Symbol {
 }
 
 impl Ident {
-    // Returns `true` for reserved identifiers used internally for elided lifetimes,
-    // unnamed method parameters, crate root module, error recovery etc.
+    /// Returns `true` for reserved identifiers used internally for elided lifetimes,
+    /// unnamed method parameters, crate root module, error recovery etc.
     pub fn is_special(self) -> bool {
         self.name.is_special()
     }
diff --git a/compiler/rustc_target/src/spec/mod.rs b/compiler/rustc_target/src/spec/mod.rs
index bd5b10d6aa7..78315afa759 100644
--- a/compiler/rustc_target/src/spec/mod.rs
+++ b/compiler/rustc_target/src/spec/mod.rs
@@ -35,10 +35,7 @@
 //! to the list specified by the target, rather than replace.
 
 use crate::abi::call::Conv;
-use crate::abi::{
-    AbiAndPrefAlign, AddressSpace, Align, Endian, Integer, Size, TargetDataLayout,
-    TargetDataLayoutErrors,
-};
+use crate::abi::{Endian, Integer, Size, TargetDataLayout, TargetDataLayoutErrors};
 use crate::json::{Json, ToJson};
 use crate::spec::abi::{lookup as lookup_abi, Abi};
 use crate::spec::crt_objects::{CrtObjects, LinkSelfContainedDefault};
@@ -1250,8 +1247,8 @@ supported_targets! {
 
     ("mips64-openwrt-linux-musl", mips64_openwrt_linux_musl),
 
-    ("aarch64-unknown-nto-qnx7.1.0", aarch64_unknown_nto_qnx_710),
-    ("x86_64-pc-nto-qnx7.1.0", x86_64_pc_nto_qnx710),
+    ("aarch64-unknown-nto-qnx710", aarch64_unknown_nto_qnx_710),
+    ("x86_64-pc-nto-qnx710", x86_64_pc_nto_qnx710),
 }
 
 /// Cow-Vec-Str: Cow<'static, [Cow<'static, str>]>
@@ -1322,92 +1319,7 @@ pub struct Target {
 
 impl Target {
     pub fn parse_data_layout<'a>(&'a self) -> Result<TargetDataLayout, TargetDataLayoutErrors<'a>> {
-        // Parse an address space index from a string.
-        let parse_address_space = |s: &'a str, cause: &'a str| {
-            s.parse::<u32>().map(AddressSpace).map_err(|err| {
-                TargetDataLayoutErrors::InvalidAddressSpace { addr_space: s, cause, err }
-            })
-        };
-
-        // Parse a bit count from a string.
-        let parse_bits = |s: &'a str, kind: &'a str, cause: &'a str| {
-            s.parse::<u64>().map_err(|err| TargetDataLayoutErrors::InvalidBits {
-                kind,
-                bit: s,
-                cause,
-                err,
-            })
-        };
-
-        // Parse a size string.
-        let size = |s: &'a str, cause: &'a str| parse_bits(s, "size", cause).map(Size::from_bits);
-
-        // Parse an alignment string.
-        let align = |s: &[&'a str], cause: &'a str| {
-            if s.is_empty() {
-                return Err(TargetDataLayoutErrors::MissingAlignment { cause });
-            }
-            let align_from_bits = |bits| {
-                Align::from_bits(bits)
-                    .map_err(|err| TargetDataLayoutErrors::InvalidAlignment { cause, err })
-            };
-            let abi = parse_bits(s[0], "alignment", cause)?;
-            let pref = s.get(1).map_or(Ok(abi), |pref| parse_bits(pref, "alignment", cause))?;
-            Ok(AbiAndPrefAlign { abi: align_from_bits(abi)?, pref: align_from_bits(pref)? })
-        };
-
-        let mut dl = TargetDataLayout::default();
-        let mut i128_align_src = 64;
-        for spec in self.data_layout.split('-') {
-            let spec_parts = spec.split(':').collect::<Vec<_>>();
-
-            match &*spec_parts {
-                ["e"] => dl.endian = Endian::Little,
-                ["E"] => dl.endian = Endian::Big,
-                [p] if p.starts_with('P') => {
-                    dl.instruction_address_space = parse_address_space(&p[1..], "P")?
-                }
-                ["a", ref a @ ..] => dl.aggregate_align = align(a, "a")?,
-                ["f32", ref a @ ..] => dl.f32_align = align(a, "f32")?,
-                ["f64", ref a @ ..] => dl.f64_align = align(a, "f64")?,
-                [p @ "p", s, ref a @ ..] | [p @ "p0", s, ref a @ ..] => {
-                    dl.pointer_size = size(s, p)?;
-                    dl.pointer_align = align(a, p)?;
-                }
-                [s, ref a @ ..] if s.starts_with('i') => {
-                    let Ok(bits) = s[1..].parse::<u64>() else {
-                        size(&s[1..], "i")?; // For the user error.
-                        continue;
-                    };
-                    let a = align(a, s)?;
-                    match bits {
-                        1 => dl.i1_align = a,
-                        8 => dl.i8_align = a,
-                        16 => dl.i16_align = a,
-                        32 => dl.i32_align = a,
-                        64 => dl.i64_align = a,
-                        _ => {}
-                    }
-                    if bits >= i128_align_src && bits <= 128 {
-                        // Default alignment for i128 is decided by taking the alignment of
-                        // largest-sized i{64..=128}.
-                        i128_align_src = bits;
-                        dl.i128_align = a;
-                    }
-                }
-                [s, ref a @ ..] if s.starts_with('v') => {
-                    let v_size = size(&s[1..], "v")?;
-                    let a = align(a, s)?;
-                    if let Some(v) = dl.vector_align.iter_mut().find(|v| v.0 == v_size) {
-                        v.1 = a;
-                        continue;
-                    }
-                    // No existing entry, add a new one.
-                    dl.vector_align.push((v_size, a));
-                }
-                _ => {} // Ignore everything else.
-            }
-        }
+        let mut dl = TargetDataLayout::parse_from_llvm_datalayout_string(&self.data_layout)?;
 
         // Perform consistency checks against the Target information.
         if dl.endian != self.endian {
@@ -1650,9 +1562,9 @@ pub struct TargetOptions {
 
     /// Flag indicating whether #[thread_local] is available for this target.
     pub has_thread_local: bool,
-    // This is mainly for easy compatibility with emscripten.
-    // If we give emcc .o files that are actually .bc files it
-    // will 'just work'.
+    /// This is mainly for easy compatibility with emscripten.
+    /// If we give emcc .o files that are actually .bc files it
+    /// will 'just work'.
     pub obj_is_bitcode: bool,
     /// Whether the target requires that emitted object code includes bitcode.
     pub forces_embed_bitcode: bool,
@@ -1792,12 +1704,12 @@ pub struct TargetOptions {
     /// since this is most common among tier 1 and tier 2 targets.
     pub supports_stack_protector: bool,
 
-    // The name of entry function.
-    // Default value is "main"
+    /// The name of entry function.
+    /// Default value is "main"
     pub entry_name: StaticCow<str>,
 
-    // The ABI of entry function.
-    // Default value is `Conv::C`, i.e. C call convention
+    /// The ABI of entry function.
+    /// Default value is `Conv::C`, i.e. C call convention
     pub entry_abi: Conv,
 }
 
diff --git a/compiler/rustc_trait_selection/src/autoderef.rs b/compiler/rustc_trait_selection/src/autoderef.rs
index af3a7ae2486..e988c77a064 100644
--- a/compiler/rustc_trait_selection/src/autoderef.rs
+++ b/compiler/rustc_trait_selection/src/autoderef.rs
@@ -1,6 +1,6 @@
 use crate::errors::AutoDerefReachedRecursionLimit;
-use crate::infer::InferCtxtExt as _;
 use crate::traits::query::evaluate_obligation::InferCtxtExt;
+use crate::traits::NormalizeExt;
 use crate::traits::{self, TraitEngine, TraitEngineExt};
 use rustc_hir as hir;
 use rustc_infer::infer::InferCtxt;
@@ -138,11 +138,10 @@ impl<'a, 'tcx> Autoderef<'a, 'tcx> {
             return None;
         }
 
-        let normalized_ty = self.infcx.partially_normalize_associated_types_in(
-            cause,
-            self.param_env,
-            tcx.mk_projection(tcx.lang_items().deref_target()?, trait_ref.substs),
-        );
+        let normalized_ty = self
+            .infcx
+            .at(&cause, self.param_env)
+            .normalize(tcx.mk_projection(tcx.lang_items().deref_target()?, trait_ref.substs));
         let mut fulfillcx = <dyn TraitEngine<'tcx>>::new_in_snapshot(tcx);
         let normalized_ty =
             normalized_ty.into_value_registering_obligations(self.infcx, &mut *fulfillcx);
diff --git a/compiler/rustc_trait_selection/src/infer.rs b/compiler/rustc_trait_selection/src/infer.rs
index 25a9c29caa7..6c70bbf7516 100644
--- a/compiler/rustc_trait_selection/src/infer.rs
+++ b/compiler/rustc_trait_selection/src/infer.rs
@@ -3,7 +3,6 @@ use crate::traits::{self, ObligationCtxt};
 
 use rustc_hir::def_id::DefId;
 use rustc_hir::lang_items::LangItem;
-use rustc_infer::traits::ObligationCause;
 use rustc_middle::arena::ArenaAllocatable;
 use rustc_middle::infer::canonical::{Canonical, CanonicalizedQueryResponse, QueryResponse};
 use rustc_middle::traits::query::Fallible;
@@ -30,15 +29,6 @@ pub trait InferCtxtExt<'tcx> {
         span: Span,
     ) -> bool;
 
-    fn partially_normalize_associated_types_in<T>(
-        &self,
-        cause: ObligationCause<'tcx>,
-        param_env: ty::ParamEnv<'tcx>,
-        value: T,
-    ) -> InferOk<'tcx, T>
-    where
-        T: TypeFoldable<'tcx>;
-
     /// Check whether a `ty` implements given trait(trait_def_id).
     /// The inputs are:
     ///
@@ -88,24 +78,6 @@ impl<'tcx> InferCtxtExt<'tcx> for InferCtxt<'tcx> {
         traits::type_known_to_meet_bound_modulo_regions(self, param_env, ty, lang_item, span)
     }
 
-    /// Normalizes associated types in `value`, potentially returning
-    /// new obligations that must further be processed.
-    #[instrument(level = "debug", skip(self, cause, param_env), ret)]
-    fn partially_normalize_associated_types_in<T>(
-        &self,
-        cause: ObligationCause<'tcx>,
-        param_env: ty::ParamEnv<'tcx>,
-        value: T,
-    ) -> InferOk<'tcx, T>
-    where
-        T: TypeFoldable<'tcx>,
-    {
-        let mut selcx = traits::SelectionContext::new(self);
-        let traits::Normalized { value, obligations } =
-            traits::normalize(&mut selcx, param_env, cause, value);
-        InferOk { value, obligations }
-    }
-
     #[instrument(level = "debug", skip(self, params), ret)]
     fn type_implements_trait(
         &self,
diff --git a/compiler/rustc_trait_selection/src/traits/auto_trait.rs b/compiler/rustc_trait_selection/src/traits/auto_trait.rs
index 52552ff3f86..8e04da4f9be 100644
--- a/compiler/rustc_trait_selection/src/traits/auto_trait.rs
+++ b/compiler/rustc_trait_selection/src/traits/auto_trait.rs
@@ -599,17 +599,17 @@ impl<'tcx> AutoTraitFinder<'tcx> {
         computed_preds: &mut FxIndexSet<ty::Predicate<'tcx>>,
         fresh_preds: &mut FxHashSet<ty::Predicate<'tcx>>,
         predicates: &mut VecDeque<ty::PolyTraitPredicate<'tcx>>,
-        select: &mut SelectionContext<'_, 'tcx>,
+        selcx: &mut SelectionContext<'_, 'tcx>,
         only_projections: bool,
     ) -> bool {
         let dummy_cause = ObligationCause::dummy();
 
         for obligation in nested {
             let is_new_pred =
-                fresh_preds.insert(self.clean_pred(select.infcx(), obligation.predicate));
+                fresh_preds.insert(self.clean_pred(selcx.infcx, obligation.predicate));
 
             // Resolve any inference variables that we can, to help selection succeed
-            let predicate = select.infcx().resolve_vars_if_possible(obligation.predicate);
+            let predicate = selcx.infcx.resolve_vars_if_possible(obligation.predicate);
 
             // We only add a predicate as a user-displayable bound if
             // it involves a generic parameter, and doesn't contain
@@ -717,10 +717,8 @@ impl<'tcx> AutoTraitFinder<'tcx> {
                     // and turn them into an explicit negative impl for our type.
                     debug!("Projecting and unifying projection predicate {:?}", predicate);
 
-                    match project::poly_project_and_unify_type(
-                        select,
-                        &obligation.with(self.tcx, p),
-                    ) {
+                    match project::poly_project_and_unify_type(selcx, &obligation.with(self.tcx, p))
+                    {
                         ProjectAndUnifyResult::MismatchedProjectionTypes(e) => {
                             debug!(
                                 "evaluate_nested_obligations: Unable to unify predicate \
@@ -745,7 +743,7 @@ impl<'tcx> AutoTraitFinder<'tcx> {
                                     computed_preds,
                                     fresh_preds,
                                     predicates,
-                                    select,
+                                    selcx,
                                     only_projections,
                                 ) {
                                     return false;
@@ -768,7 +766,7 @@ impl<'tcx> AutoTraitFinder<'tcx> {
                 }
                 ty::PredicateKind::Clause(ty::Clause::RegionOutlives(binder)) => {
                     let binder = bound_predicate.rebind(binder);
-                    select.infcx().region_outlives_predicate(&dummy_cause, binder)
+                    selcx.infcx.region_outlives_predicate(&dummy_cause, binder)
                 }
                 ty::PredicateKind::Clause(ty::Clause::TypeOutlives(binder)) => {
                     let binder = bound_predicate.rebind(binder);
@@ -777,14 +775,14 @@ impl<'tcx> AutoTraitFinder<'tcx> {
                         binder.map_bound_ref(|pred| pred.0).no_bound_vars(),
                     ) {
                         (None, Some(t_a)) => {
-                            select.infcx().register_region_obligation_with_cause(
+                            selcx.infcx.register_region_obligation_with_cause(
                                 t_a,
-                                select.infcx().tcx.lifetimes.re_static,
+                                selcx.infcx.tcx.lifetimes.re_static,
                                 &dummy_cause,
                             );
                         }
                         (Some(ty::OutlivesPredicate(t_a, r_b)), _) => {
-                            select.infcx().register_region_obligation_with_cause(
+                            selcx.infcx.register_region_obligation_with_cause(
                                 t_a,
                                 r_b,
                                 &dummy_cause,
@@ -796,14 +794,12 @@ impl<'tcx> AutoTraitFinder<'tcx> {
                 ty::PredicateKind::ConstEquate(c1, c2) => {
                     let evaluate = |c: ty::Const<'tcx>| {
                         if let ty::ConstKind::Unevaluated(unevaluated) = c.kind() {
-                            match select.infcx().const_eval_resolve(
+                            match selcx.infcx.const_eval_resolve(
                                 obligation.param_env,
                                 unevaluated,
                                 Some(obligation.cause.span),
                             ) {
-                                Ok(Some(valtree)) => {
-                                    Ok(ty::Const::from_value(select.tcx(), valtree, c.ty()))
-                                }
+                                Ok(Some(valtree)) => Ok(selcx.tcx().mk_const(valtree, c.ty())),
                                 Ok(None) => {
                                     let tcx = self.tcx;
                                     let def_id = unevaluated.def.did;
@@ -823,10 +819,7 @@ impl<'tcx> AutoTraitFinder<'tcx> {
 
                     match (evaluate(c1), evaluate(c2)) {
                         (Ok(c1), Ok(c2)) => {
-                            match select
-                                .infcx()
-                                .at(&obligation.cause, obligation.param_env)
-                                .eq(c1, c2)
+                            match selcx.infcx.at(&obligation.cause, obligation.param_env).eq(c1, c2)
                             {
                                 Ok(_) => (),
                                 Err(_) => return false,
@@ -861,7 +854,7 @@ impl<'tcx> AutoTraitFinder<'tcx> {
     }
 }
 
-// Replaces all ReVars in a type with ty::Region's, using the provided map
+/// Replaces all ReVars in a type with ty::Region's, using the provided map
 pub struct RegionReplacer<'a, 'tcx> {
     vid_to_region: &'a FxHashMap<ty::RegionVid, ty::Region<'tcx>>,
     tcx: TyCtxt<'tcx>,
diff --git a/compiler/rustc_trait_selection/src/traits/coherence.rs b/compiler/rustc_trait_selection/src/traits/coherence.rs
index 741bf206d03..99724fb28db 100644
--- a/compiler/rustc_trait_selection/src/traits/coherence.rs
+++ b/compiler/rustc_trait_selection/src/traits/coherence.rs
@@ -119,7 +119,7 @@ fn with_fresh_ty_vars<'cx, 'tcx>(
     impl_def_id: DefId,
 ) -> ty::ImplHeader<'tcx> {
     let tcx = selcx.tcx();
-    let impl_substs = selcx.infcx().fresh_substs_for_item(DUMMY_SP, impl_def_id);
+    let impl_substs = selcx.infcx.fresh_substs_for_item(DUMMY_SP, impl_def_id);
 
     let header = ty::ImplHeader {
         impl_def_id,
@@ -149,7 +149,7 @@ fn overlap<'cx, 'tcx>(
         impl1_def_id, impl2_def_id, overlap_mode
     );
 
-    selcx.infcx().probe_maybe_skip_leak_check(skip_leak_check.is_yes(), |snapshot| {
+    selcx.infcx.probe_maybe_skip_leak_check(skip_leak_check.is_yes(), |snapshot| {
         overlap_within_probe(selcx, impl1_def_id, impl2_def_id, overlap_mode, snapshot)
     })
 }
@@ -161,7 +161,7 @@ fn overlap_within_probe<'cx, 'tcx>(
     overlap_mode: OverlapMode,
     snapshot: &CombinedSnapshot<'tcx>,
 ) -> Option<OverlapResult<'tcx>> {
-    let infcx = selcx.infcx();
+    let infcx = selcx.infcx;
 
     if overlap_mode.use_negative_impl() {
         if negative_impl(infcx.tcx, impl1_def_id, impl2_def_id)
@@ -200,9 +200,9 @@ fn overlap_within_probe<'cx, 'tcx>(
     debug!("overlap: intercrate_ambiguity_causes={:#?}", intercrate_ambiguity_causes);
 
     let involves_placeholder =
-        matches!(selcx.infcx().region_constraints_added_in_snapshot(snapshot), Some(true));
+        matches!(selcx.infcx.region_constraints_added_in_snapshot(snapshot), Some(true));
 
-    let impl_header = selcx.infcx().resolve_vars_if_possible(impl1_header);
+    let impl_header = selcx.infcx.resolve_vars_if_possible(impl1_header);
     Some(OverlapResult { impl_header, intercrate_ambiguity_causes, involves_placeholder })
 }
 
@@ -214,7 +214,7 @@ fn equate_impl_headers<'cx, 'tcx>(
     // Do `a` and `b` unify? If not, no overlap.
     debug!("equate_impl_headers(impl1_header={:?}, impl2_header={:?}", impl1_header, impl2_header);
     selcx
-        .infcx()
+        .infcx
         .at(&ObligationCause::dummy(), ty::ParamEnv::empty())
         .eq_impl_headers(impl1_header, impl2_header)
         .map(|infer_ok| infer_ok.obligations)
@@ -255,7 +255,7 @@ fn implicit_negative<'cx, 'tcx>(
         "implicit_negative(impl1_header={:?}, impl2_header={:?}, obligations={:?})",
         impl1_header, impl2_header, obligations
     );
-    let infcx = selcx.infcx();
+    let infcx = selcx.infcx;
     let opt_failing_obligation = impl1_header
         .predicates
         .iter()
diff --git a/compiler/rustc_trait_selection/src/traits/engine.rs b/compiler/rustc_trait_selection/src/traits/engine.rs
index 3a05708aebc..64d01ddb09a 100644
--- a/compiler/rustc_trait_selection/src/traits/engine.rs
+++ b/compiler/rustc_trait_selection/src/traits/engine.rs
@@ -3,7 +3,7 @@ use std::fmt::Debug;
 
 use super::TraitEngine;
 use super::{ChalkFulfillmentContext, FulfillmentContext};
-use crate::infer::InferCtxtExt;
+use crate::traits::NormalizeExt;
 use rustc_data_structures::fx::FxIndexSet;
 use rustc_hir::def_id::{DefId, LocalDefId};
 use rustc_infer::infer::at::ToTrace;
@@ -104,11 +104,11 @@ impl<'a, 'tcx> ObligationCtxt<'a, 'tcx> {
 
     pub fn normalize<T: TypeFoldable<'tcx>>(
         &self,
-        cause: ObligationCause<'tcx>,
+        cause: &ObligationCause<'tcx>,
         param_env: ty::ParamEnv<'tcx>,
         value: T,
     ) -> T {
-        let infer_ok = self.infcx.partially_normalize_associated_types_in(cause, param_env, value);
+        let infer_ok = self.infcx.at(&cause, param_env).normalize(value);
         self.register_infer_ok_obligations(infer_ok)
     }
 
@@ -185,7 +185,7 @@ impl<'a, 'tcx> ObligationCtxt<'a, 'tcx> {
             // sound and then uncomment this line again.
 
             // implied_bounds.insert(ty);
-            let normalized = self.normalize(cause.clone(), param_env, ty);
+            let normalized = self.normalize(&cause, param_env, ty);
             implied_bounds.insert(normalized);
         }
         implied_bounds
diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/ambiguity.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/ambiguity.rs
index 6a5744f5f76..752b53fbc3f 100644
--- a/compiler/rustc_trait_selection/src/traits/error_reporting/ambiguity.rs
+++ b/compiler/rustc_trait_selection/src/traits/error_reporting/ambiguity.rs
@@ -17,11 +17,11 @@ pub fn recompute_applicable_impls<'tcx>(
         let placeholder_obligation =
             infcx.replace_bound_vars_with_placeholders(obligation.predicate);
         let obligation_trait_ref =
-            ocx.normalize(dummy_cause.clone(), param_env, placeholder_obligation.trait_ref);
+            ocx.normalize(&dummy_cause, param_env, placeholder_obligation.trait_ref);
 
         let impl_substs = infcx.fresh_substs_for_item(DUMMY_SP, impl_def_id);
         let impl_trait_ref = tcx.bound_impl_trait_ref(impl_def_id).unwrap().subst(tcx, impl_substs);
-        let impl_trait_ref = ocx.normalize(ObligationCause::dummy(), param_env, impl_trait_ref);
+        let impl_trait_ref = ocx.normalize(&ObligationCause::dummy(), param_env, impl_trait_ref);
 
         if let Err(_) = ocx.eq(&dummy_cause, param_env, obligation_trait_ref, impl_trait_ref) {
             return false;
diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs
index 4ac53f6302f..049b24b3997 100644
--- a/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs
+++ b/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs
@@ -9,11 +9,11 @@ use super::{
 };
 use crate::infer::error_reporting::{TyCategory, TypeAnnotationNeeded as ErrorCode};
 use crate::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind};
-use crate::infer::InferCtxtExt as _;
 use crate::infer::{self, InferCtxt, TyCtxtInferExt};
 use crate::traits::query::evaluate_obligation::InferCtxtExt as _;
-use crate::traits::query::normalize::AtExt as _;
+use crate::traits::query::normalize::QueryNormalizeExt as _;
 use crate::traits::specialize::to_pretty_impl_header;
+use crate::traits::NormalizeExt;
 use on_unimplemented::OnUnimplementedNote;
 use on_unimplemented::TypeErrCtxtExt as _;
 use rustc_data_structures::fx::{FxHashMap, FxIndexMap};
@@ -357,7 +357,8 @@ impl<'tcx> InferCtxtExt<'tcx> for InferCtxt<'tcx> {
                 ocx.register_obligation(obligation);
                 if ocx.select_all_or_error().is_empty() {
                     return Ok((
-                        ty::ClosureKind::from_def_id(self.tcx, trait_def_id)
+                        self.tcx
+                            .fn_trait_kind_from_def_id(trait_def_id)
                             .expect("expected to map DefId to ClosureKind"),
                         ty.rebind(self.resolve_vars_if_possible(var)),
                     ));
@@ -686,7 +687,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
                                 }
                                 ObligationCauseCode::BindingObligation(def_id, _)
                                 | ObligationCauseCode::ItemObligation(def_id)
-                                    if ty::ClosureKind::from_def_id(tcx, *def_id).is_some() =>
+                                    if tcx.is_fn_trait(*def_id) =>
                                 {
                                     err.code(rustc_errors::error_code!(E0059));
                                     err.set_primary_message(format!(
@@ -846,8 +847,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
                             );
                         }
 
-                        let is_fn_trait =
-                            ty::ClosureKind::from_def_id(tcx, trait_ref.def_id()).is_some();
+                        let is_fn_trait = tcx.is_fn_trait(trait_ref.def_id());
                         let is_target_feature_fn = if let ty::FnDef(def_id, _) =
                             *trait_ref.skip_binder().self_ty().kind()
                         {
@@ -877,7 +877,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
                             // Note if the `FnMut` or `FnOnce` is less general than the trait we're trying
                             // to implement.
                             let selected_kind =
-                                ty::ClosureKind::from_def_id(self.tcx, trait_ref.def_id())
+                                self.tcx.fn_trait_kind_from_def_id(trait_ref.def_id())
                                     .expect("expected to map DefId to ClosureKind");
                             if !implemented_kind.extends(selected_kind) {
                                 err.note(
@@ -1595,6 +1595,7 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
                     bound_predicate.rebind(data),
                 );
                 let mut obligations = vec![];
+                // FIXME(normalization): Change this to use `At::normalize`
                 let normalized_ty = super::normalize_projection_type(
                     &mut selcx,
                     obligation.param_env,
@@ -1933,7 +1934,7 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
             let infcx = self.tcx.infer_ctxt().build();
             infcx
                 .at(&ObligationCause::dummy(), ty::ParamEnv::empty())
-                .normalize(candidate)
+                .query_normalize(candidate)
                 .map_or(candidate, |normalized| normalized.value)
         };
 
@@ -2155,7 +2156,7 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
                     if generics.params.iter().any(|p| p.name != kw::SelfUpper)
                         && !snippet.ends_with('>')
                         && !generics.has_impl_trait()
-                        && !self.tcx.fn_trait_kind_from_lang_item(def_id).is_some()
+                        && !self.tcx.is_fn_trait(def_id)
                     {
                         // FIXME: To avoid spurious suggestions in functions where type arguments
                         // where already supplied, we check the snippet to make sure it doesn't
@@ -2535,19 +2536,12 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
                 pred.fold_with(&mut ParamToVarFolder { infcx: self, var_map: Default::default() });
 
             let InferOk { value: cleaned_pred, .. } =
-                self.infcx.partially_normalize_associated_types_in(
-                    ObligationCause::dummy(),
-                    param_env,
-                    cleaned_pred,
-                );
+                self.infcx.at(&ObligationCause::dummy(), param_env).normalize(cleaned_pred);
 
             let obligation =
                 Obligation::new(self.tcx, ObligationCause::dummy(), param_env, cleaned_pred);
 
-            // We don't use `InferCtxt::predicate_may_hold` because that
-            // will re-run predicates that overflow locally, which ends up
-            // taking a really long time to compute.
-            self.evaluate_obligation(&obligation).map_or(false, |eval| eval.may_apply())
+            self.predicate_may_hold(&obligation)
         })
     }
 
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 da6ca30cc9a..1740128727a 100644
--- a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs
+++ b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs
@@ -2,6 +2,7 @@ use super::{DefIdOrName, Obligation, ObligationCause, ObligationCauseCode, Predi
 
 use crate::autoderef::Autoderef;
 use crate::infer::InferCtxt;
+use crate::traits::NormalizeExt;
 
 use hir::def::CtorOf;
 use hir::HirId;
@@ -1336,8 +1337,9 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
                     obligation.param_env,
                     trait_pred_and_suggested_ty,
                 );
-                let suggested_ty_would_satisfy_obligation =
-                    self.predicate_must_hold_modulo_regions(&new_obligation);
+                let suggested_ty_would_satisfy_obligation = self
+                    .evaluate_obligation_no_overflow(&new_obligation)
+                    .must_apply_modulo_regions();
                 if suggested_ty_would_satisfy_obligation {
                     let sp = self
                         .tcx
@@ -1679,9 +1681,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
         ) -> Ty<'tcx> {
             let inputs = trait_ref.skip_binder().substs.type_at(1);
             let sig = match inputs.kind() {
-                ty::Tuple(inputs)
-                    if infcx.tcx.fn_trait_kind_from_lang_item(trait_ref.def_id()).is_some() =>
-                {
+                ty::Tuple(inputs) if infcx.tcx.is_fn_trait(trait_ref.def_id()) => {
                     infcx.tcx.mk_fn_sig(
                         inputs.iter(),
                         infcx.next_ty_var(TypeVariableOrigin {
@@ -1752,7 +1752,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
             && let predicates = self.tcx.predicates_of(def_id).instantiate_identity(self.tcx)
             && let Some(pred) = predicates.predicates.get(*idx)
             && let ty::PredicateKind::Clause(ty::Clause::Trait(trait_pred)) = pred.kind().skip_binder()
-            && ty::ClosureKind::from_def_id(self.tcx, trait_pred.def_id()).is_some()
+            && self.tcx.is_fn_trait(trait_pred.def_id())
         {
             let expected_self =
                 self.tcx.anonymize_late_bound_regions(pred.kind().rebind(trait_pred.self_ty()));
@@ -1766,8 +1766,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
                 .enumerate()
                 .find(|(other_idx, (pred, _))| match pred.kind().skip_binder() {
                     ty::PredicateKind::Clause(ty::Clause::Trait(trait_pred))
-                        if ty::ClosureKind::from_def_id(self.tcx, trait_pred.def_id())
-                            .is_some()
+                        if self.tcx.is_fn_trait(trait_pred.def_id())
                             && other_idx != idx
                             // Make sure that the self type matches
                             // (i.e. constraining this closure)
@@ -1991,11 +1990,6 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
             .as_local()
             .and_then(|def_id| hir.maybe_body_owned_by(def_id))
             .map(|body_id| hir.body(body_id));
-        let is_async = self
-            .tcx
-            .generator_kind(generator_did)
-            .map(|generator_kind| matches!(generator_kind, hir::GeneratorKind::Async(..)))
-            .unwrap_or(false);
         let mut visitor = AwaitsVisitor::default();
         if let Some(body) = generator_body {
             visitor.visit_body(body);
@@ -2072,6 +2066,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
 
         debug!(?interior_or_upvar_span);
         if let Some(interior_or_upvar_span) = interior_or_upvar_span {
+            let is_async = self.tcx.generator_is_async(generator_did);
             let typeck_results = match generator_data {
                 GeneratorData::Local(typeck_results) => Some(typeck_results),
                 GeneratorData::Foreign(_) => None,
@@ -2644,10 +2639,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
                                 if is_future
                                     && obligated_types.last().map_or(false, |ty| match ty.kind() {
                                         ty::Generator(last_def_id, ..) => {
-                                            matches!(
-                                                tcx.generator_kind(last_def_id),
-                                                Some(GeneratorKind::Async(..))
-                                            )
+                                            tcx.generator_is_async(*last_def_id)
                                         }
                                         _ => false,
                                     })
@@ -2673,7 +2665,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
                                 let sp = self.tcx.def_span(def_id);
 
                                 // Special-case this to say "async block" instead of `[static generator]`.
-                                let kind = tcx.generator_kind(def_id).unwrap();
+                                let kind = tcx.generator_kind(def_id).unwrap().descr();
                                 err.span_note(
                                     sp,
                                     &format!("required because it's used within this {}", kind),
@@ -2975,12 +2967,8 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
                         self.tcx.mk_substs_trait(trait_pred.self_ty(), []),
                     )
                 });
-                let InferOk { value: projection_ty, .. } = self
-                    .partially_normalize_associated_types_in(
-                        obligation.cause.clone(),
-                        obligation.param_env,
-                        projection_ty,
-                    );
+                let InferOk { value: projection_ty, .. } =
+                    self.at(&obligation.cause, obligation.param_env).normalize(projection_ty);
 
                 debug!(
                     normalized_projection_type = ?self.resolve_vars_if_possible(projection_ty)
diff --git a/compiler/rustc_trait_selection/src/traits/fulfill.rs b/compiler/rustc_trait_selection/src/traits/fulfill.rs
index 80ee363d72f..76a755ed9e0 100644
--- a/compiler/rustc_trait_selection/src/traits/fulfill.rs
+++ b/compiler/rustc_trait_selection/src/traits/fulfill.rs
@@ -199,7 +199,7 @@ impl<'a, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'tcx> {
             // code is so hot. 1 and 0 dominate; 2+ is fairly rare.
             1 => {
                 let infer_var = pending_obligation.stalled_on[0];
-                self.selcx.infcx().ty_or_const_infer_var_changed(infer_var)
+                self.selcx.infcx.ty_or_const_infer_var_changed(infer_var)
             }
             0 => {
                 // In this case we haven't changed, but wish to make a change.
@@ -210,7 +210,7 @@ impl<'a, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'tcx> {
                 // form was a perf win. See #64545 for details.
                 (|| {
                     for &infer_var in &pending_obligation.stalled_on {
-                        if self.selcx.infcx().ty_or_const_infer_var_changed(infer_var) {
+                        if self.selcx.infcx.ty_or_const_infer_var_changed(infer_var) {
                             return true;
                         }
                     }
@@ -240,13 +240,12 @@ impl<'a, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'tcx> {
         debug!(?obligation, "pre-resolve");
 
         if obligation.predicate.has_non_region_infer() {
-            obligation.predicate =
-                self.selcx.infcx().resolve_vars_if_possible(obligation.predicate);
+            obligation.predicate = self.selcx.infcx.resolve_vars_if_possible(obligation.predicate);
         }
 
         let obligation = &pending_obligation.obligation;
 
-        let infcx = self.selcx.infcx();
+        let infcx = self.selcx.infcx;
 
         if obligation.predicate.has_projections() {
             let mut obligations = Vec::new();
@@ -353,7 +352,7 @@ impl<'a, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'tcx> {
                 }
 
                 ty::PredicateKind::ClosureKind(_, closure_substs, kind) => {
-                    match self.selcx.infcx().closure_kind(closure_substs) {
+                    match self.selcx.infcx.closure_kind(closure_substs) {
                         Some(closure_kind) => {
                             if closure_kind.extends(kind) {
                                 ProcessResult::Changed(vec![])
@@ -367,7 +366,7 @@ impl<'a, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'tcx> {
 
                 ty::PredicateKind::WellFormed(arg) => {
                     match wf::obligations(
-                        self.selcx.infcx(),
+                        self.selcx.infcx,
                         obligation.param_env,
                         obligation.cause.body_id,
                         obligation.recursion_depth + 1,
@@ -384,7 +383,7 @@ impl<'a, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'tcx> {
                 }
 
                 ty::PredicateKind::Subtype(subtype) => {
-                    match self.selcx.infcx().subtype_predicate(
+                    match self.selcx.infcx.subtype_predicate(
                         &obligation.cause,
                         obligation.param_env,
                         Binder::dummy(subtype),
@@ -408,7 +407,7 @@ impl<'a, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'tcx> {
                 }
 
                 ty::PredicateKind::Coerce(coerce) => {
-                    match self.selcx.infcx().coerce_predicate(
+                    match self.selcx.infcx.coerce_predicate(
                         &obligation.cause,
                         obligation.param_env,
                         Binder::dummy(coerce),
@@ -432,7 +431,7 @@ impl<'a, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'tcx> {
 
                 ty::PredicateKind::ConstEvaluatable(uv) => {
                     match const_evaluatable::is_const_evaluatable(
-                        self.selcx.infcx(),
+                        self.selcx.infcx,
                         uv,
                         obligation.param_env,
                         obligation.cause.span,
@@ -503,7 +502,7 @@ impl<'a, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'tcx> {
 
                     let mut evaluate = |c: Const<'tcx>| {
                         if let ty::ConstKind::Unevaluated(unevaluated) = c.kind() {
-                            match self.selcx.infcx().try_const_eval_resolve(
+                            match self.selcx.infcx.try_const_eval_resolve(
                                 obligation.param_env,
                                 unevaluated,
                                 c.ty(),
@@ -531,7 +530,7 @@ impl<'a, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'tcx> {
                         (Ok(c1), Ok(c2)) => {
                             match self
                                 .selcx
-                                .infcx()
+                                .infcx
                                 .at(&obligation.cause, obligation.param_env)
                                 .eq(c1, c2)
                             {
@@ -601,7 +600,7 @@ impl<'a, 'tcx> FulfillProcessor<'a, 'tcx> {
         trait_obligation: TraitObligation<'tcx>,
         stalled_on: &mut Vec<TyOrConstInferVar<'tcx>>,
     ) -> ProcessResult<PendingPredicateObligation<'tcx>, FulfillmentErrorCode<'tcx>> {
-        let infcx = self.selcx.infcx();
+        let infcx = self.selcx.infcx;
         if obligation.predicate.is_global() {
             // no type variables present, can use evaluation for better caching.
             // FIXME: consider caching errors too.
@@ -659,7 +658,7 @@ impl<'a, 'tcx> FulfillProcessor<'a, 'tcx> {
         if obligation.predicate.is_global() {
             // no type variables present, can use evaluation for better caching.
             // FIXME: consider caching errors too.
-            if self.selcx.infcx().predicate_must_hold_considering_regions(obligation) {
+            if self.selcx.infcx.predicate_must_hold_considering_regions(obligation) {
                 if let Some(key) = ProjectionCacheKey::from_poly_projection_predicate(
                     &mut self.selcx,
                     project_obligation.predicate,
@@ -668,7 +667,7 @@ impl<'a, 'tcx> FulfillProcessor<'a, 'tcx> {
                     // evaluated all sub-obligations. We can therefore mark the 'root'
                     // obligation as complete, and skip evaluating sub-obligations.
                     self.selcx
-                        .infcx()
+                        .infcx
                         .inner
                         .borrow_mut()
                         .projection_cache()
@@ -707,7 +706,7 @@ fn substs_infer_vars<'a, 'tcx>(
     substs: ty::Binder<'tcx, SubstsRef<'tcx>>,
 ) -> impl Iterator<Item = TyOrConstInferVar<'tcx>> {
     selcx
-        .infcx()
+        .infcx
         .resolve_vars_if_possible(substs)
         .skip_binder() // ok because this check doesn't care about regions
         .iter()
diff --git a/compiler/rustc_trait_selection/src/traits/mod.rs b/compiler/rustc_trait_selection/src/traits/mod.rs
index 5285cfa6746..371367f0deb 100644
--- a/compiler/rustc_trait_selection/src/traits/mod.rs
+++ b/compiler/rustc_trait_selection/src/traits/mod.rs
@@ -56,7 +56,8 @@ pub use self::object_safety::astconv_object_safety_violations;
 pub use self::object_safety::is_vtable_safe_method;
 pub use self::object_safety::MethodViolationCode;
 pub use self::object_safety::ObjectSafetyViolation;
-pub use self::project::{normalize, normalize_projection_type, normalize_to};
+pub(crate) use self::project::{normalize, normalize_to};
+pub use self::project::{normalize_projection_type, NormalizeExt};
 pub use self::select::{EvaluationCache, SelectionCache, SelectionContext};
 pub use self::select::{EvaluationResult, IntercrateAmbiguityCause, OverflowError};
 pub use self::specialize::specialization_graph::FutureCompatOverlapError;
@@ -386,7 +387,7 @@ where
 {
     let ocx = ObligationCtxt::new(infcx);
     debug!(?value);
-    let normalized_value = ocx.normalize(cause, param_env, value);
+    let normalized_value = ocx.normalize(&cause, param_env, value);
     debug!(?normalized_value);
     debug!("select_all_or_error start");
     let errors = ocx.select_all_or_error();
@@ -453,7 +454,7 @@ pub fn impossible_predicates<'tcx>(
     let infcx = tcx.infer_ctxt().build();
     let param_env = ty::ParamEnv::reveal_all();
     let ocx = ObligationCtxt::new(&infcx);
-    let predicates = ocx.normalize(ObligationCause::dummy(), param_env, predicates);
+    let predicates = ocx.normalize(&ObligationCause::dummy(), param_env, predicates);
     for predicate in predicates {
         let obligation = Obligation::new(tcx, ObligationCause::dummy(), param_env, predicate);
         ocx.register_obligation(obligation);
diff --git a/compiler/rustc_trait_selection/src/traits/project.rs b/compiler/rustc_trait_selection/src/traits/project.rs
index ae6fa841856..113803cd179 100644
--- a/compiler/rustc_trait_selection/src/traits/project.rs
+++ b/compiler/rustc_trait_selection/src/traits/project.rs
@@ -27,6 +27,7 @@ use rustc_errors::ErrorGuaranteed;
 use rustc_hir::def::DefKind;
 use rustc_hir::def_id::DefId;
 use rustc_hir::lang_items::LangItem;
+use rustc_infer::infer::at::At;
 use rustc_infer::infer::resolve::OpportunisticRegionResolver;
 use rustc_infer::traits::ImplSourceBuiltinData;
 use rustc_middle::traits::select::OverflowError;
@@ -48,6 +49,19 @@ pub type ProjectionTyObligation<'tcx> = Obligation<'tcx, ty::ProjectionTy<'tcx>>
 
 pub(super) struct InProgress;
 
+pub trait NormalizeExt<'tcx> {
+    fn normalize<T: TypeFoldable<'tcx>>(&self, t: T) -> InferOk<'tcx, T>;
+}
+
+impl<'tcx> NormalizeExt<'tcx> for At<'_, 'tcx> {
+    fn normalize<T: TypeFoldable<'tcx>>(&self, value: T) -> InferOk<'tcx, T> {
+        let mut selcx = SelectionContext::new(self.infcx);
+        let Normalized { value, obligations } =
+            normalize(&mut selcx, self.param_env, self.cause.clone(), value);
+        InferOk { value, obligations }
+    }
+}
+
 /// When attempting to resolve `<T as TraitRef>::Name` ...
 #[derive(Debug)]
 pub enum ProjectionError<'tcx> {
@@ -194,7 +208,7 @@ pub(super) fn poly_project_and_unify_type<'cx, 'tcx>(
     selcx: &mut SelectionContext<'cx, 'tcx>,
     obligation: &PolyProjectionObligation<'tcx>,
 ) -> ProjectAndUnifyResult<'tcx> {
-    let infcx = selcx.infcx();
+    let infcx = selcx.infcx;
     let r = infcx.commit_if_ok(|_snapshot| {
         let old_universe = infcx.universe();
         let placeholder_predicate =
@@ -250,7 +264,7 @@ fn project_and_unify_type<'cx, 'tcx>(
 ) -> ProjectAndUnifyResult<'tcx> {
     let mut obligations = vec![];
 
-    let infcx = selcx.infcx();
+    let infcx = selcx.infcx;
     let normalized = match opt_normalize_projection_type(
         selcx,
         obligation.param_env,
@@ -269,7 +283,7 @@ fn project_and_unify_type<'cx, 'tcx>(
     // This allows users to omit re-mentioning all bounds on an associated type and just use an
     // `impl Trait` for the assoc type to add more bounds.
     let InferOk { value: actual, obligations: new } =
-        selcx.infcx().replace_opaque_types_with_inference_vars(
+        selcx.infcx.replace_opaque_types_with_inference_vars(
             actual,
             obligation.cause.body_id,
             obligation.cause.span,
@@ -293,7 +307,7 @@ fn project_and_unify_type<'cx, 'tcx>(
 /// them with a fully resolved type where possible. The return value
 /// combines the normalized result and any additional obligations that
 /// were incurred as result.
-pub fn normalize<'a, 'b, 'tcx, T>(
+pub(crate) fn normalize<'a, 'b, 'tcx, T>(
     selcx: &'a mut SelectionContext<'b, 'tcx>,
     param_env: ty::ParamEnv<'tcx>,
     cause: ObligationCause<'tcx>,
@@ -307,7 +321,7 @@ where
     Normalized { value, obligations }
 }
 
-pub fn normalize_to<'a, 'b, 'tcx, T>(
+pub(crate) fn normalize_to<'a, 'b, 'tcx, T>(
     selcx: &'a mut SelectionContext<'b, 'tcx>,
     param_env: ty::ParamEnv<'tcx>,
     cause: ObligationCause<'tcx>,
@@ -321,7 +335,7 @@ where
 }
 
 /// As `normalize`, but with a custom depth.
-pub fn normalize_with_depth<'a, 'b, 'tcx, T>(
+pub(crate) fn normalize_with_depth<'a, 'b, 'tcx, T>(
     selcx: &'a mut SelectionContext<'b, 'tcx>,
     param_env: ty::ParamEnv<'tcx>,
     cause: ObligationCause<'tcx>,
@@ -337,7 +351,7 @@ where
 }
 
 #[instrument(level = "info", skip(selcx, param_env, cause, obligations))]
-pub fn normalize_with_depth_to<'a, 'b, 'tcx, T>(
+pub(crate) fn normalize_with_depth_to<'a, 'b, 'tcx, T>(
     selcx: &'a mut SelectionContext<'b, 'tcx>,
     param_env: ty::ParamEnv<'tcx>,
     cause: ObligationCause<'tcx>,
@@ -357,7 +371,7 @@ where
 }
 
 #[instrument(level = "info", skip(selcx, param_env, cause, obligations))]
-pub fn try_normalize_with_depth_to<'a, 'b, 'tcx, T>(
+pub(crate) fn try_normalize_with_depth_to<'a, 'b, 'tcx, T>(
     selcx: &'a mut SelectionContext<'b, 'tcx>,
     param_env: ty::ParamEnv<'tcx>,
     cause: ObligationCause<'tcx>,
@@ -445,7 +459,7 @@ impl<'a, 'b, 'tcx> AssocTypeNormalizer<'a, 'b, 'tcx> {
     }
 
     fn fold<T: TypeFoldable<'tcx>>(&mut self, value: T) -> T {
-        let value = self.selcx.infcx().resolve_vars_if_possible(value);
+        let value = self.selcx.infcx.resolve_vars_if_possible(value);
         debug!(?value);
 
         assert!(
@@ -524,7 +538,7 @@ impl<'a, 'b, 'tcx> TypeFolder<'tcx> for AssocTypeNormalizer<'a, 'b, 'tcx> {
                                 self.param_env,
                                 ty,
                             );
-                            self.selcx.infcx().err_ctxt().report_overflow_error(&obligation, true);
+                            self.selcx.infcx.err_ctxt().report_overflow_error(&obligation, true);
                         }
 
                         let substs = substs.fold_with(self);
@@ -590,7 +604,7 @@ impl<'a, 'b, 'tcx> TypeFolder<'tcx> for AssocTypeNormalizer<'a, 'b, 'tcx> {
                 // want to figure out how to register obligations with escaping vars
                 // or handle this some other way.
 
-                let infcx = self.selcx.infcx();
+                let infcx = self.selcx.infcx;
                 let (data, mapped_regions, mapped_types, mapped_consts) =
                     BoundVarReplacer::replace_bound_vars(infcx, &mut self.universes, data);
                 let data = data.fold_with(self);
@@ -640,7 +654,7 @@ impl<'a, 'b, 'tcx> TypeFolder<'tcx> for AssocTypeNormalizer<'a, 'b, 'tcx> {
             let constant = constant.super_fold_with(self);
             debug!(?constant, ?self.param_env);
             with_replaced_escaping_bound_vars(
-                self.selcx.infcx(),
+                self.selcx.infcx,
                 &mut self.universes,
                 constant,
                 |constant| constant.eval(tcx, self.param_env),
@@ -818,7 +832,7 @@ impl<'tcx> TypeFolder<'tcx> for BoundVarReplacer<'_, 'tcx> {
                 let universe = self.universe_for(debruijn);
                 let p = ty::PlaceholderConst { universe, name: bound_const };
                 self.mapped_consts.insert(p, bound_const);
-                self.infcx.tcx.mk_const(ty::ConstKind::Placeholder(p), ct.ty())
+                self.infcx.tcx.mk_const(p, ct.ty())
             }
             _ => ct.super_fold_with(self),
         }
@@ -829,7 +843,7 @@ impl<'tcx> TypeFolder<'tcx> for BoundVarReplacer<'_, 'tcx> {
     }
 }
 
-// The inverse of `BoundVarReplacer`: replaces placeholders with the bound vars from which they came.
+/// The inverse of [`BoundVarReplacer`]: replaces placeholders with the bound vars from which they came.
 pub struct PlaceholderReplacer<'me, 'tcx> {
     infcx: &'me InferCtxt<'tcx>,
     mapped_regions: BTreeMap<ty::PlaceholderRegion, ty::BoundRegion>,
@@ -992,10 +1006,7 @@ pub fn normalize_projection_type<'a, 'b, 'tcx>(
         // and a deferred predicate to resolve this when more type
         // information is available.
 
-        selcx
-            .infcx()
-            .infer_projection(param_env, projection_ty, cause, depth + 1, obligations)
-            .into()
+        selcx.infcx.infer_projection(param_env, projection_ty, cause, depth + 1, obligations).into()
     })
 }
 
@@ -1018,7 +1029,7 @@ fn opt_normalize_projection_type<'a, 'b, 'tcx>(
     depth: usize,
     obligations: &mut Vec<PredicateObligation<'tcx>>,
 ) -> Result<Option<Term<'tcx>>, InProgress> {
-    let infcx = selcx.infcx();
+    let infcx = selcx.infcx;
     // Don't use the projection cache in intercrate mode -
     // the `infcx` may be re-used between intercrate in non-intercrate
     // mode, which could lead to using incorrect cache results.
@@ -1110,7 +1121,7 @@ fn opt_normalize_projection_type<'a, 'b, 'tcx>(
             // an impl, where-clause etc) and hence we must
             // re-normalize it
 
-            let projected_term = selcx.infcx().resolve_vars_if_possible(projected_term);
+            let projected_term = selcx.infcx.resolve_vars_if_possible(projected_term);
 
             let mut result = if projected_term.has_projections() {
                 let mut normalizer = AssocTypeNormalizer::new(
@@ -1206,9 +1217,9 @@ fn normalize_to_error<'a, 'tcx>(
         param_env,
         predicate: trait_ref.without_const().to_predicate(selcx.tcx()),
     };
-    let tcx = selcx.infcx().tcx;
+    let tcx = selcx.infcx.tcx;
     let def_id = projection_ty.item_def_id;
-    let new_value = selcx.infcx().next_ty_var(TypeVariableOrigin {
+    let new_value = selcx.infcx.next_ty_var(TypeVariableOrigin {
         kind: TypeVariableOriginKind::NormalizeProjectionType,
         span: tcx.def_span(def_id),
     });
@@ -1330,7 +1341,7 @@ fn assemble_candidate_for_impl_trait_in_trait<'cx, 'tcx>(
         let trait_predicate =
             ty::Binder::dummy(ty::TraitRef { def_id: trait_def_id, substs: trait_substs });
 
-        let _ = selcx.infcx().commit_if_ok(|_| {
+        let _ = selcx.infcx.commit_if_ok(|_| {
             match selcx.select(&obligation.with(tcx, trait_predicate)) {
                 Ok(Some(super::ImplSource::UserDefined(data))) => {
                     candidate_set.push_candidate(ProjectionCandidate::ImplTraitInTrait(
@@ -1435,7 +1446,7 @@ fn assemble_candidates_from_object_ty<'cx, 'tcx>(
     let tcx = selcx.tcx();
 
     let self_ty = obligation.predicate.self_ty();
-    let object_ty = selcx.infcx().shallow_resolve(self_ty);
+    let object_ty = selcx.infcx.shallow_resolve(self_ty);
     let data = match object_ty.kind() {
         ty::Dynamic(data, ..) => data,
         ty::Infer(ty::TyVar(_)) => {
@@ -1473,7 +1484,7 @@ fn assemble_candidates_from_predicates<'cx, 'tcx>(
     env_predicates: impl Iterator<Item = ty::Predicate<'tcx>>,
     potentially_unnormalized_candidates: bool,
 ) {
-    let infcx = selcx.infcx();
+    let infcx = selcx.infcx;
     for predicate in env_predicates {
         let bound_predicate = predicate.kind();
         if let ty::PredicateKind::Clause(ty::Clause::Projection(data)) =
@@ -1529,7 +1540,7 @@ fn assemble_candidates_from_impls<'cx, 'tcx>(
     // start out by selecting the predicate `T as TraitRef<...>`:
     let poly_trait_ref = ty::Binder::dummy(obligation.predicate.trait_ref(selcx.tcx()));
     let trait_obligation = obligation.with(selcx.tcx(), poly_trait_ref);
-    let _ = selcx.infcx().commit_if_ok(|_| {
+    let _ = selcx.infcx.commit_if_ok(|_| {
         let impl_source = match selcx.select(&trait_obligation) {
             Ok(Some(impl_source)) => impl_source,
             Ok(None) => {
@@ -1587,7 +1598,7 @@ fn assemble_candidates_from_impls<'cx, 'tcx>(
                     if obligation.param_env.reveal() == Reveal::All {
                         // NOTE(eddyb) inference variables can resolve to parameters, so
                         // assume `poly_trait_ref` isn't monomorphic, if it contains any.
-                        let poly_trait_ref = selcx.infcx().resolve_vars_if_possible(poly_trait_ref);
+                        let poly_trait_ref = selcx.infcx.resolve_vars_if_possible(poly_trait_ref);
                         !poly_trait_ref.still_further_specializable()
                     } else {
                         debug!(
@@ -1603,7 +1614,7 @@ fn assemble_candidates_from_impls<'cx, 'tcx>(
                 // While a builtin impl may be known to exist, the associated type may not yet
                 // be known. Any type with multiple potential associated types is therefore
                 // not eligible.
-                let self_ty = selcx.infcx().shallow_resolve(obligation.predicate.self_ty());
+                let self_ty = selcx.infcx.shallow_resolve(obligation.predicate.self_ty());
 
                 let lang_items = selcx.tcx().lang_items();
                 if lang_items.discriminant_kind_trait() == Some(poly_trait_ref.def_id()) {
@@ -1690,7 +1701,7 @@ fn assemble_candidates_from_impls<'cx, 'tcx>(
                         // type parameters, opaques, and unnormalized projections have pointer
                         // metadata if they're known (e.g. by the param_env) to be sized
                         ty::Param(_) | ty::Projection(..) | ty::Opaque(..)
-                            if selcx.infcx().predicate_must_hold_modulo_regions(
+                            if selcx.infcx.predicate_must_hold_modulo_regions(
                                 &obligation.with(
                                     selcx.tcx(),
                                     ty::Binder::dummy(
@@ -1818,8 +1829,7 @@ fn confirm_candidate<'cx, 'tcx>(
     // when possible for this to work. See `auto-trait-projection-recursion.rs`
     // for a case where this matters.
     if progress.term.has_infer_regions() {
-        progress.term =
-            progress.term.fold_with(&mut OpportunisticRegionResolver::new(selcx.infcx()));
+        progress.term = progress.term.fold_with(&mut OpportunisticRegionResolver::new(selcx.infcx));
     }
     progress
 }
@@ -2000,7 +2010,7 @@ fn confirm_fn_pointer_candidate<'cx, 'tcx>(
     obligation: &ProjectionTyObligation<'tcx>,
     fn_pointer_impl_source: ImplSourceFnPointerData<'tcx, PredicateObligation<'tcx>>,
 ) -> Progress<'tcx> {
-    let fn_type = selcx.infcx().shallow_resolve(fn_pointer_impl_source.fn_ty);
+    let fn_type = selcx.infcx.shallow_resolve(fn_pointer_impl_source.fn_ty);
     let sig = fn_type.fn_sig(selcx.tcx());
     let Normalized { value: sig, obligations } = normalize_with_depth(
         selcx,
@@ -2073,7 +2083,7 @@ fn confirm_param_env_candidate<'cx, 'tcx>(
     poly_cache_entry: ty::PolyProjectionPredicate<'tcx>,
     potentially_unnormalized_candidate: bool,
 ) -> Progress<'tcx> {
-    let infcx = selcx.infcx();
+    let infcx = selcx.infcx;
     let cause = &obligation.cause;
     let param_env = obligation.param_env;
 
@@ -2168,7 +2178,7 @@ fn confirm_impl_candidate<'cx, 'tcx>(
     // * `substs` ends up as `[u32, S]`
     let substs = obligation.predicate.substs.rebase_onto(tcx, trait_def_id, substs);
     let substs =
-        translate_substs(selcx.infcx(), param_env, impl_def_id, substs, assoc_ty.defining_node);
+        translate_substs(selcx.infcx, param_env, impl_def_id, substs, assoc_ty.defining_node);
     let ty = tcx.bound_type_of(assoc_ty.item.def_id);
     let is_const = matches!(tcx.def_kind(assoc_ty.item.def_id), DefKind::AssocConst);
     let term: ty::EarlyBinder<ty::Term<'tcx>> = if is_const {
@@ -2264,7 +2274,7 @@ fn confirm_impl_trait_in_trait_candidate<'tcx>(
     let impl_fn_substs =
         obligation.predicate.substs.rebase_onto(tcx, tcx.parent(trait_fn_def_id), data.substs);
     let impl_fn_substs = translate_substs(
-        selcx.infcx(),
+        selcx.infcx,
         obligation.param_env,
         data.impl_def_id,
         impl_fn_substs,
@@ -2424,7 +2434,7 @@ impl<'cx, 'tcx> ProjectionCacheKeyExt<'cx, 'tcx> for ProjectionCacheKey<'tcx> {
         selcx: &mut SelectionContext<'cx, 'tcx>,
         predicate: ty::PolyProjectionPredicate<'tcx>,
     ) -> Option<Self> {
-        let infcx = selcx.infcx();
+        let infcx = selcx.infcx;
         // We don't do cross-snapshot caching of obligations with escaping regions,
         // so there's no cache key to use
         predicate.no_bound_vars().map(|predicate| {
diff --git a/compiler/rustc_trait_selection/src/traits/query/normalize.rs b/compiler/rustc_trait_selection/src/traits/query/normalize.rs
index a875ea1578d..1aed6630870 100644
--- a/compiler/rustc_trait_selection/src/traits/query/normalize.rs
+++ b/compiler/rustc_trait_selection/src/traits/query/normalize.rs
@@ -22,13 +22,13 @@ use super::NoSolution;
 
 pub use rustc_middle::traits::query::NormalizationResult;
 
-pub trait AtExt<'tcx> {
-    fn normalize<T>(&self, value: T) -> Result<Normalized<'tcx, T>, NoSolution>
+pub trait QueryNormalizeExt<'tcx> {
+    fn query_normalize<T>(&self, value: T) -> Result<Normalized<'tcx, T>, NoSolution>
     where
         T: TypeFoldable<'tcx>;
 }
 
-impl<'cx, 'tcx> AtExt<'tcx> for At<'cx, 'tcx> {
+impl<'cx, 'tcx> QueryNormalizeExt<'tcx> for At<'cx, 'tcx> {
     /// Normalize `value` in the context of the inference context,
     /// yielding a resulting type, or an error if `value` cannot be
     /// normalized. If you don't care about regions, you should prefer
@@ -42,7 +42,7 @@ impl<'cx, 'tcx> AtExt<'tcx> for At<'cx, 'tcx> {
     /// normalizing, but for now should be used only when we actually
     /// know that normalization will succeed, since error reporting
     /// and other details are still "under development".
-    fn normalize<T>(&self, value: T) -> Result<Normalized<'tcx, T>, NoSolution>
+    fn query_normalize<T>(&self, value: T) -> Result<Normalized<'tcx, T>, NoSolution>
     where
         T: TypeFoldable<'tcx>,
     {
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 f8c7a896b53..fe5135661b5 100644
--- a/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs
+++ b/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs
@@ -9,225 +9,18 @@ use hir::LangItem;
 use rustc_hir as hir;
 use rustc_infer::traits::ObligationCause;
 use rustc_infer::traits::{Obligation, SelectionError, TraitObligation};
-use rustc_middle::ty::print::with_no_trimmed_paths;
 use rustc_middle::ty::{self, Ty, TypeVisitable};
 use rustc_target::spec::abi::Abi;
 
 use crate::traits;
-use crate::traits::coherence::Conflict;
 use crate::traits::query::evaluate_obligation::InferCtxtExt;
-use crate::traits::{util, SelectionResult};
-use crate::traits::{ErrorReporting, Overflow, Unimplemented};
+use crate::traits::util;
 
 use super::BuiltinImplConditions;
-use super::IntercrateAmbiguityCause;
-use super::OverflowError;
-use super::SelectionCandidate::{self, *};
-use super::{EvaluatedCandidate, SelectionCandidateSet, SelectionContext, TraitObligationStack};
+use super::SelectionCandidate::*;
+use super::{SelectionCandidateSet, SelectionContext, TraitObligationStack};
 
 impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
-    #[instrument(level = "debug", skip(self), ret)]
-    pub(super) fn candidate_from_obligation<'o>(
-        &mut self,
-        stack: &TraitObligationStack<'o, 'tcx>,
-    ) -> SelectionResult<'tcx, SelectionCandidate<'tcx>> {
-        // Watch out for overflow. This intentionally bypasses (and does
-        // not update) the cache.
-        self.check_recursion_limit(&stack.obligation, &stack.obligation)?;
-
-        // Check the cache. Note that we freshen the trait-ref
-        // separately rather than using `stack.fresh_trait_ref` --
-        // this is because we want the unbound variables to be
-        // replaced with fresh types starting from index 0.
-        let cache_fresh_trait_pred = self.infcx.freshen(stack.obligation.predicate);
-        debug!(?cache_fresh_trait_pred);
-        debug_assert!(!stack.obligation.predicate.has_escaping_bound_vars());
-
-        if let Some(c) =
-            self.check_candidate_cache(stack.obligation.param_env, cache_fresh_trait_pred)
-        {
-            debug!("CACHE HIT");
-            return c;
-        }
-
-        // If no match, compute result and insert into cache.
-        //
-        // FIXME(nikomatsakis) -- this cache is not taking into
-        // account cycles that may have occurred in forming the
-        // candidate. I don't know of any specific problems that
-        // result but it seems awfully suspicious.
-        let (candidate, dep_node) =
-            self.in_task(|this| this.candidate_from_obligation_no_cache(stack));
-
-        debug!("CACHE MISS");
-        self.insert_candidate_cache(
-            stack.obligation.param_env,
-            cache_fresh_trait_pred,
-            dep_node,
-            candidate.clone(),
-        );
-        candidate
-    }
-
-    fn candidate_from_obligation_no_cache<'o>(
-        &mut self,
-        stack: &TraitObligationStack<'o, 'tcx>,
-    ) -> SelectionResult<'tcx, SelectionCandidate<'tcx>> {
-        if let Err(conflict) = self.is_knowable(stack) {
-            debug!("coherence stage: not knowable");
-            if self.intercrate_ambiguity_causes.is_some() {
-                debug!("evaluate_stack: intercrate_ambiguity_causes is some");
-                // Heuristics: show the diagnostics when there are no candidates in crate.
-                if let Ok(candidate_set) = self.assemble_candidates(stack) {
-                    let mut no_candidates_apply = true;
-
-                    for c in candidate_set.vec.iter() {
-                        if self.evaluate_candidate(stack, &c)?.may_apply() {
-                            no_candidates_apply = false;
-                            break;
-                        }
-                    }
-
-                    if !candidate_set.ambiguous && no_candidates_apply {
-                        let trait_ref = stack.obligation.predicate.skip_binder().trait_ref;
-                        let self_ty = trait_ref.self_ty();
-                        let (trait_desc, self_desc) = with_no_trimmed_paths!({
-                            let trait_desc = trait_ref.print_only_trait_path().to_string();
-                            let self_desc = if self_ty.has_concrete_skeleton() {
-                                Some(self_ty.to_string())
-                            } else {
-                                None
-                            };
-                            (trait_desc, self_desc)
-                        });
-                        let cause = if let Conflict::Upstream = conflict {
-                            IntercrateAmbiguityCause::UpstreamCrateUpdate { trait_desc, self_desc }
-                        } else {
-                            IntercrateAmbiguityCause::DownstreamCrate { trait_desc, self_desc }
-                        };
-                        debug!(?cause, "evaluate_stack: pushing cause");
-                        self.intercrate_ambiguity_causes.as_mut().unwrap().insert(cause);
-                    }
-                }
-            }
-            return Ok(None);
-        }
-
-        let candidate_set = self.assemble_candidates(stack)?;
-
-        if candidate_set.ambiguous {
-            debug!("candidate set contains ambig");
-            return Ok(None);
-        }
-
-        let candidates = candidate_set.vec;
-
-        debug!(?stack, ?candidates, "assembled {} candidates", candidates.len());
-
-        // At this point, we know that each of the entries in the
-        // candidate set is *individually* applicable. Now we have to
-        // figure out if they contain mutual incompatibilities. This
-        // frequently arises if we have an unconstrained input type --
-        // for example, we are looking for `$0: Eq` where `$0` is some
-        // unconstrained type variable. In that case, we'll get a
-        // candidate which assumes $0 == int, one that assumes `$0 ==
-        // usize`, etc. This spells an ambiguity.
-
-        let mut candidates = self.filter_impls(candidates, stack.obligation);
-
-        // If there is more than one candidate, first winnow them down
-        // by considering extra conditions (nested obligations and so
-        // forth). We don't winnow if there is exactly one
-        // candidate. This is a relatively minor distinction but it
-        // can lead to better inference and error-reporting. An
-        // example would be if there was an impl:
-        //
-        //     impl<T:Clone> Vec<T> { fn push_clone(...) { ... } }
-        //
-        // and we were to see some code `foo.push_clone()` where `boo`
-        // is a `Vec<Bar>` and `Bar` does not implement `Clone`.  If
-        // we were to winnow, we'd wind up with zero candidates.
-        // Instead, we select the right impl now but report "`Bar` does
-        // not implement `Clone`".
-        if candidates.len() == 1 {
-            return self.filter_reservation_impls(candidates.pop().unwrap(), stack.obligation);
-        }
-
-        // Winnow, but record the exact outcome of evaluation, which
-        // is needed for specialization. Propagate overflow if it occurs.
-        let mut candidates = candidates
-            .into_iter()
-            .map(|c| match self.evaluate_candidate(stack, &c) {
-                Ok(eval) if eval.may_apply() => {
-                    Ok(Some(EvaluatedCandidate { candidate: c, evaluation: eval }))
-                }
-                Ok(_) => Ok(None),
-                Err(OverflowError::Canonical) => Err(Overflow(OverflowError::Canonical)),
-                Err(OverflowError::ErrorReporting) => Err(ErrorReporting),
-                Err(OverflowError::Error(e)) => Err(Overflow(OverflowError::Error(e))),
-            })
-            .flat_map(Result::transpose)
-            .collect::<Result<Vec<_>, _>>()?;
-
-        debug!(?stack, ?candidates, "winnowed to {} candidates", candidates.len());
-
-        let needs_infer = stack.obligation.predicate.has_non_region_infer();
-
-        // If there are STILL multiple candidates, we can further
-        // reduce the list by dropping duplicates -- including
-        // resolving specializations.
-        if candidates.len() > 1 {
-            let mut i = 0;
-            while i < candidates.len() {
-                let is_dup = (0..candidates.len()).filter(|&j| i != j).any(|j| {
-                    self.candidate_should_be_dropped_in_favor_of(
-                        &candidates[i],
-                        &candidates[j],
-                        needs_infer,
-                    )
-                });
-                if is_dup {
-                    debug!(candidate = ?candidates[i], "Dropping candidate #{}/{}", i, candidates.len());
-                    candidates.swap_remove(i);
-                } else {
-                    debug!(candidate = ?candidates[i], "Retaining candidate #{}/{}", i, candidates.len());
-                    i += 1;
-
-                    // If there are *STILL* multiple candidates, give up
-                    // and report ambiguity.
-                    if i > 1 {
-                        debug!("multiple matches, ambig");
-                        return Ok(None);
-                    }
-                }
-            }
-        }
-
-        // If there are *NO* candidates, then there are no impls --
-        // that we know of, anyway. Note that in the case where there
-        // are unbound type variables within the obligation, it might
-        // be the case that you could still satisfy the obligation
-        // from another crate by instantiating the type variables with
-        // a type from another crate that does have an impl. This case
-        // is checked for in `evaluate_stack` (and hence users
-        // who might care about this case, like coherence, should use
-        // that function).
-        if candidates.is_empty() {
-            // If there's an error type, 'downgrade' our result from
-            // `Err(Unimplemented)` to `Ok(None)`. This helps us avoid
-            // emitting additional spurious errors, since we're guaranteed
-            // to have emitted at least one.
-            if stack.obligation.predicate.references_error() {
-                debug!(?stack.obligation.predicate, "found error type in predicate, treating as ambiguous");
-                return Ok(None);
-            }
-            return Err(Unimplemented);
-        }
-
-        // Just one candidate left.
-        self.filter_reservation_impls(candidates.pop().unwrap().candidate, stack.obligation)
-    }
-
     #[instrument(skip(self, stack), level = "debug")]
     pub(super) fn assemble_candidates<'o>(
         &mut self,
@@ -238,7 +31,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
             param_env: obligation.param_env,
             cause: obligation.cause.clone(),
             recursion_depth: obligation.recursion_depth,
-            predicate: self.infcx().resolve_vars_if_possible(obligation.predicate),
+            predicate: self.infcx.resolve_vars_if_possible(obligation.predicate),
         };
 
         if obligation.predicate.skip_binder().self_ty().is_ty_var() {
@@ -430,9 +223,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
     ) {
         let self_ty = obligation.self_ty().skip_binder();
         if let ty::Generator(did, ..) = self_ty.kind() {
-            if let Some(rustc_hir::GeneratorKind::Async(_generator_kind)) =
-                self.tcx().generator_kind(did)
-            {
+            if self.tcx().generator_is_async(*did) {
                 debug!(?self_ty, ?obligation, "assemble_future_candidates",);
 
                 candidates.vec.push(FutureCandidate);
@@ -451,7 +242,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
         obligation: &TraitObligation<'tcx>,
         candidates: &mut SelectionCandidateSet<'tcx>,
     ) {
-        let Some(kind) = self.tcx().fn_trait_kind_from_lang_item(obligation.predicate.def_id()) else {
+        let Some(kind) = self.tcx().fn_trait_kind_from_def_id(obligation.predicate.def_id()) else {
             return;
         };
 
@@ -489,7 +280,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
         candidates: &mut SelectionCandidateSet<'tcx>,
     ) {
         // We provide impl of all fn traits for fn pointers.
-        if self.tcx().fn_trait_kind_from_lang_item(obligation.predicate.def_id()).is_none() {
+        if !self.tcx().is_fn_trait(obligation.predicate.def_id()) {
             return;
         }
 
@@ -689,9 +480,9 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
 
             debug!(?poly_trait_ref, "assemble_candidates_from_object_ty");
 
-            let poly_trait_predicate = self.infcx().resolve_vars_if_possible(obligation.predicate);
+            let poly_trait_predicate = self.infcx.resolve_vars_if_possible(obligation.predicate);
             let placeholder_trait_predicate =
-                self.infcx().replace_bound_vars_with_placeholders(poly_trait_predicate);
+                self.infcx.replace_bound_vars_with_placeholders(poly_trait_predicate);
 
             // Count only those upcast versions that match the trait-ref
             // we are looking for. Specifically, do not only check for the
@@ -940,7 +731,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
             return;
         }
 
-        let self_ty = self.infcx().shallow_resolve(obligation.self_ty());
+        let self_ty = self.infcx.shallow_resolve(obligation.self_ty());
         match self_ty.skip_binder().kind() {
             ty::Opaque(..)
             | ty::Dynamic(..)
@@ -1007,7 +798,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
         obligation: &TraitObligation<'tcx>,
         candidates: &mut SelectionCandidateSet<'tcx>,
     ) {
-        let self_ty = self.infcx().shallow_resolve(obligation.self_ty().skip_binder());
+        let self_ty = self.infcx.shallow_resolve(obligation.self_ty().skip_binder());
         match self_ty.kind() {
             ty::Tuple(_) => {
                 candidates.vec.push(BuiltinCandidate { has_nested: false });
diff --git a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs
index 8c589aa8cd1..22cd700dcb5 100644
--- a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs
+++ b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs
@@ -147,7 +147,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
 
         let trait_predicate = self.infcx.shallow_resolve(obligation.predicate);
         let placeholder_trait_predicate =
-            self.infcx().replace_bound_vars_with_placeholders(trait_predicate).trait_ref;
+            self.infcx.replace_bound_vars_with_placeholders(trait_predicate).trait_ref;
         let placeholder_self_ty = placeholder_trait_predicate.self_ty();
         let placeholder_trait_predicate = ty::Binder::dummy(placeholder_trait_predicate);
         let (def_id, substs) = match *placeholder_self_ty.kind() {
@@ -639,7 +639,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
         debug!(?obligation, "confirm_trait_alias_candidate");
 
         let alias_def_id = obligation.predicate.def_id();
-        let predicate = self.infcx().replace_bound_vars_with_placeholders(obligation.predicate);
+        let predicate = self.infcx.replace_bound_vars_with_placeholders(obligation.predicate);
         let trait_ref = predicate.trait_ref;
         let trait_def_id = trait_ref.def_id;
         let substs = trait_ref.substs;
@@ -735,7 +735,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
     ) -> Result<ImplSourceClosureData<'tcx, PredicateObligation<'tcx>>, SelectionError<'tcx>> {
         let kind = self
             .tcx()
-            .fn_trait_kind_from_lang_item(obligation.predicate.def_id())
+            .fn_trait_kind_from_def_id(obligation.predicate.def_id())
             .unwrap_or_else(|| bug!("closure candidate for non-fn trait {:?}", obligation));
 
         // Okay to skip binder because the substs on closure types never
diff --git a/compiler/rustc_trait_selection/src/traits/select/mod.rs b/compiler/rustc_trait_selection/src/traits/select/mod.rs
index 6e8706897bf..515f3a34988 100644
--- a/compiler/rustc_trait_selection/src/traits/select/mod.rs
+++ b/compiler/rustc_trait_selection/src/traits/select/mod.rs
@@ -30,6 +30,7 @@ use crate::traits::error_reporting::TypeErrCtxtExt;
 use crate::traits::project::ProjectAndUnifyResult;
 use crate::traits::project::ProjectionCacheKeyExt;
 use crate::traits::ProjectionCacheKey;
+use crate::traits::Unimplemented;
 use rustc_data_structures::fx::FxHashMap;
 use rustc_data_structures::fx::{FxHashSet, FxIndexSet};
 use rustc_data_structures::stack::ensure_sufficient_stack;
@@ -55,6 +56,7 @@ use std::fmt::{self, Display};
 use std::iter;
 
 pub use rustc_middle::traits::select::*;
+use rustc_middle::ty::print::with_no_trimmed_paths;
 
 mod candidate_assembly;
 mod confirmation;
@@ -101,7 +103,7 @@ impl IntercrateAmbiguityCause {
 }
 
 pub struct SelectionContext<'cx, 'tcx> {
-    infcx: &'cx InferCtxt<'tcx>,
+    pub infcx: &'cx InferCtxt<'tcx>,
 
     /// Freshener used specifically for entries on the obligation
     /// stack. This ensures that all entries on the stack at one time
@@ -237,10 +239,6 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
         self.intercrate_ambiguity_causes.take().unwrap_or_default()
     }
 
-    pub fn infcx(&self) -> &'cx InferCtxt<'tcx> {
-        self.infcx
-    }
-
     pub fn tcx(&self) -> TyCtxt<'tcx> {
         self.infcx.tcx
     }
@@ -309,6 +307,208 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
         self.candidate_from_obligation(&stack)
     }
 
+    #[instrument(level = "debug", skip(self), ret)]
+    fn candidate_from_obligation<'o>(
+        &mut self,
+        stack: &TraitObligationStack<'o, 'tcx>,
+    ) -> SelectionResult<'tcx, SelectionCandidate<'tcx>> {
+        // Watch out for overflow. This intentionally bypasses (and does
+        // not update) the cache.
+        self.check_recursion_limit(&stack.obligation, &stack.obligation)?;
+
+        // Check the cache. Note that we freshen the trait-ref
+        // separately rather than using `stack.fresh_trait_ref` --
+        // this is because we want the unbound variables to be
+        // replaced with fresh types starting from index 0.
+        let cache_fresh_trait_pred = self.infcx.freshen(stack.obligation.predicate);
+        debug!(?cache_fresh_trait_pred);
+        debug_assert!(!stack.obligation.predicate.has_escaping_bound_vars());
+
+        if let Some(c) =
+            self.check_candidate_cache(stack.obligation.param_env, cache_fresh_trait_pred)
+        {
+            debug!("CACHE HIT");
+            return c;
+        }
+
+        // If no match, compute result and insert into cache.
+        //
+        // FIXME(nikomatsakis) -- this cache is not taking into
+        // account cycles that may have occurred in forming the
+        // candidate. I don't know of any specific problems that
+        // result but it seems awfully suspicious.
+        let (candidate, dep_node) =
+            self.in_task(|this| this.candidate_from_obligation_no_cache(stack));
+
+        debug!("CACHE MISS");
+        self.insert_candidate_cache(
+            stack.obligation.param_env,
+            cache_fresh_trait_pred,
+            dep_node,
+            candidate.clone(),
+        );
+        candidate
+    }
+
+    fn candidate_from_obligation_no_cache<'o>(
+        &mut self,
+        stack: &TraitObligationStack<'o, 'tcx>,
+    ) -> SelectionResult<'tcx, SelectionCandidate<'tcx>> {
+        if let Err(conflict) = self.is_knowable(stack) {
+            debug!("coherence stage: not knowable");
+            if self.intercrate_ambiguity_causes.is_some() {
+                debug!("evaluate_stack: intercrate_ambiguity_causes is some");
+                // Heuristics: show the diagnostics when there are no candidates in crate.
+                if let Ok(candidate_set) = self.assemble_candidates(stack) {
+                    let mut no_candidates_apply = true;
+
+                    for c in candidate_set.vec.iter() {
+                        if self.evaluate_candidate(stack, &c)?.may_apply() {
+                            no_candidates_apply = false;
+                            break;
+                        }
+                    }
+
+                    if !candidate_set.ambiguous && no_candidates_apply {
+                        let trait_ref = stack.obligation.predicate.skip_binder().trait_ref;
+                        let self_ty = trait_ref.self_ty();
+                        let (trait_desc, self_desc) = with_no_trimmed_paths!({
+                            let trait_desc = trait_ref.print_only_trait_path().to_string();
+                            let self_desc = if self_ty.has_concrete_skeleton() {
+                                Some(self_ty.to_string())
+                            } else {
+                                None
+                            };
+                            (trait_desc, self_desc)
+                        });
+                        let cause = if let Conflict::Upstream = conflict {
+                            IntercrateAmbiguityCause::UpstreamCrateUpdate { trait_desc, self_desc }
+                        } else {
+                            IntercrateAmbiguityCause::DownstreamCrate { trait_desc, self_desc }
+                        };
+                        debug!(?cause, "evaluate_stack: pushing cause");
+                        self.intercrate_ambiguity_causes.as_mut().unwrap().insert(cause);
+                    }
+                }
+            }
+            return Ok(None);
+        }
+
+        let candidate_set = self.assemble_candidates(stack)?;
+
+        if candidate_set.ambiguous {
+            debug!("candidate set contains ambig");
+            return Ok(None);
+        }
+
+        let candidates = candidate_set.vec;
+
+        debug!(?stack, ?candidates, "assembled {} candidates", candidates.len());
+
+        // At this point, we know that each of the entries in the
+        // candidate set is *individually* applicable. Now we have to
+        // figure out if they contain mutual incompatibilities. This
+        // frequently arises if we have an unconstrained input type --
+        // for example, we are looking for `$0: Eq` where `$0` is some
+        // unconstrained type variable. In that case, we'll get a
+        // candidate which assumes $0 == int, one that assumes `$0 ==
+        // usize`, etc. This spells an ambiguity.
+
+        let mut candidates = self.filter_impls(candidates, stack.obligation);
+
+        // If there is more than one candidate, first winnow them down
+        // by considering extra conditions (nested obligations and so
+        // forth). We don't winnow if there is exactly one
+        // candidate. This is a relatively minor distinction but it
+        // can lead to better inference and error-reporting. An
+        // example would be if there was an impl:
+        //
+        //     impl<T:Clone> Vec<T> { fn push_clone(...) { ... } }
+        //
+        // and we were to see some code `foo.push_clone()` where `boo`
+        // is a `Vec<Bar>` and `Bar` does not implement `Clone`.  If
+        // we were to winnow, we'd wind up with zero candidates.
+        // Instead, we select the right impl now but report "`Bar` does
+        // not implement `Clone`".
+        if candidates.len() == 1 {
+            return self.filter_reservation_impls(candidates.pop().unwrap(), stack.obligation);
+        }
+
+        // Winnow, but record the exact outcome of evaluation, which
+        // is needed for specialization. Propagate overflow if it occurs.
+        let mut candidates = candidates
+            .into_iter()
+            .map(|c| match self.evaluate_candidate(stack, &c) {
+                Ok(eval) if eval.may_apply() => {
+                    Ok(Some(EvaluatedCandidate { candidate: c, evaluation: eval }))
+                }
+                Ok(_) => Ok(None),
+                Err(OverflowError::Canonical) => Err(Overflow(OverflowError::Canonical)),
+                Err(OverflowError::ErrorReporting) => Err(ErrorReporting),
+                Err(OverflowError::Error(e)) => Err(Overflow(OverflowError::Error(e))),
+            })
+            .flat_map(Result::transpose)
+            .collect::<Result<Vec<_>, _>>()?;
+
+        debug!(?stack, ?candidates, "winnowed to {} candidates", candidates.len());
+
+        let needs_infer = stack.obligation.predicate.has_non_region_infer();
+
+        // If there are STILL multiple candidates, we can further
+        // reduce the list by dropping duplicates -- including
+        // resolving specializations.
+        if candidates.len() > 1 {
+            let mut i = 0;
+            while i < candidates.len() {
+                let is_dup = (0..candidates.len()).filter(|&j| i != j).any(|j| {
+                    self.candidate_should_be_dropped_in_favor_of(
+                        &candidates[i],
+                        &candidates[j],
+                        needs_infer,
+                    )
+                });
+                if is_dup {
+                    debug!(candidate = ?candidates[i], "Dropping candidate #{}/{}", i, candidates.len());
+                    candidates.swap_remove(i);
+                } else {
+                    debug!(candidate = ?candidates[i], "Retaining candidate #{}/{}", i, candidates.len());
+                    i += 1;
+
+                    // If there are *STILL* multiple candidates, give up
+                    // and report ambiguity.
+                    if i > 1 {
+                        debug!("multiple matches, ambig");
+                        return Ok(None);
+                    }
+                }
+            }
+        }
+
+        // If there are *NO* candidates, then there are no impls --
+        // that we know of, anyway. Note that in the case where there
+        // are unbound type variables within the obligation, it might
+        // be the case that you could still satisfy the obligation
+        // from another crate by instantiating the type variables with
+        // a type from another crate that does have an impl. This case
+        // is checked for in `evaluate_stack` (and hence users
+        // who might care about this case, like coherence, should use
+        // that function).
+        if candidates.is_empty() {
+            // If there's an error type, 'downgrade' our result from
+            // `Err(Unimplemented)` to `Ok(None)`. This helps us avoid
+            // emitting additional spurious errors, since we're guaranteed
+            // to have emitted at least one.
+            if stack.obligation.predicate.references_error() {
+                debug!(?stack.obligation.predicate, "found error type in predicate, treating as ambiguous");
+                return Ok(None);
+            }
+            return Err(Unimplemented);
+        }
+
+        // Just one candidate left.
+        self.filter_reservation_impls(candidates.pop().unwrap().candidate, stack.obligation)
+    }
+
     ///////////////////////////////////////////////////////////////////////////
     // EVALUATION
     //
@@ -734,10 +934,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
 
                     match (evaluate(c1), evaluate(c2)) {
                         (Ok(c1), Ok(c2)) => {
-                            match self
-                                .infcx()
-                                .at(&obligation.cause, obligation.param_env)
-                                .eq(c1, c2)
+                            match self.infcx.at(&obligation.cause, obligation.param_env).eq(c1, c2)
                             {
                                 Ok(inf_ok) => self.evaluate_predicates_recursively(
                                     previous_stack,
@@ -1256,7 +1453,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
         }
 
         let obligation = &stack.obligation;
-        let predicate = self.infcx().resolve_vars_if_possible(obligation.predicate);
+        let predicate = self.infcx.resolve_vars_if_possible(obligation.predicate);
 
         // Okay to skip binder because of the nature of the
         // trait-ref-is-knowable check, which does not care about
@@ -1393,9 +1590,9 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
         &mut self,
         obligation: &TraitObligation<'tcx>,
     ) -> smallvec::SmallVec<[(usize, ty::BoundConstness); 2]> {
-        let poly_trait_predicate = self.infcx().resolve_vars_if_possible(obligation.predicate);
+        let poly_trait_predicate = self.infcx.resolve_vars_if_possible(obligation.predicate);
         let placeholder_trait_predicate =
-            self.infcx().replace_bound_vars_with_placeholders(poly_trait_predicate);
+            self.infcx.replace_bound_vars_with_placeholders(poly_trait_predicate);
         debug!(?placeholder_trait_predicate);
 
         let tcx = self.infcx.tcx;
@@ -2147,6 +2344,13 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
         match self.match_impl(impl_def_id, impl_trait_ref, obligation) {
             Ok(substs) => substs,
             Err(()) => {
+                // FIXME: A rematch may fail when a candidate cache hit occurs
+                // on thefreshened form of the trait predicate, but the match
+                // fails for some reason that is not captured in the freshened
+                // cache key. For example, equating an impl trait ref against
+                // the placeholder trait ref may fail due the Generalizer relation
+                // raising a CyclicalTy error due to a sub_root_var relation
+                // for a variable being generalized...
                 self.infcx.tcx.sess.delay_span_bug(
                     obligation.cause.span,
                     &format!(
@@ -2175,7 +2379,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
         obligation: &TraitObligation<'tcx>,
     ) -> Result<Normalized<'tcx, SubstsRef<'tcx>>, ()> {
         let placeholder_obligation =
-            self.infcx().replace_bound_vars_with_placeholders(obligation.predicate);
+            self.infcx.replace_bound_vars_with_placeholders(obligation.predicate);
         let placeholder_obligation_trait_ref = placeholder_obligation.trait_ref;
 
         let impl_substs = self.infcx.fresh_substs_for_item(obligation.cause.span, impl_def_id);
diff --git a/compiler/rustc_trait_selection/src/traits/specialize/mod.rs b/compiler/rustc_trait_selection/src/traits/specialize/mod.rs
index 7ab4bc3cb85..a251a508b48 100644
--- a/compiler/rustc_trait_selection/src/traits/specialize/mod.rs
+++ b/compiler/rustc_trait_selection/src/traits/specialize/mod.rs
@@ -230,7 +230,7 @@ fn fulfill_implication<'tcx>(
     Ok(infcx.resolve_vars_if_possible(target_substs))
 }
 
-// Query provider for `specialization_graph_of`.
+/// Query provider for `specialization_graph_of`.
 pub(super) fn specialization_graph_provider(
     tcx: TyCtxt<'_>,
     trait_id: DefId,
diff --git a/compiler/rustc_trait_selection/src/traits/util.rs b/compiler/rustc_trait_selection/src/traits/util.rs
index dae7d589d5c..51968c2d7a1 100644
--- a/compiler/rustc_trait_selection/src/traits/util.rs
+++ b/compiler/rustc_trait_selection/src/traits/util.rs
@@ -8,8 +8,8 @@ use rustc_hir::def_id::DefId;
 use rustc_middle::ty::{self, ImplSubject, ToPredicate, Ty, TyCtxt, TypeVisitable};
 use rustc_middle::ty::{GenericArg, SubstsRef};
 
+use super::NormalizeExt;
 use super::{Obligation, ObligationCause, PredicateObligation, SelectionContext};
-use crate::infer::InferCtxtExt;
 use rustc_infer::infer::InferOk;
 pub use rustc_infer::traits::{self, util::*};
 
@@ -202,15 +202,13 @@ pub fn impl_subject_and_oblig<'a, 'tcx>(
 ) -> (ImplSubject<'tcx>, impl Iterator<Item = PredicateObligation<'tcx>>) {
     let subject = selcx.tcx().bound_impl_subject(impl_def_id);
     let subject = subject.subst(selcx.tcx(), impl_substs);
-    let InferOk { value: subject, obligations: normalization_obligations1 } = selcx
-        .infcx()
-        .partially_normalize_associated_types_in(ObligationCause::dummy(), param_env, subject);
+    let InferOk { value: subject, obligations: normalization_obligations1 } =
+        selcx.infcx.at(&ObligationCause::dummy(), param_env).normalize(subject);
 
     let predicates = selcx.tcx().predicates_of(impl_def_id);
     let predicates = predicates.instantiate(selcx.tcx(), impl_substs);
-    let InferOk { value: predicates, obligations: normalization_obligations2 } = selcx
-        .infcx()
-        .partially_normalize_associated_types_in(ObligationCause::dummy(), param_env, predicates);
+    let InferOk { value: predicates, obligations: normalization_obligations2 } =
+        selcx.infcx.at(&ObligationCause::dummy(), param_env).normalize(predicates);
     let impl_obligations =
         super::predicates_for_generics(|_, _| ObligationCause::dummy(), param_env, predicates);
 
diff --git a/compiler/rustc_traits/src/dropck_outlives.rs b/compiler/rustc_traits/src/dropck_outlives.rs
index 7b4ad9fea13..66ab742f157 100644
--- a/compiler/rustc_traits/src/dropck_outlives.rs
+++ b/compiler/rustc_traits/src/dropck_outlives.rs
@@ -11,7 +11,7 @@ use rustc_trait_selection::traits::query::dropck_outlives::trivial_dropck_outliv
 use rustc_trait_selection::traits::query::dropck_outlives::{
     DropckConstraint, DropckOutlivesResult,
 };
-use rustc_trait_selection::traits::query::normalize::AtExt;
+use rustc_trait_selection::traits::query::normalize::QueryNormalizeExt;
 use rustc_trait_selection::traits::query::{CanonicalTyGoal, NoSolution};
 use rustc_trait_selection::traits::{Normalized, ObligationCause};
 
@@ -100,7 +100,7 @@ fn dropck_outlives<'tcx>(
             // to push them onto the stack to be expanded.
             for ty in constraints.dtorck_types.drain(..) {
                 let Normalized { value: ty, obligations } =
-                    ocx.infcx.at(&cause, param_env).normalize(ty)?;
+                    ocx.infcx.at(&cause, param_env).query_normalize(ty)?;
                 ocx.register_obligations(obligations);
 
                 debug!("dropck_outlives: ty from dtorck_types = {:?}", ty);
diff --git a/compiler/rustc_traits/src/normalize_erasing_regions.rs b/compiler/rustc_traits/src/normalize_erasing_regions.rs
index bd22d113b40..44fd8bfb31f 100644
--- a/compiler/rustc_traits/src/normalize_erasing_regions.rs
+++ b/compiler/rustc_traits/src/normalize_erasing_regions.rs
@@ -2,7 +2,7 @@ use rustc_infer::infer::TyCtxtInferExt;
 use rustc_middle::traits::query::NoSolution;
 use rustc_middle::ty::query::Providers;
 use rustc_middle::ty::{self, ParamEnvAnd, TyCtxt, TypeFoldable};
-use rustc_trait_selection::traits::query::normalize::AtExt;
+use rustc_trait_selection::traits::query::normalize::QueryNormalizeExt;
 use rustc_trait_selection::traits::{Normalized, ObligationCause};
 use std::sync::atomic::Ordering;
 
@@ -29,7 +29,7 @@ fn try_normalize_after_erasing_regions<'tcx, T: TypeFoldable<'tcx> + PartialEq +
     let ParamEnvAnd { param_env, value } = goal;
     let infcx = tcx.infer_ctxt().build();
     let cause = ObligationCause::dummy();
-    match infcx.at(&cause, param_env).normalize(value) {
+    match infcx.at(&cause, param_env).query_normalize(value) {
         Ok(Normalized { value: normalized_value, obligations: normalized_obligations }) => {
             // We don't care about the `obligations`; they are
             // always only region relations, and we are about to
diff --git a/compiler/rustc_traits/src/type_op.rs b/compiler/rustc_traits/src/type_op.rs
index c6c072ea3d2..7f964afde80 100644
--- a/compiler/rustc_traits/src/type_op.rs
+++ b/compiler/rustc_traits/src/type_op.rs
@@ -8,7 +8,7 @@ use rustc_middle::ty::{ParamEnvAnd, Predicate, ToPredicate};
 use rustc_middle::ty::{UserSelfTy, UserSubsts};
 use rustc_span::{Span, DUMMY_SP};
 use rustc_trait_selection::infer::InferCtxtBuilderExt;
-use rustc_trait_selection::traits::query::normalize::AtExt;
+use rustc_trait_selection::traits::query::normalize::QueryNormalizeExt;
 use rustc_trait_selection::traits::query::type_op::ascribe_user_type::AscribeUserType;
 use rustc_trait_selection::traits::query::type_op::eq::Eq;
 use rustc_trait_selection::traits::query::type_op::normalize::Normalize;
@@ -62,7 +62,7 @@ pub fn type_op_ascribe_user_type_with_span<'tcx>(
     let cause = ObligationCause::dummy_with_span(span);
 
     let ty = tcx.bound_type_of(def_id).subst(tcx, substs);
-    let ty = ocx.normalize(cause.clone(), param_env, ty);
+    let ty = ocx.normalize(&cause, param_env, ty);
     debug!("relate_type_and_user_type: ty of def-id is {:?}", ty);
 
     ocx.eq(&cause, param_env, mir_ty, ty)?;
@@ -85,14 +85,14 @@ pub fn type_op_ascribe_user_type_with_span<'tcx>(
             ObligationCauseCode::AscribeUserTypeProvePredicate(predicate_span),
         );
         let instantiated_predicate =
-            ocx.normalize(cause.clone(), param_env, instantiated_predicate);
+            ocx.normalize(&cause.clone(), param_env, instantiated_predicate);
 
         ocx.register_obligation(Obligation::new(tcx, cause, param_env, instantiated_predicate));
     }
 
     if let Some(UserSelfTy { impl_def_id, self_ty }) = user_self_ty {
         let impl_self_ty = tcx.bound_type_of(impl_def_id).subst(tcx, substs);
-        let impl_self_ty = ocx.normalize(cause.clone(), param_env, impl_self_ty);
+        let impl_self_ty = ocx.normalize(&cause, param_env, impl_self_ty);
 
         ocx.eq(&cause, param_env, self_ty, impl_self_ty)?;
 
@@ -137,7 +137,7 @@ where
 {
     let (param_env, Normalize { value }) = key.into_parts();
     let Normalized { value, obligations } =
-        ocx.infcx.at(&ObligationCause::dummy(), param_env).normalize(value)?;
+        ocx.infcx.at(&ObligationCause::dummy(), param_env).query_normalize(value)?;
     ocx.register_obligations(obligations);
     Ok(value)
 }
diff --git a/compiler/rustc_ty_utils/src/consts.rs b/compiler/rustc_ty_utils/src/consts.rs
index 2b7018bc9c3..f8ff31f971b 100644
--- a/compiler/rustc_ty_utils/src/consts.rs
+++ b/compiler/rustc_ty_utils/src/consts.rs
@@ -5,7 +5,7 @@ use rustc_middle::mir::interpret::{LitToConstError, LitToConstInput};
 use rustc_middle::thir::visit;
 use rustc_middle::thir::visit::Visitor;
 use rustc_middle::ty::abstract_const::CastKind;
-use rustc_middle::ty::{self, ConstKind, Expr, TyCtxt, TypeVisitable};
+use rustc_middle::ty::{self, Expr, TyCtxt, TypeVisitable};
 use rustc_middle::{mir, thir};
 use rustc_span::Span;
 use rustc_target::abi::VariantIdx;
@@ -32,10 +32,8 @@ pub(crate) fn destructure_const<'tcx>(
     let (fields, variant) = match const_.ty().kind() {
         ty::Array(inner_ty, _) | ty::Slice(inner_ty) => {
             // construct the consts for the elements of the array/slice
-            let field_consts = branches
-                .iter()
-                .map(|b| tcx.mk_const(ty::ConstKind::Value(*b), *inner_ty))
-                .collect::<Vec<_>>();
+            let field_consts =
+                branches.iter().map(|b| tcx.mk_const(*b, *inner_ty)).collect::<Vec<_>>();
             debug!(?field_consts);
 
             (field_consts, None)
@@ -53,7 +51,7 @@ pub(crate) fn destructure_const<'tcx>(
 
             for (field, field_valtree) in iter::zip(fields, branches) {
                 let field_ty = field.ty(tcx, substs);
-                let field_const = tcx.mk_const(ty::ConstKind::Value(*field_valtree), field_ty);
+                let field_const = tcx.mk_const(*field_valtree, field_ty);
                 field_consts.push(field_const);
             }
             debug!(?field_consts);
@@ -62,9 +60,7 @@ pub(crate) fn destructure_const<'tcx>(
         }
         ty::Tuple(elem_tys) => {
             let fields = iter::zip(*elem_tys, branches)
-                .map(|(elem_ty, elem_valtree)| {
-                    tcx.mk_const(ty::ConstKind::Value(*elem_valtree), elem_ty)
-                })
+                .map(|(elem_ty, elem_valtree)| tcx.mk_const(*elem_valtree, elem_ty))
                 .collect::<Vec<_>>();
 
             (fields, None)
@@ -129,17 +125,17 @@ fn recurse_build<'tcx>(
         }
         &ExprKind::NonHirLiteral { lit, user_ty: _ } => {
             let val = ty::ValTree::from_scalar_int(lit);
-            ty::Const::from_value(tcx, val, node.ty)
+            tcx.mk_const(val, node.ty)
         }
         &ExprKind::ZstLiteral { user_ty: _ } => {
             let val = ty::ValTree::zst();
-            ty::Const::from_value(tcx, val, node.ty)
+            tcx.mk_const(val, node.ty)
         }
         &ExprKind::NamedConst { def_id, substs, user_ty: _ } => {
             let uneval = ty::UnevaluatedConst::new(ty::WithOptConstParam::unknown(def_id), substs);
-            tcx.mk_const(ty::ConstKind::Unevaluated(uneval), node.ty)
+            tcx.mk_const(uneval, node.ty)
         }
-        ExprKind::ConstParam { param, .. } => tcx.mk_const(ty::ConstKind::Param(*param), node.ty),
+        ExprKind::ConstParam { param, .. } => tcx.mk_const(*param, node.ty),
 
         ExprKind::Call { fun, args, .. } => {
             let fun = recurse_build(tcx, body, *fun, root_span)?;
@@ -149,16 +145,16 @@ fn recurse_build<'tcx>(
                 new_args.push(recurse_build(tcx, body, id, root_span)?);
             }
             let new_args = tcx.mk_const_list(new_args.iter());
-            tcx.mk_const(ConstKind::Expr(Expr::FunctionCall(fun, new_args)), node.ty)
+            tcx.mk_const(Expr::FunctionCall(fun, new_args), node.ty)
         }
         &ExprKind::Binary { op, lhs, rhs } if check_binop(op) => {
             let lhs = recurse_build(tcx, body, lhs, root_span)?;
             let rhs = recurse_build(tcx, body, rhs, root_span)?;
-            tcx.mk_const(ConstKind::Expr(Expr::Binop(op, lhs, rhs)), node.ty)
+            tcx.mk_const(Expr::Binop(op, lhs, rhs), node.ty)
         }
         &ExprKind::Unary { op, arg } if check_unop(op) => {
             let arg = recurse_build(tcx, body, arg, root_span)?;
-            tcx.mk_const(ConstKind::Expr(Expr::UnOp(op, arg)), node.ty)
+            tcx.mk_const(Expr::UnOp(op, arg), node.ty)
         }
         // This is necessary so that the following compiles:
         //
@@ -179,11 +175,11 @@ fn recurse_build<'tcx>(
         // This is important so that `N as usize as usize` doesnt unify with `N as usize`. (untested)
         &ExprKind::Use { source } => {
             let arg = recurse_build(tcx, body, source, root_span)?;
-            tcx.mk_const(ConstKind::Expr(Expr::Cast(CastKind::Use, arg, node.ty)), node.ty)
+            tcx.mk_const(Expr::Cast(CastKind::Use, arg, node.ty), node.ty)
         }
         &ExprKind::Cast { source } => {
             let arg = recurse_build(tcx, body, source, root_span)?;
-            tcx.mk_const(ConstKind::Expr(Expr::Cast(CastKind::As, arg, node.ty)), node.ty)
+            tcx.mk_const(Expr::Cast(CastKind::As, arg, node.ty), node.ty)
         }
         ExprKind::Borrow { arg, .. } => {
             let arg_node = &body.exprs[*arg];
diff --git a/compiler/rustc_ty_utils/src/instance.rs b/compiler/rustc_ty_utils/src/instance.rs
index 4a887bc5918..c6f2b16ca21 100644
--- a/compiler/rustc_ty_utils/src/instance.rs
+++ b/compiler/rustc_ty_utils/src/instance.rs
@@ -209,7 +209,7 @@ fn resolve_associated_item<'tcx>(
             substs: future_data.substs,
         }),
         traits::ImplSource::Closure(closure_data) => {
-            let trait_closure_kind = tcx.fn_trait_kind_from_lang_item(trait_id).unwrap();
+            let trait_closure_kind = tcx.fn_trait_kind_from_def_id(trait_id).unwrap();
             Instance::resolve_closure(
                 tcx,
                 closure_data.closure_def_id,
diff --git a/compiler/rustc_ty_utils/src/layout_sanity_check.rs b/compiler/rustc_ty_utils/src/layout_sanity_check.rs
index 9eb8f684bdb..a5311dbd1b7 100644
--- a/compiler/rustc_ty_utils/src/layout_sanity_check.rs
+++ b/compiler/rustc_ty_utils/src/layout_sanity_check.rs
@@ -20,283 +20,293 @@ pub(super) fn sanity_check_layout<'tcx>(
         bug!("size is not a multiple of align, in the following layout:\n{layout:#?}");
     }
 
-    if cfg!(debug_assertions) {
-        /// Yields non-ZST fields of the type
-        fn non_zst_fields<'tcx, 'a>(
-            cx: &'a LayoutCx<'tcx, TyCtxt<'tcx>>,
-            layout: &'a TyAndLayout<'tcx>,
-        ) -> impl Iterator<Item = (Size, TyAndLayout<'tcx>)> + 'a {
-            (0..layout.layout.fields().count()).filter_map(|i| {
-                let field = layout.field(cx, i);
-                // Also checking `align == 1` here leads to test failures in
-                // `layout/zero-sized-array-union.rs`, where a type has a zero-size field with
-                // alignment 4 that still gets ignored during layout computation (which is okay
-                // since other fields already force alignment 4).
-                let zst = field.is_zst();
-                (!zst).then(|| (layout.fields.offset(i), field))
-            })
-        }
+    if !cfg!(debug_assertions) {
+        // Stop here, the rest is kind of expensive.
+        return;
+    }
 
-        fn skip_newtypes<'tcx>(
-            cx: &LayoutCx<'tcx, TyCtxt<'tcx>>,
-            layout: &TyAndLayout<'tcx>,
-        ) -> TyAndLayout<'tcx> {
-            if matches!(layout.layout.variants(), Variants::Multiple { .. }) {
-                // Definitely not a newtype of anything.
-                return *layout;
-            }
-            let mut fields = non_zst_fields(cx, layout);
-            let Some(first) = fields.next() else {
-                // No fields here, so this could be a primitive or enum -- either way it's not a newtype around a thing
-                return *layout
-            };
-            if fields.next().is_none() {
-                let (offset, first) = first;
-                if offset == Size::ZERO && first.layout.size() == layout.size {
-                    // This is a newtype, so keep recursing.
-                    // FIXME(RalfJung): I don't think it would be correct to do any checks for
-                    // alignment here, so we don't. Is that correct?
-                    return skip_newtypes(cx, &first);
-                }
+    /// Yields non-ZST fields of the type
+    fn non_zst_fields<'tcx, 'a>(
+        cx: &'a LayoutCx<'tcx, TyCtxt<'tcx>>,
+        layout: &'a TyAndLayout<'tcx>,
+    ) -> impl Iterator<Item = (Size, TyAndLayout<'tcx>)> + 'a {
+        (0..layout.layout.fields().count()).filter_map(|i| {
+            let field = layout.field(cx, i);
+            // Also checking `align == 1` here leads to test failures in
+            // `layout/zero-sized-array-union.rs`, where a type has a zero-size field with
+            // alignment 4 that still gets ignored during layout computation (which is okay
+            // since other fields already force alignment 4).
+            let zst = field.is_zst();
+            (!zst).then(|| (layout.fields.offset(i), field))
+        })
+    }
+
+    fn skip_newtypes<'tcx>(
+        cx: &LayoutCx<'tcx, TyCtxt<'tcx>>,
+        layout: &TyAndLayout<'tcx>,
+    ) -> TyAndLayout<'tcx> {
+        if matches!(layout.layout.variants(), Variants::Multiple { .. }) {
+            // Definitely not a newtype of anything.
+            return *layout;
+        }
+        let mut fields = non_zst_fields(cx, layout);
+        let Some(first) = fields.next() else {
+            // No fields here, so this could be a primitive or enum -- either way it's not a newtype around a thing
+            return *layout
+        };
+        if fields.next().is_none() {
+            let (offset, first) = first;
+            if offset == Size::ZERO && first.layout.size() == layout.size {
+                // This is a newtype, so keep recursing.
+                // FIXME(RalfJung): I don't think it would be correct to do any checks for
+                // alignment here, so we don't. Is that correct?
+                return skip_newtypes(cx, &first);
             }
-            // No more newtypes here.
-            *layout
         }
+        // No more newtypes here.
+        *layout
+    }
 
-        fn check_layout_abi<'tcx>(cx: &LayoutCx<'tcx, TyCtxt<'tcx>>, layout: &TyAndLayout<'tcx>) {
-            match layout.layout.abi() {
-                Abi::Scalar(scalar) => {
-                    // No padding in scalars.
-                    let size = scalar.size(cx);
-                    let align = scalar.align(cx).abi;
-                    assert_eq!(
-                        layout.layout.size(),
-                        size,
-                        "size mismatch between ABI and layout in {layout:#?}"
-                    );
-                    assert_eq!(
-                        layout.layout.align().abi,
-                        align,
-                        "alignment mismatch between ABI and layout in {layout:#?}"
-                    );
-                    // Check that this matches the underlying field.
-                    let inner = skip_newtypes(cx, layout);
-                    assert!(
-                        matches!(inner.layout.abi(), Abi::Scalar(_)),
-                        "`Scalar` type {} is newtype around non-`Scalar` type {}",
-                        layout.ty,
-                        inner.ty
-                    );
-                    match inner.layout.fields() {
-                        FieldsShape::Primitive => {
-                            // Fine.
-                        }
-                        FieldsShape::Union(..) => {
-                            // FIXME: I guess we could also check something here? Like, look at all fields?
-                            return;
-                        }
-                        FieldsShape::Arbitrary { .. } => {
-                            // Should be an enum, the only field is the discriminant.
-                            assert!(
-                                inner.ty.is_enum(),
-                                "`Scalar` layout for non-primitive non-enum type {}",
-                                inner.ty
-                            );
-                            assert_eq!(
-                                inner.layout.fields().count(),
-                                1,
-                                "`Scalar` layout for multiple-field type in {inner:#?}",
-                            );
-                            let offset = inner.layout.fields().offset(0);
-                            let field = inner.field(cx, 0);
-                            // The field should be at the right offset, and match the `scalar` layout.
-                            assert_eq!(
-                                offset,
-                                Size::ZERO,
-                                "`Scalar` field at non-0 offset in {inner:#?}",
-                            );
-                            assert_eq!(
-                                field.size, size,
-                                "`Scalar` field with bad size in {inner:#?}",
-                            );
-                            assert_eq!(
-                                field.align.abi, align,
-                                "`Scalar` field with bad align in {inner:#?}",
-                            );
-                            assert!(
-                                matches!(field.abi, Abi::Scalar(_)),
-                                "`Scalar` field with bad ABI in {inner:#?}",
-                            );
-                        }
-                        _ => {
-                            panic!("`Scalar` layout for non-primitive non-enum type {}", inner.ty);
-                        }
+    fn check_layout_abi<'tcx>(cx: &LayoutCx<'tcx, TyCtxt<'tcx>>, layout: &TyAndLayout<'tcx>) {
+        match layout.layout.abi() {
+            Abi::Scalar(scalar) => {
+                // No padding in scalars.
+                let size = scalar.size(cx);
+                let align = scalar.align(cx).abi;
+                assert_eq!(
+                    layout.layout.size(),
+                    size,
+                    "size mismatch between ABI and layout in {layout:#?}"
+                );
+                assert_eq!(
+                    layout.layout.align().abi,
+                    align,
+                    "alignment mismatch between ABI and layout in {layout:#?}"
+                );
+                // Check that this matches the underlying field.
+                let inner = skip_newtypes(cx, layout);
+                assert!(
+                    matches!(inner.layout.abi(), Abi::Scalar(_)),
+                    "`Scalar` type {} is newtype around non-`Scalar` type {}",
+                    layout.ty,
+                    inner.ty
+                );
+                match inner.layout.fields() {
+                    FieldsShape::Primitive => {
+                        // Fine.
                     }
-                }
-                Abi::ScalarPair(scalar1, scalar2) => {
-                    // Sanity-check scalar pairs. These are a bit more flexible and support
-                    // padding, but we can at least ensure both fields actually fit into the layout
-                    // and the alignment requirement has not been weakened.
-                    let size1 = scalar1.size(cx);
-                    let align1 = scalar1.align(cx).abi;
-                    let size2 = scalar2.size(cx);
-                    let align2 = scalar2.align(cx).abi;
-                    assert!(
-                        layout.layout.align().abi >= cmp::max(align1, align2),
-                        "alignment mismatch between ABI and layout in {layout:#?}",
-                    );
-                    let field2_offset = size1.align_to(align2);
-                    assert!(
-                        layout.layout.size() >= field2_offset + size2,
-                        "size mismatch between ABI and layout in {layout:#?}"
-                    );
-                    // Check that the underlying pair of fields matches.
-                    let inner = skip_newtypes(cx, layout);
-                    assert!(
-                        matches!(inner.layout.abi(), Abi::ScalarPair(..)),
-                        "`ScalarPair` type {} is newtype around non-`ScalarPair` type {}",
-                        layout.ty,
-                        inner.ty
-                    );
-                    if matches!(inner.layout.variants(), Variants::Multiple { .. }) {
-                        // FIXME: ScalarPair for enums is enormously complicated and it is very hard
-                        // to check anything about them.
+                    FieldsShape::Union(..) => {
+                        // FIXME: I guess we could also check something here? Like, look at all fields?
                         return;
                     }
-                    match inner.layout.fields() {
-                        FieldsShape::Arbitrary { .. } => {
-                            // Checked below.
-                        }
-                        FieldsShape::Union(..) => {
-                            // FIXME: I guess we could also check something here? Like, look at all fields?
-                            return;
-                        }
-                        _ => {
-                            panic!("`ScalarPair` layout with unexpected field shape in {inner:#?}");
-                        }
+                    FieldsShape::Arbitrary { .. } => {
+                        // Should be an enum, the only field is the discriminant.
+                        assert!(
+                            inner.ty.is_enum(),
+                            "`Scalar` layout for non-primitive non-enum type {}",
+                            inner.ty
+                        );
+                        assert_eq!(
+                            inner.layout.fields().count(),
+                            1,
+                            "`Scalar` layout for multiple-field type in {inner:#?}",
+                        );
+                        let offset = inner.layout.fields().offset(0);
+                        let field = inner.field(cx, 0);
+                        // The field should be at the right offset, and match the `scalar` layout.
+                        assert_eq!(
+                            offset,
+                            Size::ZERO,
+                            "`Scalar` field at non-0 offset in {inner:#?}",
+                        );
+                        assert_eq!(field.size, size, "`Scalar` field with bad size in {inner:#?}",);
+                        assert_eq!(
+                            field.align.abi, align,
+                            "`Scalar` field with bad align in {inner:#?}",
+                        );
+                        assert!(
+                            matches!(field.abi, Abi::Scalar(_)),
+                            "`Scalar` field with bad ABI in {inner:#?}",
+                        );
+                    }
+                    _ => {
+                        panic!("`Scalar` layout for non-primitive non-enum type {}", inner.ty);
                     }
-                    let mut fields = non_zst_fields(cx, &inner);
-                    let (offset1, field1) = fields.next().unwrap_or_else(|| {
-                        panic!("`ScalarPair` layout for type with not even one non-ZST field: {inner:#?}")
-                    });
-                    let (offset2, field2) = fields.next().unwrap_or_else(|| {
-                        panic!("`ScalarPair` layout for type with less than two non-ZST fields: {inner:#?}")
-                    });
-                    assert!(
-                        fields.next().is_none(),
-                        "`ScalarPair` layout for type with at least three non-ZST fields: {inner:#?}"
-                    );
-                    // The fields might be in opposite order.
-                    let (offset1, field1, offset2, field2) = if offset1 <= offset2 {
-                        (offset1, field1, offset2, field2)
-                    } else {
-                        (offset2, field2, offset1, field1)
-                    };
-                    // The fields should be at the right offset, and match the `scalar` layout.
-                    assert_eq!(
-                        offset1,
-                        Size::ZERO,
-                        "`ScalarPair` first field at non-0 offset in {inner:#?}",
-                    );
-                    assert_eq!(
-                        field1.size, size1,
-                        "`ScalarPair` first field with bad size in {inner:#?}",
-                    );
-                    assert_eq!(
-                        field1.align.abi, align1,
-                        "`ScalarPair` first field with bad align in {inner:#?}",
-                    );
-                    assert!(
-                        matches!(field1.abi, Abi::Scalar(_)),
-                        "`ScalarPair` first field with bad ABI in {inner:#?}",
-                    );
-                    assert_eq!(
-                        offset2, field2_offset,
-                        "`ScalarPair` second field at bad offset in {inner:#?}",
-                    );
-                    assert_eq!(
-                        field2.size, size2,
-                        "`ScalarPair` second field with bad size in {inner:#?}",
-                    );
-                    assert_eq!(
-                        field2.align.abi, align2,
-                        "`ScalarPair` second field with bad align in {inner:#?}",
-                    );
-                    assert!(
-                        matches!(field2.abi, Abi::Scalar(_)),
-                        "`ScalarPair` second field with bad ABI in {inner:#?}",
-                    );
                 }
-                Abi::Vector { count, element } => {
-                    // No padding in vectors. Alignment can be strengthened, though.
-                    assert!(
-                        layout.layout.align().abi >= element.align(cx).abi,
-                        "alignment mismatch between ABI and layout in {layout:#?}"
-                    );
-                    let size = element.size(cx) * count;
-                    assert_eq!(
-                        layout.layout.size(),
-                        size.align_to(cx.data_layout().vector_align(size).abi),
-                        "size mismatch between ABI and layout in {layout:#?}"
-                    );
+            }
+            Abi::ScalarPair(scalar1, scalar2) => {
+                // Sanity-check scalar pairs. Computing the expected size and alignment is a bit of work.
+                let size1 = scalar1.size(cx);
+                let align1 = scalar1.align(cx).abi;
+                let size2 = scalar2.size(cx);
+                let align2 = scalar2.align(cx).abi;
+                let align = cmp::max(align1, align2);
+                let field2_offset = size1.align_to(align2);
+                let size = (field2_offset + size2).align_to(align);
+                assert_eq!(
+                    layout.layout.size(),
+                    size,
+                    "size mismatch between ABI and layout in {layout:#?}"
+                );
+                assert_eq!(
+                    layout.layout.align().abi,
+                    align,
+                    "alignment mismatch between ABI and layout in {layout:#?}",
+                );
+                // Check that the underlying pair of fields matches.
+                let inner = skip_newtypes(cx, layout);
+                assert!(
+                    matches!(inner.layout.abi(), Abi::ScalarPair(..)),
+                    "`ScalarPair` type {} is newtype around non-`ScalarPair` type {}",
+                    layout.ty,
+                    inner.ty
+                );
+                if matches!(inner.layout.variants(), Variants::Multiple { .. }) {
+                    // FIXME: ScalarPair for enums is enormously complicated and it is very hard
+                    // to check anything about them.
+                    return;
+                }
+                match inner.layout.fields() {
+                    FieldsShape::Arbitrary { .. } => {
+                        // Checked below.
+                    }
+                    FieldsShape::Union(..) => {
+                        // FIXME: I guess we could also check something here? Like, look at all fields?
+                        return;
+                    }
+                    _ => {
+                        panic!("`ScalarPair` layout with unexpected field shape in {inner:#?}");
+                    }
                 }
-                Abi::Uninhabited | Abi::Aggregate { .. } => {} // Nothing to check.
+                let mut fields = non_zst_fields(cx, &inner);
+                let (offset1, field1) = fields.next().unwrap_or_else(|| {
+                    panic!(
+                        "`ScalarPair` layout for type with not even one non-ZST field: {inner:#?}"
+                    )
+                });
+                let (offset2, field2) = fields.next().unwrap_or_else(|| {
+                    panic!(
+                        "`ScalarPair` layout for type with less than two non-ZST fields: {inner:#?}"
+                    )
+                });
+                assert!(
+                    fields.next().is_none(),
+                    "`ScalarPair` layout for type with at least three non-ZST fields: {inner:#?}"
+                );
+                // The fields might be in opposite order.
+                let (offset1, field1, offset2, field2) = if offset1 <= offset2 {
+                    (offset1, field1, offset2, field2)
+                } else {
+                    (offset2, field2, offset1, field1)
+                };
+                // The fields should be at the right offset, and match the `scalar` layout.
+                assert_eq!(
+                    offset1,
+                    Size::ZERO,
+                    "`ScalarPair` first field at non-0 offset in {inner:#?}",
+                );
+                assert_eq!(
+                    field1.size, size1,
+                    "`ScalarPair` first field with bad size in {inner:#?}",
+                );
+                assert_eq!(
+                    field1.align.abi, align1,
+                    "`ScalarPair` first field with bad align in {inner:#?}",
+                );
+                assert!(
+                    matches!(field1.abi, Abi::Scalar(_)),
+                    "`ScalarPair` first field with bad ABI in {inner:#?}",
+                );
+                assert_eq!(
+                    offset2, field2_offset,
+                    "`ScalarPair` second field at bad offset in {inner:#?}",
+                );
+                assert_eq!(
+                    field2.size, size2,
+                    "`ScalarPair` second field with bad size in {inner:#?}",
+                );
+                assert_eq!(
+                    field2.align.abi, align2,
+                    "`ScalarPair` second field with bad align in {inner:#?}",
+                );
+                assert!(
+                    matches!(field2.abi, Abi::Scalar(_)),
+                    "`ScalarPair` second field with bad ABI in {inner:#?}",
+                );
             }
+            Abi::Vector { count, element } => {
+                // No padding in vectors, except possibly for trailing padding to make the size a multiple of align.
+                let size = element.size(cx) * count;
+                let align = cx.data_layout().vector_align(size).abi;
+                let size = size.align_to(align); // needed e.g. for vectors of size 3
+                assert!(align >= element.align(cx).abi); // just sanity-checking `vector_align`.
+                assert_eq!(
+                    layout.layout.size(),
+                    size,
+                    "size mismatch between ABI and layout in {layout:#?}"
+                );
+                assert_eq!(
+                    layout.layout.align().abi,
+                    align,
+                    "alignment mismatch between ABI and layout in {layout:#?}"
+                );
+                // FIXME: Do some kind of check of the inner type, like for Scalar and ScalarPair.
+            }
+            Abi::Uninhabited | Abi::Aggregate { .. } => {} // Nothing to check.
         }
+    }
 
-        check_layout_abi(cx, layout);
+    check_layout_abi(cx, layout);
 
-        if let Variants::Multiple { variants, .. } = &layout.variants {
-            for variant in variants.iter() {
-                // No nested "multiple".
-                assert!(matches!(variant.variants, Variants::Single { .. }));
-                // Variants should have the same or a smaller size as the full thing,
-                // and same for alignment.
-                if variant.size > layout.size {
-                    bug!(
-                        "Type with size {} bytes has variant with size {} bytes: {layout:#?}",
-                        layout.size.bytes(),
-                        variant.size.bytes(),
-                    )
-                }
-                if variant.align.abi > layout.align.abi {
-                    bug!(
-                        "Type with alignment {} bytes has variant with alignment {} bytes: {layout:#?}",
-                        layout.align.abi.bytes(),
-                        variant.align.abi.bytes(),
-                    )
-                }
-                // Skip empty variants.
-                if variant.size == Size::ZERO
-                    || variant.fields.count() == 0
-                    || variant.abi.is_uninhabited()
-                {
-                    // These are never actually accessed anyway, so we can skip the coherence check
-                    // for them. They also fail that check, since they have
-                    // `Aggregate`/`Uninhbaited` ABI even when the main type is
-                    // `Scalar`/`ScalarPair`. (Note that sometimes, variants with fields have size
-                    // 0, and sometimes, variants without fields have non-0 size.)
-                    continue;
-                }
-                // The top-level ABI and the ABI of the variants should be coherent.
-                let scalar_coherent = |s1: Scalar, s2: Scalar| {
-                    s1.size(cx) == s2.size(cx) && s1.align(cx) == s2.align(cx)
-                };
-                let abi_coherent = match (layout.abi, variant.abi) {
-                    (Abi::Scalar(s1), Abi::Scalar(s2)) => scalar_coherent(s1, s2),
-                    (Abi::ScalarPair(a1, b1), Abi::ScalarPair(a2, b2)) => {
-                        scalar_coherent(a1, a2) && scalar_coherent(b1, b2)
-                    }
-                    (Abi::Uninhabited, _) => true,
-                    (Abi::Aggregate { .. }, _) => true,
-                    _ => false,
-                };
-                if !abi_coherent {
-                    bug!(
-                        "Variant ABI is incompatible with top-level ABI:\nvariant={:#?}\nTop-level: {layout:#?}",
-                        variant
-                    );
+    if let Variants::Multiple { variants, .. } = &layout.variants {
+        for variant in variants.iter() {
+            // No nested "multiple".
+            assert!(matches!(variant.variants, Variants::Single { .. }));
+            // Variants should have the same or a smaller size as the full thing,
+            // and same for alignment.
+            if variant.size > layout.size {
+                bug!(
+                    "Type with size {} bytes has variant with size {} bytes: {layout:#?}",
+                    layout.size.bytes(),
+                    variant.size.bytes(),
+                )
+            }
+            if variant.align.abi > layout.align.abi {
+                bug!(
+                    "Type with alignment {} bytes has variant with alignment {} bytes: {layout:#?}",
+                    layout.align.abi.bytes(),
+                    variant.align.abi.bytes(),
+                )
+            }
+            // Skip empty variants.
+            if variant.size == Size::ZERO
+                || variant.fields.count() == 0
+                || variant.abi.is_uninhabited()
+            {
+                // These are never actually accessed anyway, so we can skip the coherence check
+                // for them. They also fail that check, since they have
+                // `Aggregate`/`Uninhbaited` ABI even when the main type is
+                // `Scalar`/`ScalarPair`. (Note that sometimes, variants with fields have size
+                // 0, and sometimes, variants without fields have non-0 size.)
+                continue;
+            }
+            // The top-level ABI and the ABI of the variants should be coherent.
+            let scalar_coherent =
+                |s1: Scalar, s2: Scalar| s1.size(cx) == s2.size(cx) && s1.align(cx) == s2.align(cx);
+            let abi_coherent = match (layout.abi, variant.abi) {
+                (Abi::Scalar(s1), Abi::Scalar(s2)) => scalar_coherent(s1, s2),
+                (Abi::ScalarPair(a1, b1), Abi::ScalarPair(a2, b2)) => {
+                    scalar_coherent(a1, a2) && scalar_coherent(b1, b2)
                 }
+                (Abi::Uninhabited, _) => true,
+                (Abi::Aggregate { .. }, _) => true,
+                _ => false,
+            };
+            if !abi_coherent {
+                bug!(
+                    "Variant ABI is incompatible with top-level ABI:\nvariant={:#?}\nTop-level: {layout:#?}",
+                    variant
+                );
             }
         }
     }
diff --git a/library/alloc/src/collections/vec_deque/drain.rs b/library/alloc/src/collections/vec_deque/drain.rs
index 41baa7102cd..89feb361ddc 100644
--- a/library/alloc/src/collections/vec_deque/drain.rs
+++ b/library/alloc/src/collections/vec_deque/drain.rs
@@ -1,12 +1,12 @@
-use core::fmt;
 use core::iter::FusedIterator;
 use core::marker::PhantomData;
-use core::mem::{self, MaybeUninit};
-use core::ptr::{self, NonNull};
+use core::mem::{self, SizedTypeProperties};
+use core::ptr::NonNull;
+use core::{fmt, ptr};
 
 use crate::alloc::{Allocator, Global};
 
-use super::{count, wrap_index, VecDeque};
+use super::VecDeque;
 
 /// A draining iterator over the elements of a `VecDeque`.
 ///
@@ -20,26 +20,70 @@ pub struct Drain<
     T: 'a,
     #[unstable(feature = "allocator_api", issue = "32838")] A: Allocator = Global,
 > {
-    after_tail: usize,
-    after_head: usize,
-    ring: NonNull<[T]>,
-    tail: usize,
-    head: usize,
+    // We can't just use a &mut VecDeque<T, A>, as that would make Drain invariant over T
+    // and we want it to be covariant instead
     deque: NonNull<VecDeque<T, A>>,
-    _phantom: PhantomData<&'a T>,
+    // drain_start is stored in deque.len
+    drain_len: usize,
+    // index into the logical array, not the physical one (always lies in [0..deque.len))
+    idx: usize,
+    // number of elements after the drain range
+    tail_len: usize,
+    remaining: usize,
+    // Needed to make Drain covariant over T
+    _marker: PhantomData<&'a T>,
 }
 
 impl<'a, T, A: Allocator> Drain<'a, T, A> {
     pub(super) unsafe fn new(
-        after_tail: usize,
-        after_head: usize,
-        ring: &'a [MaybeUninit<T>],
-        tail: usize,
-        head: usize,
-        deque: NonNull<VecDeque<T, A>>,
+        deque: &'a mut VecDeque<T, A>,
+        drain_start: usize,
+        drain_len: usize,
     ) -> Self {
-        let ring = unsafe { NonNull::new_unchecked(ring as *const [MaybeUninit<T>] as *mut _) };
-        Drain { after_tail, after_head, ring, tail, head, deque, _phantom: PhantomData }
+        let orig_len = mem::replace(&mut deque.len, drain_start);
+        let tail_len = orig_len - drain_start - drain_len;
+        Drain {
+            deque: NonNull::from(deque),
+            drain_len,
+            idx: drain_start,
+            tail_len,
+            remaining: drain_len,
+            _marker: PhantomData,
+        }
+    }
+
+    // Only returns pointers to the slices, as that's
+    // all we need to drop them. May only be called if `self.remaining != 0`.
+    unsafe fn as_slices(&self) -> (*mut [T], *mut [T]) {
+        unsafe {
+            let deque = self.deque.as_ref();
+            // FIXME: This is doing almost exactly the same thing as the else branch in `VecDeque::slice_ranges`.
+            // Unfortunately, we can't just call `slice_ranges` here, as the deque's `len` is currently
+            // just `drain_start`, so the range check would (almost) always panic. Between temporarily
+            // adjusting the deques `len` to call `slice_ranges`, and just copy pasting the `slice_ranges`
+            // implementation, this seemed like the less hacky solution, though it might be good to
+            // find a better one in the future.
+
+            // because `self.remaining != 0`, we know that `self.idx < deque.original_len`, so it's a valid
+            // logical index.
+            let wrapped_start = deque.to_physical_idx(self.idx);
+
+            let head_len = deque.capacity() - wrapped_start;
+
+            let (a_range, b_range) = if head_len >= self.remaining {
+                (wrapped_start..wrapped_start + self.remaining, 0..0)
+            } else {
+                let tail_len = self.remaining - head_len;
+                (wrapped_start..deque.capacity(), 0..tail_len)
+            };
+
+            // SAFETY: the range `self.idx..self.idx+self.remaining` lies strictly inside
+            // the range `0..deque.original_len`. because of this, and because of the fact
+            // that we acquire `a_range` and `b_range` exactly like `slice_ranges` would,
+            // it's guaranteed that `a_range` and `b_range` represent valid ranges into
+            // the deques buffer.
+            (deque.buffer_range(a_range), deque.buffer_range(b_range))
+        }
     }
 }
 
@@ -47,11 +91,10 @@ impl<'a, T, A: Allocator> Drain<'a, T, A> {
 impl<T: fmt::Debug, A: Allocator> fmt::Debug for Drain<'_, T, A> {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
         f.debug_tuple("Drain")
-            .field(&self.after_tail)
-            .field(&self.after_head)
-            .field(&self.ring)
-            .field(&self.tail)
-            .field(&self.head)
+            .field(&self.drain_len)
+            .field(&self.idx)
+            .field(&self.tail_len)
+            .field(&self.remaining)
             .finish()
     }
 }
@@ -68,57 +111,81 @@ impl<T, A: Allocator> Drop for Drain<'_, T, A> {
 
         impl<'r, 'a, T, A: Allocator> Drop for DropGuard<'r, 'a, T, A> {
             fn drop(&mut self) {
-                self.0.for_each(drop);
+                if self.0.remaining != 0 {
+                    unsafe {
+                        // SAFETY: We just checked that `self.remaining != 0`.
+                        let (front, back) = self.0.as_slices();
+                        ptr::drop_in_place(front);
+                        ptr::drop_in_place(back);
+                    }
+                }
 
                 let source_deque = unsafe { self.0.deque.as_mut() };
 
-                // T = source_deque_tail; H = source_deque_head; t = drain_tail; h = drain_head
-                //
-                //        T   t   h   H
-                // [. . . o o x x o o . . .]
-                //
-                let orig_tail = source_deque.tail;
-                let drain_tail = source_deque.head;
-                let drain_head = self.0.after_tail;
-                let orig_head = self.0.after_head;
+                let drain_start = source_deque.len();
+                let drain_len = self.0.drain_len;
+                let drain_end = drain_start + drain_len;
+
+                let orig_len = self.0.tail_len + drain_end;
 
-                let tail_len = count(orig_tail, drain_tail, source_deque.cap());
-                let head_len = count(drain_head, orig_head, source_deque.cap());
+                if T::IS_ZST {
+                    // no need to copy around any memory if T is a ZST
+                    source_deque.len = orig_len - drain_len;
+                    return;
+                }
 
-                // Restore the original head value
-                source_deque.head = orig_head;
+                let head_len = drain_start;
+                let tail_len = self.0.tail_len;
 
-                match (tail_len, head_len) {
+                match (head_len, tail_len) {
                     (0, 0) => {
                         source_deque.head = 0;
-                        source_deque.tail = 0;
+                        source_deque.len = 0;
                     }
                     (0, _) => {
-                        source_deque.tail = drain_head;
+                        source_deque.head = source_deque.to_physical_idx(drain_len);
+                        source_deque.len = orig_len - drain_len;
                     }
                     (_, 0) => {
-                        source_deque.head = drain_tail;
+                        source_deque.len = orig_len - drain_len;
                     }
                     _ => unsafe {
-                        if tail_len <= head_len {
-                            source_deque.tail = source_deque.wrap_sub(drain_head, tail_len);
-                            source_deque.wrap_copy(source_deque.tail, orig_tail, tail_len);
+                        if head_len <= tail_len {
+                            source_deque.wrap_copy(
+                                source_deque.head,
+                                source_deque.to_physical_idx(drain_len),
+                                head_len,
+                            );
+                            source_deque.head = source_deque.to_physical_idx(drain_len);
+                            source_deque.len = orig_len - drain_len;
                         } else {
-                            source_deque.head = source_deque.wrap_add(drain_tail, head_len);
-                            source_deque.wrap_copy(drain_tail, drain_head, head_len);
+                            source_deque.wrap_copy(
+                                source_deque.to_physical_idx(head_len + drain_len),
+                                source_deque.to_physical_idx(head_len),
+                                tail_len,
+                            );
+                            source_deque.len = orig_len - drain_len;
                         }
                     },
                 }
             }
         }
 
-        while let Some(item) = self.next() {
-            let guard = DropGuard(self);
-            drop(item);
-            mem::forget(guard);
+        let guard = DropGuard(self);
+        if guard.0.remaining != 0 {
+            unsafe {
+                // SAFETY: We just checked that `self.remaining != 0`.
+                let (front, back) = guard.0.as_slices();
+                // since idx is a logical index, we don't need to worry about wrapping.
+                guard.0.idx += front.len();
+                guard.0.remaining -= front.len();
+                ptr::drop_in_place(front);
+                guard.0.remaining = 0;
+                ptr::drop_in_place(back);
+            }
         }
 
-        DropGuard(self);
+        // Dropping `guard` handles moving the remaining elements into place.
     }
 }
 
@@ -128,20 +195,18 @@ impl<T, A: Allocator> Iterator for Drain<'_, T, A> {
 
     #[inline]
     fn next(&mut self) -> Option<T> {
-        if self.tail == self.head {
+        if self.remaining == 0 {
             return None;
         }
-        let tail = self.tail;
-        self.tail = wrap_index(self.tail.wrapping_add(1), self.ring.len());
-        // Safety:
-        // - `self.tail` in a ring buffer is always a valid index.
-        // - `self.head` and `self.tail` equality is checked above.
-        unsafe { Some(ptr::read(self.ring.as_ptr().get_unchecked_mut(tail))) }
+        let wrapped_idx = unsafe { self.deque.as_ref().to_physical_idx(self.idx) };
+        self.idx += 1;
+        self.remaining -= 1;
+        Some(unsafe { self.deque.as_mut().buffer_read(wrapped_idx) })
     }
 
     #[inline]
     fn size_hint(&self) -> (usize, Option<usize>) {
-        let len = count(self.tail, self.head, self.ring.len());
+        let len = self.remaining;
         (len, Some(len))
     }
 }
@@ -150,14 +215,12 @@ impl<T, A: Allocator> Iterator for Drain<'_, T, A> {
 impl<T, A: Allocator> DoubleEndedIterator for Drain<'_, T, A> {
     #[inline]
     fn next_back(&mut self) -> Option<T> {
-        if self.tail == self.head {
+        if self.remaining == 0 {
             return None;
         }
-        self.head = wrap_index(self.head.wrapping_sub(1), self.ring.len());
-        // Safety:
-        // - `self.head` in a ring buffer is always a valid index.
-        // - `self.head` and `self.tail` equality is checked above.
-        unsafe { Some(ptr::read(self.ring.as_ptr().get_unchecked_mut(self.head))) }
+        self.remaining -= 1;
+        let wrapped_idx = unsafe { self.deque.as_ref().to_physical_idx(self.idx + self.remaining) };
+        Some(unsafe { self.deque.as_mut().buffer_read(wrapped_idx) })
     }
 }
 
diff --git a/library/alloc/src/collections/vec_deque/iter.rs b/library/alloc/src/collections/vec_deque/iter.rs
index e696d7ed636..d9f3937144d 100644
--- a/library/alloc/src/collections/vec_deque/iter.rs
+++ b/library/alloc/src/collections/vec_deque/iter.rs
@@ -1,9 +1,6 @@
-use core::fmt;
 use core::iter::{FusedIterator, TrustedLen, TrustedRandomAccess, TrustedRandomAccessNoCoerce};
-use core::mem::MaybeUninit;
 use core::ops::Try;
-
-use super::{count, wrap_index, RingSlices};
+use core::{fmt, mem, slice};
 
 /// An iterator over the elements of a `VecDeque`.
 ///
@@ -13,30 +10,20 @@ use super::{count, wrap_index, RingSlices};
 /// [`iter`]: super::VecDeque::iter
 #[stable(feature = "rust1", since = "1.0.0")]
 pub struct Iter<'a, T: 'a> {
-    ring: &'a [MaybeUninit<T>],
-    tail: usize,
-    head: usize,
+    i1: slice::Iter<'a, T>,
+    i2: slice::Iter<'a, T>,
 }
 
 impl<'a, T> Iter<'a, T> {
-    pub(super) fn new(ring: &'a [MaybeUninit<T>], tail: usize, head: usize) -> Self {
-        Iter { ring, tail, head }
+    pub(super) fn new(i1: slice::Iter<'a, T>, i2: slice::Iter<'a, T>) -> Self {
+        Self { i1, i2 }
     }
 }
 
 #[stable(feature = "collection_debug", since = "1.17.0")]
 impl<T: fmt::Debug> fmt::Debug for Iter<'_, T> {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        let (front, back) = RingSlices::ring_slices(self.ring, self.head, self.tail);
-        // Safety:
-        // - `self.head` and `self.tail` in a ring buffer are always valid indices.
-        // - `RingSlices::ring_slices` guarantees that the slices split according to `self.head` and `self.tail` are initialized.
-        unsafe {
-            f.debug_tuple("Iter")
-                .field(&MaybeUninit::slice_assume_init_ref(front))
-                .field(&MaybeUninit::slice_assume_init_ref(back))
-                .finish()
-        }
+        f.debug_tuple("Iter").field(&self.i1.as_slice()).field(&self.i2.as_slice()).finish()
     }
 }
 
@@ -44,7 +31,7 @@ impl<T: fmt::Debug> fmt::Debug for Iter<'_, T> {
 #[stable(feature = "rust1", since = "1.0.0")]
 impl<T> Clone for Iter<'_, T> {
     fn clone(&self) -> Self {
-        Iter { ring: self.ring, tail: self.tail, head: self.head }
+        Iter { i1: self.i1.clone(), i2: self.i2.clone() }
     }
 }
 
@@ -54,72 +41,50 @@ impl<'a, T> Iterator for Iter<'a, T> {
 
     #[inline]
     fn next(&mut self) -> Option<&'a T> {
-        if self.tail == self.head {
-            return None;
+        match self.i1.next() {
+            Some(val) => Some(val),
+            None => {
+                // most of the time, the iterator will either always
+                // call next(), or always call next_back(). By swapping
+                // the iterators once the first one is empty, we ensure
+                // that the first branch is taken as often as possible,
+                // without sacrificing correctness, as i1 is empty anyways
+                mem::swap(&mut self.i1, &mut self.i2);
+                self.i1.next()
+            }
         }
-        let tail = self.tail;
-        self.tail = wrap_index(self.tail.wrapping_add(1), self.ring.len());
-        // Safety:
-        // - `self.tail` in a ring buffer is always a valid index.
-        // - `self.head` and `self.tail` equality is checked above.
-        unsafe { Some(self.ring.get_unchecked(tail).assume_init_ref()) }
+    }
+
+    fn advance_by(&mut self, n: usize) -> Result<(), usize> {
+        let m = match self.i1.advance_by(n) {
+            Ok(_) => return Ok(()),
+            Err(m) => m,
+        };
+        mem::swap(&mut self.i1, &mut self.i2);
+        self.i1.advance_by(n - m).map_err(|o| o + m)
     }
 
     #[inline]
     fn size_hint(&self) -> (usize, Option<usize>) {
-        let len = count(self.tail, self.head, self.ring.len());
+        let len = self.len();
         (len, Some(len))
     }
 
-    fn fold<Acc, F>(self, mut accum: Acc, mut f: F) -> Acc
+    fn fold<Acc, F>(self, accum: Acc, mut f: F) -> Acc
     where
         F: FnMut(Acc, Self::Item) -> Acc,
     {
-        let (front, back) = RingSlices::ring_slices(self.ring, self.head, self.tail);
-        // Safety:
-        // - `self.head` and `self.tail` in a ring buffer are always valid indices.
-        // - `RingSlices::ring_slices` guarantees that the slices split according to `self.head` and `self.tail` are initialized.
-        unsafe {
-            accum = MaybeUninit::slice_assume_init_ref(front).iter().fold(accum, &mut f);
-            MaybeUninit::slice_assume_init_ref(back).iter().fold(accum, &mut f)
-        }
+        let accum = self.i1.fold(accum, &mut f);
+        self.i2.fold(accum, &mut f)
     }
 
     fn try_fold<B, F, R>(&mut self, init: B, mut f: F) -> R
     where
-        Self: Sized,
         F: FnMut(B, Self::Item) -> R,
         R: Try<Output = B>,
     {
-        let (mut iter, final_res);
-        if self.tail <= self.head {
-            // Safety: single slice self.ring[self.tail..self.head] is initialized.
-            iter = unsafe { MaybeUninit::slice_assume_init_ref(&self.ring[self.tail..self.head]) }
-                .iter();
-            final_res = iter.try_fold(init, &mut f);
-        } else {
-            // Safety: two slices: self.ring[self.tail..], self.ring[..self.head] both are initialized.
-            let (front, back) = self.ring.split_at(self.tail);
-
-            let mut back_iter = unsafe { MaybeUninit::slice_assume_init_ref(back).iter() };
-            let res = back_iter.try_fold(init, &mut f);
-            let len = self.ring.len();
-            self.tail = (self.ring.len() - back_iter.len()) & (len - 1);
-            iter = unsafe { MaybeUninit::slice_assume_init_ref(&front[..self.head]).iter() };
-            final_res = iter.try_fold(res?, &mut f);
-        }
-        self.tail = self.head - iter.len();
-        final_res
-    }
-
-    fn nth(&mut self, n: usize) -> Option<Self::Item> {
-        if n >= count(self.tail, self.head, self.ring.len()) {
-            self.tail = self.head;
-            None
-        } else {
-            self.tail = wrap_index(self.tail.wrapping_add(n), self.ring.len());
-            self.next()
-        }
+        let acc = self.i1.try_fold(init, &mut f)?;
+        self.i2.try_fold(acc, &mut f)
     }
 
     #[inline]
@@ -132,8 +97,12 @@ impl<'a, T> Iterator for Iter<'a, T> {
         // Safety: The TrustedRandomAccess contract requires that callers only pass an index
         // that is in bounds.
         unsafe {
-            let idx = wrap_index(self.tail.wrapping_add(idx), self.ring.len());
-            self.ring.get_unchecked(idx).assume_init_ref()
+            let i1_len = self.i1.len();
+            if idx < i1_len {
+                self.i1.__iterator_get_unchecked(idx)
+            } else {
+                self.i2.__iterator_get_unchecked(idx - i1_len)
+            }
         }
     }
 }
@@ -142,63 +111,56 @@ impl<'a, T> Iterator for Iter<'a, T> {
 impl<'a, T> DoubleEndedIterator for Iter<'a, T> {
     #[inline]
     fn next_back(&mut self) -> Option<&'a T> {
-        if self.tail == self.head {
-            return None;
+        match self.i2.next_back() {
+            Some(val) => Some(val),
+            None => {
+                // most of the time, the iterator will either always
+                // call next(), or always call next_back(). By swapping
+                // the iterators once the second one is empty, we ensure
+                // that the first branch is taken as often as possible,
+                // without sacrificing correctness, as i2 is empty anyways
+                mem::swap(&mut self.i1, &mut self.i2);
+                self.i2.next_back()
+            }
         }
-        self.head = wrap_index(self.head.wrapping_sub(1), self.ring.len());
-        // Safety:
-        // - `self.head` in a ring buffer is always a valid index.
-        // - `self.head` and `self.tail` equality is checked above.
-        unsafe { Some(self.ring.get_unchecked(self.head).assume_init_ref()) }
     }
 
-    fn rfold<Acc, F>(self, mut accum: Acc, mut f: F) -> Acc
+    fn advance_back_by(&mut self, n: usize) -> Result<(), usize> {
+        let m = match self.i2.advance_back_by(n) {
+            Ok(_) => return Ok(()),
+            Err(m) => m,
+        };
+
+        mem::swap(&mut self.i1, &mut self.i2);
+        self.i2.advance_back_by(n - m).map_err(|o| m + o)
+    }
+
+    fn rfold<Acc, F>(self, accum: Acc, mut f: F) -> Acc
     where
         F: FnMut(Acc, Self::Item) -> Acc,
     {
-        let (front, back) = RingSlices::ring_slices(self.ring, self.head, self.tail);
-        // Safety:
-        // - `self.head` and `self.tail` in a ring buffer are always valid indices.
-        // - `RingSlices::ring_slices` guarantees that the slices split according to `self.head` and `self.tail` are initialized.
-        unsafe {
-            accum = MaybeUninit::slice_assume_init_ref(back).iter().rfold(accum, &mut f);
-            MaybeUninit::slice_assume_init_ref(front).iter().rfold(accum, &mut f)
-        }
+        let accum = self.i2.rfold(accum, &mut f);
+        self.i1.rfold(accum, &mut f)
     }
 
     fn try_rfold<B, F, R>(&mut self, init: B, mut f: F) -> R
     where
-        Self: Sized,
         F: FnMut(B, Self::Item) -> R,
         R: Try<Output = B>,
     {
-        let (mut iter, final_res);
-        if self.tail <= self.head {
-            // Safety: single slice self.ring[self.tail..self.head] is initialized.
-            iter = unsafe {
-                MaybeUninit::slice_assume_init_ref(&self.ring[self.tail..self.head]).iter()
-            };
-            final_res = iter.try_rfold(init, &mut f);
-        } else {
-            // Safety: two slices: self.ring[self.tail..], self.ring[..self.head] both are initialized.
-            let (front, back) = self.ring.split_at(self.tail);
-
-            let mut front_iter =
-                unsafe { MaybeUninit::slice_assume_init_ref(&front[..self.head]).iter() };
-            let res = front_iter.try_rfold(init, &mut f);
-            self.head = front_iter.len();
-            iter = unsafe { MaybeUninit::slice_assume_init_ref(back).iter() };
-            final_res = iter.try_rfold(res?, &mut f);
-        }
-        self.head = self.tail + iter.len();
-        final_res
+        let acc = self.i2.try_rfold(init, &mut f)?;
+        self.i1.try_rfold(acc, &mut f)
     }
 }
 
 #[stable(feature = "rust1", since = "1.0.0")]
 impl<T> ExactSizeIterator for Iter<'_, T> {
+    fn len(&self) -> usize {
+        self.i1.len() + self.i2.len()
+    }
+
     fn is_empty(&self) -> bool {
-        self.head == self.tail
+        self.i1.is_empty() && self.i2.is_empty()
     }
 }
 
diff --git a/library/alloc/src/collections/vec_deque/iter_mut.rs b/library/alloc/src/collections/vec_deque/iter_mut.rs
index b78c0d5e1b3..2c59d95cd53 100644
--- a/library/alloc/src/collections/vec_deque/iter_mut.rs
+++ b/library/alloc/src/collections/vec_deque/iter_mut.rs
@@ -1,8 +1,6 @@
-use core::fmt;
 use core::iter::{FusedIterator, TrustedLen, TrustedRandomAccess, TrustedRandomAccessNoCoerce};
-use core::marker::PhantomData;
-
-use super::{count, wrap_index, RingSlices};
+use core::ops::Try;
+use core::{fmt, mem, slice};
 
 /// A mutable iterator over the elements of a `VecDeque`.
 ///
@@ -12,39 +10,20 @@ use super::{count, wrap_index, RingSlices};
 /// [`iter_mut`]: super::VecDeque::iter_mut
 #[stable(feature = "rust1", since = "1.0.0")]
 pub struct IterMut<'a, T: 'a> {
-    // Internal safety invariant: the entire slice is dereferenceable.
-    ring: *mut [T],
-    tail: usize,
-    head: usize,
-    phantom: PhantomData<&'a mut [T]>,
+    i1: slice::IterMut<'a, T>,
+    i2: slice::IterMut<'a, T>,
 }
 
 impl<'a, T> IterMut<'a, T> {
-    pub(super) unsafe fn new(
-        ring: *mut [T],
-        tail: usize,
-        head: usize,
-        phantom: PhantomData<&'a mut [T]>,
-    ) -> Self {
-        IterMut { ring, tail, head, phantom }
+    pub(super) fn new(i1: slice::IterMut<'a, T>, i2: slice::IterMut<'a, T>) -> Self {
+        Self { i1, i2 }
     }
 }
 
-// SAFETY: we do nothing thread-local and there is no interior mutability,
-// so the usual structural `Send`/`Sync` apply.
-#[stable(feature = "rust1", since = "1.0.0")]
-unsafe impl<T: Send> Send for IterMut<'_, T> {}
-#[stable(feature = "rust1", since = "1.0.0")]
-unsafe impl<T: Sync> Sync for IterMut<'_, T> {}
-
 #[stable(feature = "collection_debug", since = "1.17.0")]
 impl<T: fmt::Debug> fmt::Debug for IterMut<'_, T> {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        let (front, back) = RingSlices::ring_slices(self.ring, self.head, self.tail);
-        // SAFETY: these are the elements we have not handed out yet, so aliasing is fine.
-        // The `IterMut` invariant also ensures everything is dereferenceable.
-        let (front, back) = unsafe { (&*front, &*back) };
-        f.debug_tuple("IterMut").field(&front).field(&back).finish()
+        f.debug_tuple("IterMut").field(&self.i1.as_slice()).field(&self.i2.as_slice()).finish()
     }
 }
 
@@ -54,44 +33,50 @@ impl<'a, T> Iterator for IterMut<'a, T> {
 
     #[inline]
     fn next(&mut self) -> Option<&'a mut T> {
-        if self.tail == self.head {
-            return None;
+        match self.i1.next() {
+            Some(val) => Some(val),
+            None => {
+                // most of the time, the iterator will either always
+                // call next(), or always call next_back(). By swapping
+                // the iterators once the first one is empty, we ensure
+                // that the first branch is taken as often as possible,
+                // without sacrificing correctness, as i1 is empty anyways
+                mem::swap(&mut self.i1, &mut self.i2);
+                self.i1.next()
+            }
         }
-        let tail = self.tail;
-        self.tail = wrap_index(self.tail.wrapping_add(1), self.ring.len());
+    }
 
-        unsafe {
-            let elem = self.ring.get_unchecked_mut(tail);
-            Some(&mut *elem)
-        }
+    fn advance_by(&mut self, n: usize) -> Result<(), usize> {
+        let m = match self.i1.advance_by(n) {
+            Ok(_) => return Ok(()),
+            Err(m) => m,
+        };
+        mem::swap(&mut self.i1, &mut self.i2);
+        self.i1.advance_by(n - m).map_err(|o| o + m)
     }
 
     #[inline]
     fn size_hint(&self) -> (usize, Option<usize>) {
-        let len = count(self.tail, self.head, self.ring.len());
+        let len = self.len();
         (len, Some(len))
     }
 
-    fn fold<Acc, F>(self, mut accum: Acc, mut f: F) -> Acc
+    fn fold<Acc, F>(self, accum: Acc, mut f: F) -> Acc
     where
         F: FnMut(Acc, Self::Item) -> Acc,
     {
-        let (front, back) = RingSlices::ring_slices(self.ring, self.head, self.tail);
-        // SAFETY: these are the elements we have not handed out yet, so aliasing is fine.
-        // The `IterMut` invariant also ensures everything is dereferenceable.
-        let (front, back) = unsafe { (&mut *front, &mut *back) };
-        accum = front.iter_mut().fold(accum, &mut f);
-        back.iter_mut().fold(accum, &mut f)
+        let accum = self.i1.fold(accum, &mut f);
+        self.i2.fold(accum, &mut f)
     }
 
-    fn nth(&mut self, n: usize) -> Option<Self::Item> {
-        if n >= count(self.tail, self.head, self.ring.len()) {
-            self.tail = self.head;
-            None
-        } else {
-            self.tail = wrap_index(self.tail.wrapping_add(n), self.ring.len());
-            self.next()
-        }
+    fn try_fold<B, F, R>(&mut self, init: B, mut f: F) -> R
+    where
+        F: FnMut(B, Self::Item) -> R,
+        R: Try<Output = B>,
+    {
+        let acc = self.i1.try_fold(init, &mut f)?;
+        self.i2.try_fold(acc, &mut f)
     }
 
     #[inline]
@@ -104,8 +89,12 @@ impl<'a, T> Iterator for IterMut<'a, T> {
         // Safety: The TrustedRandomAccess contract requires that callers only pass an index
         // that is in bounds.
         unsafe {
-            let idx = wrap_index(self.tail.wrapping_add(idx), self.ring.len());
-            &mut *self.ring.get_unchecked_mut(idx)
+            let i1_len = self.i1.len();
+            if idx < i1_len {
+                self.i1.__iterator_get_unchecked(idx)
+            } else {
+                self.i2.__iterator_get_unchecked(idx - i1_len)
+            }
         }
     }
 }
@@ -114,34 +103,56 @@ impl<'a, T> Iterator for IterMut<'a, T> {
 impl<'a, T> DoubleEndedIterator for IterMut<'a, T> {
     #[inline]
     fn next_back(&mut self) -> Option<&'a mut T> {
-        if self.tail == self.head {
-            return None;
+        match self.i2.next_back() {
+            Some(val) => Some(val),
+            None => {
+                // most of the time, the iterator will either always
+                // call next(), or always call next_back(). By swapping
+                // the iterators once the first one is empty, we ensure
+                // that the first branch is taken as often as possible,
+                // without sacrificing correctness, as i2 is empty anyways
+                mem::swap(&mut self.i1, &mut self.i2);
+                self.i2.next_back()
+            }
         }
-        self.head = wrap_index(self.head.wrapping_sub(1), self.ring.len());
+    }
 
-        unsafe {
-            let elem = self.ring.get_unchecked_mut(self.head);
-            Some(&mut *elem)
-        }
+    fn advance_back_by(&mut self, n: usize) -> Result<(), usize> {
+        let m = match self.i2.advance_back_by(n) {
+            Ok(_) => return Ok(()),
+            Err(m) => m,
+        };
+
+        mem::swap(&mut self.i1, &mut self.i2);
+        self.i2.advance_back_by(n - m).map_err(|o| m + o)
     }
 
-    fn rfold<Acc, F>(self, mut accum: Acc, mut f: F) -> Acc
+    fn rfold<Acc, F>(self, accum: Acc, mut f: F) -> Acc
     where
         F: FnMut(Acc, Self::Item) -> Acc,
     {
-        let (front, back) = RingSlices::ring_slices(self.ring, self.head, self.tail);
-        // SAFETY: these are the elements we have not handed out yet, so aliasing is fine.
-        // The `IterMut` invariant also ensures everything is dereferenceable.
-        let (front, back) = unsafe { (&mut *front, &mut *back) };
-        accum = back.iter_mut().rfold(accum, &mut f);
-        front.iter_mut().rfold(accum, &mut f)
+        let accum = self.i2.rfold(accum, &mut f);
+        self.i1.rfold(accum, &mut f)
+    }
+
+    fn try_rfold<B, F, R>(&mut self, init: B, mut f: F) -> R
+    where
+        F: FnMut(B, Self::Item) -> R,
+        R: Try<Output = B>,
+    {
+        let acc = self.i2.try_rfold(init, &mut f)?;
+        self.i1.try_rfold(acc, &mut f)
     }
 }
 
 #[stable(feature = "rust1", since = "1.0.0")]
 impl<T> ExactSizeIterator for IterMut<'_, T> {
+    fn len(&self) -> usize {
+        self.i1.len() + self.i2.len()
+    }
+
     fn is_empty(&self) -> bool {
-        self.head == self.tail
+        self.i1.is_empty() && self.i2.is_empty()
     }
 }
 
diff --git a/library/alloc/src/collections/vec_deque/mod.rs b/library/alloc/src/collections/vec_deque/mod.rs
index 537fe22a4be..ee8032ad6f0 100644
--- a/library/alloc/src/collections/vec_deque/mod.rs
+++ b/library/alloc/src/collections/vec_deque/mod.rs
@@ -10,11 +10,10 @@
 use core::cmp::{self, Ordering};
 use core::fmt;
 use core::hash::{Hash, Hasher};
-use core::iter::{repeat_n, repeat_with, FromIterator};
-use core::marker::PhantomData;
-use core::mem::{ManuallyDrop, MaybeUninit, SizedTypeProperties};
+use core::iter::{repeat_n, repeat_with, ByRefSized, FromIterator};
+use core::mem::{ManuallyDrop, SizedTypeProperties};
 use core::ops::{Index, IndexMut, Range, RangeBounds};
-use core::ptr::{self, NonNull};
+use core::ptr;
 use core::slice;
 
 // This is used in a bunch of intra-doc links.
@@ -52,14 +51,6 @@ pub use self::iter::Iter;
 
 mod iter;
 
-use self::pair_slices::PairSlices;
-
-mod pair_slices;
-
-use self::ring_slices::RingSlices;
-
-mod ring_slices;
-
 use self::spec_extend::SpecExtend;
 
 mod spec_extend;
@@ -67,11 +58,6 @@ mod spec_extend;
 #[cfg(test)]
 mod tests;
 
-const INITIAL_CAPACITY: usize = 7; // 2^3 - 1
-const MINIMUM_CAPACITY: usize = 1; // 2 - 1
-
-const MAXIMUM_ZST_CAPACITY: usize = 1 << (usize::BITS - 1); // Largest possible power of two
-
 /// A double-ended queue implemented with a growable ring buffer.
 ///
 /// The "default" usage of this type as a queue is to use [`push_back`] to add to
@@ -105,13 +91,13 @@ pub struct VecDeque<
     T,
     #[unstable(feature = "allocator_api", issue = "32838")] A: Allocator = Global,
 > {
-    // tail and head are pointers into the buffer. Tail always points
-    // to the first element that could be read, Head always points
-    // to where data should be written.
-    // If tail == head the buffer is empty. The length of the ringbuffer
-    // is defined as the distance between the two.
-    tail: usize,
+    // `self[0]`, if it exists, is `buf[head]`.
+    // `head < buf.capacity()`, unless `buf.capacity() == 0` when `head == 0`.
     head: usize,
+    // the number of initialized elements, starting from the one at `head` and potentially wrapping around.
+    // if `len == 0`, the exact value of `head` is unimportant.
+    // if `T` is zero-Sized, then `self.len <= usize::MAX`, otherwise `self.len <= isize::MAX as usize`.
+    len: usize,
     buf: RawVec<T, A>,
 }
 
@@ -124,18 +110,8 @@ impl<T: Clone, A: Allocator + Clone> Clone for VecDeque<T, A> {
     }
 
     fn clone_from(&mut self, other: &Self) {
-        self.truncate(other.len());
-
-        let mut iter = PairSlices::from(self, other);
-        while let Some((dst, src)) = iter.next() {
-            dst.clone_from_slice(&src);
-        }
-
-        if iter.has_remainder() {
-            for remainder in iter.remainder() {
-                self.extend(remainder.iter().cloned());
-            }
-        }
+        self.clear();
+        self.extend(other.iter().cloned());
     }
 }
 
@@ -180,41 +156,6 @@ impl<T, A: Allocator> VecDeque<T, A> {
         self.buf.ptr()
     }
 
-    /// Marginally more convenient
-    #[inline]
-    fn cap(&self) -> usize {
-        if T::IS_ZST {
-            // For zero sized types, we are always at maximum capacity
-            MAXIMUM_ZST_CAPACITY
-        } else {
-            self.buf.capacity()
-        }
-    }
-
-    /// Turn ptr into a slice, since the elements of the backing buffer may be uninitialized,
-    /// we will return a slice of [`MaybeUninit<T>`].
-    ///
-    /// See [`MaybeUninit::zeroed`][zeroed] for examples of correct and
-    /// incorrect usage of this method.
-    ///
-    /// [zeroed]: mem::MaybeUninit::zeroed
-    #[inline]
-    unsafe fn buffer_as_slice(&self) -> &[MaybeUninit<T>] {
-        unsafe { slice::from_raw_parts(self.ptr() as *mut MaybeUninit<T>, self.cap()) }
-    }
-
-    /// Turn ptr into a mut slice, since the elements of the backing buffer may be uninitialized,
-    /// we will return a slice of [`MaybeUninit<T>`].
-    ///
-    /// See [`MaybeUninit::zeroed`][zeroed] for examples of correct and
-    /// incorrect usage of this method.
-    ///
-    /// [zeroed]: mem::MaybeUninit::zeroed
-    #[inline]
-    unsafe fn buffer_as_mut_slice(&mut self) -> &mut [MaybeUninit<T>] {
-        unsafe { slice::from_raw_parts_mut(self.ptr() as *mut MaybeUninit<T>, self.cap()) }
-    }
-
     /// Moves an element out of the buffer
     #[inline]
     unsafe fn buffer_read(&mut self, off: usize) -> T {
@@ -229,51 +170,58 @@ impl<T, A: Allocator> VecDeque<T, A> {
         }
     }
 
-    /// Returns `true` if the buffer is at full capacity.
+    /// Returns a slice pointer into the buffer.
+    /// `range` must lie inside `0..self.capacity()`.
     #[inline]
-    fn is_full(&self) -> bool {
-        self.cap() - self.len() == 1
+    unsafe fn buffer_range(&self, range: Range<usize>) -> *mut [T] {
+        unsafe {
+            ptr::slice_from_raw_parts_mut(self.ptr().add(range.start), range.end - range.start)
+        }
     }
 
-    /// Returns the index in the underlying buffer for a given logical element
-    /// index.
+    /// Returns `true` if the buffer is at full capacity.
     #[inline]
-    fn wrap_index(&self, idx: usize) -> usize {
-        wrap_index(idx, self.cap())
+    fn is_full(&self) -> bool {
+        self.len == self.capacity()
     }
 
     /// Returns the index in the underlying buffer for a given logical element
     /// index + addend.
     #[inline]
     fn wrap_add(&self, idx: usize, addend: usize) -> usize {
-        wrap_index(idx.wrapping_add(addend), self.cap())
+        wrap_index(idx.wrapping_add(addend), self.capacity())
+    }
+
+    #[inline]
+    fn to_physical_idx(&self, idx: usize) -> usize {
+        self.wrap_add(self.head, idx)
     }
 
     /// Returns the index in the underlying buffer for a given logical element
     /// index - subtrahend.
     #[inline]
     fn wrap_sub(&self, idx: usize, subtrahend: usize) -> usize {
-        wrap_index(idx.wrapping_sub(subtrahend), self.cap())
+        wrap_index(idx.wrapping_sub(subtrahend).wrapping_add(self.capacity()), self.capacity())
     }
 
     /// Copies a contiguous block of memory len long from src to dst
     #[inline]
-    unsafe fn copy(&self, dst: usize, src: usize, len: usize) {
+    unsafe fn copy(&mut self, src: usize, dst: usize, len: usize) {
         debug_assert!(
-            dst + len <= self.cap(),
+            dst + len <= self.capacity(),
             "cpy dst={} src={} len={} cap={}",
             dst,
             src,
             len,
-            self.cap()
+            self.capacity()
         );
         debug_assert!(
-            src + len <= self.cap(),
+            src + len <= self.capacity(),
             "cpy dst={} src={} len={} cap={}",
             dst,
             src,
             len,
-            self.cap()
+            self.capacity()
         );
         unsafe {
             ptr::copy(self.ptr().add(src), self.ptr().add(dst), len);
@@ -282,22 +230,22 @@ impl<T, A: Allocator> VecDeque<T, A> {
 
     /// Copies a contiguous block of memory len long from src to dst
     #[inline]
-    unsafe fn copy_nonoverlapping(&self, dst: usize, src: usize, len: usize) {
+    unsafe fn copy_nonoverlapping(&mut self, src: usize, dst: usize, len: usize) {
         debug_assert!(
-            dst + len <= self.cap(),
+            dst + len <= self.capacity(),
             "cno dst={} src={} len={} cap={}",
             dst,
             src,
             len,
-            self.cap()
+            self.capacity()
         );
         debug_assert!(
-            src + len <= self.cap(),
+            src + len <= self.capacity(),
             "cno dst={} src={} len={} cap={}",
             dst,
             src,
             len,
-            self.cap()
+            self.capacity()
         );
         unsafe {
             ptr::copy_nonoverlapping(self.ptr().add(src), self.ptr().add(dst), len);
@@ -305,30 +253,28 @@ impl<T, A: Allocator> VecDeque<T, A> {
     }
 
     /// Copies a potentially wrapping block of memory len long from src to dest.
-    /// (abs(dst - src) + len) must be no larger than cap() (There must be at
+    /// (abs(dst - src) + len) must be no larger than capacity() (There must be at
     /// most one continuous overlapping region between src and dest).
-    unsafe fn wrap_copy(&self, dst: usize, src: usize, len: usize) {
-        #[allow(dead_code)]
-        fn diff(a: usize, b: usize) -> usize {
-            if a <= b { b - a } else { a - b }
-        }
+    unsafe fn wrap_copy(&mut self, src: usize, dst: usize, len: usize) {
         debug_assert!(
-            cmp::min(diff(dst, src), self.cap() - diff(dst, src)) + len <= self.cap(),
+            cmp::min(src.abs_diff(dst), self.capacity() - src.abs_diff(dst)) + len
+                <= self.capacity(),
             "wrc dst={} src={} len={} cap={}",
             dst,
             src,
             len,
-            self.cap()
+            self.capacity()
         );
 
-        if src == dst || len == 0 {
+        // If T is a ZST, don't do any copying.
+        if T::IS_ZST || src == dst || len == 0 {
             return;
         }
 
         let dst_after_src = self.wrap_sub(dst, src) < len;
 
-        let src_pre_wrap_len = self.cap() - src;
-        let dst_pre_wrap_len = self.cap() - dst;
+        let src_pre_wrap_len = self.capacity() - src;
+        let dst_pre_wrap_len = self.capacity() - dst;
         let src_wraps = src_pre_wrap_len < len;
         let dst_wraps = dst_pre_wrap_len < len;
 
@@ -342,7 +288,7 @@ impl<T, A: Allocator> VecDeque<T, A> {
                 //            D . . .
                 //
                 unsafe {
-                    self.copy(dst, src, len);
+                    self.copy(src, dst, len);
                 }
             }
             (false, false, true) => {
@@ -355,8 +301,8 @@ impl<T, A: Allocator> VecDeque<T, A> {
                 //    . .           D .
                 //
                 unsafe {
-                    self.copy(dst, src, dst_pre_wrap_len);
-                    self.copy(0, src + dst_pre_wrap_len, len - dst_pre_wrap_len);
+                    self.copy(src, dst, dst_pre_wrap_len);
+                    self.copy(src + dst_pre_wrap_len, 0, len - dst_pre_wrap_len);
                 }
             }
             (true, false, true) => {
@@ -369,8 +315,8 @@ impl<T, A: Allocator> VecDeque<T, A> {
                 //    . .           D .
                 //
                 unsafe {
-                    self.copy(0, src + dst_pre_wrap_len, len - dst_pre_wrap_len);
-                    self.copy(dst, src, dst_pre_wrap_len);
+                    self.copy(src + dst_pre_wrap_len, 0, len - dst_pre_wrap_len);
+                    self.copy(src, dst, dst_pre_wrap_len);
                 }
             }
             (false, true, false) => {
@@ -383,8 +329,8 @@ impl<T, A: Allocator> VecDeque<T, A> {
                 //              D . . .
                 //
                 unsafe {
-                    self.copy(dst, src, src_pre_wrap_len);
-                    self.copy(dst + src_pre_wrap_len, 0, len - src_pre_wrap_len);
+                    self.copy(src, dst, src_pre_wrap_len);
+                    self.copy(0, dst + src_pre_wrap_len, len - src_pre_wrap_len);
                 }
             }
             (true, true, false) => {
@@ -397,8 +343,8 @@ impl<T, A: Allocator> VecDeque<T, A> {
                 //    D . . .
                 //
                 unsafe {
-                    self.copy(dst + src_pre_wrap_len, 0, len - src_pre_wrap_len);
-                    self.copy(dst, src, src_pre_wrap_len);
+                    self.copy(0, dst + src_pre_wrap_len, len - src_pre_wrap_len);
+                    self.copy(src, dst, src_pre_wrap_len);
                 }
             }
             (false, true, true) => {
@@ -414,9 +360,9 @@ impl<T, A: Allocator> VecDeque<T, A> {
                 debug_assert!(dst_pre_wrap_len > src_pre_wrap_len);
                 let delta = dst_pre_wrap_len - src_pre_wrap_len;
                 unsafe {
-                    self.copy(dst, src, src_pre_wrap_len);
-                    self.copy(dst + src_pre_wrap_len, 0, delta);
-                    self.copy(0, delta, len - dst_pre_wrap_len);
+                    self.copy(src, dst, src_pre_wrap_len);
+                    self.copy(0, dst + src_pre_wrap_len, delta);
+                    self.copy(delta, 0, len - dst_pre_wrap_len);
                 }
             }
             (true, true, true) => {
@@ -432,9 +378,9 @@ impl<T, A: Allocator> VecDeque<T, A> {
                 debug_assert!(src_pre_wrap_len > dst_pre_wrap_len);
                 let delta = src_pre_wrap_len - dst_pre_wrap_len;
                 unsafe {
-                    self.copy(delta, 0, len - src_pre_wrap_len);
-                    self.copy(0, self.cap() - delta, delta);
-                    self.copy(dst, src, dst_pre_wrap_len);
+                    self.copy(0, delta, len - src_pre_wrap_len);
+                    self.copy(self.capacity() - delta, 0, delta);
+                    self.copy(src, dst, dst_pre_wrap_len);
                 }
             }
         }
@@ -444,8 +390,8 @@ impl<T, A: Allocator> VecDeque<T, A> {
     /// Assumes capacity is sufficient.
     #[inline]
     unsafe fn copy_slice(&mut self, dst: usize, src: &[T]) {
-        debug_assert!(src.len() <= self.cap());
-        let head_room = self.cap() - dst;
+        debug_assert!(src.len() <= self.capacity());
+        let head_room = self.capacity() - dst;
         if src.len() <= head_room {
             unsafe {
                 ptr::copy_nonoverlapping(src.as_ptr(), self.ptr().add(dst), src.len());
@@ -478,48 +424,100 @@ impl<T, A: Allocator> VecDeque<T, A> {
         });
     }
 
+    /// Writes all values from `iter` to `dst`, wrapping
+    /// at the end of the buffer and returns the number
+    /// of written values.
+    ///
+    /// # Safety
+    ///
+    /// Assumes that `iter` yields at most `len` items.
+    /// Assumes capacity is sufficient.
+    unsafe fn write_iter_wrapping(
+        &mut self,
+        dst: usize,
+        mut iter: impl Iterator<Item = T>,
+        len: usize,
+    ) -> usize {
+        struct Guard<'a, T, A: Allocator> {
+            deque: &'a mut VecDeque<T, A>,
+            written: usize,
+        }
+
+        impl<'a, T, A: Allocator> Drop for Guard<'a, T, A> {
+            fn drop(&mut self) {
+                self.deque.len += self.written;
+            }
+        }
+
+        let head_room = self.capacity() - dst;
+
+        let mut guard = Guard { deque: self, written: 0 };
+
+        if head_room >= len {
+            unsafe { guard.deque.write_iter(dst, iter, &mut guard.written) };
+        } else {
+            unsafe {
+                guard.deque.write_iter(
+                    dst,
+                    ByRefSized(&mut iter).take(head_room),
+                    &mut guard.written,
+                );
+                guard.deque.write_iter(0, iter, &mut guard.written)
+            };
+        }
+
+        guard.written
+    }
+
     /// Frobs the head and tail sections around to handle the fact that we
     /// just reallocated. Unsafe because it trusts old_capacity.
     #[inline]
     unsafe fn handle_capacity_increase(&mut self, old_capacity: usize) {
-        let new_capacity = self.cap();
+        let new_capacity = self.capacity();
+        debug_assert!(new_capacity >= old_capacity);
 
         // Move the shortest contiguous section of the ring buffer
-        //    T             H
+        //
+        // H := head
+        // L := last element (`self.to_physical_idx(self.len - 1)`)
+        //
+        //    H           L
         //   [o o o o o o o . ]
-        //    T             H
+        //    H           L
         // A [o o o o o o o . . . . . . . . . ]
-        //        H T
-        //   [o o . o o o o o ]
-        //          T             H
+        //        L H
+        //   [o o o o o o o o ]
+        //          H           L
         // B [. . . o o o o o o o . . . . . . ]
-        //              H T
-        //   [o o o o o . o o ]
-        //              H                 T
+        //              L H
+        //   [o o o o o o o o ]
+        //            L                   H
         // C [o o o o o . . . . . . . . . o o ]
 
-        if self.tail <= self.head {
+        // can't use is_contiguous() because the capacity is already updated.
+        if self.head <= old_capacity - self.len {
             // A
             // Nop
-        } else if self.head < old_capacity - self.tail {
-            // B
-            unsafe {
-                self.copy_nonoverlapping(old_capacity, 0, self.head);
-            }
-            self.head += old_capacity;
-            debug_assert!(self.head > self.tail);
         } else {
-            // C
-            let new_tail = new_capacity - (old_capacity - self.tail);
-            unsafe {
-                self.copy_nonoverlapping(new_tail, self.tail, old_capacity - self.tail);
+            let head_len = old_capacity - self.head;
+            let tail_len = self.len - head_len;
+            if head_len > tail_len && new_capacity - old_capacity >= tail_len {
+                // B
+                unsafe {
+                    self.copy_nonoverlapping(0, old_capacity, tail_len);
+                }
+            } else {
+                // C
+                let new_head = new_capacity - head_len;
+                unsafe {
+                    // can't use copy_nonoverlapping here, because if e.g. head_len = 2
+                    // and new_capacity = old_capacity + 1, then the heads overlap.
+                    self.copy(self.head, new_head, head_len);
+                }
+                self.head = new_head;
             }
-            self.tail = new_tail;
-            debug_assert!(self.head < self.tail);
         }
-        debug_assert!(self.head < self.cap());
-        debug_assert!(self.tail < self.cap());
-        debug_assert!(self.cap().count_ones() == 1);
+        debug_assert!(self.head < self.capacity() || self.capacity() == 0);
     }
 }
 
@@ -533,6 +531,7 @@ impl<T> VecDeque<T> {
     ///
     /// let deque: VecDeque<u32> = VecDeque::new();
     /// ```
+    // FIXME: This should probably be const
     #[inline]
     #[stable(feature = "rust1", since = "1.0.0")]
     #[must_use]
@@ -569,8 +568,8 @@ impl<T, A: Allocator> VecDeque<T, A> {
     /// ```
     #[inline]
     #[unstable(feature = "allocator_api", issue = "32838")]
-    pub fn new_in(alloc: A) -> VecDeque<T, A> {
-        VecDeque::with_capacity_in(INITIAL_CAPACITY, alloc)
+    pub const fn new_in(alloc: A) -> VecDeque<T, A> {
+        VecDeque { head: 0, len: 0, buf: RawVec::new_in(alloc) }
     }
 
     /// Creates an empty deque with space for at least `capacity` elements.
@@ -584,11 +583,7 @@ impl<T, A: Allocator> VecDeque<T, A> {
     /// ```
     #[unstable(feature = "allocator_api", issue = "32838")]
     pub fn with_capacity_in(capacity: usize, alloc: A) -> VecDeque<T, A> {
-        assert!(capacity < 1_usize << usize::BITS - 1, "capacity overflow");
-        // +1 since the ringbuffer always leaves one space empty
-        let cap = cmp::max(capacity + 1, MINIMUM_CAPACITY + 1).next_power_of_two();
-
-        VecDeque { tail: 0, head: 0, buf: RawVec::with_capacity_in(cap, alloc) }
+        VecDeque { head: 0, len: 0, buf: RawVec::with_capacity_in(capacity, alloc) }
     }
 
     /// Provides a reference to the element at the given index.
@@ -608,8 +603,8 @@ impl<T, A: Allocator> VecDeque<T, A> {
     /// ```
     #[stable(feature = "rust1", since = "1.0.0")]
     pub fn get(&self, index: usize) -> Option<&T> {
-        if index < self.len() {
-            let idx = self.wrap_add(self.tail, index);
+        if index < self.len {
+            let idx = self.to_physical_idx(index);
             unsafe { Some(&*self.ptr().add(idx)) }
         } else {
             None
@@ -637,8 +632,8 @@ impl<T, A: Allocator> VecDeque<T, A> {
     /// ```
     #[stable(feature = "rust1", since = "1.0.0")]
     pub fn get_mut(&mut self, index: usize) -> Option<&mut T> {
-        if index < self.len() {
-            let idx = self.wrap_add(self.tail, index);
+        if index < self.len {
+            let idx = self.to_physical_idx(index);
             unsafe { Some(&mut *self.ptr().add(idx)) }
         } else {
             None
@@ -672,8 +667,8 @@ impl<T, A: Allocator> VecDeque<T, A> {
     pub fn swap(&mut self, i: usize, j: usize) {
         assert!(i < self.len());
         assert!(j < self.len());
-        let ri = self.wrap_add(self.tail, i);
-        let rj = self.wrap_add(self.tail, j);
+        let ri = self.to_physical_idx(i);
+        let rj = self.to_physical_idx(j);
         unsafe { ptr::swap(self.ptr().add(ri), self.ptr().add(rj)) }
     }
 
@@ -691,7 +686,7 @@ impl<T, A: Allocator> VecDeque<T, A> {
     #[inline]
     #[stable(feature = "rust1", since = "1.0.0")]
     pub fn capacity(&self) -> usize {
-        self.cap() - 1
+        if T::IS_ZST { usize::MAX } else { self.buf.capacity() }
     }
 
     /// Reserves the minimum capacity for at least `additional` more elements to be inserted in the
@@ -718,7 +713,15 @@ impl<T, A: Allocator> VecDeque<T, A> {
     /// [`reserve`]: VecDeque::reserve
     #[stable(feature = "rust1", since = "1.0.0")]
     pub fn reserve_exact(&mut self, additional: usize) {
-        self.reserve(additional);
+        let new_cap = self.len.checked_add(additional).expect("capacity overflow");
+        let old_cap = self.capacity();
+
+        if new_cap > old_cap {
+            self.buf.reserve_exact(self.len, additional);
+            unsafe {
+                self.handle_capacity_increase(old_cap);
+            }
+        }
     }
 
     /// Reserves capacity for at least `additional` more elements to be inserted in the given
@@ -739,15 +742,13 @@ impl<T, A: Allocator> VecDeque<T, A> {
     /// ```
     #[stable(feature = "rust1", since = "1.0.0")]
     pub fn reserve(&mut self, additional: usize) {
-        let old_cap = self.cap();
-        let used_cap = self.len() + 1;
-        let new_cap = used_cap
-            .checked_add(additional)
-            .and_then(|needed_cap| needed_cap.checked_next_power_of_two())
-            .expect("capacity overflow");
+        let new_cap = self.len.checked_add(additional).expect("capacity overflow");
+        let old_cap = self.capacity();
 
         if new_cap > old_cap {
-            self.buf.reserve_exact(used_cap, new_cap - used_cap);
+            // we don't need to reserve_exact(), as the size doesn't have
+            // to be a power of 2.
+            self.buf.reserve(self.len, additional);
             unsafe {
                 self.handle_capacity_increase(old_cap);
             }
@@ -793,7 +794,17 @@ impl<T, A: Allocator> VecDeque<T, A> {
     /// ```
     #[stable(feature = "try_reserve", since = "1.57.0")]
     pub fn try_reserve_exact(&mut self, additional: usize) -> Result<(), TryReserveError> {
-        self.try_reserve(additional)
+        let new_cap =
+            self.len.checked_add(additional).ok_or(TryReserveErrorKind::CapacityOverflow)?;
+        let old_cap = self.capacity();
+
+        if new_cap > old_cap {
+            self.buf.try_reserve_exact(self.len, additional)?;
+            unsafe {
+                self.handle_capacity_increase(old_cap);
+            }
+        }
+        Ok(())
     }
 
     /// Tries to reserve capacity for at least `additional` more elements to be inserted
@@ -831,15 +842,12 @@ impl<T, A: Allocator> VecDeque<T, A> {
     /// ```
     #[stable(feature = "try_reserve", since = "1.57.0")]
     pub fn try_reserve(&mut self, additional: usize) -> Result<(), TryReserveError> {
-        let old_cap = self.cap();
-        let used_cap = self.len() + 1;
-        let new_cap = used_cap
-            .checked_add(additional)
-            .and_then(|needed_cap| needed_cap.checked_next_power_of_two())
-            .ok_or(TryReserveErrorKind::CapacityOverflow)?;
+        let new_cap =
+            self.len.checked_add(additional).ok_or(TryReserveErrorKind::CapacityOverflow)?;
+        let old_cap = self.capacity();
 
         if new_cap > old_cap {
-            self.buf.try_reserve_exact(used_cap, new_cap - used_cap)?;
+            self.buf.try_reserve(self.len, additional)?;
             unsafe {
                 self.handle_capacity_increase(old_cap);
             }
@@ -890,13 +898,14 @@ impl<T, A: Allocator> VecDeque<T, A> {
     /// ```
     #[stable(feature = "shrink_to", since = "1.56.0")]
     pub fn shrink_to(&mut self, min_capacity: usize) {
-        let min_capacity = cmp::min(min_capacity, self.capacity());
-        // We don't have to worry about an overflow as neither `self.len()` nor `self.capacity()`
-        // can ever be `usize::MAX`. +1 as the ringbuffer always leaves one space empty.
-        let target_cap = cmp::max(cmp::max(min_capacity, self.len()) + 1, MINIMUM_CAPACITY + 1)
-            .next_power_of_two();
+        let target_cap = min_capacity.max(self.len);
 
-        if target_cap < self.cap() {
+        // never shrink ZSTs
+        if T::IS_ZST || self.capacity() <= target_cap {
+            return;
+        }
+
+        if target_cap < self.capacity() {
             // There are three cases of interest:
             //   All elements are out of desired bounds
             //   Elements are contiguous, and head is out of desired bounds
@@ -905,49 +914,55 @@ impl<T, A: Allocator> VecDeque<T, A> {
             // At all other times, element positions are unaffected.
             //
             // Indicates that elements at the head should be moved.
-            let head_outside = self.head == 0 || self.head >= target_cap;
+
+            let tail_outside = (target_cap + 1..=self.capacity()).contains(&(self.head + self.len));
             // Move elements from out of desired bounds (positions after target_cap)
-            if self.tail >= target_cap && head_outside {
-                //                    T             H
+            if self.len == 0 {
+                self.head = 0;
+            } else if self.head >= target_cap && tail_outside {
+                //  H := head
+                //  L := last element
+                //                    H           L
                 //   [. . . . . . . . o o o o o o o . ]
-                //    T             H
+                //    H           L
                 //   [o o o o o o o . ]
                 unsafe {
-                    self.copy_nonoverlapping(0, self.tail, self.len());
+                    // nonoverlapping because self.head >= target_cap >= self.len
+                    self.copy_nonoverlapping(self.head, 0, self.len);
                 }
-                self.head = self.len();
-                self.tail = 0;
-            } else if self.tail != 0 && self.tail < target_cap && head_outside {
-                //          T             H
+                self.head = 0;
+            } else if self.head < target_cap && tail_outside {
+                //  H := head
+                //  L := last element
+                //          H           L
                 //   [. . . o o o o o o o . . . . . . ]
-                //        H T
+                //      L   H
                 //   [o o . o o o o o ]
-                let len = self.wrap_sub(self.head, target_cap);
+                let len = self.head + self.len - target_cap;
                 unsafe {
-                    self.copy_nonoverlapping(0, target_cap, len);
+                    self.copy_nonoverlapping(target_cap, 0, len);
                 }
-                self.head = len;
-                debug_assert!(self.head < self.tail);
-            } else if self.tail >= target_cap {
-                //              H                 T
+            } else if self.head >= target_cap {
+                //  H := head
+                //  L := last element
+                //            L                   H
                 //   [o o o o o . . . . . . . . . o o ]
-                //              H T
+                //            L   H
                 //   [o o o o o . o o ]
-                debug_assert!(self.wrap_sub(self.head, 1) < target_cap);
-                let len = self.cap() - self.tail;
-                let new_tail = target_cap - len;
+                let len = self.capacity() - self.head;
+                let new_head = target_cap - len;
                 unsafe {
-                    self.copy_nonoverlapping(new_tail, self.tail, len);
+                    // can't use copy_nonoverlapping here for the same reason
+                    // as in `handle_capacity_increase()`
+                    self.copy(self.head, new_head, len);
                 }
-                self.tail = new_tail;
-                debug_assert!(self.head < self.tail);
+                self.head = new_head;
             }
 
             self.buf.shrink_to_fit(target_cap);
 
-            debug_assert!(self.head < self.cap());
-            debug_assert!(self.tail < self.cap());
-            debug_assert!(self.cap().count_ones() == 1);
+            debug_assert!(self.head < self.capacity() || self.capacity() == 0);
+            debug_assert!(self.len <= self.capacity());
         }
     }
 
@@ -992,20 +1007,20 @@ impl<T, A: Allocator> VecDeque<T, A> {
         // * The head of the VecDeque is moved before calling `drop_in_place`,
         //   so no value is dropped twice if `drop_in_place` panics
         unsafe {
-            if len > self.len() {
+            if len >= self.len {
                 return;
             }
-            let num_dropped = self.len() - len;
+
             let (front, back) = self.as_mut_slices();
             if len > front.len() {
                 let begin = len - front.len();
                 let drop_back = back.get_unchecked_mut(begin..) as *mut _;
-                self.head = self.wrap_sub(self.head, num_dropped);
+                self.len = len;
                 ptr::drop_in_place(drop_back);
             } else {
                 let drop_back = back as *mut _;
                 let drop_front = front.get_unchecked_mut(len..) as *mut _;
-                self.head = self.wrap_sub(self.head, num_dropped);
+                self.len = len;
 
                 // Make sure the second half is dropped even when a destructor
                 // in the first one panics.
@@ -1039,7 +1054,8 @@ impl<T, A: Allocator> VecDeque<T, A> {
     /// ```
     #[stable(feature = "rust1", since = "1.0.0")]
     pub fn iter(&self) -> Iter<'_, T> {
-        Iter::new(unsafe { self.buffer_as_slice() }, self.tail, self.head)
+        let (a, b) = self.as_slices();
+        Iter::new(a.iter(), b.iter())
     }
 
     /// Returns a front-to-back iterator that returns mutable references.
@@ -1061,11 +1077,8 @@ impl<T, A: Allocator> VecDeque<T, A> {
     /// ```
     #[stable(feature = "rust1", since = "1.0.0")]
     pub fn iter_mut(&mut self) -> IterMut<'_, T> {
-        // SAFETY: The internal `IterMut` safety invariant is established because the
-        // `ring` we create is a dereferenceable slice for lifetime '_.
-        let ring = ptr::slice_from_raw_parts_mut(self.ptr(), self.cap());
-
-        unsafe { IterMut::new(ring, self.tail, self.head, PhantomData) }
+        let (a, b) = self.as_mut_slices();
+        IterMut::new(a.iter_mut(), b.iter_mut())
     }
 
     /// Returns a pair of slices which contain, in order, the contents of the
@@ -1097,14 +1110,10 @@ impl<T, A: Allocator> VecDeque<T, A> {
     #[inline]
     #[stable(feature = "deque_extras_15", since = "1.5.0")]
     pub fn as_slices(&self) -> (&[T], &[T]) {
-        // Safety:
-        // - `self.head` and `self.tail` in a ring buffer are always valid indices.
-        // - `RingSlices::ring_slices` guarantees that the slices split according to `self.head` and `self.tail` are initialized.
-        unsafe {
-            let buf = self.buffer_as_slice();
-            let (front, back) = RingSlices::ring_slices(buf, self.head, self.tail);
-            (MaybeUninit::slice_assume_init_ref(front), MaybeUninit::slice_assume_init_ref(back))
-        }
+        let (a_range, b_range) = self.slice_ranges(..);
+        // SAFETY: `slice_ranges` always returns valid ranges into
+        // the physical buffer.
+        unsafe { (&*self.buffer_range(a_range), &*self.buffer_range(b_range)) }
     }
 
     /// Returns a pair of slices which contain, in order, the contents of the
@@ -1135,16 +1144,10 @@ impl<T, A: Allocator> VecDeque<T, A> {
     #[inline]
     #[stable(feature = "deque_extras_15", since = "1.5.0")]
     pub fn as_mut_slices(&mut self) -> (&mut [T], &mut [T]) {
-        // Safety:
-        // - `self.head` and `self.tail` in a ring buffer are always valid indices.
-        // - `RingSlices::ring_slices` guarantees that the slices split according to `self.head` and `self.tail` are initialized.
-        unsafe {
-            let head = self.head;
-            let tail = self.tail;
-            let buf = self.buffer_as_mut_slice();
-            let (front, back) = RingSlices::ring_slices(buf, head, tail);
-            (MaybeUninit::slice_assume_init_mut(front), MaybeUninit::slice_assume_init_mut(back))
-        }
+        let (a_range, b_range) = self.slice_ranges(..);
+        // SAFETY: `slice_ranges` always returns valid ranges into
+        // the physical buffer.
+        unsafe { (&mut *self.buffer_range(a_range), &mut *self.buffer_range(b_range)) }
     }
 
     /// Returns the number of elements in the deque.
@@ -1161,7 +1164,7 @@ impl<T, A: Allocator> VecDeque<T, A> {
     /// ```
     #[stable(feature = "rust1", since = "1.0.0")]
     pub fn len(&self) -> usize {
-        count(self.tail, self.head, self.cap())
+        self.len
     }
 
     /// Returns `true` if the deque is empty.
@@ -1178,17 +1181,41 @@ impl<T, A: Allocator> VecDeque<T, A> {
     /// ```
     #[stable(feature = "rust1", since = "1.0.0")]
     pub fn is_empty(&self) -> bool {
-        self.tail == self.head
+        self.len == 0
     }
 
-    fn range_tail_head<R>(&self, range: R) -> (usize, usize)
+    /// Given a range into the logical buffer of the deque, this function
+    /// return two ranges into the physical buffer that correspond to
+    /// the given range.
+    fn slice_ranges<R>(&self, range: R) -> (Range<usize>, Range<usize>)
     where
         R: RangeBounds<usize>,
     {
-        let Range { start, end } = slice::range(range, ..self.len());
-        let tail = self.wrap_add(self.tail, start);
-        let head = self.wrap_add(self.tail, end);
-        (tail, head)
+        let Range { start, end } = slice::range(range, ..self.len);
+        let len = end - start;
+
+        if len == 0 {
+            (0..0, 0..0)
+        } else {
+            // `slice::range` guarantees that `start <= end <= self.len`.
+            // because `len != 0`, we know that `start < end`, so `start < self.len`
+            // and the indexing is valid.
+            let wrapped_start = self.to_physical_idx(start);
+
+            // this subtraction can never overflow because `wrapped_start` is
+            // at most `self.capacity()` (and if `self.capacity != 0`, then `wrapped_start` is strictly less
+            // than `self.capacity`).
+            let head_len = self.capacity() - wrapped_start;
+
+            if head_len >= len {
+                // we know that `len + wrapped_start <= self.capacity <= usize::MAX`, so this addition can't overflow
+                (wrapped_start..wrapped_start + len, 0..0)
+            } else {
+                // can't overflow because of the if condition
+                let tail_len = len - head_len;
+                (wrapped_start..self.capacity(), 0..tail_len)
+            }
+        }
     }
 
     /// Creates an iterator that covers the specified range in the deque.
@@ -1217,9 +1244,14 @@ impl<T, A: Allocator> VecDeque<T, A> {
     where
         R: RangeBounds<usize>,
     {
-        let (tail, head) = self.range_tail_head(range);
-        // The shared reference we have in &self is maintained in the '_ of Iter.
-        Iter::new(unsafe { self.buffer_as_slice() }, tail, head)
+        let (a_range, b_range) = self.slice_ranges(range);
+        // SAFETY: The ranges returned by `slice_ranges`
+        // are valid ranges into the physical buffer, so
+        // it's ok to pass them to `buffer_range` and
+        // dereference the result.
+        let a = unsafe { &*self.buffer_range(a_range) };
+        let b = unsafe { &*self.buffer_range(b_range) };
+        Iter::new(a.iter(), b.iter())
     }
 
     /// Creates an iterator that covers the specified mutable range in the deque.
@@ -1252,13 +1284,14 @@ impl<T, A: Allocator> VecDeque<T, A> {
     where
         R: RangeBounds<usize>,
     {
-        let (tail, head) = self.range_tail_head(range);
-
-        // SAFETY: The internal `IterMut` safety invariant is established because the
-        // `ring` we create is a dereferenceable slice for lifetime '_.
-        let ring = ptr::slice_from_raw_parts_mut(self.ptr(), self.cap());
-
-        unsafe { IterMut::new(ring, tail, head, PhantomData) }
+        let (a_range, b_range) = self.slice_ranges(range);
+        // SAFETY: The ranges returned by `slice_ranges`
+        // are valid ranges into the physical buffer, so
+        // it's ok to pass them to `buffer_range` and
+        // dereference the result.
+        let a = unsafe { &mut *self.buffer_range(a_range) };
+        let b = unsafe { &mut *self.buffer_range(b_range) };
+        IterMut::new(a.iter_mut(), b.iter_mut())
     }
 
     /// Removes the specified range from the deque in bulk, returning all
@@ -1310,39 +1343,30 @@ impl<T, A: Allocator> VecDeque<T, A> {
         // When finished, the remaining data will be copied back to cover the hole,
         // and the head/tail values will be restored correctly.
         //
-        let (drain_tail, drain_head) = self.range_tail_head(range);
+        let Range { start, end } = slice::range(range, ..self.len);
+        let drain_start = start;
+        let drain_len = end - start;
 
         // The deque's elements are parted into three segments:
-        // * self.tail  -> drain_tail
-        // * drain_tail -> drain_head
-        // * drain_head -> self.head
+        // * 0  -> drain_start
+        // * drain_start -> drain_start+drain_len
+        // * drain_start+drain_len -> self.len
         //
-        // T = self.tail; H = self.head; t = drain_tail; h = drain_head
+        // H = self.head; T = self.head+self.len; t = drain_start+drain_len; h = drain_head
         //
-        // We store drain_tail as self.head, and drain_head and self.head as
-        // after_tail and after_head respectively on the Drain. This also
+        // We store drain_start as self.len, and drain_len and self.len as
+        // drain_len and orig_len respectively on the Drain. This also
         // truncates the effective array such that if the Drain is leaked, we
         // have forgotten about the potentially moved values after the start of
         // the drain.
         //
-        //        T   t   h   H
+        //        H   h   t   T
         // [. . . o o x x o o . . .]
         //
-        let head = self.head;
-
         // "forget" about the values after the start of the drain until after
         // the drain is complete and the Drain destructor is run.
-        self.head = drain_tail;
 
-        let deque = NonNull::from(&mut *self);
-        unsafe {
-            // Crucially, we only create shared references from `self` here and read from
-            // it.  We do not write to `self` nor reborrow to a mutable reference.
-            // Hence the raw pointer we created above, for `deque`, remains valid.
-            let ring = self.buffer_as_slice();
-
-            Drain::new(drain_head, head, ring, drain_tail, drain_head, deque)
-        }
+        unsafe { Drain::new(self, drain_start, drain_len) }
     }
 
     /// Clears the deque, removing all values.
@@ -1361,6 +1385,8 @@ impl<T, A: Allocator> VecDeque<T, A> {
     #[inline]
     pub fn clear(&mut self) {
         self.truncate(0);
+        // Not strictly necessary, but leaves things in a more consistent/predictable state.
+        self.head = 0;
     }
 
     /// Returns `true` if the deque contains an element equal to the
@@ -1455,7 +1481,7 @@ impl<T, A: Allocator> VecDeque<T, A> {
     /// ```
     #[stable(feature = "rust1", since = "1.0.0")]
     pub fn back(&self) -> Option<&T> {
-        self.get(self.len().wrapping_sub(1))
+        self.get(self.len.wrapping_sub(1))
     }
 
     /// Provides a mutable reference to the back element, or `None` if the
@@ -1479,7 +1505,7 @@ impl<T, A: Allocator> VecDeque<T, A> {
     /// ```
     #[stable(feature = "rust1", since = "1.0.0")]
     pub fn back_mut(&mut self) -> Option<&mut T> {
-        self.get_mut(self.len().wrapping_sub(1))
+        self.get_mut(self.len.wrapping_sub(1))
     }
 
     /// Removes the first element and returns it, or `None` if the deque is
@@ -1503,9 +1529,10 @@ impl<T, A: Allocator> VecDeque<T, A> {
         if self.is_empty() {
             None
         } else {
-            let tail = self.tail;
-            self.tail = self.wrap_add(self.tail, 1);
-            unsafe { Some(self.buffer_read(tail)) }
+            let old_head = self.head;
+            self.head = self.to_physical_idx(1);
+            self.len -= 1;
+            Some(unsafe { self.buffer_read(old_head) })
         }
     }
 
@@ -1528,9 +1555,8 @@ impl<T, A: Allocator> VecDeque<T, A> {
         if self.is_empty() {
             None
         } else {
-            self.head = self.wrap_sub(self.head, 1);
-            let head = self.head;
-            unsafe { Some(self.buffer_read(head)) }
+            self.len -= 1;
+            Some(unsafe { self.buffer_read(self.to_physical_idx(self.len)) })
         }
     }
 
@@ -1552,10 +1578,11 @@ impl<T, A: Allocator> VecDeque<T, A> {
             self.grow();
         }
 
-        self.tail = self.wrap_sub(self.tail, 1);
-        let tail = self.tail;
+        self.head = self.wrap_sub(self.head, 1);
+        self.len += 1;
+
         unsafe {
-            self.buffer_write(tail, value);
+            self.buffer_write(self.head, value);
         }
     }
 
@@ -1577,16 +1604,14 @@ impl<T, A: Allocator> VecDeque<T, A> {
             self.grow();
         }
 
-        let head = self.head;
-        self.head = self.wrap_add(self.head, 1);
-        unsafe { self.buffer_write(head, value) }
+        unsafe { self.buffer_write(self.to_physical_idx(self.len), value) }
+        self.len += 1;
     }
 
     #[inline]
     fn is_contiguous(&self) -> bool {
-        // FIXME: Should we consider `head == 0` to mean
-        // that `self` is contiguous?
-        self.tail <= self.head
+        // Do the calculation like this to avoid overflowing if len + head > usize::MAX
+        self.head <= self.capacity() - self.len
     }
 
     /// Removes an element from anywhere in the deque and returns it,
@@ -1615,8 +1640,8 @@ impl<T, A: Allocator> VecDeque<T, A> {
     /// ```
     #[stable(feature = "deque_extras_15", since = "1.5.0")]
     pub fn swap_remove_front(&mut self, index: usize) -> Option<T> {
-        let length = self.len();
-        if length > 0 && index < length && index != 0 {
+        let length = self.len;
+        if index < length && index != 0 {
             self.swap(index, 0);
         } else if index >= length {
             return None;
@@ -1650,7 +1675,7 @@ impl<T, A: Allocator> VecDeque<T, A> {
     /// ```
     #[stable(feature = "deque_extras_15", since = "1.5.0")]
     pub fn swap_remove_back(&mut self, index: usize) -> Option<T> {
-        let length = self.len();
+        let length = self.len;
         if length > 0 && index < length - 1 {
             self.swap(index, length - 1);
         } else if index >= length {
@@ -1689,198 +1714,26 @@ impl<T, A: Allocator> VecDeque<T, A> {
             self.grow();
         }
 
-        // Move the least number of elements in the ring buffer and insert
-        // the given object
-        //
-        // At most len/2 - 1 elements will be moved. O(min(n, n-i))
-        //
-        // There are three main cases:
-        //  Elements are contiguous
-        //      - special case when tail is 0
-        //  Elements are discontiguous and the insert is in the tail section
-        //  Elements are discontiguous and the insert is in the head section
-        //
-        // For each of those there are two more cases:
-        //  Insert is closer to tail
-        //  Insert is closer to head
-        //
-        // Key: H - self.head
-        //      T - self.tail
-        //      o - Valid element
-        //      I - Insertion element
-        //      A - The element that should be after the insertion point
-        //      M - Indicates element was moved
-
-        let idx = self.wrap_add(self.tail, index);
-
-        let distance_to_tail = index;
-        let distance_to_head = self.len() - index;
-
-        let contiguous = self.is_contiguous();
-
-        match (contiguous, distance_to_tail <= distance_to_head, idx >= self.tail) {
-            (true, true, _) if index == 0 => {
-                // push_front
-                //
-                //       T
-                //       I             H
-                //      [A o o o o o o . . . . . . . . .]
-                //
-                //                       H         T
-                //      [A o o o o o o o . . . . . I]
-                //
-
-                self.tail = self.wrap_sub(self.tail, 1);
-            }
-            (true, true, _) => {
-                unsafe {
-                    // contiguous, insert closer to tail:
-                    //
-                    //             T   I         H
-                    //      [. . . o o A o o o o . . . . . .]
-                    //
-                    //           T               H
-                    //      [. . o o I A o o o o . . . . . .]
-                    //           M M
-                    //
-                    // contiguous, insert closer to tail and tail is 0:
-                    //
-                    //
-                    //       T   I         H
-                    //      [o o A o o o o . . . . . . . . .]
-                    //
-                    //                       H             T
-                    //      [o I A o o o o o . . . . . . . o]
-                    //       M                             M
-
-                    let new_tail = self.wrap_sub(self.tail, 1);
-
-                    self.copy(new_tail, self.tail, 1);
-                    // Already moved the tail, so we only copy `index - 1` elements.
-                    self.copy(self.tail, self.tail + 1, index - 1);
-
-                    self.tail = new_tail;
-                }
-            }
-            (true, false, _) => {
-                unsafe {
-                    //  contiguous, insert closer to head:
-                    //
-                    //             T       I     H
-                    //      [. . . o o o o A o o . . . . . .]
-                    //
-                    //             T               H
-                    //      [. . . o o o o I A o o . . . . .]
-                    //                       M M M
-
-                    self.copy(idx + 1, idx, self.head - idx);
-                    self.head = self.wrap_add(self.head, 1);
-                }
-            }
-            (false, true, true) => {
-                unsafe {
-                    // discontiguous, insert closer to tail, tail section:
-                    //
-                    //                   H         T   I
-                    //      [o o o o o o . . . . . o o A o o]
-                    //
-                    //                   H       T
-                    //      [o o o o o o . . . . o o I A o o]
-                    //                           M M
-
-                    self.copy(self.tail - 1, self.tail, index);
-                    self.tail -= 1;
-                }
-            }
-            (false, false, true) => {
-                unsafe {
-                    // discontiguous, insert closer to head, tail section:
-                    //
-                    //           H             T         I
-                    //      [o o . . . . . . . o o o o o A o]
-                    //
-                    //             H           T
-                    //      [o o o . . . . . . o o o o o I A]
-                    //       M M M                         M
-
-                    // copy elements up to new head
-                    self.copy(1, 0, self.head);
-
-                    // copy last element into empty spot at bottom of buffer
-                    self.copy(0, self.cap() - 1, 1);
-
-                    // move elements from idx to end forward not including ^ element
-                    self.copy(idx + 1, idx, self.cap() - 1 - idx);
-
-                    self.head += 1;
-                }
-            }
-            (false, true, false) if idx == 0 => {
-                unsafe {
-                    // discontiguous, insert is closer to tail, head section,
-                    // and is at index zero in the internal buffer:
-                    //
-                    //       I                   H     T
-                    //      [A o o o o o o o o o . . . o o o]
-                    //
-                    //                           H   T
-                    //      [A o o o o o o o o o . . o o o I]
-                    //                               M M M
-
-                    // copy elements up to new tail
-                    self.copy(self.tail - 1, self.tail, self.cap() - self.tail);
-
-                    // copy last element into empty spot at bottom of buffer
-                    self.copy(self.cap() - 1, 0, 1);
-
-                    self.tail -= 1;
-                }
-            }
-            (false, true, false) => {
-                unsafe {
-                    // discontiguous, insert closer to tail, head section:
-                    //
-                    //             I             H     T
-                    //      [o o o A o o o o o o . . . o o o]
-                    //
-                    //                           H   T
-                    //      [o o I A o o o o o o . . o o o o]
-                    //       M M                     M M M M
-
-                    // copy elements up to new tail
-                    self.copy(self.tail - 1, self.tail, self.cap() - self.tail);
-
-                    // copy last element into empty spot at bottom of buffer
-                    self.copy(self.cap() - 1, 0, 1);
-
-                    // move elements from idx-1 to end forward not including ^ element
-                    self.copy(0, 1, idx - 1);
-
-                    self.tail -= 1;
-                }
+        let k = self.len - index;
+        if k < index {
+            // `index + 1` can't overflow, because if index was usize::MAX, then either the
+            // assert would've failed, or the deque would've tried to grow past usize::MAX
+            // and panicked.
+            unsafe {
+                // see `remove()` for explanation why this wrap_copy() call is safe.
+                self.wrap_copy(self.to_physical_idx(index), self.to_physical_idx(index + 1), k);
+                self.buffer_write(self.to_physical_idx(index), value);
+                self.len += 1;
             }
-            (false, false, false) => {
-                unsafe {
-                    // discontiguous, insert closer to head, head section:
-                    //
-                    //               I     H           T
-                    //      [o o o o A o o . . . . . . o o o]
-                    //
-                    //                     H           T
-                    //      [o o o o I A o o . . . . . o o o]
-                    //                 M M M
-
-                    self.copy(idx + 1, idx, self.head - idx);
-                    self.head += 1;
-                }
+        } else {
+            let old_head = self.head;
+            self.head = self.wrap_sub(self.head, 1);
+            unsafe {
+                self.wrap_copy(old_head, self.head, index);
+                self.buffer_write(self.to_physical_idx(index), value);
+                self.len += 1;
             }
         }
-
-        // tail might've been changed so we need to recalculate
-        let new_idx = self.wrap_add(self.tail, index);
-        unsafe {
-            self.buffer_write(new_idx, value);
-        }
     }
 
     /// Removes and returns the element at `index` from the deque.
@@ -1906,156 +1759,26 @@ impl<T, A: Allocator> VecDeque<T, A> {
     /// ```
     #[stable(feature = "rust1", since = "1.0.0")]
     pub fn remove(&mut self, index: usize) -> Option<T> {
-        if self.is_empty() || self.len() <= index {
+        if self.len <= index {
             return None;
         }
 
-        // There are three main cases:
-        //  Elements are contiguous
-        //  Elements are discontiguous and the removal is in the tail section
-        //  Elements are discontiguous and the removal is in the head section
-        //      - special case when elements are technically contiguous,
-        //        but self.head = 0
-        //
-        // For each of those there are two more cases:
-        //  Insert is closer to tail
-        //  Insert is closer to head
-        //
-        // Key: H - self.head
-        //      T - self.tail
-        //      o - Valid element
-        //      x - Element marked for removal
-        //      R - Indicates element that is being removed
-        //      M - Indicates element was moved
-
-        let idx = self.wrap_add(self.tail, index);
-
-        let elem = unsafe { Some(self.buffer_read(idx)) };
-
-        let distance_to_tail = index;
-        let distance_to_head = self.len() - index;
+        let wrapped_idx = self.to_physical_idx(index);
 
-        let contiguous = self.is_contiguous();
+        let elem = unsafe { Some(self.buffer_read(wrapped_idx)) };
 
-        match (contiguous, distance_to_tail <= distance_to_head, idx >= self.tail) {
-            (true, true, _) => {
-                unsafe {
-                    // contiguous, remove closer to tail:
-                    //
-                    //             T   R         H
-                    //      [. . . o o x o o o o . . . . . .]
-                    //
-                    //               T           H
-                    //      [. . . . o o o o o o . . . . . .]
-                    //               M M
-
-                    self.copy(self.tail + 1, self.tail, index);
-                    self.tail += 1;
-                }
-            }
-            (true, false, _) => {
-                unsafe {
-                    // contiguous, remove closer to head:
-                    //
-                    //             T       R     H
-                    //      [. . . o o o o x o o . . . . . .]
-                    //
-                    //             T           H
-                    //      [. . . o o o o o o . . . . . . .]
-                    //                     M M
-
-                    self.copy(idx, idx + 1, self.head - idx - 1);
-                    self.head -= 1;
-                }
-            }
-            (false, true, true) => {
-                unsafe {
-                    // discontiguous, remove closer to tail, tail section:
-                    //
-                    //                   H         T   R
-                    //      [o o o o o o . . . . . o o x o o]
-                    //
-                    //                   H           T
-                    //      [o o o o o o . . . . . . o o o o]
-                    //                               M M
-
-                    self.copy(self.tail + 1, self.tail, index);
-                    self.tail = self.wrap_add(self.tail, 1);
-                }
-            }
-            (false, false, false) => {
-                unsafe {
-                    // discontiguous, remove closer to head, head section:
-                    //
-                    //               R     H           T
-                    //      [o o o o x o o . . . . . . o o o]
-                    //
-                    //                   H             T
-                    //      [o o o o o o . . . . . . . o o o]
-                    //               M M
-
-                    self.copy(idx, idx + 1, self.head - idx - 1);
-                    self.head -= 1;
-                }
-            }
-            (false, false, true) => {
-                unsafe {
-                    // discontiguous, remove closer to head, tail section:
-                    //
-                    //             H           T         R
-                    //      [o o o . . . . . . o o o o o x o]
-                    //
-                    //           H             T
-                    //      [o o . . . . . . . o o o o o o o]
-                    //       M M                         M M
-                    //
-                    // or quasi-discontiguous, remove next to head, tail section:
-                    //
-                    //       H                 T         R
-                    //      [. . . . . . . . . o o o o o x o]
-                    //
-                    //                         T           H
-                    //      [. . . . . . . . . o o o o o o .]
-                    //                                   M
-
-                    // draw in elements in the tail section
-                    self.copy(idx, idx + 1, self.cap() - idx - 1);
-
-                    // Prevents underflow.
-                    if self.head != 0 {
-                        // copy first element into empty spot
-                        self.copy(self.cap() - 1, 0, 1);
-
-                        // move elements in the head section backwards
-                        self.copy(0, 1, self.head - 1);
-                    }
-
-                    self.head = self.wrap_sub(self.head, 1);
-                }
-            }
-            (false, true, false) => {
-                unsafe {
-                    // discontiguous, remove closer to tail, head section:
-                    //
-                    //           R               H     T
-                    //      [o o x o o o o o o o . . . o o o]
-                    //
-                    //                           H       T
-                    //      [o o o o o o o o o o . . . . o o]
-                    //       M M M                       M M
-
-                    // draw in elements up to idx
-                    self.copy(1, 0, idx);
-
-                    // copy last element into empty spot
-                    self.copy(0, self.cap() - 1, 1);
-
-                    // move elements from tail to end forward, excluding the last one
-                    self.copy(self.tail + 1, self.tail, self.cap() - self.tail - 1);
-
-                    self.tail = self.wrap_add(self.tail, 1);
-                }
-            }
+        let k = self.len - index - 1;
+        // safety: due to the nature of the if-condition, whichever wrap_copy gets called,
+        // its length argument will be at most `self.len / 2`, so there can't be more than
+        // one overlapping area.
+        if k < index {
+            unsafe { self.wrap_copy(self.wrap_add(wrapped_idx, 1), wrapped_idx, k) };
+            self.len -= 1;
+        } else {
+            let old_head = self.head;
+            self.head = self.to_physical_idx(1);
+            unsafe { self.wrap_copy(old_head, self.head, index) };
+            self.len -= 1;
         }
 
         elem
@@ -2091,7 +1814,7 @@ impl<T, A: Allocator> VecDeque<T, A> {
     where
         A: Clone,
     {
-        let len = self.len();
+        let len = self.len;
         assert!(at <= len, "`at` out of bounds");
 
         let other_len = len - at;
@@ -2128,8 +1851,8 @@ impl<T, A: Allocator> VecDeque<T, A> {
         }
 
         // Cleanup where the ends of the buffers are
-        self.head = self.wrap_sub(self.head, other_len);
-        other.head = other.wrap_index(other_len);
+        self.len = at;
+        other.len = other_len;
 
         other
     }
@@ -2154,17 +1877,26 @@ impl<T, A: Allocator> VecDeque<T, A> {
     #[inline]
     #[stable(feature = "append", since = "1.4.0")]
     pub fn append(&mut self, other: &mut Self) {
-        self.reserve(other.len());
+        if T::IS_ZST {
+            self.len += other.len;
+            other.len = 0;
+            other.head = 0;
+            return;
+        }
+
+        self.reserve(other.len);
         unsafe {
             let (left, right) = other.as_slices();
-            self.copy_slice(self.head, left);
-            self.copy_slice(self.wrap_add(self.head, left.len()), right);
+            self.copy_slice(self.to_physical_idx(self.len), left);
+            // no overflow, because self.capacity() >= old_cap + left.len() >= self.len + left.len()
+            self.copy_slice(self.to_physical_idx(self.len + left.len()), right);
         }
         // SAFETY: Update pointers after copying to avoid leaving doppelganger
         // in case of panics.
-        self.head = self.wrap_add(self.head, other.len());
-        // Silently drop values in `other`.
-        other.tail = other.head;
+        self.len += other.len;
+        // Now that we own its values, forget everything in `other`.
+        other.len = 0;
+        other.head = 0;
     }
 
     /// Retains only the elements specified by the predicate.
@@ -2232,7 +1964,7 @@ impl<T, A: Allocator> VecDeque<T, A> {
     where
         F: FnMut(&mut T) -> bool,
     {
-        let len = self.len();
+        let len = self.len;
         let mut idx = 0;
         let mut cur = 0;
 
@@ -2270,9 +2002,8 @@ impl<T, A: Allocator> VecDeque<T, A> {
         // Extend or possibly remove this assertion when valid use-cases for growing the
         // buffer without it being full emerge
         debug_assert!(self.is_full());
-        let old_cap = self.cap();
-        self.buf.reserve_exact(old_cap, old_cap);
-        assert!(self.cap() == old_cap * 2);
+        let old_cap = self.capacity();
+        self.buf.reserve_for_push(old_cap);
         unsafe {
             self.handle_capacity_increase(old_cap);
         }
@@ -2306,7 +2037,7 @@ impl<T, A: Allocator> VecDeque<T, A> {
     /// ```
     #[stable(feature = "vec_resize_with", since = "1.33.0")]
     pub fn resize_with(&mut self, new_len: usize, generator: impl FnMut() -> T) {
-        let len = self.len();
+        let len = self.len;
 
         if new_len > len {
             self.extend(repeat_with(generator).take(new_len - len))
@@ -2372,110 +2103,129 @@ impl<T, A: Allocator> VecDeque<T, A> {
     /// ```
     #[stable(feature = "deque_make_contiguous", since = "1.48.0")]
     pub fn make_contiguous(&mut self) -> &mut [T] {
+        if T::IS_ZST {
+            self.head = 0;
+        }
+
         if self.is_contiguous() {
-            let tail = self.tail;
-            let head = self.head;
-            // Safety:
-            // - `self.head` and `self.tail` in a ring buffer are always valid indices.
-            // - `RingSlices::ring_slices` guarantees that the slices split according to `self.head` and `self.tail` are initialized.
-            return unsafe {
-                MaybeUninit::slice_assume_init_mut(
-                    RingSlices::ring_slices(self.buffer_as_mut_slice(), head, tail).0,
-                )
-            };
+            unsafe { return slice::from_raw_parts_mut(self.ptr().add(self.head), self.len) }
         }
 
-        let buf = self.buf.ptr();
-        let cap = self.cap();
-        let len = self.len();
+        let &mut Self { head, len, .. } = self;
+        let ptr = self.ptr();
+        let cap = self.capacity();
 
-        let free = self.tail - self.head;
-        let tail_len = cap - self.tail;
+        let free = cap - len;
+        let head_len = cap - head;
+        let tail = len - head_len;
+        let tail_len = tail;
 
-        if free >= tail_len {
-            // there is enough free space to copy the tail in one go,
-            // this means that we first shift the head backwards, and then
-            // copy the tail to the correct position.
+        if free >= head_len {
+            // there is enough free space to copy the head in one go,
+            // this means that we first shift the tail backwards, and then
+            // copy the head to the correct position.
             //
             // from: DEFGH....ABC
             // to:   ABCDEFGH....
             unsafe {
-                ptr::copy(buf, buf.add(tail_len), self.head);
+                self.copy(0, head_len, tail_len);
                 // ...DEFGH.ABC
-                ptr::copy_nonoverlapping(buf.add(self.tail), buf, tail_len);
+                self.copy_nonoverlapping(head, 0, head_len);
                 // ABCDEFGH....
-
-                self.tail = 0;
-                self.head = len;
             }
-        } else if free > self.head {
-            // FIXME: We currently do not consider ....ABCDEFGH
-            // to be contiguous because `head` would be `0` in this
-            // case. While we probably want to change this it
-            // isn't trivial as a few places expect `is_contiguous`
-            // to mean that we can just slice using `buf[tail..head]`.
 
-            // there is enough free space to copy the head in one go,
-            // this means that we first shift the tail forwards, and then
-            // copy the head to the correct position.
+            self.head = 0;
+        } else if free >= tail_len {
+            // there is enough free space to copy the tail in one go,
+            // this means that we first shift the head forwards, and then
+            // copy the tail to the correct position.
             //
             // from: FGH....ABCDE
             // to:   ...ABCDEFGH.
             unsafe {
-                ptr::copy(buf.add(self.tail), buf.add(self.head), tail_len);
+                self.copy(head, tail, head_len);
                 // FGHABCDE....
-                ptr::copy_nonoverlapping(buf, buf.add(self.head + tail_len), self.head);
+                self.copy_nonoverlapping(0, tail + head_len, tail_len);
                 // ...ABCDEFGH.
-
-                self.tail = self.head;
-                self.head = self.wrap_add(self.tail, len);
             }
+
+            self.head = tail;
         } else {
-            // free is smaller than both head and tail,
-            // this means we have to slowly "swap" the tail and the head.
+            // `free` is smaller than both `head_len` and `tail_len`.
+            // the general algorithm for this first moves the slices
+            // right next to each other and then uses `slice::rotate`
+            // to rotate them into place:
             //
-            // from: EFGHI...ABCD or HIJK.ABCDEFG
-            // to:   ABCDEFGHI... or ABCDEFGHIJK.
-            let mut left_edge: usize = 0;
-            let mut right_edge: usize = self.tail;
-            unsafe {
-                // The general problem looks like this
-                // GHIJKLM...ABCDEF - before any swaps
-                // ABCDEFM...GHIJKL - after 1 pass of swaps
-                // ABCDEFGHIJM...KL - swap until the left edge reaches the temp store
-                //                  - then restart the algorithm with a new (smaller) store
-                // Sometimes the temp store is reached when the right edge is at the end
-                // of the buffer - this means we've hit the right order with fewer swaps!
-                // E.g
-                // EF..ABCD
-                // ABCDEF.. - after four only swaps we've finished
-                while left_edge < len && right_edge != cap {
-                    let mut right_offset = 0;
-                    for i in left_edge..right_edge {
-                        right_offset = (i - left_edge) % (cap - right_edge);
-                        let src = right_edge + right_offset;
-                        ptr::swap(buf.add(i), buf.add(src));
+            // initially:   HIJK..ABCDEFG
+            // step 1:      ..HIJKABCDEFG
+            // step 2:      ..ABCDEFGHIJK
+            //
+            // or:
+            //
+            // initially:   FGHIJK..ABCDE
+            // step 1:      FGHIJKABCDE..
+            // step 2:      ABCDEFGHIJK..
+
+            // pick the shorter of the 2 slices to reduce the amount
+            // of memory that needs to be moved around.
+            if head_len > tail_len {
+                // tail is shorter, so:
+                //  1. copy tail forwards
+                //  2. rotate used part of the buffer
+                //  3. update head to point to the new beginning (which is just `free`)
+
+                unsafe {
+                    // if there is no free space in the buffer, then the slices are already
+                    // right next to each other and we don't need to move any memory.
+                    if free != 0 {
+                        // because we only move the tail forward as much as there's free space
+                        // behind it, we don't overwrite any elements of the head slice, and
+                        // the slices end up right next to each other.
+                        self.copy(0, free, tail_len);
                     }
-                    let n_ops = right_edge - left_edge;
-                    left_edge += n_ops;
-                    right_edge += right_offset + 1;
+
+                    // We just copied the tail right next to the head slice,
+                    // so all of the elements in the range are initialized
+                    let slice = &mut *self.buffer_range(free..self.capacity());
+
+                    // because the deque wasn't contiguous, we know that `tail_len < self.len == slice.len()`,
+                    // so this will never panic.
+                    slice.rotate_left(tail_len);
+
+                    // the used part of the buffer now is `free..self.capacity()`, so set
+                    // `head` to the beginning of that range.
+                    self.head = free;
                 }
+            } else {
+                // head is shorter so:
+                //  1. copy head backwards
+                //  2. rotate used part of the buffer
+                //  3. update head to point to the new beginning (which is the beginning of the buffer)
+
+                unsafe {
+                    // if there is no free space in the buffer, then the slices are already
+                    // right next to each other and we don't need to move any memory.
+                    if free != 0 {
+                        // copy the head slice to lie right behind the tail slice.
+                        self.copy(self.head, tail_len, head_len);
+                    }
+
+                    // because we copied the head slice so that both slices lie right
+                    // next to each other, all the elements in the range are initialized.
+                    let slice = &mut *self.buffer_range(0..self.len);
 
-                self.tail = 0;
-                self.head = len;
+                    // because the deque wasn't contiguous, we know that `head_len < self.len == slice.len()`
+                    // so this will never panic.
+                    slice.rotate_right(head_len);
+
+                    // the used part of the buffer now is `0..self.len`, so set
+                    // `head` to the beginning of that range.
+                    self.head = 0;
+                }
             }
         }
 
-        let tail = self.tail;
-        let head = self.head;
-        // Safety:
-        // - `self.head` and `self.tail` in a ring buffer are always valid indices.
-        // - `RingSlices::ring_slices` guarantees that the slices split according to `self.head` and `self.tail` are initialized.
-        unsafe {
-            MaybeUninit::slice_assume_init_mut(
-                RingSlices::ring_slices(self.buffer_as_mut_slice(), head, tail).0,
-            )
-        }
+        unsafe { slice::from_raw_parts_mut(ptr.add(self.head), self.len) }
     }
 
     /// Rotates the double-ended queue `mid` places to the left.
@@ -2513,7 +2263,7 @@ impl<T, A: Allocator> VecDeque<T, A> {
     #[stable(feature = "vecdeque_rotate", since = "1.36.0")]
     pub fn rotate_left(&mut self, mid: usize) {
         assert!(mid <= self.len());
-        let k = self.len() - mid;
+        let k = self.len - mid;
         if mid <= k {
             unsafe { self.rotate_left_inner(mid) }
         } else {
@@ -2556,7 +2306,7 @@ impl<T, A: Allocator> VecDeque<T, A> {
     #[stable(feature = "vecdeque_rotate", since = "1.36.0")]
     pub fn rotate_right(&mut self, k: usize) {
         assert!(k <= self.len());
-        let mid = self.len() - k;
+        let mid = self.len - k;
         if k <= mid {
             unsafe { self.rotate_right_inner(k) }
         } else {
@@ -2567,26 +2317,24 @@ impl<T, A: Allocator> VecDeque<T, A> {
     // SAFETY: the following two methods require that the rotation amount
     // be less than half the length of the deque.
     //
-    // `wrap_copy` requires that `min(x, cap() - x) + copy_len <= cap()`,
-    // but than `min` is never more than half the capacity, regardless of x,
+    // `wrap_copy` requires that `min(x, capacity() - x) + copy_len <= capacity()`,
+    // but then `min` is never more than half the capacity, regardless of x,
     // so it's sound to call here because we're calling with something
     // less than half the length, which is never above half the capacity.
 
     unsafe fn rotate_left_inner(&mut self, mid: usize) {
         debug_assert!(mid * 2 <= self.len());
         unsafe {
-            self.wrap_copy(self.head, self.tail, mid);
+            self.wrap_copy(self.head, self.to_physical_idx(self.len), mid);
         }
-        self.head = self.wrap_add(self.head, mid);
-        self.tail = self.wrap_add(self.tail, mid);
+        self.head = self.to_physical_idx(mid);
     }
 
     unsafe fn rotate_right_inner(&mut self, k: usize) {
         debug_assert!(k * 2 <= self.len());
         self.head = self.wrap_sub(self.head, k);
-        self.tail = self.wrap_sub(self.tail, k);
         unsafe {
-            self.wrap_copy(self.tail, self.head, k);
+            self.wrap_copy(self.to_physical_idx(self.len), self.head, k);
         }
     }
 
@@ -2844,23 +2592,19 @@ impl<T: Clone, A: Allocator> VecDeque<T, A> {
 
 /// Returns the index in the underlying buffer for a given logical element index.
 #[inline]
-fn wrap_index(index: usize, size: usize) -> usize {
-    // size is always a power of 2
-    debug_assert!(size.is_power_of_two());
-    index & (size - 1)
-}
-
-/// Calculate the number of elements left to be read in the buffer
-#[inline]
-fn count(tail: usize, head: usize, size: usize) -> usize {
-    // size is always a power of 2
-    (head.wrapping_sub(tail)) & (size - 1)
+fn wrap_index(logical_index: usize, capacity: usize) -> usize {
+    debug_assert!(
+        (logical_index == 0 && capacity == 0)
+            || logical_index < capacity
+            || (logical_index - capacity) < capacity
+    );
+    if logical_index >= capacity { logical_index - capacity } else { logical_index }
 }
 
 #[stable(feature = "rust1", since = "1.0.0")]
 impl<T: PartialEq, A: Allocator> PartialEq for VecDeque<T, A> {
     fn eq(&self, other: &Self) -> bool {
-        if self.len() != other.len() {
+        if self.len != other.len() {
             return false;
         }
         let (sa, sb) = self.as_slices();
@@ -2924,7 +2668,7 @@ impl<T: Ord, A: Allocator> Ord for VecDeque<T, A> {
 #[stable(feature = "rust1", since = "1.0.0")]
 impl<T: Hash, A: Allocator> Hash for VecDeque<T, A> {
     fn hash<H: Hasher>(&self, state: &mut H) {
-        state.write_length_prefix(self.len());
+        state.write_length_prefix(self.len);
         // It's not possible to use Hash::hash_slice on slices
         // returned by as_slices method as their length can vary
         // in otherwise identical deques.
@@ -3033,7 +2777,7 @@ impl<'a, T: 'a + Copy, A: Allocator> Extend<&'a T> for VecDeque<T, A> {
 #[stable(feature = "rust1", since = "1.0.0")]
 impl<T: fmt::Debug, A: Allocator> fmt::Debug for VecDeque<T, A> {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        f.debug_list().entries(self).finish()
+        f.debug_list().entries(self.iter()).finish()
     }
 }
 
@@ -3044,31 +2788,12 @@ impl<T, A: Allocator> From<Vec<T, A>> for VecDeque<T, A> {
     /// [`Vec<T>`]: crate::vec::Vec
     /// [`VecDeque<T>`]: crate::collections::VecDeque
     ///
-    /// This avoids reallocating where possible, but the conditions for that are
-    /// strict, and subject to change, and so shouldn't be relied upon unless the
-    /// `Vec<T>` came from `From<VecDeque<T>>` and hasn't been reallocated.
-    fn from(mut other: Vec<T, A>) -> Self {
-        let len = other.len();
-        if T::IS_ZST {
-            // There's no actual allocation for ZSTs to worry about capacity,
-            // but `VecDeque` can't handle as much length as `Vec`.
-            assert!(len < MAXIMUM_ZST_CAPACITY, "capacity overflow");
-        } else {
-            // We need to resize if the capacity is not a power of two, too small or
-            // doesn't have at least one free space. We do this while it's still in
-            // the `Vec` so the items will drop on panic.
-            let min_cap = cmp::max(MINIMUM_CAPACITY, len) + 1;
-            let cap = cmp::max(min_cap, other.capacity()).next_power_of_two();
-            if other.capacity() != cap {
-                other.reserve_exact(cap - len);
-            }
-        }
-
-        unsafe {
-            let (other_buf, len, capacity, alloc) = other.into_raw_parts_with_alloc();
-            let buf = RawVec::from_raw_parts_in(other_buf, capacity, alloc);
-            VecDeque { tail: 0, head: len, buf }
-        }
+    /// In its current implementation, this is a very cheap
+    /// conversion. This isn't yet a guarantee though, and
+    /// shouldn't be relied on.
+    fn from(other: Vec<T, A>) -> Self {
+        let (ptr, len, cap, alloc) = other.into_raw_parts_with_alloc();
+        Self { head: 0, len, buf: unsafe { RawVec::from_raw_parts_in(ptr, cap, alloc) } }
     }
 }
 
@@ -3110,11 +2835,11 @@ impl<T, A: Allocator> From<VecDeque<T, A>> for Vec<T, A> {
             let other = ManuallyDrop::new(other);
             let buf = other.buf.ptr();
             let len = other.len();
-            let cap = other.cap();
+            let cap = other.capacity();
             let alloc = ptr::read(other.allocator());
 
-            if other.tail != 0 {
-                ptr::copy(buf.add(other.tail), buf, len);
+            if other.head != 0 {
+                ptr::copy(buf.add(other.head), buf, len);
             }
             Vec::from_raw_parts_in(buf, len, cap, alloc)
         }
@@ -3141,8 +2866,8 @@ impl<T, const N: usize> From<[T; N]> for VecDeque<T> {
                 ptr::copy_nonoverlapping(arr.as_ptr(), deq.ptr(), N);
             }
         }
-        deq.tail = 0;
-        deq.head = N;
+        deq.head = 0;
+        deq.len = N;
         deq
     }
 }
diff --git a/library/alloc/src/collections/vec_deque/pair_slices.rs b/library/alloc/src/collections/vec_deque/pair_slices.rs
deleted file mode 100644
index 6735424a3ef..00000000000
--- a/library/alloc/src/collections/vec_deque/pair_slices.rs
+++ /dev/null
@@ -1,67 +0,0 @@
-use core::cmp::{self};
-use core::mem::replace;
-
-use crate::alloc::Allocator;
-
-use super::VecDeque;
-
-/// PairSlices pairs up equal length slice parts of two deques
-///
-/// For example, given deques "A" and "B" with the following division into slices:
-///
-/// A: [0 1 2] [3 4 5]
-/// B: [a b] [c d e]
-///
-/// It produces the following sequence of matching slices:
-///
-/// ([0 1], [a b])
-/// (\[2\], \[c\])
-/// ([3 4], [d e])
-///
-/// and the uneven remainder of either A or B is skipped.
-pub struct PairSlices<'a, 'b, T> {
-    a0: &'a mut [T],
-    a1: &'a mut [T],
-    b0: &'b [T],
-    b1: &'b [T],
-}
-
-impl<'a, 'b, T> PairSlices<'a, 'b, T> {
-    pub fn from<A: Allocator>(to: &'a mut VecDeque<T, A>, from: &'b VecDeque<T, A>) -> Self {
-        let (a0, a1) = to.as_mut_slices();
-        let (b0, b1) = from.as_slices();
-        PairSlices { a0, a1, b0, b1 }
-    }
-
-    pub fn has_remainder(&self) -> bool {
-        !self.b0.is_empty()
-    }
-
-    pub fn remainder(self) -> impl Iterator<Item = &'b [T]> {
-        IntoIterator::into_iter([self.b0, self.b1])
-    }
-}
-
-impl<'a, 'b, T> Iterator for PairSlices<'a, 'b, T> {
-    type Item = (&'a mut [T], &'b [T]);
-    fn next(&mut self) -> Option<Self::Item> {
-        // Get next part length
-        let part = cmp::min(self.a0.len(), self.b0.len());
-        if part == 0 {
-            return None;
-        }
-        let (p0, p1) = replace(&mut self.a0, &mut []).split_at_mut(part);
-        let (q0, q1) = self.b0.split_at(part);
-
-        // Move a1 into a0, if it's empty (and b1, b0 the same way).
-        self.a0 = p1;
-        self.b0 = q1;
-        if self.a0.is_empty() {
-            self.a0 = replace(&mut self.a1, &mut []);
-        }
-        if self.b0.is_empty() {
-            self.b0 = replace(&mut self.b1, &[]);
-        }
-        Some((p0, q0))
-    }
-}
diff --git a/library/alloc/src/collections/vec_deque/ring_slices.rs b/library/alloc/src/collections/vec_deque/ring_slices.rs
deleted file mode 100644
index dd0fa7d6074..00000000000
--- a/library/alloc/src/collections/vec_deque/ring_slices.rs
+++ /dev/null
@@ -1,56 +0,0 @@
-use core::ptr::{self};
-
-/// Returns the two slices that cover the `VecDeque`'s valid range
-pub trait RingSlices: Sized {
-    fn slice(self, from: usize, to: usize) -> Self;
-    fn split_at(self, i: usize) -> (Self, Self);
-
-    fn ring_slices(buf: Self, head: usize, tail: usize) -> (Self, Self) {
-        let contiguous = tail <= head;
-        if contiguous {
-            let (empty, buf) = buf.split_at(0);
-            (buf.slice(tail, head), empty)
-        } else {
-            let (mid, right) = buf.split_at(tail);
-            let (left, _) = mid.split_at(head);
-            (right, left)
-        }
-    }
-}
-
-impl<T> RingSlices for &[T] {
-    fn slice(self, from: usize, to: usize) -> Self {
-        &self[from..to]
-    }
-    fn split_at(self, i: usize) -> (Self, Self) {
-        (*self).split_at(i)
-    }
-}
-
-impl<T> RingSlices for &mut [T] {
-    fn slice(self, from: usize, to: usize) -> Self {
-        &mut self[from..to]
-    }
-    fn split_at(self, i: usize) -> (Self, Self) {
-        (*self).split_at_mut(i)
-    }
-}
-
-impl<T> RingSlices for *mut [T] {
-    fn slice(self, from: usize, to: usize) -> Self {
-        assert!(from <= to && to < self.len());
-        // Not using `get_unchecked_mut` to keep this a safe operation.
-        let len = to - from;
-        ptr::slice_from_raw_parts_mut(self.as_mut_ptr().wrapping_add(from), len)
-    }
-
-    fn split_at(self, mid: usize) -> (Self, Self) {
-        let len = self.len();
-        let ptr = self.as_mut_ptr();
-        assert!(mid <= len);
-        (
-            ptr::slice_from_raw_parts_mut(ptr, mid),
-            ptr::slice_from_raw_parts_mut(ptr.wrapping_add(mid), len - mid),
-        )
-    }
-}
diff --git a/library/alloc/src/collections/vec_deque/spec_extend.rs b/library/alloc/src/collections/vec_deque/spec_extend.rs
index 97ff8b76524..dccf40ccb38 100644
--- a/library/alloc/src/collections/vec_deque/spec_extend.rs
+++ b/library/alloc/src/collections/vec_deque/spec_extend.rs
@@ -1,6 +1,6 @@
 use crate::alloc::Allocator;
 use crate::vec;
-use core::iter::{ByRefSized, TrustedLen};
+use core::iter::TrustedLen;
 use core::slice;
 
 use super::VecDeque;
@@ -17,19 +17,33 @@ where
     default fn spec_extend(&mut self, mut iter: I) {
         // This function should be the moral equivalent of:
         //
-        //      for item in iter {
-        //          self.push_back(item);
-        //      }
-        while let Some(element) = iter.next() {
-            if self.len() == self.capacity() {
-                let (lower, _) = iter.size_hint();
-                self.reserve(lower.saturating_add(1));
-            }
+        // for item in iter {
+        //     self.push_back(item);
+        // }
+
+        // May only be called if `deque.len() < deque.capacity()`
+        unsafe fn push_unchecked<T, A: Allocator>(deque: &mut VecDeque<T, A>, element: T) {
+            // SAFETY: Because of the precondition, it's guaranteed that there is space
+            // in the logical array after the last element.
+            unsafe { deque.buffer_write(deque.to_physical_idx(deque.len), element) };
+            // This can't overflow because `deque.len() < deque.capacity() <= usize::MAX`.
+            deque.len += 1;
+        }
 
-            let head = self.head;
-            self.head = self.wrap_add(self.head, 1);
-            unsafe {
-                self.buffer_write(head, element);
+        while let Some(element) = iter.next() {
+            let (lower, _) = iter.size_hint();
+            self.reserve(lower.saturating_add(1));
+
+            // SAFETY: We just reserved space for at least one element.
+            unsafe { push_unchecked(self, element) };
+
+            // Inner loop to avoid repeatedly calling `reserve`.
+            while self.len < self.capacity() {
+                let Some(element) = iter.next() else {
+                    return;
+                };
+                // SAFETY: The loop condition guarantees that `self.len() < self.capacity()`.
+                unsafe { push_unchecked(self, element) };
             }
         }
     }
@@ -39,7 +53,7 @@ impl<T, I, A: Allocator> SpecExtend<T, I> for VecDeque<T, A>
 where
     I: TrustedLen<Item = T>,
 {
-    default fn spec_extend(&mut self, mut iter: I) {
+    default fn spec_extend(&mut self, iter: I) {
         // This is the case for a TrustedLen iterator.
         let (low, high) = iter.size_hint();
         if let Some(additional) = high {
@@ -51,35 +65,12 @@ where
             );
             self.reserve(additional);
 
-            struct WrapAddOnDrop<'a, T, A: Allocator> {
-                vec_deque: &'a mut VecDeque<T, A>,
-                written: usize,
-            }
-
-            impl<'a, T, A: Allocator> Drop for WrapAddOnDrop<'a, T, A> {
-                fn drop(&mut self) {
-                    self.vec_deque.head =
-                        self.vec_deque.wrap_add(self.vec_deque.head, self.written);
-                }
-            }
-
-            let mut wrapper = WrapAddOnDrop { vec_deque: self, written: 0 };
-
-            let head_room = wrapper.vec_deque.cap() - wrapper.vec_deque.head;
-            unsafe {
-                wrapper.vec_deque.write_iter(
-                    wrapper.vec_deque.head,
-                    ByRefSized(&mut iter).take(head_room),
-                    &mut wrapper.written,
-                );
-
-                if additional > head_room {
-                    wrapper.vec_deque.write_iter(0, iter, &mut wrapper.written);
-                }
-            }
+            let written = unsafe {
+                self.write_iter_wrapping(self.to_physical_idx(self.len), iter, additional)
+            };
 
             debug_assert_eq!(
-                additional, wrapper.written,
+                additional, written,
                 "The number of items written to VecDeque doesn't match the TrustedLen size hint"
             );
         } else {
@@ -99,8 +90,8 @@ impl<T, A: Allocator> SpecExtend<T, vec::IntoIter<T>> for VecDeque<T, A> {
         self.reserve(slice.len());
 
         unsafe {
-            self.copy_slice(self.head, slice);
-            self.head = self.wrap_add(self.head, slice.len());
+            self.copy_slice(self.to_physical_idx(self.len), slice);
+            self.len += slice.len();
         }
         iterator.forget_remaining_elements();
     }
@@ -125,8 +116,8 @@ where
         self.reserve(slice.len());
 
         unsafe {
-            self.copy_slice(self.head, slice);
-            self.head = self.wrap_add(self.head, slice.len());
+            self.copy_slice(self.to_physical_idx(self.len), slice);
+            self.len += slice.len();
         }
     }
 }
diff --git a/library/alloc/src/collections/vec_deque/tests.rs b/library/alloc/src/collections/vec_deque/tests.rs
index 6e0f83020f9..220ad71beab 100644
--- a/library/alloc/src/collections/vec_deque/tests.rs
+++ b/library/alloc/src/collections/vec_deque/tests.rs
@@ -10,7 +10,7 @@ fn bench_push_back_100(b: &mut test::Bencher) {
             deq.push_back(i);
         }
         deq.head = 0;
-        deq.tail = 0;
+        deq.len = 0;
     })
 }
 
@@ -22,7 +22,7 @@ fn bench_push_front_100(b: &mut test::Bencher) {
             deq.push_front(i);
         }
         deq.head = 0;
-        deq.tail = 0;
+        deq.len = 0;
     })
 }
 
@@ -35,8 +35,8 @@ fn bench_pop_back_100(b: &mut test::Bencher) {
     unsafe { deq.ptr().write_bytes(0u8, size + 1) };
 
     b.iter(|| {
-        deq.head = size;
-        deq.tail = 0;
+        deq.head = 0;
+        deq.len = 100;
         while !deq.is_empty() {
             test::black_box(deq.pop_back());
         }
@@ -85,8 +85,8 @@ fn bench_pop_front_100(b: &mut test::Bencher) {
     unsafe { deq.ptr().write_bytes(0u8, size + 1) };
 
     b.iter(|| {
-        deq.head = size;
-        deq.tail = 0;
+        deq.head = 0;
+        deq.len = 100;
         while !deq.is_empty() {
             test::black_box(deq.pop_front());
         }
@@ -105,9 +105,9 @@ fn test_swap_front_back_remove() {
         for len in 0..final_len {
             let expected: VecDeque<_> =
                 if back { (0..len).collect() } else { (0..len).rev().collect() };
-            for tail_pos in 0..usable_cap {
-                tester.tail = tail_pos;
-                tester.head = tail_pos;
+            for head_pos in 0..usable_cap {
+                tester.head = head_pos;
+                tester.len = 0;
                 if back {
                     for i in 0..len * 2 {
                         tester.push_front(i);
@@ -124,8 +124,8 @@ fn test_swap_front_back_remove() {
                         assert_eq!(tester.swap_remove_front(idx), Some(len * 2 - 1 - i));
                     }
                 }
-                assert!(tester.tail < tester.cap());
-                assert!(tester.head < tester.cap());
+                assert!(tester.head <= tester.capacity());
+                assert!(tester.len <= tester.capacity());
                 assert_eq!(tester, expected);
             }
         }
@@ -150,18 +150,18 @@ fn test_insert() {
     for len in minlen..cap {
         // 0, 1, 2, .., len - 1
         let expected = (0..).take(len).collect::<VecDeque<_>>();
-        for tail_pos in 0..cap {
+        for head_pos in 0..cap {
             for to_insert in 0..len {
-                tester.tail = tail_pos;
-                tester.head = tail_pos;
+                tester.head = head_pos;
+                tester.len = 0;
                 for i in 0..len {
                     if i != to_insert {
                         tester.push_back(i);
                     }
                 }
                 tester.insert(to_insert, to_insert);
-                assert!(tester.tail < tester.cap());
-                assert!(tester.head < tester.cap());
+                assert!(tester.head <= tester.capacity());
+                assert!(tester.len <= tester.capacity());
                 assert_eq!(tester, expected);
             }
         }
@@ -257,13 +257,14 @@ fn test_swap_panic() {
 #[test]
 fn test_reserve_exact() {
     let mut tester: VecDeque<i32> = VecDeque::with_capacity(1);
-    assert!(tester.capacity() == 1);
+    assert_eq!(tester.capacity(), 1);
     tester.reserve_exact(50);
-    assert!(tester.capacity() >= 51);
+    assert_eq!(tester.capacity(), 50);
     tester.reserve_exact(40);
-    assert!(tester.capacity() >= 51);
+    // reserving won't shrink the buffer
+    assert_eq!(tester.capacity(), 50);
     tester.reserve_exact(200);
-    assert!(tester.capacity() >= 200);
+    assert_eq!(tester.capacity(), 200);
 }
 
 #[test]
@@ -323,6 +324,7 @@ fn test_contains() {
 #[test]
 fn test_rotate_left_right() {
     let mut tester: VecDeque<_> = (1..=10).collect();
+    tester.reserve(1);
 
     assert_eq!(tester.len(), 10);
 
@@ -463,7 +465,7 @@ fn test_binary_search_key() {
 }
 
 #[test]
-fn make_contiguous_big_tail() {
+fn make_contiguous_big_head() {
     let mut tester = VecDeque::with_capacity(15);
 
     for i in 0..3 {
@@ -478,14 +480,14 @@ fn make_contiguous_big_tail() {
     assert_eq!(tester.capacity(), 15);
     assert_eq!((&[9, 8, 7, 6, 5, 4, 3] as &[_], &[0, 1, 2] as &[_]), tester.as_slices());
 
-    let expected_start = tester.head;
+    let expected_start = tester.as_slices().1.len();
     tester.make_contiguous();
-    assert_eq!(tester.tail, expected_start);
+    assert_eq!(tester.head, expected_start);
     assert_eq!((&[9, 8, 7, 6, 5, 4, 3, 0, 1, 2] as &[_], &[] as &[_]), tester.as_slices());
 }
 
 #[test]
-fn make_contiguous_big_head() {
+fn make_contiguous_big_tail() {
     let mut tester = VecDeque::with_capacity(15);
 
     for i in 0..8 {
@@ -499,44 +501,46 @@ fn make_contiguous_big_head() {
     // 01234567......98
     let expected_start = 0;
     tester.make_contiguous();
-    assert_eq!(tester.tail, expected_start);
+    assert_eq!(tester.head, expected_start);
     assert_eq!((&[9, 8, 0, 1, 2, 3, 4, 5, 6, 7] as &[_], &[] as &[_]), tester.as_slices());
 }
 
 #[test]
 fn make_contiguous_small_free() {
-    let mut tester = VecDeque::with_capacity(15);
+    let mut tester = VecDeque::with_capacity(16);
 
-    for i in 'A' as u8..'I' as u8 {
+    for i in b'A'..b'I' {
         tester.push_back(i as char);
     }
 
-    for i in 'I' as u8..'N' as u8 {
+    for i in b'I'..b'N' {
         tester.push_front(i as char);
     }
 
+    assert_eq!(tester, ['M', 'L', 'K', 'J', 'I', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H']);
+
     // ABCDEFGH...MLKJI
     let expected_start = 0;
     tester.make_contiguous();
-    assert_eq!(tester.tail, expected_start);
+    assert_eq!(tester.head, expected_start);
     assert_eq!(
         (&['M', 'L', 'K', 'J', 'I', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H'] as &[_], &[] as &[_]),
         tester.as_slices()
     );
 
     tester.clear();
-    for i in 'I' as u8..'N' as u8 {
+    for i in b'I'..b'N' {
         tester.push_back(i as char);
     }
 
-    for i in 'A' as u8..'I' as u8 {
+    for i in b'A'..b'I' {
         tester.push_front(i as char);
     }
 
     // IJKLM...HGFEDCBA
-    let expected_start = 0;
+    let expected_start = 3;
     tester.make_contiguous();
-    assert_eq!(tester.tail, expected_start);
+    assert_eq!(tester.head, expected_start);
     assert_eq!(
         (&['H', 'G', 'F', 'E', 'D', 'C', 'B', 'A', 'I', 'J', 'K', 'L', 'M'] as &[_], &[] as &[_]),
         tester.as_slices()
@@ -545,16 +549,55 @@ fn make_contiguous_small_free() {
 
 #[test]
 fn make_contiguous_head_to_end() {
-    let mut dq = VecDeque::with_capacity(3);
-    dq.push_front('B');
-    dq.push_front('A');
-    dq.push_back('C');
-    dq.make_contiguous();
-    let expected_tail = 0;
-    let expected_head = 3;
-    assert_eq!(expected_tail, dq.tail);
-    assert_eq!(expected_head, dq.head);
-    assert_eq!((&['A', 'B', 'C'] as &[_], &[] as &[_]), dq.as_slices());
+    let mut tester = VecDeque::with_capacity(16);
+
+    for i in b'A'..b'L' {
+        tester.push_back(i as char);
+    }
+
+    for i in b'L'..b'Q' {
+        tester.push_front(i as char);
+    }
+
+    assert_eq!(
+        tester,
+        ['P', 'O', 'N', 'M', 'L', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K']
+    );
+
+    // ABCDEFGHIJKPONML
+    let expected_start = 0;
+    tester.make_contiguous();
+    assert_eq!(tester.head, expected_start);
+    assert_eq!(
+        (
+            &['P', 'O', 'N', 'M', 'L', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K']
+                as &[_],
+            &[] as &[_]
+        ),
+        tester.as_slices()
+    );
+
+    tester.clear();
+    for i in b'L'..b'Q' {
+        tester.push_back(i as char);
+    }
+
+    for i in b'A'..b'L' {
+        tester.push_front(i as char);
+    }
+
+    // LMNOPKJIHGFEDCBA
+    let expected_start = 0;
+    tester.make_contiguous();
+    assert_eq!(tester.head, expected_start);
+    assert_eq!(
+        (
+            &['K', 'J', 'I', 'H', 'G', 'F', 'E', 'D', 'C', 'B', 'A', 'L', 'M', 'N', 'O', 'P']
+                as &[_],
+            &[] as &[_]
+        ),
+        tester.as_slices()
+    );
 }
 
 #[test]
@@ -588,10 +631,10 @@ fn test_remove() {
     for len in minlen..cap - 1 {
         // 0, 1, 2, .., len - 1
         let expected = (0..).take(len).collect::<VecDeque<_>>();
-        for tail_pos in 0..cap {
+        for head_pos in 0..cap {
             for to_remove in 0..=len {
-                tester.tail = tail_pos;
-                tester.head = tail_pos;
+                tester.head = head_pos;
+                tester.len = 0;
                 for i in 0..len {
                     if i == to_remove {
                         tester.push_back(1234);
@@ -602,8 +645,8 @@ fn test_remove() {
                     tester.push_back(1234);
                 }
                 tester.remove(to_remove);
-                assert!(tester.tail < tester.cap());
-                assert!(tester.head < tester.cap());
+                assert!(tester.head <= tester.capacity());
+                assert!(tester.len <= tester.capacity());
                 assert_eq!(tester, expected);
             }
         }
@@ -617,11 +660,11 @@ fn test_range() {
     let cap = tester.capacity();
     let minlen = if cfg!(miri) { cap - 1 } else { 0 }; // Miri is too slow
     for len in minlen..=cap {
-        for tail in 0..=cap {
+        for head in 0..=cap {
             for start in 0..=len {
                 for end in start..=len {
-                    tester.tail = tail;
-                    tester.head = tail;
+                    tester.head = head;
+                    tester.len = 0;
                     for i in 0..len {
                         tester.push_back(i);
                     }
@@ -642,17 +685,17 @@ fn test_range_mut() {
 
     let cap = tester.capacity();
     for len in 0..=cap {
-        for tail in 0..=cap {
+        for head in 0..=cap {
             for start in 0..=len {
                 for end in start..=len {
-                    tester.tail = tail;
-                    tester.head = tail;
+                    tester.head = head;
+                    tester.len = 0;
                     for i in 0..len {
                         tester.push_back(i);
                     }
 
                     let head_was = tester.head;
-                    let tail_was = tester.tail;
+                    let len_was = tester.len;
 
                     // Check that we iterate over the correct values
                     let range: VecDeque<_> = tester.range_mut(start..end).map(|v| *v).collect();
@@ -662,8 +705,8 @@ fn test_range_mut() {
                     // We shouldn't have changed the capacity or made the
                     // head or tail out of bounds
                     assert_eq!(tester.capacity(), cap);
-                    assert_eq!(tester.tail, tail_was);
                     assert_eq!(tester.head, head_was);
+                    assert_eq!(tester.len, len_was);
                 }
             }
         }
@@ -676,11 +719,11 @@ fn test_drain() {
 
     let cap = tester.capacity();
     for len in 0..=cap {
-        for tail in 0..=cap {
+        for head in 0..cap {
             for drain_start in 0..=len {
                 for drain_end in drain_start..=len {
-                    tester.tail = tail;
-                    tester.head = tail;
+                    tester.head = head;
+                    tester.len = 0;
                     for i in 0..len {
                         tester.push_back(i);
                     }
@@ -693,8 +736,8 @@ fn test_drain() {
                     // We shouldn't have changed the capacity or made the
                     // head or tail out of bounds
                     assert_eq!(tester.capacity(), cap);
-                    assert!(tester.tail < tester.cap());
-                    assert!(tester.head < tester.cap());
+                    assert!(tester.head <= tester.capacity());
+                    assert!(tester.len <= tester.capacity());
 
                     // We should see the correct values in the VecDeque
                     let expected: VecDeque<_> = (0..drain_start).chain(drain_end..len).collect();
@@ -721,17 +764,18 @@ fn test_shrink_to_fit() {
     for len in 0..=cap {
         // 0, 1, 2, .., len - 1
         let expected = (0..).take(len).collect::<VecDeque<_>>();
-        for tail_pos in 0..=max_cap {
-            tester.tail = tail_pos;
-            tester.head = tail_pos;
+        for head_pos in 0..=max_cap {
+            tester.reserve(head_pos);
+            tester.head = head_pos;
+            tester.len = 0;
             tester.reserve(63);
             for i in 0..len {
                 tester.push_back(i);
             }
             tester.shrink_to_fit();
             assert!(tester.capacity() <= cap);
-            assert!(tester.tail < tester.cap());
-            assert!(tester.head < tester.cap());
+            assert!(tester.head <= tester.capacity());
+            assert!(tester.len <= tester.capacity());
             assert_eq!(tester, expected);
         }
     }
@@ -758,17 +802,17 @@ fn test_split_off() {
             // at, at + 1, .., len - 1 (may be empty)
             let expected_other = (at..).take(len - at).collect::<VecDeque<_>>();
 
-            for tail_pos in 0..cap {
-                tester.tail = tail_pos;
-                tester.head = tail_pos;
+            for head_pos in 0..cap {
+                tester.head = head_pos;
+                tester.len = 0;
                 for i in 0..len {
                     tester.push_back(i);
                 }
                 let result = tester.split_off(at);
-                assert!(tester.tail < tester.cap());
-                assert!(tester.head < tester.cap());
-                assert!(result.tail < result.cap());
-                assert!(result.head < result.cap());
+                assert!(tester.head <= tester.capacity());
+                assert!(tester.len <= tester.capacity());
+                assert!(result.head <= result.capacity());
+                assert!(result.len <= result.capacity());
                 assert_eq!(tester, expected_self);
                 assert_eq!(result, expected_other);
             }
@@ -785,16 +829,10 @@ fn test_from_vec() {
             vec.extend(0..len);
 
             let vd = VecDeque::from(vec.clone());
-            assert!(vd.cap().is_power_of_two());
             assert_eq!(vd.len(), vec.len());
             assert!(vd.into_iter().eq(vec));
         }
     }
-
-    let vec = Vec::from([(); MAXIMUM_ZST_CAPACITY - 1]);
-    let vd = VecDeque::from(vec.clone());
-    assert!(vd.cap().is_power_of_two());
-    assert_eq!(vd.len(), vec.len());
 }
 
 #[test]
@@ -846,10 +884,6 @@ fn test_extend_impl(trusted_len: bool) {
             }
 
             assert_eq!(self.test, self.expected);
-            let (a1, b1) = self.test.as_slices();
-            let (a2, b2) = self.expected.as_slices();
-            assert_eq!(a1, a2);
-            assert_eq!(b1, b2);
         }
 
         fn drain<R: RangeBounds<usize> + Clone>(&mut self, range: R) {
@@ -872,7 +906,7 @@ fn test_extend_impl(trusted_len: bool) {
     let mut tester = VecDequeTester::new(trusted_len);
 
     // Initial capacity
-    tester.test_extend(0..tester.remaining_capacity() - 1);
+    tester.test_extend(0..tester.remaining_capacity());
 
     // Grow
     tester.test_extend(1024..2048);
@@ -880,7 +914,7 @@ fn test_extend_impl(trusted_len: bool) {
     // Wrap around
     tester.drain(..128);
 
-    tester.test_extend(0..tester.remaining_capacity() - 1);
+    tester.test_extend(0..tester.remaining_capacity());
 
     // Continue
     tester.drain(256..);
@@ -893,16 +927,6 @@ fn test_extend_impl(trusted_len: bool) {
 }
 
 #[test]
-#[should_panic = "capacity overflow"]
-fn test_from_vec_zst_overflow() {
-    use crate::vec::Vec;
-    let vec = Vec::from([(); MAXIMUM_ZST_CAPACITY]);
-    let vd = VecDeque::from(vec.clone()); // no room for +1
-    assert!(vd.cap().is_power_of_two());
-    assert_eq!(vd.len(), vec.len());
-}
-
-#[test]
 fn test_from_array() {
     fn test<const N: usize>() {
         let mut array: [usize; N] = [0; N];
@@ -917,7 +941,6 @@ fn test_from_array() {
             assert_eq!(deq[i], i);
         }
 
-        assert!(deq.cap().is_power_of_two());
         assert_eq!(deq.len(), N);
     }
     test::<0>();
@@ -925,11 +948,6 @@ fn test_from_array() {
     test::<2>();
     test::<32>();
     test::<35>();
-
-    let array = [(); MAXIMUM_ZST_CAPACITY - 1];
-    let deq = VecDeque::from(array);
-    assert!(deq.cap().is_power_of_two());
-    assert_eq!(deq.len(), MAXIMUM_ZST_CAPACITY - 1);
 }
 
 #[test]
diff --git a/library/alloc/tests/vec_deque.rs b/library/alloc/tests/vec_deque.rs
index c1b9e7af9d8..d04de5a074b 100644
--- a/library/alloc/tests/vec_deque.rs
+++ b/library/alloc/tests/vec_deque.rs
@@ -465,7 +465,6 @@ fn test_drain() {
         for i in 6..9 {
             d.push_front(i);
         }
-
         assert_eq!(d.drain(..).collect::<Vec<_>>(), [8, 7, 6, 0, 1, 2, 3, 4]);
         assert!(d.is_empty());
     }
@@ -1142,7 +1141,7 @@ fn test_reserve_exact_2() {
     v.push_back(16);
 
     v.reserve_exact(16);
-    assert!(v.capacity() >= 48)
+    assert!(v.capacity() >= 33)
 }
 
 #[test]
@@ -1157,7 +1156,7 @@ fn test_try_reserve() {
     // * overflow may trigger when adding `len` to `cap` (in number of elements)
     // * overflow may trigger when multiplying `new_cap` by size_of::<T> (to get bytes)
 
-    const MAX_CAP: usize = (isize::MAX as usize + 1) / 2 - 1;
+    const MAX_CAP: usize = isize::MAX as usize;
     const MAX_USIZE: usize = usize::MAX;
 
     {
@@ -1248,7 +1247,7 @@ fn test_try_reserve_exact() {
     // This is exactly the same as test_try_reserve with the method changed.
     // See that test for comments.
 
-    const MAX_CAP: usize = (isize::MAX as usize + 1) / 2 - 1;
+    const MAX_CAP: usize = isize::MAX as usize;
     const MAX_USIZE: usize = usize::MAX;
 
     {
@@ -1391,7 +1390,8 @@ fn test_rotate_nop() {
 
 #[test]
 fn test_rotate_left_parts() {
-    let mut v: VecDeque<_> = (1..=7).collect();
+    let mut v: VecDeque<_> = VecDeque::with_capacity(8);
+    v.extend(1..=7);
     v.rotate_left(2);
     assert_eq!(v.as_slices(), (&[3, 4, 5, 6, 7, 1][..], &[2][..]));
     v.rotate_left(2);
@@ -1410,7 +1410,8 @@ fn test_rotate_left_parts() {
 
 #[test]
 fn test_rotate_right_parts() {
-    let mut v: VecDeque<_> = (1..=7).collect();
+    let mut v: VecDeque<_> = VecDeque::with_capacity(8);
+    v.extend(1..=7);
     v.rotate_right(2);
     assert_eq!(v.as_slices(), (&[6, 7][..], &[1, 2, 3, 4, 5][..]));
     v.rotate_right(2);
diff --git a/library/core/src/intrinsics/mir.rs b/library/core/src/intrinsics/mir.rs
index 1bacdc39148..8ba1c122884 100644
--- a/library/core/src/intrinsics/mir.rs
+++ b/library/core/src/intrinsics/mir.rs
@@ -80,6 +80,8 @@ define!("mir_goto", fn Goto(destination: BasicBlock) -> BasicBlock);
 define!("mir_retag", fn Retag<T>(place: T));
 define!("mir_retag_raw", fn RetagRaw<T>(place: T));
 define!("mir_move", fn Move<T>(place: T) -> T);
+define!("mir_static", fn Static<T>(s: T) -> &'static T);
+define!("mir_static_mut", fn StaticMut<T>(s: T) -> *mut T);
 
 /// Convenience macro for generating custom MIR.
 ///
@@ -90,10 +92,14 @@ pub macro mir {
     (
         $(let $local_decl:ident $(: $local_decl_ty:ty)? ;)*
 
-        $entry_block:block
+        {
+            $($entry:tt)*
+        }
 
         $(
-            $block_name:ident = $block:block
+            $block_name:ident = {
+                $($block:tt)*
+            }
         )*
     ) => {{
         // First, we declare all basic blocks.
@@ -109,11 +115,22 @@ pub macro mir {
                 let $local_decl $(: $local_decl_ty)? ;
             )*
 
+            ::core::intrinsics::mir::__internal_extract_let!($($entry)*);
+            $(
+                ::core::intrinsics::mir::__internal_extract_let!($($block)*);
+            )*
+
             {
                 // Finally, the contents of the basic blocks
-                $entry_block;
+                ::core::intrinsics::mir::__internal_remove_let!({
+                    {}
+                    { $($entry)* }
+                });
                 $(
-                    $block;
+                    ::core::intrinsics::mir::__internal_remove_let!({
+                        {}
+                        { $($block)* }
+                    });
                 )*
 
                 RET
@@ -121,3 +138,152 @@ pub macro mir {
         }
     }}
 }
+
+/// Helper macro that extracts the `let` declarations out of a bunch of statements.
+///
+/// This macro is written using the "statement muncher" strategy. Each invocation parses the first
+/// statement out of the input, does the appropriate thing with it, and then recursively calls the
+/// same macro on the remainder of the input.
+#[doc(hidden)]
+pub macro __internal_extract_let {
+    // If it's a `let` like statement, keep the `let`
+    (
+        let $var:ident $(: $ty:ty)? = $expr:expr; $($rest:tt)*
+    ) => {
+        let $var $(: $ty)?;
+        ::core::intrinsics::mir::__internal_extract_let!($($rest)*);
+    },
+    // Due to #86730, we have to handle const blocks separately
+    (
+        let $var:ident $(: $ty:ty)? = const $block:block; $($rest:tt)*
+    ) => {
+        let $var $(: $ty)?;
+        ::core::intrinsics::mir::__internal_extract_let!($($rest)*);
+    },
+    // Otherwise, output nothing
+    (
+        $stmt:stmt; $($rest:tt)*
+    ) => {
+        ::core::intrinsics::mir::__internal_extract_let!($($rest)*);
+    },
+    (
+        $expr:expr
+    ) => {}
+}
+
+/// Helper macro that removes the `let` declarations from a bunch of statements.
+///
+/// Because expression position macros cannot expand to statements + expressions, we need to be
+/// slightly creative here. The general strategy is also statement munching as above, but the output
+/// of the macro is "stored" in the subsequent macro invocation. Easiest understood via example:
+/// ```text
+/// invoke!(
+///     {
+///         {
+///             x = 5;
+///         }
+///         {
+///             let d = e;
+///             Call()
+///         }
+///     }
+/// )
+/// ```
+/// becomes
+/// ```text
+/// invoke!(
+///     {
+///         {
+///             x = 5;
+///             d = e;
+///         }
+///         {
+///             Call()
+///         }
+///     }
+/// )
+/// ```
+#[doc(hidden)]
+pub macro __internal_remove_let {
+    // If it's a `let` like statement, remove the `let`
+    (
+        {
+            {
+                $($already_parsed:tt)*
+            }
+            {
+                let $var:ident $(: $ty:ty)? = $expr:expr;
+                $($rest:tt)*
+            }
+        }
+    ) => { ::core::intrinsics::mir::__internal_remove_let!(
+        {
+            {
+                $($already_parsed)*
+                $var = $expr;
+            }
+            {
+                $($rest)*
+            }
+        }
+    )},
+    // Due to #86730 , we have to handle const blocks separately
+    (
+        {
+            {
+                $($already_parsed:tt)*
+            }
+            {
+                let $var:ident $(: $ty:ty)? = const $block:block;
+                $($rest:tt)*
+            }
+        }
+    ) => { ::core::intrinsics::mir::__internal_remove_let!(
+        {
+            {
+                $($already_parsed)*
+                $var = const $block;
+            }
+            {
+                $($rest)*
+            }
+        }
+    )},
+    // Otherwise, keep going
+    (
+        {
+            {
+                $($already_parsed:tt)*
+            }
+            {
+                $stmt:stmt;
+                $($rest:tt)*
+            }
+        }
+    ) => { ::core::intrinsics::mir::__internal_remove_let!(
+        {
+            {
+                $($already_parsed)*
+                $stmt;
+            }
+            {
+                $($rest)*
+            }
+        }
+    )},
+    (
+        {
+            {
+                $($already_parsed:tt)*
+            }
+            {
+                $expr:expr
+            }
+        }
+    ) => {
+        {
+            $($already_parsed)*
+            $expr
+        }
+    },
+}
diff --git a/library/core/src/macros/mod.rs b/library/core/src/macros/mod.rs
index c20ca69a1c6..f29cd357d6b 100644
--- a/library/core/src/macros/mod.rs
+++ b/library/core/src/macros/mod.rs
@@ -1546,6 +1546,18 @@ pub(crate) mod builtin {
         /* compiler built-in */
     }
 
+    /// Unstable placeholder for type ascription.
+    #[rustc_builtin_macro]
+    #[unstable(
+        feature = "type_ascription",
+        issue = "23416",
+        reason = "placeholder syntax for type ascription"
+    )]
+    #[cfg(not(bootstrap))]
+    pub macro type_ascribe($expr:expr, $ty:ty) {
+        /* compiler built-in */
+    }
+
     /// Unstable implementation detail of the `rustc` compiler, do not use.
     #[rustc_builtin_macro]
     #[stable(feature = "rust1", since = "1.0.0")]
diff --git a/library/core/src/mem/mod.rs b/library/core/src/mem/mod.rs
index ec35914f1e3..383bdc7b6e2 100644
--- a/library/core/src/mem/mod.rs
+++ b/library/core/src/mem/mod.rs
@@ -1113,7 +1113,10 @@ impl<T> fmt::Debug for Discriminant<T> {
 /// # Stability
 ///
 /// The discriminant of an enum variant may change if the enum definition changes. A discriminant
-/// of some variant will not change between compilations with the same compiler.
+/// of some variant will not change between compilations with the same compiler. See the [Reference]
+/// for more information.
+///
+/// [Reference]: ../../reference/items/enumerations.html#custom-discriminant-values-for-fieldless-enumerations
 ///
 /// # Examples
 ///
@@ -1129,6 +1132,62 @@ impl<T> fmt::Debug for Discriminant<T> {
 /// assert_eq!(mem::discriminant(&Foo::B(1)), mem::discriminant(&Foo::B(2)));
 /// assert_ne!(mem::discriminant(&Foo::B(3)), mem::discriminant(&Foo::C(3)));
 /// ```
+///
+/// ## Accessing the numeric value of the discriminant
+///
+/// Note that it is *undefined behavior* to [`transmute`] from [`Discriminant`] to a primitive!
+///
+/// If an enum has only unit variants, then the numeric value of the discriminant can be accessed
+/// with an [`as`] cast:
+///
+/// ```
+/// enum Enum {
+///     Foo,
+///     Bar,
+///     Baz,
+/// }
+///
+/// assert_eq!(0, Enum::Foo as isize);
+/// assert_eq!(1, Enum::Bar as isize);
+/// assert_eq!(2, Enum::Baz as isize);
+/// ```
+///
+/// If an enum has opted-in to having a [primitive representation] for its discriminant,
+/// then it's possible to use pointers to read the memory location storing the discriminant.
+/// That **cannot** be done for enums using the [default representation], however, as it's
+/// undefined what layout the discriminant has and where it's stored — it might not even be
+/// stored at all!
+///
+/// [`as`]: ../../std/keyword.as.html
+/// [primitive representation]: ../../reference/type-layout.html#primitive-representations
+/// [default representation]: ../../reference/type-layout.html#the-default-representation
+/// ```
+/// #[repr(u8)]
+/// enum Enum {
+///     Unit,
+///     Tuple(bool),
+///     Struct { a: bool },
+/// }
+///
+/// impl Enum {
+///     fn discriminant(&self) -> u8 {
+///         // SAFETY: Because `Self` is marked `repr(u8)`, its layout is a `repr(C)` `union`
+///         // between `repr(C)` structs, each of which has the `u8` discriminant as its first
+///         // field, so we can read the discriminant without offsetting the pointer.
+///         unsafe { *<*const _>::from(self).cast::<u8>() }
+///     }
+/// }
+///
+/// let unit_like = Enum::Unit;
+/// let tuple_like = Enum::Tuple(true);
+/// let struct_like = Enum::Struct { a: false };
+/// assert_eq!(0, unit_like.discriminant());
+/// assert_eq!(1, tuple_like.discriminant());
+/// assert_eq!(2, struct_like.discriminant());
+///
+/// // ⚠️ This is undefined behavior. Don't do this. ⚠️
+/// // assert_eq!(0, unsafe { std::mem::transmute::<_, u8>(std::mem::discriminant(&unit_like)) });
+/// ```
 #[stable(feature = "discriminant_value", since = "1.21.0")]
 #[rustc_const_unstable(feature = "const_discriminant", issue = "69821")]
 #[cfg_attr(not(test), rustc_diagnostic_item = "mem_discriminant")]
diff --git a/library/core/src/prelude/v1.rs b/library/core/src/prelude/v1.rs
index d3d255a802d..2d67d742c68 100644
--- a/library/core/src/prelude/v1.rs
+++ b/library/core/src/prelude/v1.rs
@@ -98,3 +98,11 @@ pub use crate::macros::builtin::cfg_accessible;
     reason = "`cfg_eval` is a recently implemented feature"
 )]
 pub use crate::macros::builtin::cfg_eval;
+
+#[unstable(
+    feature = "type_ascription",
+    issue = "23416",
+    reason = "placeholder syntax for type ascription"
+)]
+#[cfg(not(bootstrap))]
+pub use crate::macros::builtin::type_ascribe;
diff --git a/library/core/src/str/converts.rs b/library/core/src/str/converts.rs
index b0c55ca4f51..5f8748206d7 100644
--- a/library/core/src/str/converts.rs
+++ b/library/core/src/str/converts.rs
@@ -77,7 +77,7 @@ use super::Utf8Error;
 /// let sparkle_heart = [240, 159, 146, 150];
 ///
 /// // We know these bytes are valid, so just use `unwrap()`.
-/// let sparkle_heart = str::from_utf8(&sparkle_heart).unwrap();
+/// let sparkle_heart: &str = str::from_utf8(&sparkle_heart).unwrap();
 ///
 /// assert_eq!("💖", sparkle_heart);
 /// ```
diff --git a/library/std/src/fs.rs b/library/std/src/fs.rs
index 188ff00e1f8..0660e03c1a8 100644
--- a/library/std/src/fs.rs
+++ b/library/std/src/fs.rs
@@ -401,7 +401,7 @@ impl File {
     ///     Ok(())
     /// }
     /// ```
-    #[unstable(feature = "file_create_new", issue = "none")]
+    #[unstable(feature = "file_create_new", issue = "105135")]
     pub fn create_new<P: AsRef<Path>>(path: P) -> io::Result<File> {
         OpenOptions::new().read(true).write(true).create_new(true).open(path.as_ref())
     }
diff --git a/library/std/src/path.rs b/library/std/src/path.rs
index af88b9070c1..6c957c2fa90 100644
--- a/library/std/src/path.rs
+++ b/library/std/src/path.rs
@@ -1463,6 +1463,30 @@ impl PathBuf {
         true
     }
 
+    /// Yields a mutable reference to the underlying [`OsString`] instance.
+    ///
+    /// # Examples
+    ///
+    /// ```
+    /// #![feature(path_as_mut_os_str)]
+    /// use std::path::{Path, PathBuf};
+    ///
+    /// let mut path = PathBuf::from("/foo");
+    ///
+    /// path.push("bar");
+    /// assert_eq!(path, Path::new("/foo/bar"));
+    ///
+    /// // OsString's `push` does not add a separator.
+    /// path.as_mut_os_string().push("baz");
+    /// assert_eq!(path, Path::new("/foo/barbaz"));
+    /// ```
+    #[unstable(feature = "path_as_mut_os_str", issue = "105021")]
+    #[must_use]
+    #[inline]
+    pub fn as_mut_os_string(&mut self) -> &mut OsString {
+        &mut self.inner
+    }
+
     /// Consumes the `PathBuf`, yielding its internal [`OsString`] storage.
     ///
     /// # Examples
@@ -1993,6 +2017,28 @@ impl Path {
         &self.inner
     }
 
+    /// Yields a mutable reference to the underlying [`OsStr`] slice.
+    ///
+    /// # Examples
+    ///
+    /// ```
+    /// #![feature(path_as_mut_os_str)]
+    /// use std::path::{Path, PathBuf};
+    ///
+    /// let mut path = PathBuf::from("/Foo.TXT").into_boxed_path();
+    ///
+    /// assert_ne!(&*path, Path::new("/foo.txt"));
+    ///
+    /// path.as_mut_os_str().make_ascii_lowercase();
+    /// assert_eq!(&*path, Path::new("/foo.txt"));
+    /// ```
+    #[unstable(feature = "path_as_mut_os_str", issue = "105021")]
+    #[must_use]
+    #[inline]
+    pub fn as_mut_os_str(&mut self) -> &mut OsStr {
+        &mut self.inner
+    }
+
     /// Yields a [`&str`] slice if the `Path` is valid unicode.
     ///
     /// This conversion may entail doing a check for UTF-8 validity.
diff --git a/library/std/src/prelude/v1.rs b/library/std/src/prelude/v1.rs
index d5ac16e6b94..a5a798078eb 100644
--- a/library/std/src/prelude/v1.rs
+++ b/library/std/src/prelude/v1.rs
@@ -85,6 +85,15 @@ pub use core::prelude::v1::cfg_accessible;
 )]
 pub use core::prelude::v1::cfg_eval;
 
+// Do not `doc(no_inline)` either.
+#[unstable(
+    feature = "type_ascription",
+    issue = "23416",
+    reason = "placeholder syntax for type ascription"
+)]
+#[cfg(not(bootstrap))]
+pub use core::prelude::v1::type_ascribe;
+
 // The file so far is equivalent to src/libcore/prelude/v1.rs,
 // and below to src/liballoc/prelude.rs.
 // Those files are duplicated rather than using glob imports
diff --git a/library/std/src/sys/common/alloc.rs b/library/std/src/sys/common/alloc.rs
index e8e7c51cb9b..3edbe728077 100644
--- a/library/std/src/sys/common/alloc.rs
+++ b/library/std/src/sys/common/alloc.rs
@@ -4,7 +4,7 @@ use crate::ptr;
 
 // The minimum alignment guaranteed by the architecture. This value is used to
 // add fast paths for low alignment values.
-#[cfg(all(any(
+#[cfg(any(
     target_arch = "x86",
     target_arch = "arm",
     target_arch = "mips",
@@ -16,9 +16,9 @@ use crate::ptr;
     target_arch = "hexagon",
     all(target_arch = "riscv32", not(target_os = "espidf")),
     all(target_arch = "xtensa", not(target_os = "espidf")),
-)))]
+))]
 pub const MIN_ALIGN: usize = 8;
-#[cfg(all(any(
+#[cfg(any(
     target_arch = "x86_64",
     target_arch = "aarch64",
     target_arch = "mips64",
@@ -26,13 +26,13 @@ pub const MIN_ALIGN: usize = 8;
     target_arch = "sparc64",
     target_arch = "riscv64",
     target_arch = "wasm64",
-)))]
+))]
 pub const MIN_ALIGN: usize = 16;
 // The allocator on the esp-idf platform guarantees 4 byte alignment.
-#[cfg(all(any(
+#[cfg(any(
     all(target_arch = "riscv32", target_os = "espidf"),
     all(target_arch = "xtensa", target_os = "espidf"),
-)))]
+))]
 pub const MIN_ALIGN: usize = 4;
 
 pub unsafe fn realloc_fallback(
diff --git a/library/std/src/sys/hermit/fs.rs b/library/std/src/sys/hermit/fs.rs
index af297ff1ec7..6fb92c037ee 100644
--- a/library/std/src/sys/hermit/fs.rs
+++ b/library/std/src/sys/hermit/fs.rs
@@ -1,10 +1,8 @@
-use crate::convert::TryFrom;
-use crate::ffi::{CStr, CString, OsString};
+use crate::ffi::{CStr, OsString};
 use crate::fmt;
 use crate::hash::{Hash, Hasher};
 use crate::io::{self, Error, ErrorKind};
 use crate::io::{BorrowedCursor, IoSlice, IoSliceMut, SeekFrom};
-use crate::os::unix::ffi::OsStrExt;
 use crate::path::{Path, PathBuf};
 use crate::sys::common::small_c_string::run_path_with_cstr;
 use crate::sys::cvt;
diff --git a/library/std/src/sys/hermit/thread.rs b/library/std/src/sys/hermit/thread.rs
index e53a1fea6a0..8f65544a9e8 100644
--- a/library/std/src/sys/hermit/thread.rs
+++ b/library/std/src/sys/hermit/thread.rs
@@ -5,6 +5,7 @@ use crate::ffi::CStr;
 use crate::io;
 use crate::mem;
 use crate::num::NonZeroUsize;
+use crate::ptr;
 use crate::sys::hermit::abi;
 use crate::sys::hermit::thread_local_dtor::run_dtors;
 use crate::time::Duration;
@@ -47,7 +48,7 @@ impl Thread {
         extern "C" fn thread_start(main: usize) {
             unsafe {
                 // Finally, let's run some code.
-                Box::from_raw(main as *mut Box<dyn FnOnce()>)();
+                Box::from_raw(ptr::from_exposed_addr::<Box<dyn FnOnce()>>(main).cast_mut())();
 
                 // run all destructors
                 run_dtors();
diff --git a/library/std/src/sys/wasi/net.rs b/library/std/src/sys/wasi/net.rs
index 590d268c380..cf4ebba1a39 100644
--- a/library/std/src/sys/wasi/net.rs
+++ b/library/std/src/sys/wasi/net.rs
@@ -119,8 +119,14 @@ impl TcpStream {
         unsupported()
     }
 
-    pub fn shutdown(&self, _: Shutdown) -> io::Result<()> {
-        unsupported()
+    pub fn shutdown(&self, how: Shutdown) -> io::Result<()> {
+        let wasi_how = match how {
+            Shutdown::Read => wasi::SDFLAGS_RD,
+            Shutdown::Write => wasi::SDFLAGS_WR,
+            Shutdown::Both => wasi::SDFLAGS_RD | wasi::SDFLAGS_WR,
+        };
+
+        unsafe { wasi::sock_shutdown(self.socket().as_raw_fd() as _, wasi_how).map_err(err2io) }
     }
 
     pub fn duplicate(&self) -> io::Result<TcpStream> {
diff --git a/library/std/src/sys/windows/args.rs b/library/std/src/sys/windows/args.rs
index 01f26298290..6741ae46d32 100644
--- a/library/std/src/sys/windows/args.rs
+++ b/library/std/src/sys/windows/args.rs
@@ -9,17 +9,16 @@ mod tests;
 use crate::ffi::OsString;
 use crate::fmt;
 use crate::io;
-use crate::marker::PhantomData;
 use crate::num::NonZeroU16;
 use crate::os::windows::prelude::*;
 use crate::path::PathBuf;
-use crate::ptr::NonNull;
 use crate::sys::c;
 use crate::sys::process::ensure_no_nuls;
 use crate::sys::windows::os::current_exe;
+use crate::sys_common::wstr::WStrUnits;
 use crate::vec;
 
-use core::iter;
+use crate::iter;
 
 /// This is the const equivalent to `NonZeroU16::new(n).unwrap()`
 ///
@@ -199,55 +198,6 @@ impl ExactSizeIterator for Args {
     }
 }
 
-/// A safe iterator over a LPWSTR
-/// (aka a pointer to a series of UTF-16 code units terminated by a NULL).
-struct WStrUnits<'a> {
-    // The pointer must never be null...
-    lpwstr: NonNull<u16>,
-    // ...and the memory it points to must be valid for this lifetime.
-    lifetime: PhantomData<&'a [u16]>,
-}
-impl WStrUnits<'_> {
-    /// Create the iterator. Returns `None` if `lpwstr` is null.
-    ///
-    /// SAFETY: `lpwstr` must point to a null-terminated wide string that lives
-    /// at least as long as the lifetime of this struct.
-    unsafe fn new(lpwstr: *const u16) -> Option<Self> {
-        Some(Self { lpwstr: NonNull::new(lpwstr as _)?, lifetime: PhantomData })
-    }
-    fn peek(&self) -> Option<NonZeroU16> {
-        // SAFETY: It's always safe to read the current item because we don't
-        // ever move out of the array's bounds.
-        unsafe { NonZeroU16::new(*self.lpwstr.as_ptr()) }
-    }
-    /// Advance the iterator while `predicate` returns true.
-    /// Returns the number of items it advanced by.
-    fn advance_while<P: FnMut(NonZeroU16) -> bool>(&mut self, mut predicate: P) -> usize {
-        let mut counter = 0;
-        while let Some(w) = self.peek() {
-            if !predicate(w) {
-                break;
-            }
-            counter += 1;
-            self.next();
-        }
-        counter
-    }
-}
-impl Iterator for WStrUnits<'_> {
-    // This can never return zero as that marks the end of the string.
-    type Item = NonZeroU16;
-    fn next(&mut self) -> Option<NonZeroU16> {
-        // SAFETY: If NULL is reached we immediately return.
-        // Therefore it's safe to advance the pointer after that.
-        unsafe {
-            let next = self.peek()?;
-            self.lpwstr = NonNull::new_unchecked(self.lpwstr.as_ptr().add(1));
-            Some(next)
-        }
-    }
-}
-
 #[derive(Debug)]
 pub(crate) enum Arg {
     /// Add quotes (if needed)
diff --git a/library/std/src/sys_common/mod.rs b/library/std/src/sys_common/mod.rs
index 069b13e9d85..b1987aa0f62 100644
--- a/library/std/src/sys_common/mod.rs
+++ b/library/std/src/sys_common/mod.rs
@@ -32,6 +32,7 @@ pub mod thread;
 pub mod thread_info;
 pub mod thread_local_dtor;
 pub mod thread_parker;
+pub mod wstr;
 pub mod wtf8;
 
 cfg_if::cfg_if! {
diff --git a/library/std/src/sys_common/wstr.rs b/library/std/src/sys_common/wstr.rs
new file mode 100644
index 00000000000..b230fd1a829
--- /dev/null
+++ b/library/std/src/sys_common/wstr.rs
@@ -0,0 +1,59 @@
+//! This module contains constructs to work with 16-bit characters (UCS-2 or UTF-16)
+#![allow(dead_code)]
+
+use crate::marker::PhantomData;
+use crate::num::NonZeroU16;
+use crate::ptr::NonNull;
+
+/// A safe iterator over a LPWSTR
+/// (aka a pointer to a series of UTF-16 code units terminated by a NULL).
+pub struct WStrUnits<'a> {
+    // The pointer must never be null...
+    lpwstr: NonNull<u16>,
+    // ...and the memory it points to must be valid for this lifetime.
+    lifetime: PhantomData<&'a [u16]>,
+}
+
+impl WStrUnits<'_> {
+    /// Create the iterator. Returns `None` if `lpwstr` is null.
+    ///
+    /// SAFETY: `lpwstr` must point to a null-terminated wide string that lives
+    /// at least as long as the lifetime of this struct.
+    pub unsafe fn new(lpwstr: *const u16) -> Option<Self> {
+        Some(Self { lpwstr: NonNull::new(lpwstr as _)?, lifetime: PhantomData })
+    }
+
+    pub fn peek(&self) -> Option<NonZeroU16> {
+        // SAFETY: It's always safe to read the current item because we don't
+        // ever move out of the array's bounds.
+        unsafe { NonZeroU16::new(*self.lpwstr.as_ptr()) }
+    }
+
+    /// Advance the iterator while `predicate` returns true.
+    /// Returns the number of items it advanced by.
+    pub fn advance_while<P: FnMut(NonZeroU16) -> bool>(&mut self, mut predicate: P) -> usize {
+        let mut counter = 0;
+        while let Some(w) = self.peek() {
+            if !predicate(w) {
+                break;
+            }
+            counter += 1;
+            self.next();
+        }
+        counter
+    }
+}
+
+impl Iterator for WStrUnits<'_> {
+    // This can never return zero as that marks the end of the string.
+    type Item = NonZeroU16;
+    fn next(&mut self) -> Option<NonZeroU16> {
+        // SAFETY: If NULL is reached we immediately return.
+        // Therefore it's safe to advance the pointer after that.
+        unsafe {
+            let next = self.peek()?;
+            self.lpwstr = NonNull::new_unchecked(self.lpwstr.as_ptr().add(1));
+            Some(next)
+        }
+    }
+}
diff --git a/src/bootstrap/bootstrap.py b/src/bootstrap/bootstrap.py
index 57128685d91..2d5018d934e 100644
--- a/src/bootstrap/bootstrap.py
+++ b/src/bootstrap/bootstrap.py
@@ -441,6 +441,7 @@ class RustBuild(object):
 
             self.fix_bin_or_dylib("{}/bin/rustc".format(bin_root))
             self.fix_bin_or_dylib("{}/bin/rustdoc".format(bin_root))
+            self.fix_bin_or_dylib("{}/libexec/rust-analyzer-proc-macro-srv".format(bin_root))
             lib_dir = "{}/lib".format(bin_root)
             for lib in os.listdir(lib_dir):
                 if lib.endswith(".so"):
diff --git a/src/bootstrap/builder.rs b/src/bootstrap/builder.rs
index 251431a15eb..cff5fd8c5b0 100644
--- a/src/bootstrap/builder.rs
+++ b/src/bootstrap/builder.rs
@@ -644,6 +644,7 @@ impl<'a> Builder<'a> {
                 test::CrateLibrustc,
                 test::CrateRustdoc,
                 test::CrateRustdocJsonTypes,
+                test::CrateJsonDocLint,
                 test::Linkcheck,
                 test::TierCheck,
                 test::ReplacePlaceholderTest,
diff --git a/src/bootstrap/download.rs b/src/bootstrap/download.rs
index d0f389df973..6ae283f32a5 100644
--- a/src/bootstrap/download.rs
+++ b/src/bootstrap/download.rs
@@ -360,6 +360,7 @@ impl Config {
 
             self.fix_bin_or_dylib(&bin_root.join("bin").join("rustc"));
             self.fix_bin_or_dylib(&bin_root.join("bin").join("rustdoc"));
+            self.fix_bin_or_dylib(&bin_root.join("libexec").join("rust-analyzer-proc-macro-srv"));
             let lib_dir = bin_root.join("lib");
             for lib in t!(fs::read_dir(&lib_dir), lib_dir.display().to_string()) {
                 let lib = t!(lib);
diff --git a/src/bootstrap/lib.rs b/src/bootstrap/lib.rs
index f4fa556b974..a3d1893afbb 100644
--- a/src/bootstrap/lib.rs
+++ b/src/bootstrap/lib.rs
@@ -647,9 +647,10 @@ impl Build {
         if !update(true).status().map_or(false, |status| status.success()) {
             self.run(&mut update(false));
         }
-
+        self.run(Command::new("git").args(&["stash", "push"]).current_dir(&absolute_path));
         self.run(Command::new("git").args(&["reset", "-q", "--hard"]).current_dir(&absolute_path));
-        self.run(Command::new("git").args(&["clean", "-qdfx"]).current_dir(absolute_path));
+        self.run(Command::new("git").args(&["clean", "-qdfx"]).current_dir(&absolute_path));
+        self.run(Command::new("git").args(&["stash", "pop"]).current_dir(absolute_path));
     }
 
     /// If any submodule has been initialized already, sync it unconditionally.
diff --git a/src/bootstrap/test.rs b/src/bootstrap/test.rs
index a69979d7f07..39cedfdac5f 100644
--- a/src/bootstrap/test.rs
+++ b/src/bootstrap/test.rs
@@ -91,6 +91,42 @@ fn try_run_quiet(builder: &Builder<'_>, cmd: &mut Command) -> bool {
 }
 
 #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
+pub struct CrateJsonDocLint {
+    host: TargetSelection,
+}
+
+impl Step for CrateJsonDocLint {
+    type Output = ();
+    const ONLY_HOSTS: bool = true;
+    const DEFAULT: bool = true;
+
+    fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
+        run.path("src/tools/jsondoclint")
+    }
+
+    fn make_run(run: RunConfig<'_>) {
+        run.builder.ensure(CrateJsonDocLint { host: run.target });
+    }
+
+    fn run(self, builder: &Builder<'_>) {
+        let bootstrap_host = builder.config.build;
+        let compiler = builder.compiler(0, bootstrap_host);
+
+        let cargo = tool::prepare_tool_cargo(
+            builder,
+            compiler,
+            Mode::ToolBootstrap,
+            bootstrap_host,
+            "test",
+            "src/tools/jsondoclint",
+            SourceType::InTree,
+            &[],
+        );
+        try_run(builder, &mut cargo.into());
+    }
+}
+
+#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
 pub struct Linkcheck {
     host: TargetSelection,
 }
diff --git a/src/ci/docker/host-x86_64/test-various/Dockerfile b/src/ci/docker/host-x86_64/test-various/Dockerfile
index 0bddffa3436..7c09e3a582f 100644
--- a/src/ci/docker/host-x86_64/test-various/Dockerfile
+++ b/src/ci/docker/host-x86_64/test-various/Dockerfile
@@ -19,11 +19,18 @@ RUN apt-get update && DEBIAN_FRONTEND=noninteractive apt-get install -y --no-ins
   wget \
   patch \
   ovmf \
+  qemu-efi-aarch64 \
+  qemu-system-arm \
   qemu-system-x86
 
 RUN curl -sL https://nodejs.org/dist/v15.14.0/node-v15.14.0-linux-x64.tar.xz | \
   tar -xJ
 
+# Install 32-bit OVMF files for the i686-unknown-uefi test. This package
+# is not available in ubuntu 20.04, so download a 22.04 package.
+RUN curl -sL --output ovmf-ia32.deb http://mirrors.kernel.org/ubuntu/pool/universe/e/edk2/ovmf-ia32_2022.02-3_all.deb
+RUN dpkg -i ovmf-ia32.deb && rm ovmf-ia32.deb
+
 WORKDIR /build/
 COPY scripts/musl-patch-configure.diff /build/
 COPY scripts/musl-toolchain.sh /build/
@@ -68,7 +75,11 @@ ENV MUSL_TARGETS=x86_64-unknown-linux-musl \
 ENV MUSL_SCRIPT python3 /checkout/x.py --stage 2 test --host='' --target $MUSL_TARGETS
 
 COPY host-x86_64/test-various/uefi_qemu_test /uefi_qemu_test
-ENV UEFI_TARGETS=x86_64-unknown-uefi \
+ENV UEFI_TARGETS=aarch64-unknown-uefi,i686-unknown-uefi,x86_64-unknown-uefi \
+    CC_aarch64_unknown_uefi=clang-11 \
+    CXX_aarch64_unknown_uefi=clang++-11 \
+    CC_i686_unknown_uefi=clang-11 \
+    CXX_i686_unknown_uefi=clang++-11 \
     CC_x86_64_unknown_uefi=clang-11 \
     CXX_x86_64_unknown_uefi=clang++-11
 ENV UEFI_SCRIPT python3 /checkout/x.py --stage 2 build --host='' --target $UEFI_TARGETS && \
diff --git a/src/ci/docker/host-x86_64/test-various/uefi_qemu_test/run.py b/src/ci/docker/host-x86_64/test-various/uefi_qemu_test/run.py
index 46793ce3afa..ffae7b0d4ac 100644
--- a/src/ci/docker/host-x86_64/test-various/uefi_qemu_test/run.py
+++ b/src/ci/docker/host-x86_64/test-various/uefi_qemu_test/run.py
@@ -8,8 +8,11 @@ import tempfile
 
 from pathlib import Path
 
+TARGET_AARCH64 = 'aarch64-unknown-uefi'
+TARGET_I686 = 'i686-unknown-uefi'
+TARGET_X86_64 = 'x86_64-unknown-uefi'
 
-def run(*cmd, capture=False, check=True, env=None):
+def run(*cmd, capture=False, check=True, env=None, timeout=None):
     """Print and run a command, optionally capturing the output."""
     cmd = [str(p) for p in cmd]
     print(' '.join(cmd))
@@ -17,10 +20,39 @@ def run(*cmd, capture=False, check=True, env=None):
                           capture_output=capture,
                           check=check,
                           env=env,
-                          text=True)
-
+                          text=True,
+                          timeout=timeout)
+
+def build_and_run(tmp_dir, target):
+    if target == TARGET_AARCH64:
+        boot_file_name = 'bootaa64.efi'
+        ovmf_dir = Path('/usr/share/AAVMF')
+        ovmf_code = 'AAVMF_CODE.fd'
+        ovmf_vars = 'AAVMF_VARS.fd'
+        qemu = 'qemu-system-aarch64'
+        machine = 'virt'
+        cpu = 'cortex-a72'
+    elif target == TARGET_I686:
+        boot_file_name = 'bootia32.efi'
+        ovmf_dir = Path('/usr/share/OVMF')
+        ovmf_code = 'OVMF32_CODE_4M.secboot.fd'
+        ovmf_vars = 'OVMF32_VARS_4M.fd'
+        # The i686 target intentionally uses 64-bit qemu; the important
+        # difference is that the OVMF code provides a 32-bit environment.
+        qemu = 'qemu-system-x86_64'
+        machine = 'q35'
+        cpu = 'qemu64'
+    elif target == TARGET_X86_64:
+        boot_file_name = 'bootx64.efi'
+        ovmf_dir = Path('/usr/share/OVMF')
+        ovmf_code = 'OVMF_CODE.fd'
+        ovmf_vars = 'OVMF_VARS.fd'
+        qemu = 'qemu-system-x86_64'
+        machine = 'q35'
+        cpu = 'qemu64'
+    else:
+        raise KeyError('invalid target')
 
-def build_and_run(tmp_dir):
     host_artifacts = Path('/checkout/obj/build/x86_64-unknown-linux-gnu')
     stage0 = host_artifacts / 'stage0/bin'
     stage2 = host_artifacts / 'stage2/bin'
@@ -33,7 +65,6 @@ def build_and_run(tmp_dir):
     shutil.copytree('/uefi_qemu_test', test_crate)
 
     # Build the UEFI executable.
-    target = 'x86_64-unknown-uefi'
     run('cargo',
         'build',
         '--manifest-path',
@@ -49,14 +80,24 @@ def build_and_run(tmp_dir):
 
     # Copy the executable into the ESP.
     src_exe_path = test_crate / 'target' / target / 'debug/uefi_qemu_test.efi'
-    shutil.copy(src_exe_path, boot / 'bootx64.efi')
+    shutil.copy(src_exe_path, boot / boot_file_name)
+    print(src_exe_path, boot / boot_file_name)
+
+    # Select the appropriate EDK2 build.
+    ovmf_code = ovmf_dir / ovmf_code
+    ovmf_vars = ovmf_dir / ovmf_vars
+
+    # Make a writable copy of the vars file. aarch64 doesn't boot
+    # correctly with read-only vars.
+    ovmf_rw_vars = Path(tmp_dir) / 'vars.fd'
+    shutil.copy(ovmf_vars, ovmf_rw_vars)
 
     # Run the executable in QEMU and capture the output.
-    qemu = 'qemu-system-x86_64'
-    ovmf_dir = Path('/usr/share/OVMF')
-    ovmf_code = ovmf_dir / 'OVMF_CODE.fd'
-    ovmf_vars = ovmf_dir / 'OVMF_VARS.fd'
     output = run(qemu,
+                 '-machine',
+                 machine,
+                 '-cpu',
+                 cpu,
                  '-display',
                  'none',
                  '-serial',
@@ -64,7 +105,7 @@ def build_and_run(tmp_dir):
                  '-drive',
                  f'if=pflash,format=raw,readonly=on,file={ovmf_code}',
                  '-drive',
-                 f'if=pflash,format=raw,readonly=on,file={ovmf_vars}',
+                 f'if=pflash,format=raw,readonly=off,file={ovmf_rw_vars}',
                  '-drive',
                  f'format=raw,file=fat:rw:{esp}',
                  capture=True,
@@ -73,7 +114,9 @@ def build_and_run(tmp_dir):
                  # shutdown under some circumstances. That has been
                  # fixed in newer versions of QEMU, but for now just
                  # don't check the exit status.
-                 check=False).stdout
+                 check=False,
+                 # Set a timeout to kill the VM in case something goes wrong.
+                 timeout=60).stdout
 
     if 'Hello World!' in output:
         print('VM produced expected output')
@@ -86,10 +129,13 @@ def build_and_run(tmp_dir):
 
 
 def main():
-    # Create a temporary directory so that we have a writeable
-    # workspace.
-    with tempfile.TemporaryDirectory() as tmp_dir:
-        build_and_run(tmp_dir)
+    targets = [TARGET_AARCH64, TARGET_I686, TARGET_X86_64]
+
+    for target in targets:
+        # Create a temporary directory so that we have a writeable
+        # workspace.
+        with tempfile.TemporaryDirectory() as tmp_dir:
+            build_and_run(tmp_dir, target)
 
 
 if __name__ == "__main__":
diff --git a/src/ci/docker/scripts/fuchsia-test-runner.py b/src/ci/docker/scripts/fuchsia-test-runner.py
index a2708d16947..a2708d16947 100644..100755
--- a/src/ci/docker/scripts/fuchsia-test-runner.py
+++ b/src/ci/docker/scripts/fuchsia-test-runner.py
diff --git a/src/doc/rustc/src/command-line-arguments.md b/src/doc/rustc/src/command-line-arguments.md
index 2dc182b3d83..ef6eee75f1c 100644
--- a/src/doc/rustc/src/command-line-arguments.md
+++ b/src/doc/rustc/src/command-line-arguments.md
@@ -104,6 +104,33 @@ This modifier has no effect when building other targets like executables or dyna
 
 The default for this modifier is `+bundle`.
 
+### Linking modifiers: `verbatim`
+
+This modifier is compatible with all linking kinds.
+
+`+verbatim` means that rustc itself won't add any target-specified library prefixes or suffixes
+(like `lib` or `.a`) to the library name, and will try its best to ask for the same thing from the
+linker.
+
+For `ld`-like linkers supporting GNU extensions rustc will use the `-l:filename` syntax (note the
+colon) when passing the library, so the linker won't add any prefixes or suffixes to it.
+See [`-l namespec`](https://sourceware.org/binutils/docs/ld/Options.html) in ld documentation for
+more details. \
+For linkers not supporting any verbatim modifiers (e.g. `link.exe` or `ld64`) the library name will
+be passed as is. So the most reliable cross-platform use scenarios for this option are when no
+linker is involved, for example bundling native libraries into rlibs.
+
+`-verbatim` means that rustc will either add a target-specific prefix and suffix to the library
+name before passing it to linker, or won't prevent linker from implicitly adding it. \
+In case of `raw-dylib` kind in particular `.dll` will be added to the library name on Windows.
+
+The default for this modifier is `-verbatim`.
+
+NOTE: Even with `+verbatim` and `-l:filename` syntax `ld`-like linkers do not typically support
+passing absolute paths to libraries. Usually such paths need to be passed as input files without
+using any options like `-l`, e.g. `ld /my/absolute/path`. \
+`-Clink-arg=/my/absolute/path` can be used for doing this from stable `rustc`.
+
 <a id="option-crate-type"></a>
 ## `--crate-type`: a list of types of crates for the compiler to emit
 
diff --git a/src/doc/rustc/src/platform-support.md b/src/doc/rustc/src/platform-support.md
index 27e911c6be5..d0c3ddf2606 100644
--- a/src/doc/rustc/src/platform-support.md
+++ b/src/doc/rustc/src/platform-support.md
@@ -214,7 +214,7 @@ target | std | host | notes
 [`aarch64-kmc-solid_asp3`](platform-support/kmc-solid.md) | ✓ |  | ARM64 SOLID with TOPPERS/ASP3
 [`aarch64-nintendo-switch-freestanding`](platform-support/aarch64-nintendo-switch-freestanding.md) | * |  | ARM64 Nintendo Switch, Horizon
 [`aarch64-pc-windows-gnullvm`](platform-support/pc-windows-gnullvm.md) | ✓ | ✓ |
-[`aarch64-unknown-nto-qnx7.1.0`](platform-support/nto-qnx.md) | ? |  | ARM64 QNX Neutrino 7.1 RTOS |
+[`aarch64-unknown-nto-qnx710`](platform-support/nto-qnx.md) | ? |  | ARM64 QNX Neutrino 7.1 RTOS |
 `aarch64-unknown-freebsd` | ✓ | ✓ | ARM64 FreeBSD
 `aarch64-unknown-hermit` | ✓ |  | ARM64 HermitCore
 `aarch64-unknown-linux-gnu_ilp32` | ✓ | ✓ | ARM64 Linux (ILP32 ABI)
@@ -305,7 +305,7 @@ target | std | host | notes
 `x86_64-apple-ios-macabi` | ✓ |  | Apple Catalyst on x86_64
 `x86_64-apple-tvos` | * | | x86 64-bit tvOS
 [`x86_64-apple-watchos-sim`](platform-support/apple-watchos.md) | ✓ | | x86 64-bit Apple WatchOS simulator
-[`x86_64-pc-nto-qnx7.1.0`](platform-support/nto-qnx.md) | ? |  | x86 64-bit QNX Neutrino 7.1 RTOS |
+[`x86_64-pc-nto-qnx710`](platform-support/nto-qnx.md) | ? |  | x86 64-bit QNX Neutrino 7.1 RTOS |
 [`x86_64-pc-windows-gnullvm`](platform-support/pc-windows-gnullvm.md) | ✓ | ✓ |
 `x86_64-pc-windows-msvc` | * |  | 64-bit Windows XP support
 `x86_64-sun-solaris` | ? |  | Deprecated target for 64-bit Solaris 10/11, illumos
diff --git a/src/doc/rustc/src/platform-support/fuchsia.md b/src/doc/rustc/src/platform-support/fuchsia.md
index 5de29b35e6b..fbf999f9715 100644
--- a/src/doc/rustc/src/platform-support/fuchsia.md
+++ b/src/doc/rustc/src/platform-support/fuchsia.md
@@ -189,17 +189,45 @@ Fuchsia as well. A recent version (14+) of clang should be sufficient to compile
 Rust for Fuchsia.
 
 x86-64 and AArch64 Fuchsia targets can be enabled using the following
-configuration.
-
-In `config.toml`, add:
+configuration in `config.toml`:
 
 ```toml
 [build]
 target = ["<host_platform>", "aarch64-fuchsia", "x86_64-fuchsia"]
+
+[rust]
+lld = true
+
+[target.x86_64-fuchsia]
+cc = "clang"
+cxx = "clang++"
+
+[target.aarch64-fuchsia]
+cc = "clang"
+cxx = "clang++"
+```
+
+Though not strictly required, you may also want to use `clang` for your host
+target as well:
+
+```toml
+[target.<host_platform>]
+cc = "clang"
+cxx = "clang++"
+```
+
+By default, the Rust compiler installs itself to `/usr/local` on most UNIX
+systems. You may want to install it to another location (e.g. a local `install`
+directory) by setting a custom prefix in `config.toml`:
+
+```toml
+[install]
+# Make sure to use the absolute path to your install directory
+prefix = "<RUST_SRC_PATH>/install"
 ```
 
-Additionally, the following environment variables must be configured (for
-example, using a script like `config-env.sh`):
+Next, the following environment variables must be configured. For example, using
+a script we name `config-env.sh`:
 
 ```sh
 # Configure this environment variable to be the path to the downloaded SDK
@@ -215,8 +243,11 @@ export LDFLAGS_x86_64_fuchsia="--target=x86_64-fuchsia --sysroot=${SDK_PATH}/arc
 export CARGO_TARGET_X86_64_FUCHSIA_RUSTFLAGS="-C link-arg=--sysroot=${SDK_PATH}/arch/x64/sysroot -Lnative=${SDK_PATH}/arch/x64/sysroot/lib -Lnative=${SDK_PATH}/arch/x64/lib"
 ```
 
-These can be run together in a shell environment by executing
-`(source config-env.sh && ./x.py install)`.
+Finally, the Rust compiler can be built and installed:
+
+```sh
+(source config-env.sh && ./x.py install)
+```
 
 Once `rustc` is installed, we can create a new working directory to work from,
 `hello_fuchsia` along with `hello_fuchsia/src`:
@@ -641,31 +672,38 @@ available on the [Fuchsia devsite].
 
 ### Running the compiler test suite
 
-Pre-requisites for running the Rust test suite on Fuchsia are:
-1. Checkout of Rust source.
-1. Setup of `config-env.sh` and `config.toml` from "[Targeting Fuchsia with a compiler built from source](#targeting-fuchsia-with-a-compiler-built-from-source)".
-1. Download of the Fuchsia SDK. Minimum supported SDK version is [9.20220726.1.1](https://chrome-infra-packages.appspot.com/p/fuchsia/sdk/core/linux-amd64/+/version:9.20220726.1.1)
+The commands in this section assume that they are being run from inside your
+local Rust source checkout:
+
+```sh
+cd ${RUST_SRC_PATH}
+```
+
+To run the Rust test suite on an emulated Fuchsia device, you must install the
+Rust compiler locally. See "[Targeting Fuchsia with a compiler built from source](#targeting-fuchsia-with-a-compiler-built-from-source)"
+for the steps to build locally.
 
-Interfacing with the Fuchsia emulator is handled by our test runner script located
-at `${RUST_SRC_PATH}/src/ci/docker/scripts/fuchsia-test-runner.py`.
+You'll also need to download a copy of the Fuchsia SDK. The current minimum
+supported SDK version is [9.20220726.1.1](https://chrome-infra-packages.appspot.com/p/fuchsia/sdk/core/linux-amd64/+/version:9.20220726.1.1).
 
-We start by activating our Fuchsia test environment. From a terminal:
+Fuchsia's test runner interacts with the Fuchsia emulator and is located at
+`src/ci/docker/scripts/fuchsia-test-runner.py`. We can use it to start our
+test environment with:
 
-**Issue command from ${RUST_SRC_PATH}**
 ```sh
 src/ci/docker/scripts/fuchsia-test-runner.py start
-    --rust .
+    --rust ${RUST_SRC_PATH}/install
     --sdk ${SDK_PATH}
     --target-arch {x64,arm64}
 ```
 
-Next, for ease of commands, we copy `config-env.sh` and `config.toml` into our Rust source
-code path, `${RUST_SRC_PATH}`.
+Where `${RUST_SRC_PATH}/install` is the `prefix` set in `config.toml` and
+`${SDK_PATH}` is the path to the downloaded and unzipped SDK.
 
-From there, we utilize `x.py` to run our tests, using the test runner script to
-run the tests on our emulator. To run the full `src/test/ui` test suite:
+Once our environment is started, we can run our tests using `x.py` as usual. The
+test runner script will run the compiled tests on an emulated Fuchsia device. To
+run the full `src/test/ui` test suite:
 
-**Run from ${RUST_SRC_PATH}**
 ```sh
 ( \
     source config-env.sh &&                                                   \
@@ -695,9 +733,8 @@ run the tests on our emulator. To run the full `src/test/ui` test suite:
 *Note: The test suite cannot be run in parallel at the moment, so `x.py`
 must be run with `--jobs 1` to ensure only one test runs at a time.*
 
-When finished, stop the test environment:
+When finished, the test runner can be used to stop the test environment:
 
-**Issue command from ${RUST_SRC_PATH}**
 ```sh
 src/ci/docker/scripts/fuchsia-test-runner.py stop
 ```
diff --git a/src/doc/rustc/src/platform-support/nto-qnx.md b/src/doc/rustc/src/platform-support/nto-qnx.md
index 2f6ea94d113..37d0c31976c 100644
--- a/src/doc/rustc/src/platform-support/nto-qnx.md
+++ b/src/doc/rustc/src/platform-support/nto-qnx.md
@@ -93,15 +93,15 @@ Run the following:
 
 ```bash
 env \
-    CC_aarch64-unknown-nto-qnx7.1.0="qcc" \
-    CFLAGS_aarch64-unknown-nto-qnx7.1.0="-Vgcc_ntoaarch64le_cxx" \
-    CXX_aarch64-unknown-nto-qnx7.1.0="qcc" \
-    AR_aarch64_unknown_nto_qnx7.1.0="ntoaarch64-ar" \
-    CC_x86_64-pc-nto-qnx7.1.0="qcc" \
-    CFLAGS_x86_64-pc-nto-qnx7.1.0="-Vgcc_ntox86_64_cxx" \
-    CXX_x86_64-pc-nto-qnx7.1.0="qcc" \
-    AR_x86_64_pc_nto_qnx7.1.0="ntox86_64-ar" \
-        ./x.py build --target aarch64-unknown-nto-qnx7.1.0 --target x86_64-pc-nto-qnx7.1.0 --target x86_64-unknown-linux-gnu rustc library/core library/alloc/
+    CC_aarch64-unknown-nto-qnx710="qcc" \
+    CFLAGS_aarch64-unknown-nto-qnx710="-Vgcc_ntoaarch64le_cxx" \
+    CXX_aarch64-unknown-nto-qnx710="qcc" \
+    AR_aarch64_unknown_nto_qnx710="ntoaarch64-ar" \
+    CC_x86_64-pc-nto-qnx710="qcc" \
+    CFLAGS_x86_64-pc-nto-qnx710="-Vgcc_ntox86_64_cxx" \
+    CXX_x86_64-pc-nto-qnx710="qcc" \
+    AR_x86_64_pc_nto_qnx710="ntox86_64-ar" \
+        ./x.py build --target aarch64-unknown-nto-qnx710 --target x86_64-pc-nto-qnx710 --target x86_64-unknown-linux-gnu rustc library/core library/alloc/
 ```
 
 ## Building Rust programs
diff --git a/src/doc/rustc/src/target-tier-policy.md b/src/doc/rustc/src/target-tier-policy.md
index df9131ce84a..95932db14e1 100644
--- a/src/doc/rustc/src/target-tier-policy.md
+++ b/src/doc/rustc/src/target-tier-policy.md
@@ -158,6 +158,8 @@ approved by the appropriate team for that shared code before acceptance.
     the name of the target makes people extremely likely to form incorrect
     beliefs about what it targets, the name should be changed or augmented to
     disambiguate it.
+  - If possible, use only letters, numbers, dashes and underscores for the name.
+    Periods (`.`) are known to cause issues in Cargo.
 - Tier 3 targets may have unusual requirements to build or use, but must not
   create legal issues or impose onerous legal terms for the Rust project or for
   Rust developers or users.
diff --git a/src/doc/style-guide/src/principles.md b/src/doc/style-guide/src/principles.md
index b02b3c0471f..21668973194 100644
--- a/src/doc/style-guide/src/principles.md
+++ b/src/doc/style-guide/src/principles.md
@@ -8,7 +8,9 @@ following principles (in rough priority order):
     - avoiding misleading formatting
     - accessibility - readable and editable by users using the the widest
       variety of hardware, including non-visual accessibility interfaces
-    - readability of code when quoted in rustc error messages
+    - readability of code in contexts without syntax highlighting or IDE
+      assistance, such as rustc error messages, diffs, grep, and other
+      plain-text contexts
 
 * aesthetics
     - sense of 'beauty'
diff --git a/src/doc/unstable-book/src/language-features/native-link-modifiers-verbatim.md b/src/doc/unstable-book/src/language-features/native-link-modifiers-verbatim.md
deleted file mode 100644
index 02bd87e5095..00000000000
--- a/src/doc/unstable-book/src/language-features/native-link-modifiers-verbatim.md
+++ /dev/null
@@ -1,20 +0,0 @@
-# `native_link_modifiers_verbatim`
-
-The tracking issue for this feature is: [#81490]
-
-[#81490]: https://github.com/rust-lang/rust/issues/81490
-
-------------------------
-
-The `native_link_modifiers_verbatim` feature allows you to use the `verbatim` modifier.
-
-`+verbatim` means that rustc itself won't add any target-specified library prefixes or suffixes (like `lib` or `.a`) to the library name, and will try its best to ask for the same thing from the linker.
-
-For `ld`-like linkers rustc will use the `-l:filename` syntax (note the colon) when passing the library, so the linker won't add any prefixes or suffixes as well.
-See [`-l namespec`](https://sourceware.org/binutils/docs/ld/Options.html) in ld documentation for more details.
-For linkers not supporting any verbatim modifiers (e.g. `link.exe` or `ld64`) the library name will be passed as is.
-
-The default for this modifier is `-verbatim`.
-
-This RFC changes the behavior of `raw-dylib` linking kind specified by [RFC 2627](https://github.com/rust-lang/rfcs/pull/2627). The `.dll` suffix (or other target-specified suffixes for other targets) is now added automatically.
-If your DLL doesn't have the `.dll` suffix, it can be specified with `+verbatim`.
diff --git a/src/etc/gdb_providers.py b/src/etc/gdb_providers.py
index c351c3450f5..32b8d8e24c6 100644
--- a/src/etc/gdb_providers.py
+++ b/src/etc/gdb_providers.py
@@ -144,20 +144,16 @@ class StdVecDequeProvider:
     def __init__(self, valobj):
         self.valobj = valobj
         self.head = int(valobj["head"])
-        self.tail = int(valobj["tail"])
+        self.size = int(valobj["len"])
         self.cap = int(valobj["buf"]["cap"])
         self.data_ptr = unwrap_unique_or_non_null(valobj["buf"]["ptr"])
-        if self.head >= self.tail:
-            self.size = self.head - self.tail
-        else:
-            self.size = self.cap + self.head - self.tail
 
     def to_string(self):
         return "VecDeque(size={})".format(self.size)
 
     def children(self):
         return _enumerate_array_elements(
-            (self.data_ptr + ((self.tail + index) % self.cap)) for index in xrange(self.size)
+            (self.data_ptr + ((self.head + index) % self.cap)) for index in xrange(self.size)
         )
 
     @staticmethod
diff --git a/src/etc/lldb_providers.py b/src/etc/lldb_providers.py
index 8a9927e7d96..697ad4293c3 100644
--- a/src/etc/lldb_providers.py
+++ b/src/etc/lldb_providers.py
@@ -356,7 +356,7 @@ class StdSliceSyntheticProvider:
 class StdVecDequeSyntheticProvider:
     """Pretty-printer for alloc::collections::vec_deque::VecDeque<T>
 
-    struct VecDeque<T> { tail: usize, head: usize, buf: RawVec<T> }
+    struct VecDeque<T> { head: usize, len: usize, buf: RawVec<T> }
     """
 
     def __init__(self, valobj, dict):
@@ -373,7 +373,7 @@ class StdVecDequeSyntheticProvider:
     def get_child_index(self, name):
         # type: (str) -> int
         index = name.lstrip('[').rstrip(']')
-        if index.isdigit() and self.tail <= index and (self.tail + index) % self.cap < self.head:
+        if index.isdigit() and int(index) < self.size:
             return int(index)
         else:
             return -1
@@ -381,20 +381,16 @@ class StdVecDequeSyntheticProvider:
     def get_child_at_index(self, index):
         # type: (int) -> SBValue
         start = self.data_ptr.GetValueAsUnsigned()
-        address = start + ((index + self.tail) % self.cap) * self.element_type_size
+        address = start + ((index + self.head) % self.cap) * self.element_type_size
         element = self.data_ptr.CreateValueFromAddress("[%s]" % index, address, self.element_type)
         return element
 
     def update(self):
         # type: () -> None
         self.head = self.valobj.GetChildMemberWithName("head").GetValueAsUnsigned()
-        self.tail = self.valobj.GetChildMemberWithName("tail").GetValueAsUnsigned()
+        self.size = self.valobj.GetChildMemberWithName("len").GetValueAsUnsigned()
         self.buf = self.valobj.GetChildMemberWithName("buf")
         self.cap = self.buf.GetChildMemberWithName("cap").GetValueAsUnsigned()
-        if self.head >= self.tail:
-            self.size = self.head - self.tail
-        else:
-            self.size = self.cap + self.head - self.tail
 
         self.data_ptr = unwrap_unique_or_non_null(self.buf.GetChildMemberWithName("ptr"))
 
diff --git a/src/etc/natvis/liballoc.natvis b/src/etc/natvis/liballoc.natvis
index 41f4a3767f5..c4ad98ec1d3 100644
--- a/src/etc/natvis/liballoc.natvis
+++ b/src/etc/natvis/liballoc.natvis
@@ -12,20 +12,19 @@
     </Expand>
   </Type>
   <Type Name="alloc::collections::vec_deque::VecDeque&lt;*&gt;">
-    <DisplayString>{{ len={tail &lt;= head ? head - tail : buf.cap - tail + head} }}</DisplayString>
+    <DisplayString>{{ len={len} }}</DisplayString>
     <Expand>
-      <Item Name="[len]" ExcludeView="simple">tail &lt;= head ? head - tail : buf.cap - tail + head</Item>
+      <Item Name="[len]" ExcludeView="simple">len</Item>
       <Item Name="[capacity]" ExcludeView="simple">buf.cap</Item>
       <CustomListItems>
-        <Variable Name="i" InitialValue="tail" />
-
-        <Size>tail &lt;= head ? head - tail : buf.cap - tail + head</Size>
+        <Variable Name="i" InitialValue="0" />
+        <Size>len</Size>
         <Loop>
-          <If Condition="i == head">
+          <If Condition="i == len">
             <Break/>
           </If>
-          <Item>buf.ptr.pointer.pointer[i]</Item>
-          <Exec>i = (i + 1 == buf.cap ? 0 : i + 1)</Exec>
+          <Item>buf.ptr.pointer.pointer[(i + head) % buf.cap]</Item>
+          <Exec>i = i + 1</Exec>
         </Loop>
       </CustomListItems>
     </Expand>
diff --git a/src/librustdoc/clean/cfg.rs b/src/librustdoc/clean/cfg.rs
index f33f5d27d1a..1843a21205c 100644
--- a/src/librustdoc/clean/cfg.rs
+++ b/src/librustdoc/clean/cfg.rs
@@ -50,7 +50,7 @@ impl Cfg {
     ) -> Result<Option<Cfg>, InvalidCfgError> {
         match nested_cfg {
             NestedMetaItem::MetaItem(ref cfg) => Cfg::parse_without(cfg, exclude),
-            NestedMetaItem::Literal(ref lit) => {
+            NestedMetaItem::Lit(ref lit) => {
                 Err(InvalidCfgError { msg: "unexpected literal", span: lit.span })
             }
         }
diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs
index 582586d33fe..80909919ba2 100644
--- a/src/librustdoc/clean/mod.rs
+++ b/src/librustdoc/clean/mod.rs
@@ -893,7 +893,7 @@ fn clean_fn_decl_legacy_const_generics(func: &mut Function, attrs: &[ast::Attrib
         .filter(|a| a.has_name(sym::rustc_legacy_const_generics))
         .filter_map(|a| a.meta_item_list())
     {
-        for (pos, literal) in meta_item_list.iter().filter_map(|meta| meta.literal()).enumerate() {
+        for (pos, literal) in meta_item_list.iter().filter_map(|meta| meta.lit()).enumerate() {
             match literal.kind {
                 ast::LitKind::Int(a, _) => {
                     let gen = func.generics.params.remove(0);
@@ -1581,14 +1581,14 @@ fn normalize<'tcx>(cx: &mut DocContext<'tcx>, ty: Ty<'tcx>) -> Option<Ty<'tcx>>
     }
 
     use crate::rustc_trait_selection::infer::TyCtxtInferExt;
-    use crate::rustc_trait_selection::traits::query::normalize::AtExt;
+    use crate::rustc_trait_selection::traits::query::normalize::QueryNormalizeExt;
     use rustc_middle::traits::ObligationCause;
 
     // Try to normalize `<X as Y>::T` to a type
     let infcx = cx.tcx.infer_ctxt().build();
     let normalized = infcx
         .at(&ObligationCause::dummy(), cx.param_env)
-        .normalize(ty)
+        .query_normalize(ty)
         .map(|resolved| infcx.resolve_vars_if_possible(resolved.value));
     match normalized {
         Ok(normalized_value) => {
@@ -2233,6 +2233,26 @@ fn clean_extern_crate<'tcx>(
 fn clean_use_statement<'tcx>(
     import: &hir::Item<'tcx>,
     name: Symbol,
+    path: &hir::UsePath<'tcx>,
+    kind: hir::UseKind,
+    cx: &mut DocContext<'tcx>,
+    inlined_names: &mut FxHashSet<(ItemType, Symbol)>,
+) -> Vec<Item> {
+    let mut items = Vec::new();
+    let hir::UsePath { segments, ref res, span } = *path;
+    for &res in res {
+        if let Res::Def(DefKind::Ctor(..), _) | Res::SelfCtor(..) = res {
+            continue;
+        }
+        let path = hir::Path { segments, res, span };
+        items.append(&mut clean_use_statement_inner(import, name, &path, kind, cx, inlined_names));
+    }
+    items
+}
+
+fn clean_use_statement_inner<'tcx>(
+    import: &hir::Item<'tcx>,
+    name: Symbol,
     path: &hir::Path<'tcx>,
     kind: hir::UseKind,
     cx: &mut DocContext<'tcx>,
diff --git a/src/librustdoc/clean/types.rs b/src/librustdoc/clean/types.rs
index 340d6d7df18..2590bb0df3f 100644
--- a/src/librustdoc/clean/types.rs
+++ b/src/librustdoc/clean/types.rs
@@ -115,7 +115,6 @@ impl From<DefId> for ItemId {
 #[derive(Clone, Debug)]
 pub(crate) struct Crate {
     pub(crate) module: Item,
-    pub(crate) primitives: ThinVec<(DefId, PrimitiveType)>,
     /// Only here so that they can be filtered through the rustdoc passes.
     pub(crate) external_traits: Rc<RefCell<FxHashMap<DefId, Trait>>>,
 }
@@ -243,7 +242,9 @@ impl ExternalCrate {
                         hir::ItemKind::Use(path, hir::UseKind::Single)
                             if tcx.visibility(id.owner_id).is_public() =>
                         {
-                            as_keyword(path.res.expect_non_local())
+                            path.res
+                                .iter()
+                                .find_map(|res| as_keyword(res.expect_non_local()))
                                 .map(|(_, prim)| (id.owner_id.to_def_id(), prim))
                         }
                         _ => None,
@@ -311,10 +312,11 @@ impl ExternalCrate {
                         hir::ItemKind::Use(path, hir::UseKind::Single)
                             if tcx.visibility(id.owner_id).is_public() =>
                         {
-                            as_primitive(path.res.expect_non_local()).map(|(_, prim)| {
+                            path.res
+                                .iter()
+                                .find_map(|res| as_primitive(res.expect_non_local()))
                                 // Pretend the primitive is local.
-                                (id.owner_id.to_def_id(), prim)
-                            })
+                                .map(|(_, prim)| (id.owner_id.to_def_id(), prim))
                         }
                         _ => None,
                     }
@@ -1306,7 +1308,7 @@ impl Attributes {
         for attr in self.other_attrs.lists(sym::doc).filter(|a| a.has_name(sym::alias)) {
             if let Some(values) = attr.meta_item_list() {
                 for l in values {
-                    match l.literal().unwrap().kind {
+                    match l.lit().unwrap().kind {
                         ast::LitKind::Str(s, _) => {
                             aliases.insert(s);
                         }
@@ -2572,7 +2574,7 @@ mod size_asserts {
     use super::*;
     use rustc_data_structures::static_assert_size;
     // tidy-alphabetical-start
-    static_assert_size!(Crate, 72); // frequently moved by-value
+    static_assert_size!(Crate, 64); // frequently moved by-value
     static_assert_size!(DocFragment, 32);
     static_assert_size!(GenericArg, 32);
     static_assert_size!(GenericArgs, 32);
diff --git a/src/librustdoc/clean/utils.rs b/src/librustdoc/clean/utils.rs
index 35e2720fdff..246560bad29 100644
--- a/src/librustdoc/clean/utils.rs
+++ b/src/librustdoc/clean/utils.rs
@@ -73,7 +73,7 @@ pub(crate) fn krate(cx: &mut DocContext<'_>) -> Crate {
         }));
     }
 
-    Crate { module, primitives, external_traits: cx.external_traits.clone() }
+    Crate { module, external_traits: cx.external_traits.clone() }
 }
 
 pub(crate) fn substs_to_args<'tcx>(
@@ -106,7 +106,7 @@ fn external_generic_args<'tcx>(
 ) -> GenericArgs {
     let args = substs_to_args(cx, substs, has_self);
 
-    if cx.tcx.fn_trait_kind_from_lang_item(did).is_some() {
+    if cx.tcx.fn_trait_kind_from_def_id(did).is_some() {
         let inputs =
             // The trait's first substitution is the one after self, if there is one.
             match substs.iter().nth(if has_self { 1 } else { 0 }).unwrap().expect_ty().kind() {
diff --git a/src/librustdoc/core.rs b/src/librustdoc/core.rs
index 893249e88cf..da0df596c41 100644
--- a/src/librustdoc/core.rs
+++ b/src/librustdoc/core.rs
@@ -489,7 +489,7 @@ impl<'tcx> Visitor<'tcx> for EmitIgnoredResolutionErrors<'tcx> {
         self.tcx.hir()
     }
 
-    fn visit_path(&mut self, path: &'tcx Path<'_>, _id: HirId) {
+    fn visit_path(&mut self, path: &Path<'tcx>, _id: HirId) {
         debug!("visiting path {:?}", path);
         if path.res == Res::Err {
             // We have less context here than in rustc_resolve,
diff --git a/src/librustdoc/doctest.rs b/src/librustdoc/doctest.rs
index cb50c3ae829..81d9c46447a 100644
--- a/src/librustdoc/doctest.rs
+++ b/src/librustdoc/doctest.rs
@@ -19,7 +19,7 @@ use rustc_span::edition::Edition;
 use rustc_span::source_map::SourceMap;
 use rustc_span::symbol::sym;
 use rustc_span::{BytePos, FileName, Pos, Span, DUMMY_SP};
-use rustc_target::spec::TargetTriple;
+use rustc_target::spec::{Target, TargetTriple};
 use tempfile::Builder as TempFileBuilder;
 
 use std::env;
@@ -293,6 +293,16 @@ struct UnusedExterns {
     unused_extern_names: Vec<String>,
 }
 
+fn add_exe_suffix(input: String, target: &TargetTriple) -> String {
+    let exe_suffix = match target {
+        TargetTriple::TargetTriple(_) => Target::expect_builtin(target).options.exe_suffix,
+        TargetTriple::TargetJson { contents, .. } => {
+            Target::from_json(contents.parse().unwrap()).unwrap().0.options.exe_suffix
+        }
+    };
+    input + &exe_suffix
+}
+
 fn run_test(
     test: &str,
     crate_name: &str,
@@ -313,7 +323,9 @@ fn run_test(
     let (test, line_offset, supports_color) =
         make_test(test, Some(crate_name), lang_string.test_harness, opts, edition, Some(test_id));
 
-    let output_file = outdir.path().join("rust_out");
+    // Make sure we emit well-formed executable names for our target.
+    let rust_out = add_exe_suffix("rust_out".to_owned(), &target);
+    let output_file = outdir.path().join(rust_out);
 
     let rustc_binary = rustdoc_options
         .test_builder
diff --git a/src/librustdoc/html/render/mod.rs b/src/librustdoc/html/render/mod.rs
index ef28e2a855a..08f8096b07b 100644
--- a/src/librustdoc/html/render/mod.rs
+++ b/src/librustdoc/html/render/mod.rs
@@ -847,10 +847,10 @@ fn assoc_method(
         render_attributes_in_code(w, meth);
         (0, "", Ending::Newline)
     };
-    w.reserve(header_len + "<a href=\"\" class=\"fnname\">{".len() + "</a>".len());
+    w.reserve(header_len + "<a href=\"\" class=\"fn\">{".len() + "</a>".len());
     write!(
         w,
-        "{indent}{vis}{constness}{asyncness}{unsafety}{defaultness}{abi}fn <a{href} class=\"fnname\">{name}</a>\
+        "{indent}{vis}{constness}{asyncness}{unsafety}{defaultness}{abi}fn <a{href} class=\"fn\">{name}</a>\
          {generics}{decl}{notable_traits}{where_clause}",
         indent = indent_str,
         vis = vis,
diff --git a/src/librustdoc/html/render/span_map.rs b/src/librustdoc/html/render/span_map.rs
index 151ec2b28ad..4514894cabe 100644
--- a/src/librustdoc/html/render/span_map.rs
+++ b/src/librustdoc/html/render/span_map.rs
@@ -140,7 +140,7 @@ impl<'tcx> Visitor<'tcx> for SpanMapVisitor<'tcx> {
         self.tcx.hir()
     }
 
-    fn visit_path(&mut self, path: &'tcx rustc_hir::Path<'tcx>, _id: HirId) {
+    fn visit_path(&mut self, path: &rustc_hir::Path<'tcx>, _id: HirId) {
         if self.handle_macro(path.span) {
             return;
         }
@@ -190,12 +190,4 @@ impl<'tcx> Visitor<'tcx> for SpanMapVisitor<'tcx> {
         }
         intravisit::walk_expr(self, expr);
     }
-
-    fn visit_use(&mut self, path: &'tcx rustc_hir::Path<'tcx>, id: HirId) {
-        if self.handle_macro(path.span) {
-            return;
-        }
-        self.handle_path(path);
-        intravisit::walk_use(self, path, id);
-    }
 }
diff --git a/src/librustdoc/html/static/css/rustdoc.css b/src/librustdoc/html/static/css/rustdoc.css
index 98a5b761ded..6bff2cd2f1a 100644
--- a/src/librustdoc/html/static/css/rustdoc.css
+++ b/src/librustdoc/html/static/css/rustdoc.css
@@ -197,9 +197,7 @@ a.srclink,
 #help-button > a,
 details.rustdoc-toggle.top-doc > summary,
 details.rustdoc-toggle.non-exhaustive > summary,
-.scraped-example-title,
-.more-examples-toggle summary, .more-examples-toggle .hide-more,
-.example-links a,
+.scraped-example-list,
 /* This selector is for the items listed in the "all items" page. */
 ul.all-items {
 	font-family: "Fira Sans", Arial, NanumBarunGothic, sans-serif;
@@ -242,7 +240,6 @@ h1 a,
 }
 
 .content span.fn, .content a.fn,
-.content .fnname,
 .content span.method, .content a.method,
 .content span.tymethod, .content a.tymethod {
 	color: var(--function-link-color);
@@ -315,7 +312,7 @@ main {
 	position: relative;
 	flex-grow: 1;
 	padding: 10px 15px 40px 45px;
-	min-width: 0;
+	min-width: 0; /* avoid growing beyond the size limit */
 }
 
 .source main {
@@ -481,15 +478,11 @@ ul.block, .block li {
 	list-style: none;
 }
 
-.block a,
-.sidebar h2 a,
-.sidebar h3 a {
+.sidebar-elems a,
+.sidebar > h2 a {
 	display: block;
-	padding: 0.25rem;
+	padding: 0.25rem; /* 4px */
 	margin-left: -0.25rem;
-
-	text-overflow: ellipsis;
-	overflow: hidden;
 }
 
 .sidebar h2 {
@@ -523,6 +516,8 @@ ul.block, .block li {
 
 .sidebar-elems .block li a {
 	white-space: nowrap;
+	text-overflow: ellipsis;
+	overflow: hidden;
 }
 
 .mobile-topbar {
@@ -897,7 +892,6 @@ so that we can apply CSS-filters to change the arrow color in themes */
 	white-space: nowrap;
 	text-overflow: ellipsis;
 	overflow: hidden;
-	display: block;
 }
 
 .search-results a:hover,
@@ -1512,15 +1506,15 @@ details.rustdoc-toggle > summary.hideme > span {
 }
 
 details.rustdoc-toggle > summary::before {
+	background: url("toggle-plus-1092eb4930d581b0.svg") no-repeat top left;
 	content: "";
 	cursor: pointer;
 	width: 16px;
 	height: 16px;
-	background-repeat: no-repeat;
-	background-position: top left;
 	display: inline-block;
 	vertical-align: middle;
 	opacity: .5;
+	filter: var(--toggle-filter);
 }
 
 details.rustdoc-toggle > summary.hideme > span,
@@ -1598,19 +1592,9 @@ details.rustdoc-toggle[open] > summary.hideme > span {
 
 details.rustdoc-toggle[open] > summary::before,
 details.rustdoc-toggle[open] > summary.hideme::before {
-	background-image: url("toggle-minus-31bbd6e4c77f5c96.svg");
-}
-
-details.rustdoc-toggle > summary::before {
-	background-image: url("toggle-plus-1092eb4930d581b0.svg");
-}
-
-details.rustdoc-toggle[open] > summary::before,
-details.rustdoc-toggle[open] > summary.hideme::before {
+	background: url("toggle-minus-31bbd6e4c77f5c96.svg") no-repeat top left;
 	width: 16px;
 	height: 16px;
-	background-repeat: no-repeat;
-	background-position: top left;
 	display: inline-block;
 	content: "";
 }
diff --git a/src/librustdoc/html/static/css/themes/ayu.css b/src/librustdoc/html/static/css/themes/ayu.css
index de7db7d438c..6e0905e730d 100644
--- a/src/librustdoc/html/static/css/themes/ayu.css
+++ b/src/librustdoc/html/static/css/themes/ayu.css
@@ -21,6 +21,7 @@ Original by Dempfi (https://github.com/dempfi/ayu)
 	--right-side-color: grey;
 	--code-attribute-color: #999;
 	--toggles-color: #999;
+	--toggle-filter: invert(100%);
 	--search-input-focused-border-color: #5c6773; /* Same as `--border-color`. */
 	--copy-path-button-color: #fff;
 	--copy-path-img-filter: invert(70%);
@@ -158,10 +159,6 @@ body.source .example-wrap pre.rust a {
 	background: #333;
 }
 
-details.rustdoc-toggle > summary::before {
-	filter: invert(100%);
-}
-
 .module-item .stab,
 .import-item .stab {
 	color: #000;
diff --git a/src/librustdoc/html/static/css/themes/dark.css b/src/librustdoc/html/static/css/themes/dark.css
index d8929f32338..334fc3de561 100644
--- a/src/librustdoc/html/static/css/themes/dark.css
+++ b/src/librustdoc/html/static/css/themes/dark.css
@@ -16,6 +16,7 @@
 	--right-side-color: grey;
 	--code-attribute-color: #999;
 	--toggles-color: #999;
+	--toggle-filter: invert(100%);
 	--search-input-focused-border-color: #008dfd;
 	--copy-path-button-color: #999;
 	--copy-path-img-filter: invert(50%);
@@ -89,10 +90,6 @@ body.source .example-wrap pre.rust a {
 	background: #333;
 }
 
-details.rustdoc-toggle > summary::before {
-	filter: invert(100%);
-}
-
 #titles > button:not(.selected) {
 	background-color: #252525;
 	border-top-color: #252525;
diff --git a/src/librustdoc/html/static/css/themes/light.css b/src/librustdoc/html/static/css/themes/light.css
index 78a0cdcc3bc..453e7508af4 100644
--- a/src/librustdoc/html/static/css/themes/light.css
+++ b/src/librustdoc/html/static/css/themes/light.css
@@ -16,6 +16,7 @@
 	--right-side-color: grey;
 	--code-attribute-color: #999;
 	--toggles-color: #999;
+	--toggle-filter: none;
 	--search-input-focused-border-color: #66afe9;
 	--copy-path-button-color: #999;
 	--copy-path-img-filter: invert(50%);
diff --git a/src/librustdoc/html/static/js/main.js b/src/librustdoc/html/static/js/main.js
index 2a109ffbc60..623f46b1096 100644
--- a/src/librustdoc/html/static/js/main.js
+++ b/src/librustdoc/html/static/js/main.js
@@ -202,6 +202,7 @@ function loadCss(cssUrl) {
         if (event.ctrlKey || event.altKey || event.metaKey) {
             return;
         }
+        window.hideAllModals(false);
         addClass(getSettingsButton(), "rotate");
         event.preventDefault();
         // Sending request for the CSS and the JS files at the same time so it will
@@ -301,13 +302,15 @@ function loadCss(cssUrl) {
 
             const params = searchState.getQueryStringParams();
             if (params.search !== undefined) {
-                const search = searchState.outputElement();
-                search.innerHTML = "<h3 class=\"search-loading\">" +
-                    searchState.loadingText + "</h3>";
-                searchState.showResults(search);
+                searchState.setLoadingSearch();
                 loadSearch();
             }
         },
+        setLoadingSearch: () => {
+            const search = searchState.outputElement();
+            search.innerHTML = "<h3 class=\"search-loading\">" + searchState.loadingText + "</h3>";
+            searchState.showResults(search);
+        },
     };
 
     function getPageId() {
@@ -377,7 +380,7 @@ function loadCss(cssUrl) {
         }
         ev.preventDefault();
         searchState.defocus();
-        window.hidePopoverMenus();
+        window.hideAllModals(true); // true = reset focus for notable traits
     }
 
     function handleShortcut(ev) {
@@ -767,6 +770,7 @@ function loadCss(cssUrl) {
     };
 
     function showSidebar() {
+        window.hideAllModals(false);
         window.rustdocMobileScrollLock();
         const sidebar = document.getElementsByClassName("sidebar")[0];
         addClass(sidebar, "shown");
@@ -843,7 +847,7 @@ function loadCss(cssUrl) {
             // Make this function idempotent.
             return;
         }
-        hideNotable(false);
+        window.hideAllModals(false);
         const ty = e.getAttribute("data-ty");
         const wrapper = document.createElement("div");
         wrapper.innerHTML = "<div class=\"docblock\">" + window.NOTABLE_TRAITS[ty] + "</div>";
@@ -1050,13 +1054,23 @@ function loadCss(cssUrl) {
     }
 
     /**
+     * Hide popover menus, notable trait tooltips, and the sidebar (if applicable).
+     *
+     * Pass "true" to reset focus for notable traits.
+     */
+    window.hideAllModals = function(switchFocus) {
+        hideSidebar();
+        window.hidePopoverMenus();
+        hideNotable(switchFocus);
+    };
+
+    /**
      * Hide all the popover menus.
      */
     window.hidePopoverMenus = function() {
         onEachLazy(document.querySelectorAll(".search-form .popover"), elem => {
             elem.style.display = "none";
         });
-        hideNotable(false);
     };
 
     /**
@@ -1081,7 +1095,7 @@ function loadCss(cssUrl) {
     function showHelp() {
         const menu = getHelpMenu(true);
         if (menu.style.display === "none") {
-            window.hidePopoverMenus();
+            window.hideAllModals();
             menu.style.display = "";
         }
     }
diff --git a/src/librustdoc/html/static/js/search.js b/src/librustdoc/html/static/js/search.js
index 4999bb35994..23ae4e97082 100644
--- a/src/librustdoc/html/static/js/search.js
+++ b/src/librustdoc/html/static/js/search.js
@@ -1766,13 +1766,13 @@ function initSearch(rawSearchIndex) {
      * @param {boolean} [forced]
      */
     function search(e, forced) {
-        const params = searchState.getQueryStringParams();
-        const query = parseQuery(searchState.input.value.trim());
-
         if (e) {
             e.preventDefault();
         }
 
+        const query = parseQuery(searchState.input.value.trim());
+        let filterCrates = getFilterCrates();
+
         if (!forced && query.userQuery === currentResults) {
             if (query.userQuery.length > 0) {
                 putBackSearch();
@@ -1780,7 +1780,9 @@ function initSearch(rawSearchIndex) {
             return;
         }
 
-        let filterCrates = getFilterCrates();
+        searchState.setLoadingSearch();
+
+        const params = searchState.getQueryStringParams();
 
         // In case we have no information about the saved crate and there is a URL query parameter,
         // we override it with the URL query parameter.
diff --git a/src/librustdoc/html/static/js/settings.js b/src/librustdoc/html/static/js/settings.js
index 5256ae916a7..589bfc79360 100644
--- a/src/librustdoc/html/static/js/settings.js
+++ b/src/librustdoc/html/static/js/settings.js
@@ -268,7 +268,7 @@
             event.preventDefault();
             const shouldDisplaySettings = settingsMenu.style.display === "none";
 
-            window.hidePopoverMenus();
+            window.hideAllModals();
             if (shouldDisplaySettings) {
                 displaySettings();
             }
diff --git a/src/librustdoc/json/mod.rs b/src/librustdoc/json/mod.rs
index e4df1332521..83be1a16eb2 100644
--- a/src/librustdoc/json/mod.rs
+++ b/src/librustdoc/json/mod.rs
@@ -225,10 +225,10 @@ impl<'tcx> FormatRenderer<'tcx> for JsonRenderer<'tcx> {
 
                 types::ItemEnum::Function(_)
                 | types::ItemEnum::Module(_)
+                | types::ItemEnum::Import(_)
                 | types::ItemEnum::AssocConst { .. }
                 | types::ItemEnum::AssocType { .. } => true,
                 types::ItemEnum::ExternCrate { .. }
-                | types::ItemEnum::Import(_)
                 | types::ItemEnum::StructField(_)
                 | types::ItemEnum::Variant(_)
                 | types::ItemEnum::TraitAlias(_)
diff --git a/src/librustdoc/passes/collect_trait_impls.rs b/src/librustdoc/passes/collect_trait_impls.rs
index 6b699c79014..d57f981d51a 100644
--- a/src/librustdoc/passes/collect_trait_impls.rs
+++ b/src/librustdoc/passes/collect_trait_impls.rs
@@ -8,7 +8,7 @@ use crate::formats::cache::Cache;
 use crate::visit::DocVisitor;
 
 use rustc_data_structures::fx::{FxHashMap, FxHashSet};
-use rustc_hir::def_id::DefId;
+use rustc_hir::def_id::{DefId, LOCAL_CRATE};
 use rustc_middle::ty::{self, DefIdTree};
 use rustc_span::symbol::sym;
 
@@ -25,7 +25,9 @@ pub(crate) fn collect_trait_impls(mut krate: Crate, cx: &mut DocContext<'_>) ->
         synth.impls
     });
 
-    let prims: FxHashSet<PrimitiveType> = krate.primitives.iter().map(|p| p.1).collect();
+    let local_crate = ExternalCrate { crate_num: LOCAL_CRATE };
+    let prims: FxHashSet<PrimitiveType> =
+        local_crate.primitives(cx.tcx).iter().map(|p| p.1).collect();
 
     let crate_items = {
         let mut coll = ItemCollector::new();
diff --git a/src/librustdoc/visit_ast.rs b/src/librustdoc/visit_ast.rs
index c788b9f4093..22068ebe041 100644
--- a/src/librustdoc/visit_ast.rs
+++ b/src/librustdoc/visit_ast.rs
@@ -301,39 +301,40 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> {
             hir::ItemKind::GlobalAsm(..) => {}
             hir::ItemKind::Use(_, hir::UseKind::ListStem) => {}
             hir::ItemKind::Use(path, kind) => {
-                let is_glob = kind == hir::UseKind::Glob;
-
-                // Struct and variant constructors and proc macro stubs always show up alongside
-                // their definitions, we've already processed them so just discard these.
-                if let Res::Def(DefKind::Ctor(..), _) | Res::SelfCtor(..) = path.res {
-                    return;
-                }
-
-                let attrs = self.cx.tcx.hir().attrs(item.hir_id());
+                for &res in &path.res {
+                    // Struct and variant constructors and proc macro stubs always show up alongside
+                    // their definitions, we've already processed them so just discard these.
+                    if let Res::Def(DefKind::Ctor(..), _) | Res::SelfCtor(..) = res {
+                        continue;
+                    }
 
-                // If there was a private module in the current path then don't bother inlining
-                // anything as it will probably be stripped anyway.
-                if is_pub && self.inside_public_path {
-                    let please_inline = attrs.iter().any(|item| match item.meta_item_list() {
-                        Some(ref list) if item.has_name(sym::doc) => {
-                            list.iter().any(|i| i.has_name(sym::inline))
+                    let attrs = self.cx.tcx.hir().attrs(item.hir_id());
+
+                    // If there was a private module in the current path then don't bother inlining
+                    // anything as it will probably be stripped anyway.
+                    if is_pub && self.inside_public_path {
+                        let please_inline = attrs.iter().any(|item| match item.meta_item_list() {
+                            Some(ref list) if item.has_name(sym::doc) => {
+                                list.iter().any(|i| i.has_name(sym::inline))
+                            }
+                            _ => false,
+                        });
+                        let is_glob = kind == hir::UseKind::Glob;
+                        let ident = if is_glob { None } else { Some(name) };
+                        if self.maybe_inline_local(
+                            item.hir_id(),
+                            res,
+                            ident,
+                            is_glob,
+                            om,
+                            please_inline,
+                        ) {
+                            continue;
                         }
-                        _ => false,
-                    });
-                    let ident = if is_glob { None } else { Some(name) };
-                    if self.maybe_inline_local(
-                        item.hir_id(),
-                        path.res,
-                        ident,
-                        is_glob,
-                        om,
-                        please_inline,
-                    ) {
-                        return;
                     }
-                }
 
-                om.items.push((item, renamed, parent_id))
+                    om.items.push((item, renamed, parent_id))
+                }
             }
             hir::ItemKind::Macro(ref macro_def, _) => {
                 // `#[macro_export] macro_rules!` items are handled separately in `visit()`,
diff --git a/src/rustdoc-json-types/lib.rs b/src/rustdoc-json-types/lib.rs
index dd40f350752..1ee96b8231c 100644
--- a/src/rustdoc-json-types/lib.rs
+++ b/src/rustdoc-json-types/lib.rs
@@ -615,6 +615,10 @@ pub struct FunctionPointer {
 
 #[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
 pub struct FnDecl {
+    /// List of argument names and their type.
+    ///
+    /// Note that not all names will be valid identifiers, as some of
+    /// them may be patterns.
     pub inputs: Vec<(String, Type)>,
     pub output: Option<Type>,
     pub c_variadic: bool,
diff --git a/src/test/codegen-units/item-collection/asm-sym.rs b/src/test/codegen-units/item-collection/asm-sym.rs
new file mode 100644
index 00000000000..8bafb95bc16
--- /dev/null
+++ b/src/test/codegen-units/item-collection/asm-sym.rs
@@ -0,0 +1,20 @@
+// needs-asm-support
+// compile-flags: -Ccodegen-units=1 -Zprint-mono-items=lazy --crate-type=lib
+
+#[inline(always)]
+pub unsafe fn f() {
+    //~ MONO_ITEM static f::S @@ asm_sym-cgu.0[External]
+    static S: usize = 1;
+    //~ MONO_ITEM fn f::fun @@ asm_sym-cgu.0[External]
+    fn fun() {}
+    core::arch::asm!("/* {0} {1} */", sym S, sym fun);
+}
+
+//~ MONO_ITEM fn g @@ asm_sym-cgu.0[External]
+pub unsafe fn g() {
+    //~ MONO_ITEM static g::S @@ asm_sym-cgu.0[Internal]
+    static S: usize = 2;
+    //~ MONO_ITEM fn g::fun @@ asm_sym-cgu.0[Internal]
+    fn fun() {}
+    core::arch::asm!("/* {0} {1} */", sym S, sym fun);
+}
diff --git a/src/test/codegen/auxiliary/extern_decl.rs b/src/test/codegen/auxiliary/extern_decl.rs
new file mode 100644
index 00000000000..edc48351869
--- /dev/null
+++ b/src/test/codegen/auxiliary/extern_decl.rs
@@ -0,0 +1,11 @@
+// Auxiliary crate that exports a function and static. Both always
+// evaluate to `71`. We force mutability on the static to prevent
+// it from being inlined as constant.
+
+#![crate_type = "lib"]
+
+#[no_mangle]
+pub fn extern_fn() -> u8 { unsafe { extern_static } }
+
+#[no_mangle]
+pub static mut extern_static: u8 = 71;
diff --git a/src/test/codegen/dllimports/main.rs b/src/test/codegen/dllimports/main.rs
index bb3134e81c9..ab599992ffd 100644
--- a/src/test/codegen/dllimports/main.rs
+++ b/src/test/codegen/dllimports/main.rs
@@ -1,17 +1,6 @@
 // This test is for *-windows-msvc only.
-// ignore-android
-// ignore-dragonfly
-// ignore-emscripten
-// ignore-freebsd
+// only-windows
 // ignore-gnu
-// ignore-haiku
-// ignore-ios
-// ignore-linux
-// ignore-macos
-// ignore-netbsd
-// ignore-openbsd
-// ignore-solaris
-// ignore-sgx no dynamic linking
 
 // aux-build:dummy.rs
 // aux-build:wrapper.rs
diff --git a/src/test/codegen/panic-abort-windows.rs b/src/test/codegen/panic-abort-windows.rs
index 9ee4bfc4711..2ee29762dcd 100644
--- a/src/test/codegen/panic-abort-windows.rs
+++ b/src/test/codegen/panic-abort-windows.rs
@@ -1,16 +1,5 @@
-// This test is for *-windows-msvc only.
-// ignore-android
-// ignore-dragonfly
-// ignore-emscripten
-// ignore-freebsd
-// ignore-haiku
-// ignore-ios
-// ignore-linux
-// ignore-macos
-// ignore-netbsd
-// ignore-openbsd
-// ignore-solaris
-// ignore-sgx
+// This test is for *-windows only.
+// only-windows
 
 // compile-flags: -C no-prepopulate-passes -C panic=abort -O
 
diff --git a/src/test/codegen/static-relocation-model-msvc.rs b/src/test/codegen/static-relocation-model-msvc.rs
new file mode 100644
index 00000000000..b2afc7deb67
--- /dev/null
+++ b/src/test/codegen/static-relocation-model-msvc.rs
@@ -0,0 +1,26 @@
+// Verify linkage of external symbols in the static relocation model on MSVC.
+//
+// compile-flags: -O -C relocation-model=static
+// aux-build: extern_decl.rs
+// only-x86_64-pc-windows-msvc
+
+#![crate_type = "rlib"]
+
+extern crate extern_decl;
+
+// The `extern_decl` definitions are imported from a statically linked rust
+// crate, thus they are expected to be marked `dso_local` without `dllimport`.
+//
+// The `access_extern()` symbol is from this compilation unit, thus we expect
+// it to be marked `dso_local` as well, given the static relocation model.
+//
+// CHECK: @extern_static = external dso_local local_unnamed_addr global i8
+// CHECK: define dso_local i8 @access_extern() {{.*}}
+// CHECK: declare dso_local i8 @extern_fn() {{.*}}
+
+#[no_mangle]
+pub fn access_extern() -> u8 {
+    unsafe {
+        extern_decl::extern_fn() + extern_decl::extern_static
+    }
+}
diff --git a/src/test/debuginfo/pretty-std.rs b/src/test/debuginfo/pretty-std.rs
index d8c6344e0b6..7bb2810c2b2 100644
--- a/src/test/debuginfo/pretty-std.rs
+++ b/src/test/debuginfo/pretty-std.rs
@@ -138,7 +138,7 @@
 // cdb-command: dx vecdeque
 // cdb-check:vecdeque         : { len=0x2 } [Type: alloc::collections::vec_deque::VecDeque<i32,alloc::alloc::Global>]
 // cdb-check:    [<Raw View>]     [Type: alloc::collections::vec_deque::VecDeque<i32,alloc::alloc::Global>]
-// cdb-check:    [len]            : 0x2
+// cdb-check:    [len]            : 0x2 [Type: unsigned [...]]
 // cdb-check:    [capacity]       : 0x8 [Type: unsigned [...]]
 // cdb-check:    [0x0]            : 90 [Type: int]
 // cdb-check:    [0x1]            : 20 [Type: int]
@@ -175,7 +175,7 @@ fn main() {
     linkedlist.push_front(128);
 
     // VecDeque
-    let mut vecdeque = VecDeque::new();
+    let mut vecdeque = VecDeque::with_capacity(8);
     vecdeque.push_back(20);
     vecdeque.push_front(90);
 
diff --git a/src/test/mir-opt/building/custom/arbitrary_let.arbitrary_let.built.after.mir b/src/test/mir-opt/building/custom/arbitrary_let.arbitrary_let.built.after.mir
new file mode 100644
index 00000000000..20dd251e7e3
--- /dev/null
+++ b/src/test/mir-opt/building/custom/arbitrary_let.arbitrary_let.built.after.mir
@@ -0,0 +1,22 @@
+// MIR for `arbitrary_let` after built
+
+fn arbitrary_let(_1: i32) -> i32 {
+    let mut _0: i32;                     // return place in scope 0 at $DIR/arbitrary_let.rs:+0:29: +0:32
+    let mut _2: i32;                     // in scope 0 at $SRC_DIR/core/src/intrinsics/mir.rs:LL:COL
+    let mut _3: i32;                     // in scope 0 at $SRC_DIR/core/src/intrinsics/mir.rs:LL:COL
+
+    bb0: {
+        _2 = _1;                         // scope 0 at $SRC_DIR/core/src/intrinsics/mir.rs:LL:COL
+        goto -> bb2;                     // scope 0 at $DIR/arbitrary_let.rs:+4:13: +4:25
+    }
+
+    bb1: {
+        _0 = _3;                         // scope 0 at $DIR/arbitrary_let.rs:+7:13: +7:20
+        return;                          // scope 0 at $DIR/arbitrary_let.rs:+8:13: +8:21
+    }
+
+    bb2: {
+        _3 = _2;                         // scope 0 at $SRC_DIR/core/src/intrinsics/mir.rs:LL:COL
+        goto -> bb1;                     // scope 0 at $DIR/arbitrary_let.rs:+12:13: +12:24
+    }
+}
diff --git a/src/test/mir-opt/building/custom/arbitrary_let.rs b/src/test/mir-opt/building/custom/arbitrary_let.rs
new file mode 100644
index 00000000000..776df3151ff
--- /dev/null
+++ b/src/test/mir-opt/building/custom/arbitrary_let.rs
@@ -0,0 +1,28 @@
+#![feature(custom_mir, core_intrinsics)]
+
+extern crate core;
+use core::intrinsics::mir::*;
+use core::ptr::{addr_of, addr_of_mut};
+
+// EMIT_MIR arbitrary_let.arbitrary_let.built.after.mir
+#[custom_mir(dialect = "built")]
+fn arbitrary_let(x: i32) -> i32 {
+    mir!(
+        {
+            let y = x;
+            Goto(second)
+        }
+        third = {
+            RET = z;
+            Return()
+        }
+        second = {
+            let z = y;
+            Goto(third)
+        }
+    )
+}
+
+fn main() {
+    assert_eq!(arbitrary_let(5), 5);
+}
diff --git a/src/test/mir-opt/building/custom/consts.consts.built.after.mir b/src/test/mir-opt/building/custom/consts.consts.built.after.mir
new file mode 100644
index 00000000000..ba753cfc20c
--- /dev/null
+++ b/src/test/mir-opt/building/custom/consts.consts.built.after.mir
@@ -0,0 +1,22 @@
+// MIR for `consts` after built
+
+fn consts() -> () {
+    let mut _0: ();                      // return place in scope 0 at $DIR/consts.rs:+0:27: +0:27
+    let mut _1: u8;                      // in scope 0 at $SRC_DIR/core/src/intrinsics/mir.rs:LL:COL
+    let mut _2: i8;                      // in scope 0 at $SRC_DIR/core/src/intrinsics/mir.rs:LL:COL
+    let mut _3: u32;                     // in scope 0 at $SRC_DIR/core/src/intrinsics/mir.rs:LL:COL
+    let mut _4: i32;                     // in scope 0 at $SRC_DIR/core/src/intrinsics/mir.rs:LL:COL
+    let mut _5: fn() {consts::<10>};     // in scope 0 at $SRC_DIR/core/src/intrinsics/mir.rs:LL:COL
+
+    bb0: {
+        _1 = const 5_u8;                 // scope 0 at $SRC_DIR/core/src/intrinsics/mir.rs:LL:COL
+        _2 = const _;                    // scope 0 at $SRC_DIR/core/src/intrinsics/mir.rs:LL:COL
+        _3 = const C;                    // scope 0 at $SRC_DIR/core/src/intrinsics/mir.rs:LL:COL
+        _4 = const _;                    // scope 0 at $SRC_DIR/core/src/intrinsics/mir.rs:LL:COL
+        _5 = consts::<10>;               // scope 0 at $SRC_DIR/core/src/intrinsics/mir.rs:LL:COL
+                                         // mir::Constant
+                                         // + span: $DIR/consts.rs:16:18: 16:30
+                                         // + literal: Const { ty: fn() {consts::<10>}, val: Value(<ZST>) }
+        return;                          // scope 0 at $DIR/consts.rs:+7:9: +7:17
+    }
+}
diff --git a/src/test/mir-opt/building/custom/consts.rs b/src/test/mir-opt/building/custom/consts.rs
new file mode 100644
index 00000000000..ff4fe1a9324
--- /dev/null
+++ b/src/test/mir-opt/building/custom/consts.rs
@@ -0,0 +1,36 @@
+#![feature(custom_mir, core_intrinsics, inline_const)]
+
+extern crate core;
+use core::intrinsics::mir::*;
+
+const D: i32 = 5;
+
+// EMIT_MIR consts.consts.built.after.mir
+#[custom_mir(dialect = "built")]
+fn consts<const C: u32>() {
+    mir!({
+        let _a = 5_u8;
+        let _b = const { 5_i8 };
+        let _c = C;
+        let _d = D;
+        let _e = consts::<10>;
+        Return()
+    })
+}
+
+static S: i32 = 5;
+static mut T: i32 = 10;
+// EMIT_MIR consts.statics.built.after.mir
+#[custom_mir(dialect = "built")]
+fn statics() {
+    mir!({
+        let _a: &i32 = Static(S);
+        let _b: *mut i32 = StaticMut(T);
+        Return()
+    })
+}
+
+fn main() {
+    consts::<5>();
+    statics();
+}
diff --git a/src/test/mir-opt/building/custom/consts.statics.built.after.mir b/src/test/mir-opt/building/custom/consts.statics.built.after.mir
new file mode 100644
index 00000000000..ee768e263ec
--- /dev/null
+++ b/src/test/mir-opt/building/custom/consts.statics.built.after.mir
@@ -0,0 +1,27 @@
+// MIR for `statics` after built
+
+fn statics() -> () {
+    let mut _0: ();                      // return place in scope 0 at $DIR/consts.rs:+0:14: +0:14
+    let mut _1: &i32;                    // in scope 0 at $SRC_DIR/core/src/intrinsics/mir.rs:LL:COL
+    let mut _2: *mut i32;                // in scope 0 at $SRC_DIR/core/src/intrinsics/mir.rs:LL:COL
+
+    bb0: {
+        _1 = const {alloc1: &i32};       // scope 0 at $SRC_DIR/core/src/intrinsics/mir.rs:LL:COL
+                                         // mir::Constant
+                                         // + span: $DIR/consts.rs:27:31: 27:32
+                                         // + literal: Const { ty: &i32, val: Value(Scalar(alloc1)) }
+        _2 = const {alloc2: *mut i32};   // scope 0 at $SRC_DIR/core/src/intrinsics/mir.rs:LL:COL
+                                         // mir::Constant
+                                         // + span: $DIR/consts.rs:28:38: 28:39
+                                         // + literal: Const { ty: *mut i32, val: Value(Scalar(alloc2)) }
+        return;                          // scope 0 at $DIR/consts.rs:+4:9: +4:17
+    }
+}
+
+alloc2 (static: T, size: 4, align: 4) {
+    0a 00 00 00                                     │ ....
+}
+
+alloc1 (static: S, size: 4, align: 4) {
+    05 00 00 00                                     │ ....
+}
diff --git a/src/test/mir-opt/building/custom/references.immut_ref.built.after.mir b/src/test/mir-opt/building/custom/references.immut_ref.built.after.mir
index 4a5ddde4081..4d38d45c0f4 100644
--- a/src/test/mir-opt/building/custom/references.immut_ref.built.after.mir
+++ b/src/test/mir-opt/building/custom/references.immut_ref.built.after.mir
@@ -5,10 +5,10 @@ fn immut_ref(_1: &i32) -> &i32 {
     let mut _2: *const i32;              // in scope 0 at $SRC_DIR/core/src/intrinsics/mir.rs:LL:COL
 
     bb0: {
-        _2 = &raw const (*_1);           // scope 0 at $DIR/references.rs:+0:1: +0:34
-        Retag([raw] _2);                 // scope 0 at $DIR/references.rs:+0:1: +0:34
-        _0 = &(*_2);                     // scope 0 at $DIR/references.rs:+0:1: +0:34
-        Retag(_0);                       // scope 0 at $DIR/references.rs:+0:1: +0:34
-        return;                          // scope 0 at $DIR/references.rs:+0:1: +0:34
+        _2 = &raw const (*_1);           // scope 0 at $DIR/references.rs:+5:13: +5:29
+        Retag([raw] _2);                 // scope 0 at $DIR/references.rs:+6:13: +6:24
+        _0 = &(*_2);                     // scope 0 at $DIR/references.rs:+7:13: +7:23
+        Retag(_0);                       // scope 0 at $DIR/references.rs:+8:13: +8:23
+        return;                          // scope 0 at $DIR/references.rs:+9:13: +9:21
     }
 }
diff --git a/src/test/mir-opt/building/custom/references.mut_ref.built.after.mir b/src/test/mir-opt/building/custom/references.mut_ref.built.after.mir
index ec8509f69d1..01bc8a9cd35 100644
--- a/src/test/mir-opt/building/custom/references.mut_ref.built.after.mir
+++ b/src/test/mir-opt/building/custom/references.mut_ref.built.after.mir
@@ -5,10 +5,10 @@ fn mut_ref(_1: &mut i32) -> &mut i32 {
     let mut _2: *mut i32;                // in scope 0 at $SRC_DIR/core/src/intrinsics/mir.rs:LL:COL
 
     bb0: {
-        _2 = &raw mut (*_1);             // scope 0 at $DIR/references.rs:+0:1: +0:40
-        Retag([raw] _2);                 // scope 0 at $DIR/references.rs:+0:1: +0:40
-        _0 = &mut (*_2);                 // scope 0 at $DIR/references.rs:+0:1: +0:40
-        Retag(_0);                       // scope 0 at $DIR/references.rs:+0:1: +0:40
-        return;                          // scope 0 at $DIR/references.rs:+0:1: +0:40
+        _2 = &raw mut (*_1);             // scope 0 at $DIR/references.rs:+5:13: +5:33
+        Retag([raw] _2);                 // scope 0 at $DIR/references.rs:+6:13: +6:24
+        _0 = &mut (*_2);                 // scope 0 at $DIR/references.rs:+7:13: +7:26
+        Retag(_0);                       // scope 0 at $DIR/references.rs:+8:13: +8:23
+        return;                          // scope 0 at $DIR/references.rs:+9:13: +9:21
     }
 }
diff --git a/src/test/mir-opt/building/custom/simple_assign.simple.built.after.mir b/src/test/mir-opt/building/custom/simple_assign.simple.built.after.mir
index a5a2834c2e1..d7560fde69c 100644
--- a/src/test/mir-opt/building/custom/simple_assign.simple.built.after.mir
+++ b/src/test/mir-opt/building/custom/simple_assign.simple.built.after.mir
@@ -6,13 +6,13 @@ fn simple(_1: i32) -> i32 {
     let mut _3: i32;                     // in scope 0 at $SRC_DIR/core/src/intrinsics/mir.rs:LL:COL
 
     bb0: {
-        _2 = _1;                         // scope 0 at $DIR/simple_assign.rs:+0:1: +0:29
-        goto -> bb1;                     // scope 0 at $DIR/simple_assign.rs:+0:1: +0:29
+        _2 = _1;                         // scope 0 at $DIR/simple_assign.rs:+6:13: +6:22
+        goto -> bb1;                     // scope 0 at $DIR/simple_assign.rs:+7:13: +7:23
     }
 
     bb1: {
-        _3 = move _2;                    // scope 0 at $DIR/simple_assign.rs:+0:1: +0:29
-        _0 = _3;                         // scope 0 at $DIR/simple_assign.rs:+0:1: +0:29
-        return;                          // scope 0 at $DIR/simple_assign.rs:+0:1: +0:29
+        _3 = move _2;                    // scope 0 at $DIR/simple_assign.rs:+11:13: +11:32
+        _0 = _3;                         // scope 0 at $DIR/simple_assign.rs:+12:13: +12:24
+        return;                          // scope 0 at $DIR/simple_assign.rs:+13:13: +13:21
     }
 }
diff --git a/src/test/mir-opt/building/custom/simple_assign.simple_ref.built.after.mir b/src/test/mir-opt/building/custom/simple_assign.simple_ref.built.after.mir
index 6c90f0130a2..2b0e8f1047b 100644
--- a/src/test/mir-opt/building/custom/simple_assign.simple_ref.built.after.mir
+++ b/src/test/mir-opt/building/custom/simple_assign.simple_ref.built.after.mir
@@ -4,7 +4,7 @@ fn simple_ref(_1: &mut i32) -> &mut i32 {
     let mut _0: &mut i32;                // return place in scope 0 at $DIR/simple_assign.rs:+0:35: +0:43
 
     bb0: {
-        _0 = move _1;                    // scope 0 at $DIR/simple_assign.rs:+0:1: +0:43
-        return;                          // scope 0 at $DIR/simple_assign.rs:+0:1: +0:43
+        _0 = move _1;                    // scope 0 at $DIR/simple_assign.rs:+2:9: +2:22
+        return;                          // scope 0 at $DIR/simple_assign.rs:+3:9: +3:17
     }
 }
diff --git a/src/test/run-make/coverage-reports/Makefile b/src/test/run-make/coverage-reports/Makefile
index 407992c9f43..436aebf1174 100644
--- a/src/test/run-make/coverage-reports/Makefile
+++ b/src/test/run-make/coverage-reports/Makefile
@@ -132,7 +132,7 @@ include clear_expected_if_blessed
 			--instr-profile="$(TMPDIR)"/$@.profdata \
 			$(call BIN,"$(TMPDIR)"/$@) \
 			$$( \
-				for file in $(TMPDIR)/rustdoc-$@/*/rust_out; do \
+				for file in $(TMPDIR)/rustdoc-$@/*/rust_out*; do \
 				[ -x "$$file" ] && printf "%s %s " -object $$file; \
 				done \
 			) \
diff --git a/src/test/run-make/native-link-modifier-verbatim-linker/Makefile b/src/test/run-make/native-link-modifier-verbatim-linker/Makefile
index e56e1e94ec5..666e4084ce2 100644
--- a/src/test/run-make/native-link-modifier-verbatim-linker/Makefile
+++ b/src/test/run-make/native-link-modifier-verbatim-linker/Makefile
@@ -6,10 +6,10 @@ include ../../run-make-fulldeps/tools.mk
 all:
 	# Verbatim allows specify precise name.
 	$(RUSTC) local_native_dep.rs --crate-type=staticlib -o $(TMPDIR)/local_some_strange_name.ext
-	$(RUSTC) main.rs -Zunstable-options -l static:+verbatim=local_some_strange_name.ext
+	$(RUSTC) main.rs -l static:+verbatim=local_some_strange_name.ext
 
 	# With verbatim any other name cannot be used (local).
 	$(RUSTC) local_native_dep.rs --crate-type=staticlib -o $(TMPDIR)/liblocal_native_dep.a
 	$(RUSTC) local_native_dep.rs --crate-type=staticlib -o $(TMPDIR)/local_native_dep.a
 	$(RUSTC) local_native_dep.rs --crate-type=staticlib -o $(TMPDIR)/local_native_dep.lib
-	$(RUSTC) main.rs -Zunstable-options -l static:+verbatim=local_native_dep 2>&1 | $(CGREP) "local_native_dep"
+	$(RUSTC) main.rs -l static:+verbatim=local_native_dep 2>&1 | $(CGREP) "local_native_dep"
diff --git a/src/test/run-make/native-link-modifier-verbatim-rustc/Makefile b/src/test/run-make/native-link-modifier-verbatim-rustc/Makefile
index 1093b1cd369..6f01f37804a 100644
--- a/src/test/run-make/native-link-modifier-verbatim-rustc/Makefile
+++ b/src/test/run-make/native-link-modifier-verbatim-rustc/Makefile
@@ -3,10 +3,10 @@ include ../../run-make-fulldeps/tools.mk
 all:
 	# Verbatim allows specify precise name.
 	$(RUSTC) upstream_native_dep.rs --crate-type=staticlib -o $(TMPDIR)/upstream_some_strange_name.ext
-	$(RUSTC) rust_dep.rs -Zunstable-options -l static:+verbatim=upstream_some_strange_name.ext --crate-type rlib
+	$(RUSTC) rust_dep.rs -l static:+verbatim=upstream_some_strange_name.ext --crate-type rlib
 
 	# With verbatim any other name cannot be used (upstream).
 	$(RUSTC) upstream_native_dep.rs --crate-type=staticlib -o $(TMPDIR)/libupstream_native_dep.a
 	$(RUSTC) upstream_native_dep.rs --crate-type=staticlib -o $(TMPDIR)/upstream_native_dep.a
 	$(RUSTC) upstream_native_dep.rs --crate-type=staticlib -o $(TMPDIR)/upstream_native_dep.lib
-	$(RUSTC) rust_dep.rs -Zunstable-options -l static:+verbatim=upstream_native_dep --crate-type rlib 2>&1 | $(CGREP) "upstream_native_dep"
+	$(RUSTC) rust_dep.rs -l static:+verbatim=upstream_native_dep --crate-type rlib 2>&1 | $(CGREP) "upstream_native_dep"
diff --git a/src/test/run-make/raw-dylib-c/lib.rs b/src/test/run-make/raw-dylib-c/lib.rs
index 005ffcdda5c..5fb1204037c 100644
--- a/src/test/run-make/raw-dylib-c/lib.rs
+++ b/src/test/run-make/raw-dylib-c/lib.rs
@@ -1,4 +1,4 @@
-#![feature(raw_dylib, native_link_modifiers_verbatim)]
+#![feature(raw_dylib)]
 
 #[link(name = "extern_1.dll", kind = "raw-dylib", modifiers = "+verbatim")]
 extern {
diff --git a/src/test/run-make/rlib-format-packed-bundled-libs-2/rust_dep.rs b/src/test/run-make/rlib-format-packed-bundled-libs-2/rust_dep.rs
index d99dda05cf2..77e41e237d4 100644
--- a/src/test/run-make/rlib-format-packed-bundled-libs-2/rust_dep.rs
+++ b/src/test/run-make/rlib-format-packed-bundled-libs-2/rust_dep.rs
@@ -1,4 +1,3 @@
-#![feature(native_link_modifiers_verbatim)]
 #[link(name = "native_dep.ext", kind = "static", modifiers = "+verbatim")]
 extern "C" {
     fn native_f1() -> i32;
diff --git a/src/test/rustdoc-gui/item-decl-colors.goml b/src/test/rustdoc-gui/item-decl-colors.goml
index 9a46f256056..2e07f19b13d 100644
--- a/src/test/rustdoc-gui/item-decl-colors.goml
+++ b/src/test/rustdoc-gui/item-decl-colors.goml
@@ -30,7 +30,7 @@ define-function: (
         ("assert-css", (".item-decl .primitive", {"color": |primitive_color|}, ALL)),
         ("goto", "file://" + |DOC_PATH| + "/test_docs/trait.TraitWithoutGenerics.html"),
         ("assert-css", (".item-decl .constant", {"color": |constant_color|}, ALL)),
-        ("assert-css", (".item-decl .fnname", {"color": |fn_color|}, ALL)),
+        ("assert-css", (".item-decl .fn", {"color": |fn_color|}, ALL)),
         ("assert-css", (".item-decl .associatedtype", {"color": |assoc_type_color|}, ALL)),
     ],
 )
diff --git a/src/test/rustdoc-gui/notable-trait.goml b/src/test/rustdoc-gui/notable-trait.goml
index aab3b11433e..7d4bd27d42d 100644
--- a/src/test/rustdoc-gui/notable-trait.goml
+++ b/src/test/rustdoc-gui/notable-trait.goml
@@ -200,12 +200,14 @@ move-cursor-to: "//*[@class='notable popover']"
 assert-count: ("//*[@class='notable popover']", 1)
 press-key: "Escape"
 assert-count: ("//*[@class='notable popover']", 0)
+assert: "#method\.create_an_iterator_from_read .notable-traits:focus"
 
 // Check that clicking outside works.
 click: "//*[@id='method.create_an_iterator_from_read']//*[@class='notable-traits']"
 assert-count: ("//*[@class='notable popover']", 1)
 click: ".search-input"
 assert-count: ("//*[@class='notable popover']", 0)
+assert-false: "#method\.create_an_iterator_from_read .notable-traits:focus"
 
 // Check that pressing tab over and over works.
 click: "//*[@id='method.create_an_iterator_from_read']//*[@class='notable-traits']"
@@ -224,7 +226,7 @@ assert: "#method\.create_an_iterator_from_read .notable-traits:focus"
 // Now we check that the focus isn't given back to the wrong item when opening
 // another popover.
 store-window-property: (scroll, "scrollY")
-click: "#method\.create_an_iterator_from_read .fnname"
+click: "#method\.create_an_iterator_from_read .fn"
 // We ensure that the scroll position changed.
 assert-window-property-false: {"scrollY": |scroll|}
 // Store the new position.
@@ -238,7 +240,7 @@ assert-window-property-false: {"scrollY": |scroll|}
 
 // Same but with Escape handling.
 store-window-property: (scroll, "scrollY")
-click: "#method\.create_an_iterator_from_read .fnname"
+click: "#method\.create_an_iterator_from_read .fn"
 // We ensure that the scroll position changed.
 assert-window-property-false: {"scrollY": |scroll|}
 // Store the new position.
@@ -249,3 +251,26 @@ click: "#settings-menu a"
 press-key: "Escape"
 // We ensure we didn't come back to the previous focused item.
 assert-window-property-false: {"scrollY": |scroll|}
+
+// Opening the mobile sidebar should close the popover.
+size: (650, 600)
+click: "//*[@id='method.create_an_iterator_from_read']//*[@class='notable-traits']"
+assert-count: ("//*[@class='notable popover']", 1)
+click: ".sidebar-menu-toggle"
+assert: "//*[@class='sidebar shown']"
+assert-count: ("//*[@class='notable popover']", 0)
+assert-false: "#method\.create_an_iterator_from_read .notable-traits:focus"
+// Clicking a notable popover should close the sidebar.
+click: "//*[@id='method.create_an_iterator_from_read']//*[@class='notable-traits']"
+assert-count: ("//*[@class='notable popover']", 1)
+assert-false: "//*[@class='sidebar shown']"
+
+// Also check the focus handling for the help button.
+size: (1100, 600)
+reload:
+assert-count: ("//*[@class='notable popover']", 0)
+click: "//*[@id='method.create_an_iterator_from_read']//*[@class='notable-traits']"
+assert-count: ("//*[@class='notable popover']", 1)
+click: "#help-button a"
+assert-count: ("//*[@class='notable popover']", 0)
+assert-false: "#method\.create_an_iterator_from_read .notable-traits:focus"
diff --git a/src/test/rustdoc-gui/pocket-menu.goml b/src/test/rustdoc-gui/pocket-menu.goml
index fb63ea62a48..c3649dc7bda 100644
--- a/src/test/rustdoc-gui/pocket-menu.goml
+++ b/src/test/rustdoc-gui/pocket-menu.goml
@@ -75,3 +75,24 @@ assert-css: (
 )
 compare-elements-css: ("#help-button .popover", "#help-button .top", ["border-color"])
 compare-elements-css: ("#help-button .popover", "#help-button .bottom", ["border-color"])
+
+// Opening the mobile sidebar should close the settings popover.
+size: (650, 600)
+click: "#settings-menu a"
+assert-css: ("#settings-menu .popover", {"display": "block"})
+click: ".sidebar-menu-toggle"
+assert: "//*[@class='sidebar shown']"
+assert-css: ("#settings-menu .popover", {"display": "none"})
+// Opening the settings popover should close the sidebar.
+click: "#settings-menu a"
+assert-css: ("#settings-menu .popover", {"display": "block"})
+assert-false: "//*[@class='sidebar shown']"
+
+// Opening the settings popover at start (which async loads stuff) should also close.
+reload:
+click: ".sidebar-menu-toggle"
+assert: "//*[@class='sidebar shown']"
+assert-false: "#settings-menu .popover"
+click: "#settings-menu a"
+assert-false: "//*[@class='sidebar shown']"
+wait-for: "#settings-menu .popover"
diff --git a/src/test/rustdoc-gui/scrape-examples-fonts.goml b/src/test/rustdoc-gui/scrape-examples-fonts.goml
new file mode 100644
index 00000000000..b7d7f4ccb4a
--- /dev/null
+++ b/src/test/rustdoc-gui/scrape-examples-fonts.goml
@@ -0,0 +1,8 @@
+goto: "file://" + |DOC_PATH| + "/scrape_examples/fn.test_many.html"
+
+store-value: (font, '"Fira Sans", Arial, NanumBarunGothic, sans-serif')
+
+wait-for-css: (".scraped-example-title", {"font-family": |font|})
+wait-for-css: (".more-examples-toggle summary", {"font-family": |font|})
+wait-for-css: (".more-examples-toggle .hide-more", {"font-family": |font|})
+wait-for-css: (".example-links a", {"font-family": |font|})
diff --git a/src/test/rustdoc-gui/sidebar-mobile.goml b/src/test/rustdoc-gui/sidebar-mobile.goml
index 453873f1b81..38d01f7f612 100644
--- a/src/test/rustdoc-gui/sidebar-mobile.goml
+++ b/src/test/rustdoc-gui/sidebar-mobile.goml
@@ -32,6 +32,12 @@ assert-css: ("//nav[contains(@class, 'sidebar')]//h2/a[text()='In test_docs']/pa
 click: "body"
 assert-css: (".sidebar", {"display": "block", "left": "-1000px"})
 
+// Open the sidebar menu, and make sure pressing Escape closes it.
+click: ".sidebar-menu-toggle"
+assert-css: (".sidebar", {"left": "0px"})
+press-key: "Escape"
+assert-css: (".sidebar", {"display": "block", "left": "-1000px"})
+
 // Check that the topbar is visible
 assert-property: (".mobile-topbar", {"clientHeight": "45"})
 
@@ -48,23 +54,35 @@ compare-elements-position-near: (".block.keyword li:nth-child(1)", ".mobile-topb
 
 // Now checking the background color of the sidebar.
 show-text: true
-local-storage: {"rustdoc-use-system-theme": "false", "rustdoc-theme": "dark"}
-reload:
-
-// Open the sidebar menu.
-click: ".sidebar-menu-toggle"
-assert-css: (".sidebar", {"background-color": "rgb(80, 80, 80)", "color": "rgb(221, 221, 221)"})
-
-local-storage: {"rustdoc-use-system-theme": "false", "rustdoc-theme": "ayu"}
-reload:
 
-// Open the sidebar menu.
-click: ".sidebar-menu-toggle"
-assert-css: (".sidebar", {"background-color": "rgb(20, 25, 31)", "color": "rgb(197, 197, 197)"})
+define-function: (
+    "check-colors",
+    (theme, color, background),
+    [
+        ("local-storage", {"rustdoc-use-system-theme": "false", "rustdoc-theme": |theme|}),
+        ("reload"),
 
-local-storage: {"rustdoc-use-system-theme": "false", "rustdoc-theme": "light"}
-reload:
+        // Open the sidebar menu.
+        ("click", ".sidebar-menu-toggle"),
+        ("assert-css", (".sidebar", {
+            "background-color": |background|,
+            "color": |color|,
+        })),
+    ],
+)
 
-// Open the sidebar menu.
-click: ".sidebar-menu-toggle"
-assert-css: (".sidebar", {"background-color": "rgb(245, 245, 245)", "color": "rgb(0, 0, 0)"})
+call-function: ("check-colors", {
+    "theme": "ayu",
+    "color": "rgb(197, 197, 197)",
+    "background": "rgb(20, 25, 31)",
+})
+call-function: ("check-colors", {
+    "theme": "dark",
+    "color": "rgb(221, 221, 221)",
+    "background": "rgb(80, 80, 80)",
+})
+call-function: ("check-colors", {
+    "theme": "light",
+    "color": "rgb(0, 0, 0)",
+    "background": "rgb(245, 245, 245)",
+})
diff --git a/src/test/rustdoc-gui/src/scrape_examples/examples/check-many-1.rs b/src/test/rustdoc-gui/src/scrape_examples/examples/check-many-1.rs
new file mode 100644
index 00000000000..1d1bc5002aa
--- /dev/null
+++ b/src/test/rustdoc-gui/src/scrape_examples/examples/check-many-1.rs
@@ -0,0 +1,3 @@
+fn main() {
+    scrape_examples::test_many();
+}
diff --git a/src/test/rustdoc-gui/src/scrape_examples/examples/check-many-2.rs b/src/test/rustdoc-gui/src/scrape_examples/examples/check-many-2.rs
new file mode 100644
index 00000000000..1d1bc5002aa
--- /dev/null
+++ b/src/test/rustdoc-gui/src/scrape_examples/examples/check-many-2.rs
@@ -0,0 +1,3 @@
+fn main() {
+    scrape_examples::test_many();
+}
diff --git a/src/test/rustdoc-gui/src/scrape_examples/examples/check-many-3.rs b/src/test/rustdoc-gui/src/scrape_examples/examples/check-many-3.rs
new file mode 100644
index 00000000000..1d1bc5002aa
--- /dev/null
+++ b/src/test/rustdoc-gui/src/scrape_examples/examples/check-many-3.rs
@@ -0,0 +1,3 @@
+fn main() {
+    scrape_examples::test_many();
+}
diff --git a/src/test/rustdoc-gui/src/scrape_examples/examples/check-many-4.rs b/src/test/rustdoc-gui/src/scrape_examples/examples/check-many-4.rs
new file mode 100644
index 00000000000..1d1bc5002aa
--- /dev/null
+++ b/src/test/rustdoc-gui/src/scrape_examples/examples/check-many-4.rs
@@ -0,0 +1,3 @@
+fn main() {
+    scrape_examples::test_many();
+}
diff --git a/src/test/rustdoc-gui/src/scrape_examples/examples/check-many-5.rs b/src/test/rustdoc-gui/src/scrape_examples/examples/check-many-5.rs
new file mode 100644
index 00000000000..1d1bc5002aa
--- /dev/null
+++ b/src/test/rustdoc-gui/src/scrape_examples/examples/check-many-5.rs
@@ -0,0 +1,3 @@
+fn main() {
+    scrape_examples::test_many();
+}
diff --git a/src/test/rustdoc-gui/src/scrape_examples/examples/check-many-6.rs b/src/test/rustdoc-gui/src/scrape_examples/examples/check-many-6.rs
new file mode 100644
index 00000000000..1d1bc5002aa
--- /dev/null
+++ b/src/test/rustdoc-gui/src/scrape_examples/examples/check-many-6.rs
@@ -0,0 +1,3 @@
+fn main() {
+    scrape_examples::test_many();
+}
diff --git a/src/test/rustdoc-gui/src/scrape_examples/examples/check-many-7.rs b/src/test/rustdoc-gui/src/scrape_examples/examples/check-many-7.rs
new file mode 100644
index 00000000000..1d1bc5002aa
--- /dev/null
+++ b/src/test/rustdoc-gui/src/scrape_examples/examples/check-many-7.rs
@@ -0,0 +1,3 @@
+fn main() {
+    scrape_examples::test_many();
+}
diff --git a/src/test/rustdoc-gui/src/scrape_examples/src/lib.rs b/src/test/rustdoc-gui/src/scrape_examples/src/lib.rs
index 6412de2c03a..88b03cf2603 100644
--- a/src/test/rustdoc-gui/src/scrape_examples/src/lib.rs
+++ b/src/test/rustdoc-gui/src/scrape_examples/src/lib.rs
@@ -5,3 +5,5 @@
 /// test();
 /// ```
 pub fn test() {}
+
+pub fn test_many() {}
diff --git a/src/test/rustdoc-gui/toggle-docs.goml b/src/test/rustdoc-gui/toggle-docs.goml
index 8c9fd0a8866..b7d10723767 100644
--- a/src/test/rustdoc-gui/toggle-docs.goml
+++ b/src/test/rustdoc-gui/toggle-docs.goml
@@ -40,3 +40,32 @@ assert-attribute-false: (
 click: "#toggle-all-docs"
 wait-for-text: ("#toggle-all-docs", "[−]")
 assert-attribute: ("details.rustdoc-toggle", {"open": ""}, ALL)
+
+// Checking the toggles style.
+show-text: true
+define-function: (
+    "check-color",
+    (theme, filter),
+    [
+        // Setting the theme.
+	("local-storage", {"rustdoc-theme": |theme|, "rustdoc-use-system-theme": "false"}),
+        // We reload the page so the local storage settings are being used.
+        ("reload"),
+
+        ("assert-css", ("details.rustdoc-toggle > summary::before", {
+            "opacity": "0.5",
+            "filter": |filter|,
+        }, ALL)),
+        ("move-cursor-to", "details.rustdoc-toggle summary"),
+        ("assert-css", ("details.rustdoc-toggle > summary:hover::before", {
+            "opacity": "1",
+            "filter": |filter|,
+        })),
+        // moving the cursor somewhere else to not mess with next function calls.
+        ("move-cursor-to", ".search-input"),
+    ]
+)
+
+call-function: ("check-color", {"theme": "ayu", "filter": "invert(1)"})
+call-function: ("check-color", {"theme": "dark", "filter": "invert(1)"})
+call-function: ("check-color", {"theme": "light", "filter": "none"})
diff --git a/src/test/rustdoc-gui/where-whitespace.goml b/src/test/rustdoc-gui/where-whitespace.goml
index 776c8ec721e..41596a9bccc 100644
--- a/src/test/rustdoc-gui/where-whitespace.goml
+++ b/src/test/rustdoc-gui/where-whitespace.goml
@@ -5,13 +5,13 @@ show-text: true
 // line than "pub trait Whitespace<Idx>").
 compare-elements-position-false: (".item-decl code", ".where.fmt-newline", ("y"))
 // And that the code following it isn't on the same line either.
-compare-elements-position-false: (".item-decl .fnname", ".where.fmt-newline", ("y"))
+compare-elements-position-false: (".item-decl .fn", ".where.fmt-newline", ("y"))
 
 goto: "file://" + |DOC_PATH| + "/lib2/struct.WhereWhitespace.html"
 // We make the screen a bit wider to ensure that the trait impl is on one line.
 size: (915, 915)
 
-compare-elements-position-false: ("#method\.new .fnname", "#method\.new .where.fmt-newline", ("y"))
+compare-elements-position-false: ("#method\.new .fn", "#method\.new .where.fmt-newline", ("y"))
 // We ensure that both the trait name and the struct name are on the same line in
 // "impl<K, T> Whitespace<&K> for WhereWhitespace<T>".
 compare-elements-position: (
diff --git a/src/test/rustdoc-json/enums/auxiliary/color.rs b/src/test/rustdoc-json/enums/auxiliary/color.rs
new file mode 100644
index 00000000000..7188f793832
--- /dev/null
+++ b/src/test/rustdoc-json/enums/auxiliary/color.rs
@@ -0,0 +1,5 @@
+pub enum Color {
+    Red,
+    Green,
+    Blue,
+}
diff --git a/src/test/rustdoc-json/enums/doc_link_to_foreign_variant.rs b/src/test/rustdoc-json/enums/doc_link_to_foreign_variant.rs
new file mode 100644
index 00000000000..470b195a292
--- /dev/null
+++ b/src/test/rustdoc-json/enums/doc_link_to_foreign_variant.rs
@@ -0,0 +1,11 @@
+// aux-build: color.rs
+
+//! The purpose of this test it to have a link to [a foreign variant](Red).
+
+extern crate color;
+use color::Color::Red;
+
+// @set red = "$.index[*][?(@.inner.is_crate == true)].links.Red"
+
+// @!has "$.index[*][?(@.name == 'Red')]"
+// @!has "$.index[*][?(@.name == 'Color')]"
diff --git a/src/test/rustdoc-json/enums/use_variant_foreign.rs b/src/test/rustdoc-json/enums/use_variant_foreign.rs
new file mode 100644
index 00000000000..11bb6ce1f3a
--- /dev/null
+++ b/src/test/rustdoc-json/enums/use_variant_foreign.rs
@@ -0,0 +1,9 @@
+// aux-build: color.rs
+
+extern crate color;
+
+// @is "$.index[*][?(@.inner.name == 'Red')].kind" '"import"'
+pub use color::Color::Red;
+
+// @!has "$.index[*][?(@.name == 'Red')]"
+// @!has "$.index[*][?(@.name == 'Color')]"
diff --git a/src/test/rustdoc-json/fns/pattern_arg.rs b/src/test/rustdoc-json/fns/pattern_arg.rs
new file mode 100644
index 00000000000..32b7da0fae4
--- /dev/null
+++ b/src/test/rustdoc-json/fns/pattern_arg.rs
@@ -0,0 +1,7 @@
+// @is "$.index[*][?(@.name=='fst')].inner.decl.inputs[0][0]" '"(x, _)"'
+pub fn fst<X, Y>((x, _): (X, Y)) -> X {
+    x
+}
+
+// @is "$.index[*][?(@.name=='drop_int')].inner.decl.inputs[0][0]" '"_"'
+pub fn drop_int(_: i32) {}
diff --git a/src/test/rustdoc-json/traits/uses_extern_trait.rs b/src/test/rustdoc-json/traits/uses_extern_trait.rs
index 430dd1543f5..a4add43c6a1 100644
--- a/src/test/rustdoc-json/traits/uses_extern_trait.rs
+++ b/src/test/rustdoc-json/traits/uses_extern_trait.rs
@@ -3,5 +3,10 @@ pub fn drop_default<T: core::default::Default>(_x: T) {}
 
 // FIXME(adotinthevoid): Theses shouldn't be here
 // @has "$.index[*][?(@.name=='Debug')]"
-// @set Debug_fmt = "$.index[*][?(@.name=='Debug')].inner.items[*]"
+
+// Debug may have several items. All we depend on here the that `fmt` is first. See
+// https://github.com/rust-lang/rust/pull/104525#issuecomment-1331087852 for why we
+// can't use [*].
+
+// @set Debug_fmt = "$.index[*][?(@.name=='Debug')].inner.items[0]"
 // @has "$.index[*][?(@.name=='fmt')].id" $Debug_fmt
diff --git a/src/test/rustdoc/anchors.no_method_anchor.html b/src/test/rustdoc/anchors.no_method_anchor.html
index 521fdcb7877..b9ec8bf4c09 100644
--- a/src/test/rustdoc/anchors.no_method_anchor.html
+++ b/src/test/rustdoc/anchors.no_method_anchor.html
@@ -1 +1 @@
-<section id="method.new" class="method has-srclink"><a class="srclink rightside" href="../src/foo/anchors.rs.html#48">source</a><h4 class="code-header">pub fn <a href="#method.new" class="fnname">new</a>() -&gt; Self</h4></section>
\ No newline at end of file
+<section id="method.new" class="method has-srclink"><a class="srclink rightside" href="../src/foo/anchors.rs.html#48">source</a><h4 class="code-header">pub fn <a href="#method.new" class="fn">new</a>() -&gt; Self</h4></section>
\ No newline at end of file
diff --git a/src/test/rustdoc/anchors.no_trait_method_anchor.html b/src/test/rustdoc/anchors.no_trait_method_anchor.html
index d7bd525ff0f..4308ddad412 100644
--- a/src/test/rustdoc/anchors.no_trait_method_anchor.html
+++ b/src/test/rustdoc/anchors.no_trait_method_anchor.html
@@ -1 +1 @@
-<section id="method.bar" class="method has-srclink"><a class="srclink rightside" href="../src/foo/anchors.rs.html#23">source</a><h4 class="code-header">fn <a href="#method.bar" class="fnname">bar</a>()</h4></section>
\ No newline at end of file
+<section id="method.bar" class="method has-srclink"><a class="srclink rightside" href="../src/foo/anchors.rs.html#23">source</a><h4 class="code-header">fn <a href="#method.bar" class="fn">bar</a>()</h4></section>
\ No newline at end of file
diff --git a/src/test/rustdoc/anchors.no_tymethod_anchor.html b/src/test/rustdoc/anchors.no_tymethod_anchor.html
index e668e5e4db1..91eed8a3742 100644
--- a/src/test/rustdoc/anchors.no_tymethod_anchor.html
+++ b/src/test/rustdoc/anchors.no_tymethod_anchor.html
@@ -1 +1 @@
-<section id="tymethod.foo" class="method has-srclink"><a class="srclink rightside" href="../src/foo/anchors.rs.html#20">source</a><h4 class="code-header">fn <a href="#tymethod.foo" class="fnname">foo</a>()</h4></section>
\ No newline at end of file
+<section id="tymethod.foo" class="method has-srclink"><a class="srclink rightside" href="../src/foo/anchors.rs.html#20">source</a><h4 class="code-header">fn <a href="#tymethod.foo" class="fn">foo</a>()</h4></section>
\ No newline at end of file
diff --git a/src/test/rustdoc/decl-trailing-whitespace.declaration.html b/src/test/rustdoc/decl-trailing-whitespace.declaration.html
index e60caaeff38..02b51b34461 100644
--- a/src/test/rustdoc/decl-trailing-whitespace.declaration.html
+++ b/src/test/rustdoc/decl-trailing-whitespace.declaration.html
@@ -1,7 +1,7 @@
 <code>pub trait Write {
-    fn <a href="#tymethod.poll_write" class="fnname">poll_write</a>(<br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;self: <a class="enum" href="{{channel}}/core/option/enum.Option.html" title="enum core::option::Option">Option</a>&lt;<a class="struct" href="{{channel}}/alloc/string/struct.String.html" title="struct alloc::string::String">String</a>&gt;,<br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;cx: &amp;mut <a class="enum" href="{{channel}}/core/option/enum.Option.html" title="enum core::option::Option">Option</a>&lt;<a class="struct" href="{{channel}}/alloc/string/struct.String.html" title="struct alloc::string::String">String</a>&gt;,<br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;buf: &amp;mut [<a class="primitive" href="{{channel}}/std/primitive.usize.html">usize</a>]<br />&#160;&#160;&#160;&#160;) -&gt; <a class="enum" href="{{channel}}/core/option/enum.Option.html" title="enum core::option::Option">Option</a>&lt;<a class="enum" href="{{channel}}/core/result/enum.Result.html" title="enum core::result::Result">Result</a>&lt;<a class="primitive" href="{{channel}}/std/primitive.usize.html">usize</a>, <a class="struct" href="struct.Error.html" title="struct foo::Error">Error</a>&gt;&gt;;
-<span class="item-spacer" />    fn <a href="#tymethod.poll_flush" class="fnname">poll_flush</a>(<br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;self: <a class="enum" href="{{channel}}/core/option/enum.Option.html" title="enum core::option::Option">Option</a>&lt;<a class="struct" href="{{channel}}/alloc/string/struct.String.html" title="struct alloc::string::String">String</a>&gt;,<br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;cx: &amp;mut <a class="enum" href="{{channel}}/core/option/enum.Option.html" title="enum core::option::Option">Option</a>&lt;<a class="struct" href="{{channel}}/alloc/string/struct.String.html" title="struct alloc::string::String">String</a>&gt;<br />&#160;&#160;&#160;&#160;) -&gt; <a class="enum" href="{{channel}}/core/option/enum.Option.html" title="enum core::option::Option">Option</a>&lt;<a class="enum" href="{{channel}}/core/result/enum.Result.html" title="enum core::result::Result">Result</a>&lt;<a class="primitive" href="{{channel}}/std/primitive.unit.html">()</a>, <a class="struct" href="struct.Error.html" title="struct foo::Error">Error</a>&gt;&gt;;
-<span class="item-spacer" />    fn <a href="#tymethod.poll_close" class="fnname">poll_close</a>(<br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;self: <a class="enum" href="{{channel}}/core/option/enum.Option.html" title="enum core::option::Option">Option</a>&lt;<a class="struct" href="{{channel}}/alloc/string/struct.String.html" title="struct alloc::string::String">String</a>&gt;,<br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;cx: &amp;mut <a class="enum" href="{{channel}}/core/option/enum.Option.html" title="enum core::option::Option">Option</a>&lt;<a class="struct" href="{{channel}}/alloc/string/struct.String.html" title="struct alloc::string::String">String</a>&gt;<br />&#160;&#160;&#160;&#160;) -&gt; <a class="enum" href="{{channel}}/core/option/enum.Option.html" title="enum core::option::Option">Option</a>&lt;<a class="enum" href="{{channel}}/core/result/enum.Result.html" title="enum core::result::Result">Result</a>&lt;<a class="primitive" href="{{channel}}/std/primitive.unit.html">()</a>, <a class="struct" href="struct.Error.html" title="struct foo::Error">Error</a>&gt;&gt;;
+    fn <a href="#tymethod.poll_write" class="fn">poll_write</a>(<br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;self: <a class="enum" href="{{channel}}/core/option/enum.Option.html" title="enum core::option::Option">Option</a>&lt;<a class="struct" href="{{channel}}/alloc/string/struct.String.html" title="struct alloc::string::String">String</a>&gt;,<br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;cx: &amp;mut <a class="enum" href="{{channel}}/core/option/enum.Option.html" title="enum core::option::Option">Option</a>&lt;<a class="struct" href="{{channel}}/alloc/string/struct.String.html" title="struct alloc::string::String">String</a>&gt;,<br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;buf: &amp;mut [<a class="primitive" href="{{channel}}/std/primitive.usize.html">usize</a>]<br />&#160;&#160;&#160;&#160;) -&gt; <a class="enum" href="{{channel}}/core/option/enum.Option.html" title="enum core::option::Option">Option</a>&lt;<a class="enum" href="{{channel}}/core/result/enum.Result.html" title="enum core::result::Result">Result</a>&lt;<a class="primitive" href="{{channel}}/std/primitive.usize.html">usize</a>, <a class="struct" href="struct.Error.html" title="struct foo::Error">Error</a>&gt;&gt;;
+<span class="item-spacer" />    fn <a href="#tymethod.poll_flush" class="fn">poll_flush</a>(<br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;self: <a class="enum" href="{{channel}}/core/option/enum.Option.html" title="enum core::option::Option">Option</a>&lt;<a class="struct" href="{{channel}}/alloc/string/struct.String.html" title="struct alloc::string::String">String</a>&gt;,<br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;cx: &amp;mut <a class="enum" href="{{channel}}/core/option/enum.Option.html" title="enum core::option::Option">Option</a>&lt;<a class="struct" href="{{channel}}/alloc/string/struct.String.html" title="struct alloc::string::String">String</a>&gt;<br />&#160;&#160;&#160;&#160;) -&gt; <a class="enum" href="{{channel}}/core/option/enum.Option.html" title="enum core::option::Option">Option</a>&lt;<a class="enum" href="{{channel}}/core/result/enum.Result.html" title="enum core::result::Result">Result</a>&lt;<a class="primitive" href="{{channel}}/std/primitive.unit.html">()</a>, <a class="struct" href="struct.Error.html" title="struct foo::Error">Error</a>&gt;&gt;;
+<span class="item-spacer" />    fn <a href="#tymethod.poll_close" class="fn">poll_close</a>(<br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;self: <a class="enum" href="{{channel}}/core/option/enum.Option.html" title="enum core::option::Option">Option</a>&lt;<a class="struct" href="{{channel}}/alloc/string/struct.String.html" title="struct alloc::string::String">String</a>&gt;,<br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;cx: &amp;mut <a class="enum" href="{{channel}}/core/option/enum.Option.html" title="enum core::option::Option">Option</a>&lt;<a class="struct" href="{{channel}}/alloc/string/struct.String.html" title="struct alloc::string::String">String</a>&gt;<br />&#160;&#160;&#160;&#160;) -&gt; <a class="enum" href="{{channel}}/core/option/enum.Option.html" title="enum core::option::Option">Option</a>&lt;<a class="enum" href="{{channel}}/core/result/enum.Result.html" title="enum core::result::Result">Result</a>&lt;<a class="primitive" href="{{channel}}/std/primitive.unit.html">()</a>, <a class="struct" href="struct.Error.html" title="struct foo::Error">Error</a>&gt;&gt;;
 
-    fn <a href="#method.poll_write_vectored" class="fnname">poll_write_vectored</a>(<br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;self: <a class="enum" href="{{channel}}/core/option/enum.Option.html" title="enum core::option::Option">Option</a>&lt;<a class="struct" href="{{channel}}/alloc/string/struct.String.html" title="struct alloc::string::String">String</a>&gt;,<br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;cx: &amp;mut <a class="enum" href="{{channel}}/core/option/enum.Option.html" title="enum core::option::Option">Option</a>&lt;<a class="struct" href="{{channel}}/alloc/string/struct.String.html" title="struct alloc::string::String">String</a>&gt;,<br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;bufs: &amp;[<a class="primitive" href="{{channel}}/std/primitive.usize.html">usize</a>]<br />&#160;&#160;&#160;&#160;) -&gt; <a class="enum" href="{{channel}}/core/option/enum.Option.html" title="enum core::option::Option">Option</a>&lt;<a class="enum" href="{{channel}}/core/result/enum.Result.html" title="enum core::result::Result">Result</a>&lt;<a class="primitive" href="{{channel}}/std/primitive.usize.html">usize</a>, <a class="struct" href="struct.Error.html" title="struct foo::Error">Error</a>&gt;&gt; { ... }
-}</code>
+    fn <a href="#method.poll_write_vectored" class="fn">poll_write_vectored</a>(<br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;self: <a class="enum" href="{{channel}}/core/option/enum.Option.html" title="enum core::option::Option">Option</a>&lt;<a class="struct" href="{{channel}}/alloc/string/struct.String.html" title="struct alloc::string::String">String</a>&gt;,<br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;cx: &amp;mut <a class="enum" href="{{channel}}/core/option/enum.Option.html" title="enum core::option::Option">Option</a>&lt;<a class="struct" href="{{channel}}/alloc/string/struct.String.html" title="struct alloc::string::String">String</a>&gt;,<br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;bufs: &amp;[<a class="primitive" href="{{channel}}/std/primitive.usize.html">usize</a>]<br />&#160;&#160;&#160;&#160;) -&gt; <a class="enum" href="{{channel}}/core/option/enum.Option.html" title="enum core::option::Option">Option</a>&lt;<a class="enum" href="{{channel}}/core/result/enum.Result.html" title="enum core::result::Result">Result</a>&lt;<a class="primitive" href="{{channel}}/std/primitive.usize.html">usize</a>, <a class="struct" href="struct.Error.html" title="struct foo::Error">Error</a>&gt;&gt; { ... }
+}</code>
\ No newline at end of file
diff --git a/src/test/rustdoc/deref-to-primitive.rs b/src/test/rustdoc/deref-to-primitive.rs
new file mode 100644
index 00000000000..527de780d48
--- /dev/null
+++ b/src/test/rustdoc/deref-to-primitive.rs
@@ -0,0 +1,15 @@
+#![crate_name = "foo"]
+
+// @has 'foo/struct.Foo.html'
+// @has - '//*[@id="deref-methods-i32"]' 'Methods from Deref<Target = i32>'
+// @has - '//*[@id="deref-methods-i32-1"]//*[@id="associatedconstant.BITS"]/h4' \
+//        'pub const BITS: u32 = 32u32'
+pub struct Foo(i32);
+
+impl std::ops::Deref for Foo {
+    type Target = i32;
+
+    fn deref(&self) -> &Self::Target {
+        &self.0
+    }
+}
diff --git a/src/test/rustdoc/extern-default-method.no_href_on_anchor.html b/src/test/rustdoc/extern-default-method.no_href_on_anchor.html
index dab0a649529..ef14836ccb8 100644
--- a/src/test/rustdoc/extern-default-method.no_href_on_anchor.html
+++ b/src/test/rustdoc/extern-default-method.no_href_on_anchor.html
@@ -1 +1 @@
-<a class="fnname">provided</a>(&amp;self)
\ No newline at end of file
+<a class="fn">provided</a>(&amp;self)
\ No newline at end of file
diff --git a/src/test/rustdoc/extern-default-method.rs b/src/test/rustdoc/extern-default-method.rs
index 8139f5b2619..fc28b230a5f 100644
--- a/src/test/rustdoc/extern-default-method.rs
+++ b/src/test/rustdoc/extern-default-method.rs
@@ -11,13 +11,13 @@ extern crate rustdoc_extern_default_method as ext;
 // However, the method in the trait impl should *not* have a link (an `href` attribute) to
 // its corresponding item in the trait declaration since it would otherwise be broken.
 //
-// In older versions of rustdoc, the impl item (`a[@class="fnname"]`) used to link to
+// In older versions of rustdoc, the impl item (`a[@class="fn"]`) used to link to
 // `#method.provided` – i.e. "to itself". Put in quotes since that was actually incorrect in
 // general: If the type `Struct` also had an inherent method called `provided`, the impl item
 // would link to that one even though those two methods are distinct items!
 
 // @count extern_default_method/struct.Struct.html '//*[@id="method.provided"]' 1
-// @count extern_default_method/struct.Struct.html '//*[@id="method.provided"]//a[@class="fnname"]' 1
-// @snapshot no_href_on_anchor - '//*[@id="method.provided"]//a[@class="fnname"]'
+// @count extern_default_method/struct.Struct.html '//*[@id="method.provided"]//a[@class="fn"]' 1
+// @snapshot no_href_on_anchor - '//*[@id="method.provided"]//a[@class="fn"]'
 // @has extern_default_method/struct.Struct.html '//*[@id="method.provided"]//a[@class="anchor"]/@href' #method.provided
 pub use ext::Struct;
diff --git a/src/test/rustdoc/foreigntype.rs b/src/test/rustdoc/foreigntype.rs
index 891cdd5fed7..29f9c2926e9 100644
--- a/src/test/rustdoc/foreigntype.rs
+++ b/src/test/rustdoc/foreigntype.rs
@@ -6,7 +6,7 @@ extern "C" {
 }
 
 impl ExtType {
-    // @has - '//a[@class="fnname"]' 'do_something'
+    // @has - '//a[@class="fn"]' 'do_something'
     pub fn do_something(&self) {}
 }
 
diff --git a/src/test/rustdoc/inline_cross/issue-24183.method_no_where_self_sized.html b/src/test/rustdoc/inline_cross/issue-24183.method_no_where_self_sized.html
index 6955a961499..f3c1c045202 100644
--- a/src/test/rustdoc/inline_cross/issue-24183.method_no_where_self_sized.html
+++ b/src/test/rustdoc/inline_cross/issue-24183.method_no_where_self_sized.html
@@ -1 +1 @@
-<h4 class="code-header">fn <a href="#method.touch" class="fnname">touch</a>(&amp;self)</h4>
\ No newline at end of file
+<h4 class="code-header">fn <a href="#method.touch" class="fn">touch</a>(&amp;self)</h4>
\ No newline at end of file
diff --git a/src/test/rustdoc/trait-impl-items-links-and-anchors.rs b/src/test/rustdoc/trait-impl-items-links-and-anchors.rs
index fba594c3827..a125fa03679 100644
--- a/src/test/rustdoc/trait-impl-items-links-and-anchors.rs
+++ b/src/test/rustdoc/trait-impl-items-links-and-anchors.rs
@@ -13,10 +13,10 @@ impl MyTrait for String {
     // @has trait_impl_items_links_and_anchors/trait.MyTrait.html '//*[@id="associatedconstant.VALUE-1"]//a[@class="constant"]/@href' #associatedconstant.VALUE
     // @has trait_impl_items_links_and_anchors/trait.MyTrait.html '//*[@id="associatedconstant.VALUE-1"]//a[@class="anchor"]/@href' #associatedconstant.VALUE-1
     const VALUE: u32 = 5;
-    // @has trait_impl_items_links_and_anchors/trait.MyTrait.html '//*[@id="method.trait_function"]//a[@class="fnname"]/@href' #tymethod.trait_function
+    // @has trait_impl_items_links_and_anchors/trait.MyTrait.html '//*[@id="method.trait_function"]//a[@class="fn"]/@href' #tymethod.trait_function
     // @has trait_impl_items_links_and_anchors/trait.MyTrait.html '//*[@id="method.trait_function"]//a[@class="anchor"]/@href' #method.trait_function
     fn trait_function(&self) {}
-    // @has trait_impl_items_links_and_anchors/trait.MyTrait.html '//*[@id="method.defaulted_override-1"]//a[@class="fnname"]/@href' #method.defaulted_override
+    // @has trait_impl_items_links_and_anchors/trait.MyTrait.html '//*[@id="method.defaulted_override-1"]//a[@class="fn"]/@href' #method.defaulted_override
     // @has trait_impl_items_links_and_anchors/trait.MyTrait.html '//*[@id="method.defaulted_override-1"]//a[@class="anchor"]/@href' #method.defaulted_override-1
     fn defaulted_override(&self) {}
 }
@@ -28,10 +28,10 @@ impl MyTrait for Vec<u8> {
     // @has trait_impl_items_links_and_anchors/trait.MyTrait.html '//*[@id="associatedconstant.VALUE-2"]//a[@class="constant"]/@href' #associatedconstant.VALUE
     // @has trait_impl_items_links_and_anchors/trait.MyTrait.html '//*[@id="associatedconstant.VALUE-2"]//a[@class="anchor"]/@href' #associatedconstant.VALUE-2
     const VALUE: u32 = 5;
-    // @has trait_impl_items_links_and_anchors/trait.MyTrait.html '//*[@id="method.trait_function"]//a[@class="fnname"]/@href' #tymethod.trait_function
+    // @has trait_impl_items_links_and_anchors/trait.MyTrait.html '//*[@id="method.trait_function"]//a[@class="fn"]/@href' #tymethod.trait_function
     // @has trait_impl_items_links_and_anchors/trait.MyTrait.html '//*[@id="method.trait_function-1"]//a[@class="anchor"]/@href' #method.trait_function-1
     fn trait_function(&self) {}
-    // @has trait_impl_items_links_and_anchors/trait.MyTrait.html '//*[@id="method.defaulted_override-2"]//a[@class="fnname"]/@href' #method.defaulted_override
+    // @has trait_impl_items_links_and_anchors/trait.MyTrait.html '//*[@id="method.defaulted_override-2"]//a[@class="fn"]/@href' #method.defaulted_override
     // @has trait_impl_items_links_and_anchors/trait.MyTrait.html '//*[@id="method.defaulted_override-2"]//a[@class="anchor"]/@href' #method.defaulted_override-2
     fn defaulted_override(&self) {}
 }
@@ -45,13 +45,13 @@ impl MyTrait for MyStruct {
     // @has trait_impl_items_links_and_anchors/struct.MyStruct.html '//*[@id="associatedconstant.VALUE"]//a[@class="constant"]/@href' trait.MyTrait.html#associatedconstant.VALUE
     // @has trait_impl_items_links_and_anchors/struct.MyStruct.html '//*[@id="associatedconstant.VALUE"]//a[@class="anchor"]/@href' #associatedconstant.VALUE
     const VALUE: u32 = 20;
-    // @has trait_impl_items_links_and_anchors/struct.MyStruct.html '//*[@id="method.trait_function"]//a[@class="fnname"]/@href' trait.MyTrait.html#tymethod.trait_function
+    // @has trait_impl_items_links_and_anchors/struct.MyStruct.html '//*[@id="method.trait_function"]//a[@class="fn"]/@href' trait.MyTrait.html#tymethod.trait_function
     // @has trait_impl_items_links_and_anchors/struct.MyStruct.html '//*[@id="method.trait_function"]//a[@class="anchor"]/@href' #method.trait_function
     fn trait_function(&self) {}
-    // @has trait_impl_items_links_and_anchors/struct.MyStruct.html '//*[@id="method.defaulted_override"]//a[@class="fnname"]/@href' trait.MyTrait.html#method.defaulted_override
+    // @has trait_impl_items_links_and_anchors/struct.MyStruct.html '//*[@id="method.defaulted_override"]//a[@class="fn"]/@href' trait.MyTrait.html#method.defaulted_override
     // @has trait_impl_items_links_and_anchors/struct.MyStruct.html '//*[@id="method.defaulted_override"]//a[@class="anchor"]/@href' #method.defaulted_override
     fn defaulted_override(&self) {}
-    // @has trait_impl_items_links_and_anchors/struct.MyStruct.html '//*[@id="method.defaulted"]//a[@class="fnname"]/@href' trait.MyTrait.html#method.defaulted
+    // @has trait_impl_items_links_and_anchors/struct.MyStruct.html '//*[@id="method.defaulted"]//a[@class="fn"]/@href' trait.MyTrait.html#method.defaulted
     // @has trait_impl_items_links_and_anchors/struct.MyStruct.html '//*[@id="method.defaulted"]//a[@class="anchor"]/@href' #method.defaulted
 }
 
diff --git a/src/test/rustdoc/where.SWhere_TraitWhere_item-decl.html b/src/test/rustdoc/where.SWhere_TraitWhere_item-decl.html
index 24ab77703d1..d5d6c556d80 100644
--- a/src/test/rustdoc/where.SWhere_TraitWhere_item-decl.html
+++ b/src/test/rustdoc/where.SWhere_TraitWhere_item-decl.html
@@ -1,8 +1,8 @@
 <div class="item-decl"><pre class="rust trait"><code>pub trait TraitWhere {
     type <a href="#associatedtype.Item" class="associatedtype">Item</a>&lt;'a&gt;<br />&#160;&#160;&#160;&#160;<span class="where">where<br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;Self: 'a</span>;
 
-    fn <a href="#method.func" class="fnname">func</a>(self)<br />&#160;&#160;&#160;&#160;<span class="where">where<br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;Self: <a class="trait" href="{{channel}}/core/marker/trait.Sized.html" title="trait core::marker::Sized">Sized</a></span>,
+    fn <a href="#method.func" class="fn">func</a>(self)<br />&#160;&#160;&#160;&#160;<span class="where">where<br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;Self: <a class="trait" href="{{channel}}/core/marker/trait.Sized.html" title="trait core::marker::Sized">Sized</a></span>,
     { ... }
-<span class="item-spacer" />    fn <a href="#method.lines" class="fnname">lines</a>(self) -&gt; <a class="struct" href="{{channel}}/std/io/struct.Lines.html" title="struct std::io::Lines">Lines</a>&lt;Self&gt;<br />&#160;&#160;&#160;&#160;<span class="where">where<br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;Self: <a class="trait" href="{{channel}}/core/marker/trait.Sized.html" title="trait core::marker::Sized">Sized</a></span>,
+<span class="item-spacer" />    fn <a href="#method.lines" class="fn">lines</a>(self) -&gt; <a class="struct" href="{{channel}}/std/io/struct.Lines.html" title="struct std::io::Lines">Lines</a>&lt;Self&gt;<br />&#160;&#160;&#160;&#160;<span class="where">where<br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;Self: <a class="trait" href="{{channel}}/core/marker/trait.Sized.html" title="trait core::marker::Sized">Sized</a></span>,
     { ... }
 }</code></pre></div>
\ No newline at end of file
diff --git a/src/test/rustdoc/whitespace-after-where-clause.trait.html b/src/test/rustdoc/whitespace-after-where-clause.trait.html
index 16b55823703..50cfe362328 100644
--- a/src/test/rustdoc/whitespace-after-where-clause.trait.html
+++ b/src/test/rustdoc/whitespace-after-where-clause.trait.html
@@ -1,6 +1,6 @@
 <div class="item-decl"><pre class="rust trait"><code>pub trait ToOwned&lt;T&gt;<span class="where fmt-newline">where<br />&#160;&#160;&#160;&#160;T: <a class="trait" href="{{channel}}/core/clone/trait.Clone.html" title="trait core::clone::Clone">Clone</a>,</span>{
     type <a href="#associatedtype.Owned" class="associatedtype">Owned</a>;
 
-    fn <a href="#tymethod.to_owned" class="fnname">to_owned</a>(&amp;self) -&gt; Self::<a class="associatedtype" href="trait.ToOwned.html#associatedtype.Owned" title="type foo::ToOwned::Owned">Owned</a>;
-<span class="item-spacer" />    fn <a href="#tymethod.whatever" class="fnname">whatever</a>(&amp;self) -&gt; T;
+    fn <a href="#tymethod.to_owned" class="fn">to_owned</a>(&amp;self) -&gt; Self::<a class="associatedtype" href="trait.ToOwned.html#associatedtype.Owned" title="type foo::ToOwned::Owned">Owned</a>;
+<span class="item-spacer" />    fn <a href="#tymethod.whatever" class="fn">whatever</a>(&amp;self) -&gt; T;
 }</code></pre></div>
\ No newline at end of file
diff --git a/src/test/rustdoc/whitespace-after-where-clause.trait2.html b/src/test/rustdoc/whitespace-after-where-clause.trait2.html
index eeca6e1f500..21eb89b7501 100644
--- a/src/test/rustdoc/whitespace-after-where-clause.trait2.html
+++ b/src/test/rustdoc/whitespace-after-where-clause.trait2.html
@@ -1,6 +1,6 @@
 <div class="item-decl"><pre class="rust trait"><code>pub trait ToOwned2&lt;T:&#160;<a class="trait" href="{{channel}}/core/clone/trait.Clone.html" title="trait core::clone::Clone">Clone</a>&gt; {
     type <a href="#associatedtype.Owned" class="associatedtype">Owned</a>;
 
-    fn <a href="#tymethod.to_owned" class="fnname">to_owned</a>(&amp;self) -&gt; Self::<a class="associatedtype" href="trait.ToOwned2.html#associatedtype.Owned" title="type foo::ToOwned2::Owned">Owned</a>;
-<span class="item-spacer" />    fn <a href="#tymethod.whatever" class="fnname">whatever</a>(&amp;self) -&gt; T;
+    fn <a href="#tymethod.to_owned" class="fn">to_owned</a>(&amp;self) -&gt; Self::<a class="associatedtype" href="trait.ToOwned2.html#associatedtype.Owned" title="type foo::ToOwned2::Owned">Owned</a>;
+<span class="item-spacer" />    fn <a href="#tymethod.whatever" class="fn">whatever</a>(&amp;self) -&gt; T;
 }</code></pre></div>
\ No newline at end of file
diff --git a/src/test/ui/alloc-error/alloc-error-handler-bad-signature-1.stderr b/src/test/ui/alloc-error/alloc-error-handler-bad-signature-1.stderr
index d0911fa3985..dd3665f22ac 100644
--- a/src/test/ui/alloc-error/alloc-error-handler-bad-signature-1.stderr
+++ b/src/test/ui/alloc-error/alloc-error-handler-bad-signature-1.stderr
@@ -4,8 +4,8 @@ error[E0308]: mismatched types
 LL |    #[alloc_error_handler]
    |    ---------------------- in this procedural macro expansion
 LL |    fn oom(
-   |   _^
-   |  |_|
+   |  __^
+   | | _|
    | ||
 LL | ||     info: &Layout,
 LL | || ) -> ()
@@ -30,8 +30,8 @@ error[E0308]: mismatched types
 LL |    #[alloc_error_handler]
    |    ---------------------- in this procedural macro expansion
 LL |    fn oom(
-   |   _^
-   |  |_|
+   |  __^
+   | | _|
    | ||
 LL | ||     info: &Layout,
 LL | || ) -> ()
diff --git a/src/test/ui/alloc-error/alloc-error-handler-bad-signature-2.stderr b/src/test/ui/alloc-error/alloc-error-handler-bad-signature-2.stderr
index 5777279855d..adb652fe616 100644
--- a/src/test/ui/alloc-error/alloc-error-handler-bad-signature-2.stderr
+++ b/src/test/ui/alloc-error/alloc-error-handler-bad-signature-2.stderr
@@ -4,14 +4,12 @@ error[E0308]: mismatched types
 LL |    #[alloc_error_handler]
    |    ---------------------- in this procedural macro expansion
 LL |    fn oom(
-   |   _^
-   |  |_|
+   |  __^
+   | | _|
    | ||
 LL | ||     info: Layout,
 LL | || ) {
-   | || -
-   | ||_|
-   | |  arguments to this function are incorrect
+   | ||_- arguments to this function are incorrect
 LL | |      loop {}
 LL | |  }
    | |__^ expected struct `Layout`, found struct `core::alloc::Layout`
@@ -42,14 +40,12 @@ error[E0308]: mismatched types
 LL |    #[alloc_error_handler]
    |    ---------------------- in this procedural macro expansion
 LL |    fn oom(
-   |   _^
-   |  |_|
+   |  __^
+   | | _|
    | ||
 LL | ||     info: Layout,
 LL | || ) {
-   | || ^
-   | ||_|
-   | |  expected `!`, found `()`
+   | ||_^ expected `!`, found `()`
 LL | |      loop {}
 LL | |  }
    | |__- expected `!` because of return type
diff --git a/src/test/ui/associated-consts/issue-93835.rs b/src/test/ui/associated-consts/issue-93835.rs
index 5c7b065983e..b2a437fcbfb 100644
--- a/src/test/ui/associated-consts/issue-93835.rs
+++ b/src/test/ui/associated-consts/issue-93835.rs
@@ -1,9 +1,11 @@
+#![feature(type_ascription)]
+
 fn e() {
-    p:a<p:p<e=6>>
-    //~^ ERROR comparison operators
+    type_ascribe!(p, a<p:p<e=6>>);
+    //~^ ERROR cannot find type `a` in this scope
     //~| ERROR cannot find value
     //~| ERROR associated const equality
-    //~| ERROR associated const equality
+    //~| ERROR cannot find trait `p` in this scope
     //~| ERROR associated type bounds
 }
 
diff --git a/src/test/ui/associated-consts/issue-93835.stderr b/src/test/ui/associated-consts/issue-93835.stderr
index 0406a16a397..be0573a1301 100644
--- a/src/test/ui/associated-consts/issue-93835.stderr
+++ b/src/test/ui/associated-consts/issue-93835.stderr
@@ -1,65 +1,40 @@
-error: comparison operators cannot be chained
-  --> $DIR/issue-93835.rs:2:8
-   |
-LL | fn e() {
-   |        - while parsing this struct
-LL |     p:a<p:p<e=6>>
-   |        ^        ^
-   |
-   = help: use `::<...>` instead of `<...>` to specify lifetime, type, or const arguments
-   = help: or use `(...)` if you meant to specify fn arguments
-
 error[E0425]: cannot find value `p` in this scope
-  --> $DIR/issue-93835.rs:2:5
-   |
-LL |     p:a<p:p<e=6>>
-   |     ^ not found in this scope
-   |
-help: you might have meant to write a `struct` literal
-   |
-LL ~ fn e() { SomeStruct {
-LL |     p:a<p:p<e=6>>
- ...
-LL |
-LL ~ }}
+  --> $DIR/issue-93835.rs:4:19
    |
-help: maybe you meant to write a path separator here
-   |
-LL |     p::a<p:p<e=6>>
-   |      ~~
-help: maybe you meant to write an assignment here
-   |
-LL |     let p:a<p:p<e=6>>
-   |     ~~~~~
+LL |     type_ascribe!(p, a<p:p<e=6>>);
+   |                   ^ not found in this scope
 
-error[E0658]: associated const equality is incomplete
-  --> $DIR/issue-93835.rs:2:13
+error[E0412]: cannot find type `a` in this scope
+  --> $DIR/issue-93835.rs:4:22
    |
-LL |     p:a<p:p<e=6>>
-   |             ^^^
+LL |     type_ascribe!(p, a<p:p<e=6>>);
+   |                      ^ not found in this scope
+
+error[E0405]: cannot find trait `p` in this scope
+  --> $DIR/issue-93835.rs:4:26
    |
-   = note: see issue #92827 <https://github.com/rust-lang/rust/issues/92827> for more information
-   = help: add `#![feature(associated_const_equality)]` to the crate attributes to enable
+LL |     type_ascribe!(p, a<p:p<e=6>>);
+   |                          ^ not found in this scope
 
 error[E0658]: associated const equality is incomplete
-  --> $DIR/issue-93835.rs:2:13
+  --> $DIR/issue-93835.rs:4:28
    |
-LL |     p:a<p:p<e=6>>
-   |             ^^^
+LL |     type_ascribe!(p, a<p:p<e=6>>);
+   |                            ^^^
    |
    = note: see issue #92827 <https://github.com/rust-lang/rust/issues/92827> for more information
    = help: add `#![feature(associated_const_equality)]` to the crate attributes to enable
 
 error[E0658]: associated type bounds are unstable
-  --> $DIR/issue-93835.rs:2:9
+  --> $DIR/issue-93835.rs:4:24
    |
-LL |     p:a<p:p<e=6>>
-   |         ^^^^^^^^
+LL |     type_ascribe!(p, a<p:p<e=6>>);
+   |                        ^^^^^^^^
    |
    = note: see issue #52662 <https://github.com/rust-lang/rust/issues/52662> for more information
    = help: add `#![feature(associated_type_bounds)]` to the crate attributes to enable
 
 error: aborting due to 5 previous errors
 
-Some errors have detailed explanations: E0425, E0658.
-For more information about an error, try `rustc --explain E0425`.
+Some errors have detailed explanations: E0405, E0412, E0425, E0658.
+For more information about an error, try `rustc --explain E0405`.
diff --git a/src/test/ui/async-await/async-block-control-flow-static-semantics.rs b/src/test/ui/async-await/async-block-control-flow-static-semantics.rs
index 446212ca767..bc9d127931d 100644
--- a/src/test/ui/async-await/async-block-control-flow-static-semantics.rs
+++ b/src/test/ui/async-await/async-block-control-flow-static-semantics.rs
@@ -15,7 +15,7 @@ fn return_targets_async_block_not_fn() -> u8 {
         return 0u8;
     };
     let _: &dyn Future<Output = ()> = &block;
-    //~^ ERROR expected `impl Future<Output = u8>` to be a future that resolves to `()`, but it resolves to `u8`
+    //~^ ERROR to be a future that resolves to `()`, but it resolves to `u8`
 }
 
 async fn return_targets_async_block_not_async_fn() -> u8 {
@@ -24,7 +24,7 @@ async fn return_targets_async_block_not_async_fn() -> u8 {
         return 0u8;
     };
     let _: &dyn Future<Output = ()> = &block;
-    //~^ ERROR expected `impl Future<Output = u8>` to be a future that resolves to `()`, but it resolves to `u8`
+    //~^ ERROR to be a future that resolves to `()`, but it resolves to `u8`
 }
 
 fn no_break_in_async_block() {
diff --git a/src/test/ui/async-await/async-block-control-flow-static-semantics.stderr b/src/test/ui/async-await/async-block-control-flow-static-semantics.stderr
index b8ca64fae83..c4487eb840a 100644
--- a/src/test/ui/async-await/async-block-control-flow-static-semantics.stderr
+++ b/src/test/ui/async-await/async-block-control-flow-static-semantics.stderr
@@ -29,13 +29,13 @@ LL | |
 LL | | }
    | |_^ expected `u8`, found `()`
 
-error[E0271]: expected `impl Future<Output = u8>` to be a future that resolves to `()`, but it resolves to `u8`
+error[E0271]: expected `[async block@$DIR/async-block-control-flow-static-semantics.rs:23:17: 25:6]` to be a future that resolves to `()`, but it resolves to `u8`
   --> $DIR/async-block-control-flow-static-semantics.rs:26:39
    |
 LL |     let _: &dyn Future<Output = ()> = &block;
    |                                       ^^^^^^ expected `()`, found `u8`
    |
-   = note: required for the cast from `impl Future<Output = u8>` to the object type `dyn Future<Output = ()>`
+   = note: required for the cast from `[async block@$DIR/async-block-control-flow-static-semantics.rs:23:17: 25:6]` to the object type `dyn Future<Output = ()>`
 
 error[E0308]: mismatched types
   --> $DIR/async-block-control-flow-static-semantics.rs:12:43
@@ -45,13 +45,13 @@ LL | fn return_targets_async_block_not_fn() -> u8 {
    |    |
    |    implicitly returns `()` as its body has no tail or `return` expression
 
-error[E0271]: expected `impl Future<Output = u8>` to be a future that resolves to `()`, but it resolves to `u8`
+error[E0271]: expected `[async block@$DIR/async-block-control-flow-static-semantics.rs:14:17: 16:6]` to be a future that resolves to `()`, but it resolves to `u8`
   --> $DIR/async-block-control-flow-static-semantics.rs:17:39
    |
 LL |     let _: &dyn Future<Output = ()> = &block;
    |                                       ^^^^^^ expected `()`, found `u8`
    |
-   = note: required for the cast from `impl Future<Output = u8>` to the object type `dyn Future<Output = ()>`
+   = note: required for the cast from `[async block@$DIR/async-block-control-flow-static-semantics.rs:14:17: 16:6]` to the object type `dyn Future<Output = ()>`
 
 error[E0308]: mismatched types
   --> $DIR/async-block-control-flow-static-semantics.rs:49:44
diff --git a/src/test/ui/async-await/generator-desc.stderr b/src/test/ui/async-await/generator-desc.stderr
index 774c97966b1..1686153acf9 100644
--- a/src/test/ui/async-await/generator-desc.stderr
+++ b/src/test/ui/async-await/generator-desc.stderr
@@ -8,8 +8,8 @@ LL |     fun(async {}, async {});
    |         |         arguments to this function are incorrect
    |         the expected `async` block
    |
-   = note: expected `async` block `impl Future<Output = ()>` (`async` block)
-              found `async` block `impl Future<Output = ()>` (`async` block)
+   = note: expected `async` block `[async block@$DIR/generator-desc.rs:10:9: 10:17]`
+              found `async` block `[async block@$DIR/generator-desc.rs:10:19: 10:27]`
 note: function defined here
   --> $SRC_DIR/core/src/future/mod.rs:LL:COL
    |
@@ -53,8 +53,8 @@ LL |     fun((async || {})(), (async || {})());
    |     |             the expected `async` closure body
    |     arguments to this function are incorrect
    |
-   = note: expected `async` closure body `impl Future<Output = ()>` (`async` closure body)
-              found `async` closure body `impl Future<Output = ()>` (`async` closure body)
+   = note: expected `async` closure body `[async closure body@$DIR/generator-desc.rs:14:19: 14:21]`
+              found `async` closure body `[async closure body@$DIR/generator-desc.rs:14:36: 14:38]`
 note: function defined here
   --> $DIR/generator-desc.rs:8:4
    |
diff --git a/src/test/ui/async-await/issue-67252-unnamed-future.stderr b/src/test/ui/async-await/issue-67252-unnamed-future.stderr
index af99b608ca1..fcba4410ba9 100644
--- a/src/test/ui/async-await/issue-67252-unnamed-future.stderr
+++ b/src/test/ui/async-await/issue-67252-unnamed-future.stderr
@@ -8,7 +8,7 @@ LL | |         AFuture.await;
 LL | |     });
    | |_____^ future created by async block is not `Send`
    |
-   = help: within `impl Future<Output = ()>`, the trait `Send` is not implemented for `*mut ()`
+   = help: within `[async block@$DIR/issue-67252-unnamed-future.rs:18:11: 21:6]`, the trait `Send` is not implemented for `*mut ()`
 note: future is not `Send` as this value is used across an await
   --> $DIR/issue-67252-unnamed-future.rs:20:16
    |
diff --git a/src/test/ui/async-await/issue-86507.stderr b/src/test/ui/async-await/issue-86507.stderr
index 0e21dba980d..8c2c06da25c 100644
--- a/src/test/ui/async-await/issue-86507.stderr
+++ b/src/test/ui/async-await/issue-86507.stderr
@@ -13,7 +13,7 @@ note: captured value is not `Send` because `&` references cannot be sent unless
    |
 LL |                     let x = x;
    |                             ^ has type `&T` which is not `Send`, because `T` is not `Sync`
-   = note: required for the cast from `impl Future<Output = ()>` to the object type `dyn Future<Output = ()> + Send`
+   = note: required for the cast from `[async block@$DIR/issue-86507.rs:18:17: 20:18]` to the object type `dyn Future<Output = ()> + Send`
 help: consider further restricting this bound
    |
 LL |     fn bar<'me, 'async_trait, T: Send + std::marker::Sync>(x: &'me T)
diff --git a/src/test/ui/async-await/issues/issue-65436-raw-ptr-not-send.no_drop_tracking.stderr b/src/test/ui/async-await/issues/issue-65436-raw-ptr-not-send.no_drop_tracking.stderr
index a723503776b..ab196dca20c 100644
--- a/src/test/ui/async-await/issues/issue-65436-raw-ptr-not-send.no_drop_tracking.stderr
+++ b/src/test/ui/async-await/issues/issue-65436-raw-ptr-not-send.no_drop_tracking.stderr
@@ -8,7 +8,7 @@ LL | |         bar(Foo(std::ptr::null())).await;
 LL | |     })
    | |_____^ future created by async block is not `Send`
    |
-   = help: within `impl Future<Output = ()>`, the trait `Send` is not implemented for `*const u8`
+   = help: within `[async block@$DIR/issue-65436-raw-ptr-not-send.rs:16:17: 19:6]`, the trait `Send` is not implemented for `*const u8`
 note: future is not `Send` as this value is used across an await
   --> $DIR/issue-65436-raw-ptr-not-send.rs:18:35
    |
diff --git a/src/test/ui/chalkify/bugs/async.stderr b/src/test/ui/chalkify/bugs/async.stderr
index 6f22d2c593a..4804df13340 100644
--- a/src/test/ui/chalkify/bugs/async.stderr
+++ b/src/test/ui/chalkify/bugs/async.stderr
@@ -1,4 +1,4 @@
-error[E0277]: `impl Future<Output = u32>` is not a future
+error[E0277]: `[async fn body@$DIR/async.rs:7:29: 9:2]` is not a future
   --> $DIR/async.rs:7:29
    |
 LL |   async fn foo(x: u32) -> u32 {
@@ -7,18 +7,18 @@ LL | |     x
 LL | | }
    | | ^
    | | |
-   | |_`impl Future<Output = u32>` is not a future
+   | |_`[async fn body@$DIR/async.rs:7:29: 9:2]` is not a future
    |   required by a bound introduced by this call
    |
-   = help: the trait `Future` is not implemented for `impl Future<Output = u32>`
-   = note: impl Future<Output = u32> must be a future or must implement `IntoFuture` to be awaited
+   = help: the trait `Future` is not implemented for `[async fn body@$DIR/async.rs:7:29: 9:2]`
+   = note: [async fn body@$DIR/async.rs:7:29: 9:2] must be a future or must implement `IntoFuture` to be awaited
 note: required by a bound in `identity_future`
   --> $SRC_DIR/core/src/future/mod.rs:LL:COL
    |
 LL | pub const fn identity_future<O, Fut: Future<Output = O>>(f: Fut) -> Fut {
    |                                      ^^^^^^^^^^^^^^^^^^ required by this bound in `identity_future`
 
-error[E0277]: the size for values of type `<impl Future<Output = u32> as Future>::Output` cannot be known at compilation time
+error[E0277]: the size for values of type `<[async fn body@$DIR/async.rs:7:29: 9:2] as Future>::Output` cannot be known at compilation time
   --> $DIR/async.rs:7:29
    |
 LL |   async fn foo(x: u32) -> u32 {
@@ -27,23 +27,23 @@ LL | |     x
 LL | | }
    | |_^ doesn't have a size known at compile-time
    |
-   = help: the trait `Sized` is not implemented for `<impl Future<Output = u32> as Future>::Output`
+   = help: the trait `Sized` is not implemented for `<[async fn body@$DIR/async.rs:7:29: 9:2] as Future>::Output`
 note: required by a bound in `identity_future`
   --> $SRC_DIR/core/src/future/mod.rs:LL:COL
    |
 LL | pub const fn identity_future<O, Fut: Future<Output = O>>(f: Fut) -> Fut {
    |                              ^ required by this bound in `identity_future`
 
-error[E0277]: `impl Future<Output = u32>` is not a future
+error[E0277]: `[async fn body@$DIR/async.rs:7:29: 9:2]` is not a future
   --> $DIR/async.rs:7:25
    |
 LL | async fn foo(x: u32) -> u32 {
-   |                         ^^^ `impl Future<Output = u32>` is not a future
+   |                         ^^^ `[async fn body@$DIR/async.rs:7:29: 9:2]` is not a future
    |
-   = help: the trait `Future` is not implemented for `impl Future<Output = u32>`
-   = note: impl Future<Output = u32> must be a future or must implement `IntoFuture` to be awaited
+   = help: the trait `Future` is not implemented for `[async fn body@$DIR/async.rs:7:29: 9:2]`
+   = note: [async fn body@$DIR/async.rs:7:29: 9:2] must be a future or must implement `IntoFuture` to be awaited
 
-error[E0280]: the requirement `<impl Future<Output = u32> as Future>::Output == u32` is not satisfied
+error[E0280]: the requirement `<[async fn body@$DIR/async.rs:7:29: 9:2] as Future>::Output == u32` is not satisfied
   --> $DIR/async.rs:7:25
    |
 LL | async fn foo(x: u32) -> u32 {
diff --git a/src/test/ui/closures/issue-90871.rs b/src/test/ui/closures/issue-90871.rs
index 9c70bbc85ac..7ce061cd388 100644
--- a/src/test/ui/closures/issue-90871.rs
+++ b/src/test/ui/closures/issue-90871.rs
@@ -1,5 +1,7 @@
+#![feature(type_ascription)]
+
 fn main() {
-    2: n([u8; || 1])
+    type_ascribe!(2, n([u8; || 1]))
     //~^ ERROR cannot find type `n` in this scope
     //~| ERROR mismatched types
 }
diff --git a/src/test/ui/closures/issue-90871.stderr b/src/test/ui/closures/issue-90871.stderr
index 1e102cc9805..a482750fbd0 100644
--- a/src/test/ui/closures/issue-90871.stderr
+++ b/src/test/ui/closures/issue-90871.stderr
@@ -1,21 +1,26 @@
 error[E0412]: cannot find type `n` in this scope
-  --> $DIR/issue-90871.rs:2:8
+  --> $DIR/issue-90871.rs:4:22
    |
-LL |     2: n([u8; || 1])
-   |        ^ expecting a type here because of type ascription
+LL |     type_ascribe!(2, n([u8; || 1]))
+   |                      ^ help: a trait with a similar name exists: `Fn`
+   |
+  ::: $SRC_DIR/core/src/ops/function.rs:LL:COL
+   |
+LL | pub trait Fn<Args: Tuple>: FnMut<Args> {
+   | -------------------------------------- similarly named trait `Fn` defined here
 
 error[E0308]: mismatched types
-  --> $DIR/issue-90871.rs:2:15
+  --> $DIR/issue-90871.rs:4:29
    |
-LL |     2: n([u8; || 1])
-   |               ^^^^ expected `usize`, found closure
+LL |     type_ascribe!(2, n([u8; || 1]))
+   |                             ^^^^ expected `usize`, found closure
    |
    = note: expected type `usize`
-           found closure `[closure@$DIR/issue-90871.rs:2:15: 2:17]`
+           found closure `[closure@$DIR/issue-90871.rs:4:29: 4:31]`
 help: use parentheses to call this closure
    |
-LL |     2: n([u8; (|| 1)()])
-   |               +    +++
+LL |     type_ascribe!(2, n([u8; (|| 1)()]))
+   |                             +    +++
 
 error: aborting due to 2 previous errors
 
diff --git a/src/test/ui/coercion/coerce-expect-unsized-ascribed.rs b/src/test/ui/coercion/coerce-expect-unsized-ascribed.rs
index c139e823c2a..d7b11317ad5 100644
--- a/src/test/ui/coercion/coerce-expect-unsized-ascribed.rs
+++ b/src/test/ui/coercion/coerce-expect-unsized-ascribed.rs
@@ -6,27 +6,27 @@
 use std::fmt::Debug;
 
 pub fn main() {
-    let _ = box { [1, 2, 3] }: Box<[i32]>; //~ ERROR mismatched types
-    let _ = box if true { [1, 2, 3] } else { [1, 3, 4] }: Box<[i32]>; //~ ERROR mismatched types
-    let _ = box match true { true => [1, 2, 3], false => [1, 3, 4] }: Box<[i32]>;
+    let _ = type_ascribe!(box { [1, 2, 3] }, Box<[i32]>); //~ ERROR mismatched types
+    let _ = type_ascribe!(box if true { [1, 2, 3] } else { [1, 3, 4] }, Box<[i32]>); //~ ERROR mismatched types
+    let _ = type_ascribe!(box match true { true => [1, 2, 3], false => [1, 3, 4] }, Box<[i32]>);
     //~^ ERROR mismatched types
-    let _ = box { |x| (x as u8) }: Box<dyn Fn(i32) -> _>; //~ ERROR mismatched types
-    let _ = box if true { false } else { true }: Box<dyn Debug>; //~ ERROR mismatched types
-    let _ = box match true { true => 'a', false => 'b' }: Box<dyn Debug>; //~ ERROR mismatched types
+    let _ = type_ascribe!(box { |x| (x as u8) }, Box<dyn Fn(i32) -> _>); //~ ERROR mismatched types
+    let _ = type_ascribe!(box if true { false } else { true }, Box<dyn Debug>); //~ ERROR mismatched types
+    let _ = type_ascribe!(box match true { true => 'a', false => 'b' }, Box<dyn Debug>); //~ ERROR mismatched types
 
-    let _ = &{ [1, 2, 3] }: &[i32]; //~ ERROR mismatched types
-    let _ = &if true { [1, 2, 3] } else { [1, 3, 4] }: &[i32]; //~ ERROR mismatched types
-    let _ = &match true { true => [1, 2, 3], false => [1, 3, 4] }: &[i32];
+    let _ = type_ascribe!(&{ [1, 2, 3] }, &[i32]); //~ ERROR mismatched types
+    let _ = type_ascribe!(&if true { [1, 2, 3] } else { [1, 3, 4] }, &[i32]); //~ ERROR mismatched types
+    let _ = type_ascribe!(&match true { true => [1, 2, 3], false => [1, 3, 4] }, &[i32]);
     //~^ ERROR mismatched types
-    let _ = &{ |x| (x as u8) }: &dyn Fn(i32) -> _; //~ ERROR mismatched types
-    let _ = &if true { false } else { true }: &dyn Debug; //~ ERROR mismatched types
-    let _ = &match true { true => 'a', false => 'b' }: &dyn Debug; //~ ERROR mismatched types
+    let _ = type_ascribe!(&{ |x| (x as u8) }, &dyn Fn(i32) -> _); //~ ERROR mismatched types
+    let _ = type_ascribe!(&if true { false } else { true }, &dyn Debug); //~ ERROR mismatched types
+    let _ = type_ascribe!(&match true { true => 'a', false => 'b' }, &dyn Debug); //~ ERROR mismatched types
 
-    let _ = Box::new([1, 2, 3]): Box<[i32]>; //~ ERROR mismatched types
-    let _ = Box::new(|x| (x as u8)): Box<dyn Fn(i32) -> _>; //~ ERROR mismatched types
+    let _ = type_ascribe!(Box::new([1, 2, 3]), Box<[i32]>); //~ ERROR mismatched types
+    let _ = type_ascribe!(Box::new(|x| (x as u8)), Box<dyn Fn(i32) -> _>); //~ ERROR mismatched types
 
-    let _ = vec![
+    let _ = type_ascribe!(vec![
         Box::new(|x| (x as u8)),
         box |x| (x as i16 as u8),
-    ]: Vec<Box<dyn Fn(i32) -> _>>;
+    ], Vec<Box<dyn Fn(i32) -> _>>);
 }
diff --git a/src/test/ui/coercion/coerce-expect-unsized-ascribed.stderr b/src/test/ui/coercion/coerce-expect-unsized-ascribed.stderr
index 9d614e610ad..44968244c4d 100644
--- a/src/test/ui/coercion/coerce-expect-unsized-ascribed.stderr
+++ b/src/test/ui/coercion/coerce-expect-unsized-ascribed.stderr
@@ -1,128 +1,128 @@
 error[E0308]: mismatched types
-  --> $DIR/coerce-expect-unsized-ascribed.rs:9:13
+  --> $DIR/coerce-expect-unsized-ascribed.rs:9:27
    |
-LL |     let _ = box { [1, 2, 3] }: Box<[i32]>;
-   |             ^^^^^^^^^^^^^^^^^ expected slice `[i32]`, found array `[i32; 3]`
+LL |     let _ = type_ascribe!(box { [1, 2, 3] }, Box<[i32]>);
+   |                           ^^^^^^^^^^^^^^^^^ expected slice `[i32]`, found array `[i32; 3]`
    |
    = note: expected struct `Box<[i32]>`
               found struct `Box<[i32; 3]>`
 
 error[E0308]: mismatched types
-  --> $DIR/coerce-expect-unsized-ascribed.rs:10:13
+  --> $DIR/coerce-expect-unsized-ascribed.rs:10:27
    |
-LL |     let _ = box if true { [1, 2, 3] } else { [1, 3, 4] }: Box<[i32]>;
-   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected slice `[i32]`, found array `[i32; 3]`
+LL |     let _ = type_ascribe!(box if true { [1, 2, 3] } else { [1, 3, 4] }, Box<[i32]>);
+   |                           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected slice `[i32]`, found array `[i32; 3]`
    |
    = note: expected struct `Box<[i32]>`
               found struct `Box<[i32; 3]>`
 
 error[E0308]: mismatched types
-  --> $DIR/coerce-expect-unsized-ascribed.rs:11:13
+  --> $DIR/coerce-expect-unsized-ascribed.rs:11:27
    |
-LL |     let _ = box match true { true => [1, 2, 3], false => [1, 3, 4] }: Box<[i32]>;
-   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected slice `[i32]`, found array `[i32; 3]`
+LL |     let _ = type_ascribe!(box match true { true => [1, 2, 3], false => [1, 3, 4] }, Box<[i32]>);
+   |                           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected slice `[i32]`, found array `[i32; 3]`
    |
    = note: expected struct `Box<[i32]>`
               found struct `Box<[i32; 3]>`
 
 error[E0308]: mismatched types
-  --> $DIR/coerce-expect-unsized-ascribed.rs:13:13
+  --> $DIR/coerce-expect-unsized-ascribed.rs:13:27
    |
-LL |     let _ = box { |x| (x as u8) }: Box<dyn Fn(i32) -> _>;
-   |             ^^^^^^^^^^^^^^^^^^^^^ expected trait object `dyn Fn`, found closure
+LL |     let _ = type_ascribe!(box { |x| (x as u8) }, Box<dyn Fn(i32) -> _>);
+   |                           ^^^^^^^^^^^^^^^^^^^^^ expected trait object `dyn Fn`, found closure
    |
    = note: expected struct `Box<dyn Fn(i32) -> u8>`
-              found struct `Box<[closure@$DIR/coerce-expect-unsized-ascribed.rs:13:19: 13:22]>`
+              found struct `Box<[closure@$DIR/coerce-expect-unsized-ascribed.rs:13:33: 13:36]>`
 
 error[E0308]: mismatched types
-  --> $DIR/coerce-expect-unsized-ascribed.rs:14:13
+  --> $DIR/coerce-expect-unsized-ascribed.rs:14:27
    |
-LL |     let _ = box if true { false } else { true }: Box<dyn Debug>;
-   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected trait object `dyn Debug`, found `bool`
+LL |     let _ = type_ascribe!(box if true { false } else { true }, Box<dyn Debug>);
+   |                           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected trait object `dyn Debug`, found `bool`
    |
    = note: expected struct `Box<dyn Debug>`
               found struct `Box<bool>`
 
 error[E0308]: mismatched types
-  --> $DIR/coerce-expect-unsized-ascribed.rs:15:13
+  --> $DIR/coerce-expect-unsized-ascribed.rs:15:27
    |
-LL |     let _ = box match true { true => 'a', false => 'b' }: Box<dyn Debug>;
-   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected trait object `dyn Debug`, found `char`
+LL |     let _ = type_ascribe!(box match true { true => 'a', false => 'b' }, Box<dyn Debug>);
+   |                           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected trait object `dyn Debug`, found `char`
    |
    = note: expected struct `Box<dyn Debug>`
               found struct `Box<char>`
 
 error[E0308]: mismatched types
-  --> $DIR/coerce-expect-unsized-ascribed.rs:17:13
+  --> $DIR/coerce-expect-unsized-ascribed.rs:17:27
    |
-LL |     let _ = &{ [1, 2, 3] }: &[i32];
-   |             ^^^^^^^^^^^^^^ expected slice `[i32]`, found array `[i32; 3]`
+LL |     let _ = type_ascribe!(&{ [1, 2, 3] }, &[i32]);
+   |                           ^^^^^^^^^^^^^^ expected slice `[i32]`, found array `[i32; 3]`
    |
    = note: expected reference `&[i32]`
               found reference `&[i32; 3]`
 
 error[E0308]: mismatched types
-  --> $DIR/coerce-expect-unsized-ascribed.rs:18:13
+  --> $DIR/coerce-expect-unsized-ascribed.rs:18:27
    |
-LL |     let _ = &if true { [1, 2, 3] } else { [1, 3, 4] }: &[i32];
-   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected slice `[i32]`, found array `[i32; 3]`
+LL |     let _ = type_ascribe!(&if true { [1, 2, 3] } else { [1, 3, 4] }, &[i32]);
+   |                           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected slice `[i32]`, found array `[i32; 3]`
    |
    = note: expected reference `&[i32]`
               found reference `&[i32; 3]`
 
 error[E0308]: mismatched types
-  --> $DIR/coerce-expect-unsized-ascribed.rs:19:13
+  --> $DIR/coerce-expect-unsized-ascribed.rs:19:27
    |
-LL |     let _ = &match true { true => [1, 2, 3], false => [1, 3, 4] }: &[i32];
-   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected slice `[i32]`, found array `[i32; 3]`
+LL |     let _ = type_ascribe!(&match true { true => [1, 2, 3], false => [1, 3, 4] }, &[i32]);
+   |                           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected slice `[i32]`, found array `[i32; 3]`
    |
    = note: expected reference `&[i32]`
               found reference `&[i32; 3]`
 
 error[E0308]: mismatched types
-  --> $DIR/coerce-expect-unsized-ascribed.rs:21:13
+  --> $DIR/coerce-expect-unsized-ascribed.rs:21:27
    |
-LL |     let _ = &{ |x| (x as u8) }: &dyn Fn(i32) -> _;
-   |             ^^^^^^^^^^^^^^^^^^ expected trait object `dyn Fn`, found closure
+LL |     let _ = type_ascribe!(&{ |x| (x as u8) }, &dyn Fn(i32) -> _);
+   |                           ^^^^^^^^^^^^^^^^^^ expected trait object `dyn Fn`, found closure
    |
    = note: expected reference `&dyn Fn(i32) -> u8`
-              found reference `&[closure@$DIR/coerce-expect-unsized-ascribed.rs:21:16: 21:19]`
+              found reference `&[closure@$DIR/coerce-expect-unsized-ascribed.rs:21:30: 21:33]`
 
 error[E0308]: mismatched types
-  --> $DIR/coerce-expect-unsized-ascribed.rs:22:13
+  --> $DIR/coerce-expect-unsized-ascribed.rs:22:27
    |
-LL |     let _ = &if true { false } else { true }: &dyn Debug;
-   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected trait object `dyn Debug`, found `bool`
+LL |     let _ = type_ascribe!(&if true { false } else { true }, &dyn Debug);
+   |                           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected trait object `dyn Debug`, found `bool`
    |
    = note: expected reference `&dyn Debug`
               found reference `&bool`
 
 error[E0308]: mismatched types
-  --> $DIR/coerce-expect-unsized-ascribed.rs:23:13
+  --> $DIR/coerce-expect-unsized-ascribed.rs:23:27
    |
-LL |     let _ = &match true { true => 'a', false => 'b' }: &dyn Debug;
-   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected trait object `dyn Debug`, found `char`
+LL |     let _ = type_ascribe!(&match true { true => 'a', false => 'b' }, &dyn Debug);
+   |                           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected trait object `dyn Debug`, found `char`
    |
    = note: expected reference `&dyn Debug`
               found reference `&char`
 
 error[E0308]: mismatched types
-  --> $DIR/coerce-expect-unsized-ascribed.rs:25:13
+  --> $DIR/coerce-expect-unsized-ascribed.rs:25:27
    |
-LL |     let _ = Box::new([1, 2, 3]): Box<[i32]>;
-   |             ^^^^^^^^^^^^^^^^^^^ expected slice `[i32]`, found array `[i32; 3]`
+LL |     let _ = type_ascribe!(Box::new([1, 2, 3]), Box<[i32]>);
+   |                           ^^^^^^^^^^^^^^^^^^^ expected slice `[i32]`, found array `[i32; 3]`
    |
    = note: expected struct `Box<[i32]>`
               found struct `Box<[i32; 3]>`
 
 error[E0308]: mismatched types
-  --> $DIR/coerce-expect-unsized-ascribed.rs:26:13
+  --> $DIR/coerce-expect-unsized-ascribed.rs:26:27
    |
-LL |     let _ = Box::new(|x| (x as u8)): Box<dyn Fn(i32) -> _>;
-   |             ^^^^^^^^^^^^^^^^^^^^^^^ expected trait object `dyn Fn`, found closure
+LL |     let _ = type_ascribe!(Box::new(|x| (x as u8)), Box<dyn Fn(i32) -> _>);
+   |                           ^^^^^^^^^^^^^^^^^^^^^^^ expected trait object `dyn Fn`, found closure
    |
    = note: expected struct `Box<dyn Fn(i32) -> u8>`
-              found struct `Box<[closure@$DIR/coerce-expect-unsized-ascribed.rs:26:22: 26:25]>`
+              found struct `Box<[closure@$DIR/coerce-expect-unsized-ascribed.rs:26:36: 26:39]>`
 
 error: aborting due to 14 previous errors
 
diff --git a/src/test/ui/consts/const_in_pattern/accept_structural.rs b/src/test/ui/consts/const_in_pattern/accept_structural.rs
index 5093fe53915..1f56f581c02 100644
--- a/src/test/ui/consts/const_in_pattern/accept_structural.rs
+++ b/src/test/ui/consts/const_in_pattern/accept_structural.rs
@@ -45,7 +45,7 @@ fn main() {
     const TUPLE: (OND, OND) = (None, None);
     match (None, None) { TUPLE => dbg!(TUPLE), _ => panic!("whoops"), };
 
-    const TYPE_ASCRIPTION: OND = None: OND;
+    const TYPE_ASCRIPTION: OND = type_ascribe!(None, OND);
     match None { TYPE_ASCRIPTION => dbg!(TYPE_ASCRIPTION), _ => panic!("whoops"), };
 
     const ARRAY: [OND; 2] = [None, None];
diff --git a/src/test/ui/consts/const_in_pattern/reject_non_structural.rs b/src/test/ui/consts/const_in_pattern/reject_non_structural.rs
index 7a8169bec45..75fde0d92de 100644
--- a/src/test/ui/consts/const_in_pattern/reject_non_structural.rs
+++ b/src/test/ui/consts/const_in_pattern/reject_non_structural.rs
@@ -53,7 +53,7 @@ fn main() {
     match (None, Some(NoDerive)) { TUPLE => dbg!(TUPLE), _ => panic!("whoops"), };
     //~^ ERROR must be annotated with `#[derive(PartialEq, Eq)]`
 
-    const TYPE_ASCRIPTION: OND = Some(NoDerive): OND;
+    const TYPE_ASCRIPTION: OND = type_ascribe!(Some(NoDerive), OND);
     match Some(NoDerive) { TYPE_ASCRIPTION => dbg!(TYPE_ASCRIPTION), _ => panic!("whoops"), };
     //~^ ERROR must be annotated with `#[derive(PartialEq, Eq)]`
 
diff --git a/src/test/ui/consts/miri_unleashed/tls.stderr b/src/test/ui/consts/miri_unleashed/tls.stderr
index 436c5112360..7aaeadd0403 100644
--- a/src/test/ui/consts/miri_unleashed/tls.stderr
+++ b/src/test/ui/consts/miri_unleashed/tls.stderr
@@ -2,13 +2,13 @@ error[E0080]: could not evaluate static initializer
   --> $DIR/tls.rs:11:25
    |
 LL |     unsafe { let _val = A; }
-   |                         ^ cannot access thread local static (DefId(0:6 ~ tls[78b0]::A))
+   |                         ^ cannot access thread local static (DefId(0:4 ~ tls[78b0]::A))
 
 error[E0080]: could not evaluate static initializer
   --> $DIR/tls.rs:18:26
    |
 LL |     unsafe { let _val = &A; }
-   |                          ^ cannot access thread local static (DefId(0:6 ~ tls[78b0]::A))
+   |                          ^ cannot access thread local static (DefId(0:4 ~ tls[78b0]::A))
 
 warning: skipping const checks
    |
diff --git a/src/test/ui/deriving/issue-105101.rs b/src/test/ui/deriving/issue-105101.rs
new file mode 100644
index 00000000000..1a377feb919
--- /dev/null
+++ b/src/test/ui/deriving/issue-105101.rs
@@ -0,0 +1,9 @@
+// compile-flags: --crate-type=lib
+
+#[derive(Default)] //~ ERROR multiple declared defaults
+enum E {
+    #[default]
+    A,
+    #[default]
+    A, //~ ERROR defined multiple times
+}
diff --git a/src/test/ui/deriving/issue-105101.stderr b/src/test/ui/deriving/issue-105101.stderr
new file mode 100644
index 00000000000..0f6f67043f3
--- /dev/null
+++ b/src/test/ui/deriving/issue-105101.stderr
@@ -0,0 +1,29 @@
+error: multiple declared defaults
+  --> $DIR/issue-105101.rs:3:10
+   |
+LL | #[derive(Default)]
+   |          ^^^^^^^
+...
+LL |     A,
+   |     - first default
+LL |     #[default]
+LL |     A,
+   |     - additional default
+   |
+   = note: only one variant can be default
+   = note: this error originates in the derive macro `Default` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error[E0428]: the name `A` is defined multiple times
+  --> $DIR/issue-105101.rs:8:5
+   |
+LL |     A,
+   |     - previous definition of the type `A` here
+LL |     #[default]
+LL |     A,
+   |     ^ `A` redefined here
+   |
+   = note: `A` must be defined only once in the type namespace of this enum
+
+error: aborting due to 2 previous errors
+
+For more information about this error, try `rustc --explain E0428`.
diff --git a/src/test/ui/enum/issue-67945-2.rs b/src/test/ui/enum/issue-67945-2.rs
index e5044468da1..2eb123b7328 100644
--- a/src/test/ui/enum/issue-67945-2.rs
+++ b/src/test/ui/enum/issue-67945-2.rs
@@ -1,7 +1,7 @@
 #![feature(type_ascription)]
 
 enum Bug<S> { //~ ERROR parameter `S` is never used
-    Var = 0: S,
+    Var = type_ascribe!(0, S),
     //~^ ERROR generic parameters may not be used
 }
 
diff --git a/src/test/ui/enum/issue-67945-2.stderr b/src/test/ui/enum/issue-67945-2.stderr
index 4f5e236a37b..63d3521afe4 100644
--- a/src/test/ui/enum/issue-67945-2.stderr
+++ b/src/test/ui/enum/issue-67945-2.stderr
@@ -1,8 +1,8 @@
 error: generic parameters may not be used in const operations
-  --> $DIR/issue-67945-2.rs:4:14
+  --> $DIR/issue-67945-2.rs:4:28
    |
-LL |     Var = 0: S,
-   |              ^ cannot perform const operation using `S`
+LL |     Var = type_ascribe!(0, S),
+   |                            ^ cannot perform const operation using `S`
    |
    = note: type parameters may not be used in const expressions
    = help: use `#![feature(generic_const_exprs)]` to allow generic const expressions
diff --git a/src/test/ui/feature-gates/feature-gate-native_link_modifiers_verbatim.rs b/src/test/ui/feature-gates/feature-gate-native_link_modifiers_verbatim.rs
deleted file mode 100644
index 7b09195dc3f..00000000000
--- a/src/test/ui/feature-gates/feature-gate-native_link_modifiers_verbatim.rs
+++ /dev/null
@@ -1,5 +0,0 @@
-#[link(name = "foo", modifiers = "+verbatim")]
-//~^ ERROR: linking modifier `verbatim` is unstable
-extern "C" {}
-
-fn main() {}
diff --git a/src/test/ui/feature-gates/feature-gate-native_link_modifiers_verbatim.stderr b/src/test/ui/feature-gates/feature-gate-native_link_modifiers_verbatim.stderr
deleted file mode 100644
index 3bfbeb8db35..00000000000
--- a/src/test/ui/feature-gates/feature-gate-native_link_modifiers_verbatim.stderr
+++ /dev/null
@@ -1,12 +0,0 @@
-error[E0658]: linking modifier `verbatim` is unstable
-  --> $DIR/feature-gate-native_link_modifiers_verbatim.rs:1:34
-   |
-LL | #[link(name = "foo", modifiers = "+verbatim")]
-   |                                  ^^^^^^^^^^^
-   |
-   = note: see issue #81490 <https://github.com/rust-lang/rust/issues/81490> for more information
-   = help: add `#![feature(native_link_modifiers_verbatim)]` to the crate attributes to enable
-
-error: aborting due to previous error
-
-For more information about this error, try `rustc --explain E0658`.
diff --git a/src/test/ui/generator/clone-impl-async.rs b/src/test/ui/generator/clone-impl-async.rs
index 83c51526b7b..9e9b59d3633 100644
--- a/src/test/ui/generator/clone-impl-async.rs
+++ b/src/test/ui/generator/clone-impl-async.rs
@@ -15,42 +15,42 @@ fn main() {
         drop(non_clone);
     };
     check_copy(&inner_non_clone);
-    //~^ ERROR the trait bound `impl Future<Output = ()>: Copy` is not satisfied
+    //~^ ERROR : Copy` is not satisfied
     check_clone(&inner_non_clone);
-    //~^ ERROR the trait bound `impl Future<Output = ()>: Clone` is not satisfied
+    //~^ ERROR : Clone` is not satisfied
 
     let non_clone = NonClone;
     let outer_non_clone = async move {
         drop(non_clone);
     };
     check_copy(&outer_non_clone);
-    //~^ ERROR the trait bound `impl Future<Output = ()>: Copy` is not satisfied
+    //~^ ERROR : Copy` is not satisfied
     check_clone(&outer_non_clone);
-    //~^ ERROR the trait bound `impl Future<Output = ()>: Clone` is not satisfied
+    //~^ ERROR : Clone` is not satisfied
 
     let maybe_copy_clone = async move {};
     check_copy(&maybe_copy_clone);
-    //~^ ERROR the trait bound `impl Future<Output = ()>: Copy` is not satisfied
+    //~^ ERROR : Copy` is not satisfied
     check_clone(&maybe_copy_clone);
-    //~^ ERROR the trait bound `impl Future<Output = ()>: Clone` is not satisfied
+    //~^ ERROR : Clone` is not satisfied
 
     let inner_non_clone_fn = the_inner_non_clone_fn();
     check_copy(&inner_non_clone_fn);
-    //~^ ERROR the trait bound `impl Future<Output = ()>: Copy` is not satisfied
+    //~^ ERROR : Copy` is not satisfied
     check_clone(&inner_non_clone_fn);
-    //~^ ERROR the trait bound `impl Future<Output = ()>: Clone` is not satisfied
+    //~^ ERROR : Clone` is not satisfied
 
     let outer_non_clone_fn = the_outer_non_clone_fn(NonClone);
     check_copy(&outer_non_clone_fn);
-    //~^ ERROR the trait bound `impl Future<Output = ()>: Copy` is not satisfied
+    //~^ ERROR : Copy` is not satisfied
     check_clone(&outer_non_clone_fn);
-    //~^ ERROR the trait bound `impl Future<Output = ()>: Clone` is not satisfied
+    //~^ ERROR : Clone` is not satisfied
 
     let maybe_copy_clone_fn = the_maybe_copy_clone_fn();
     check_copy(&maybe_copy_clone_fn);
-    //~^ ERROR the trait bound `impl Future<Output = ()>: Copy` is not satisfied
+    //~^ ERROR : Copy` is not satisfied
     check_clone(&maybe_copy_clone_fn);
-    //~^ ERROR the trait bound `impl Future<Output = ()>: Clone` is not satisfied
+    //~^ ERROR : Clone` is not satisfied
 }
 
 async fn the_inner_non_clone_fn() {
@@ -64,8 +64,7 @@ async fn the_outer_non_clone_fn(non_clone: NonClone) {
     drop(non_clone);
 }
 
-async fn the_maybe_copy_clone_fn() {
-}
+async fn the_maybe_copy_clone_fn() {}
 
 fn check_copy<T: Copy>(_x: &T) {}
 fn check_clone<T: Clone>(_x: &T) {}
diff --git a/src/test/ui/generator/clone-impl-async.stderr b/src/test/ui/generator/clone-impl-async.stderr
index cbb58d2af18..9854728876f 100644
--- a/src/test/ui/generator/clone-impl-async.stderr
+++ b/src/test/ui/generator/clone-impl-async.stderr
@@ -1,83 +1,83 @@
-error[E0277]: the trait bound `impl Future<Output = ()>: Copy` is not satisfied
+error[E0277]: the trait bound `[async block@$DIR/clone-impl-async.rs:12:27: 16:6]: Copy` is not satisfied
   --> $DIR/clone-impl-async.rs:17:16
    |
 LL |     check_copy(&inner_non_clone);
-   |     ---------- ^^^^^^^^^^^^^^^^ the trait `Copy` is not implemented for `impl Future<Output = ()>`
+   |     ---------- ^^^^^^^^^^^^^^^^ the trait `Copy` is not implemented for `[async block@$DIR/clone-impl-async.rs:12:27: 16:6]`
    |     |
    |     required by a bound introduced by this call
    |
 note: required by a bound in `check_copy`
-  --> $DIR/clone-impl-async.rs:70:18
+  --> $DIR/clone-impl-async.rs:69:18
    |
 LL | fn check_copy<T: Copy>(_x: &T) {}
    |                  ^^^^ required by this bound in `check_copy`
 
-error[E0277]: the trait bound `impl Future<Output = ()>: Clone` is not satisfied
+error[E0277]: the trait bound `[async block@$DIR/clone-impl-async.rs:12:27: 16:6]: Clone` is not satisfied
   --> $DIR/clone-impl-async.rs:19:17
    |
 LL |     check_clone(&inner_non_clone);
-   |     ----------- ^^^^^^^^^^^^^^^^ the trait `Clone` is not implemented for `impl Future<Output = ()>`
+   |     ----------- ^^^^^^^^^^^^^^^^ the trait `Clone` is not implemented for `[async block@$DIR/clone-impl-async.rs:12:27: 16:6]`
    |     |
    |     required by a bound introduced by this call
    |
 note: required by a bound in `check_clone`
-  --> $DIR/clone-impl-async.rs:71:19
+  --> $DIR/clone-impl-async.rs:70:19
    |
 LL | fn check_clone<T: Clone>(_x: &T) {}
    |                   ^^^^^ required by this bound in `check_clone`
 
-error[E0277]: the trait bound `impl Future<Output = ()>: Copy` is not satisfied
+error[E0277]: the trait bound `[async block@$DIR/clone-impl-async.rs:23:27: 25:6]: Copy` is not satisfied
   --> $DIR/clone-impl-async.rs:26:16
    |
 LL |     check_copy(&outer_non_clone);
-   |     ---------- ^^^^^^^^^^^^^^^^ the trait `Copy` is not implemented for `impl Future<Output = ()>`
+   |     ---------- ^^^^^^^^^^^^^^^^ the trait `Copy` is not implemented for `[async block@$DIR/clone-impl-async.rs:23:27: 25:6]`
    |     |
    |     required by a bound introduced by this call
    |
 note: required by a bound in `check_copy`
-  --> $DIR/clone-impl-async.rs:70:18
+  --> $DIR/clone-impl-async.rs:69:18
    |
 LL | fn check_copy<T: Copy>(_x: &T) {}
    |                  ^^^^ required by this bound in `check_copy`
 
-error[E0277]: the trait bound `impl Future<Output = ()>: Clone` is not satisfied
+error[E0277]: the trait bound `[async block@$DIR/clone-impl-async.rs:23:27: 25:6]: Clone` is not satisfied
   --> $DIR/clone-impl-async.rs:28:17
    |
 LL |     check_clone(&outer_non_clone);
-   |     ----------- ^^^^^^^^^^^^^^^^ the trait `Clone` is not implemented for `impl Future<Output = ()>`
+   |     ----------- ^^^^^^^^^^^^^^^^ the trait `Clone` is not implemented for `[async block@$DIR/clone-impl-async.rs:23:27: 25:6]`
    |     |
    |     required by a bound introduced by this call
    |
 note: required by a bound in `check_clone`
-  --> $DIR/clone-impl-async.rs:71:19
+  --> $DIR/clone-impl-async.rs:70:19
    |
 LL | fn check_clone<T: Clone>(_x: &T) {}
    |                   ^^^^^ required by this bound in `check_clone`
 
-error[E0277]: the trait bound `impl Future<Output = ()>: Copy` is not satisfied
+error[E0277]: the trait bound `[async block@$DIR/clone-impl-async.rs:31:28: 31:41]: Copy` is not satisfied
   --> $DIR/clone-impl-async.rs:32:16
    |
 LL |     check_copy(&maybe_copy_clone);
-   |     ---------- ^^^^^^^^^^^^^^^^^ the trait `Copy` is not implemented for `impl Future<Output = ()>`
+   |     ---------- ^^^^^^^^^^^^^^^^^ the trait `Copy` is not implemented for `[async block@$DIR/clone-impl-async.rs:31:28: 31:41]`
    |     |
    |     required by a bound introduced by this call
    |
 note: required by a bound in `check_copy`
-  --> $DIR/clone-impl-async.rs:70:18
+  --> $DIR/clone-impl-async.rs:69:18
    |
 LL | fn check_copy<T: Copy>(_x: &T) {}
    |                  ^^^^ required by this bound in `check_copy`
 
-error[E0277]: the trait bound `impl Future<Output = ()>: Clone` is not satisfied
+error[E0277]: the trait bound `[async block@$DIR/clone-impl-async.rs:31:28: 31:41]: Clone` is not satisfied
   --> $DIR/clone-impl-async.rs:34:17
    |
 LL |     check_clone(&maybe_copy_clone);
-   |     ----------- ^^^^^^^^^^^^^^^^^ the trait `Clone` is not implemented for `impl Future<Output = ()>`
+   |     ----------- ^^^^^^^^^^^^^^^^^ the trait `Clone` is not implemented for `[async block@$DIR/clone-impl-async.rs:31:28: 31:41]`
    |     |
    |     required by a bound introduced by this call
    |
 note: required by a bound in `check_clone`
-  --> $DIR/clone-impl-async.rs:71:19
+  --> $DIR/clone-impl-async.rs:70:19
    |
 LL | fn check_clone<T: Clone>(_x: &T) {}
    |                   ^^^^^ required by this bound in `check_clone`
@@ -91,7 +91,7 @@ LL |     check_copy(&inner_non_clone_fn);
    |     required by a bound introduced by this call
    |
 note: required by a bound in `check_copy`
-  --> $DIR/clone-impl-async.rs:70:18
+  --> $DIR/clone-impl-async.rs:69:18
    |
 LL | fn check_copy<T: Copy>(_x: &T) {}
    |                  ^^^^ required by this bound in `check_copy`
@@ -105,7 +105,7 @@ LL |     check_clone(&inner_non_clone_fn);
    |     required by a bound introduced by this call
    |
 note: required by a bound in `check_clone`
-  --> $DIR/clone-impl-async.rs:71:19
+  --> $DIR/clone-impl-async.rs:70:19
    |
 LL | fn check_clone<T: Clone>(_x: &T) {}
    |                   ^^^^^ required by this bound in `check_clone`
@@ -119,7 +119,7 @@ LL |     check_copy(&outer_non_clone_fn);
    |     required by a bound introduced by this call
    |
 note: required by a bound in `check_copy`
-  --> $DIR/clone-impl-async.rs:70:18
+  --> $DIR/clone-impl-async.rs:69:18
    |
 LL | fn check_copy<T: Copy>(_x: &T) {}
    |                  ^^^^ required by this bound in `check_copy`
@@ -133,7 +133,7 @@ LL |     check_clone(&outer_non_clone_fn);
    |     required by a bound introduced by this call
    |
 note: required by a bound in `check_clone`
-  --> $DIR/clone-impl-async.rs:71:19
+  --> $DIR/clone-impl-async.rs:70:19
    |
 LL | fn check_clone<T: Clone>(_x: &T) {}
    |                   ^^^^^ required by this bound in `check_clone`
@@ -147,7 +147,7 @@ LL |     check_copy(&maybe_copy_clone_fn);
    |     required by a bound introduced by this call
    |
 note: required by a bound in `check_copy`
-  --> $DIR/clone-impl-async.rs:70:18
+  --> $DIR/clone-impl-async.rs:69:18
    |
 LL | fn check_copy<T: Copy>(_x: &T) {}
    |                  ^^^^ required by this bound in `check_copy`
@@ -161,7 +161,7 @@ LL |     check_clone(&maybe_copy_clone_fn);
    |     required by a bound introduced by this call
    |
 note: required by a bound in `check_clone`
-  --> $DIR/clone-impl-async.rs:71:19
+  --> $DIR/clone-impl-async.rs:70:19
    |
 LL | fn check_clone<T: Clone>(_x: &T) {}
    |                   ^^^^^ required by this bound in `check_clone`
diff --git a/src/test/ui/generator/print/generator-print-verbose-1.stderr b/src/test/ui/generator/print/generator-print-verbose-1.stderr
index 2e020780480..ed0628bbbc3 100644
--- a/src/test/ui/generator/print/generator-print-verbose-1.stderr
+++ b/src/test/ui/generator/print/generator-print-verbose-1.stderr
@@ -9,7 +9,7 @@ note: generator is not `Send` as this value is used across a yield
   --> $DIR/generator-print-verbose-1.rs:35:9
    |
 LL |         let _non_send_gen = make_non_send_generator();
-   |             ------------- has type `Opaque(DefId(0:44 ~ generator_print_verbose_1[749a]::make_non_send_generator::{opaque#0}), [])` which is not `Send`
+   |             ------------- has type `Opaque(DefId(0:34 ~ generator_print_verbose_1[749a]::make_non_send_generator::{opaque#0}), [])` which is not `Send`
 LL |         yield;
    |         ^^^^^ yield occurs here, with `_non_send_gen` maybe used later
 LL |     };
@@ -35,17 +35,17 @@ note: required because it's used within this generator
    |
 LL |     || {
    |     ^^
-note: required because it appears within the type `Opaque(DefId(0:45 ~ generator_print_verbose_1[749a]::make_gen2::{opaque#0}), [std::sync::Arc<std::cell::RefCell<i32>>])`
+note: required because it appears within the type `Opaque(DefId(0:35 ~ generator_print_verbose_1[749a]::make_gen2::{opaque#0}), [std::sync::Arc<std::cell::RefCell<i32>>])`
   --> $DIR/generator-print-verbose-1.rs:41:30
    |
 LL | pub fn make_gen2<T>(t: T) -> impl Generator<Return = T> {
    |                              ^^^^^^^^^^^^^^^^^^^^^^^^^^
-note: required because it appears within the type `Opaque(DefId(0:46 ~ generator_print_verbose_1[749a]::make_non_send_generator2::{opaque#0}), [])`
+note: required because it appears within the type `Opaque(DefId(0:36 ~ generator_print_verbose_1[749a]::make_non_send_generator2::{opaque#0}), [])`
   --> $DIR/generator-print-verbose-1.rs:47:34
    |
 LL | fn make_non_send_generator2() -> impl Generator<Return = Arc<RefCell<i32>>> {
    |                                  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-   = note: required because it captures the following types: `Opaque(DefId(0:46 ~ generator_print_verbose_1[749a]::make_non_send_generator2::{opaque#0}), [])`, `()`
+   = note: required because it captures the following types: `Opaque(DefId(0:36 ~ generator_print_verbose_1[749a]::make_non_send_generator2::{opaque#0}), [])`, `()`
 note: required because it's used within this generator
   --> $DIR/generator-print-verbose-1.rs:52:20
    |
diff --git a/src/test/ui/generic-associated-types/self-outlives-lint.stderr b/src/test/ui/generic-associated-types/self-outlives-lint.stderr
index 58172bf06b5..9e9b2e18abe 100644
--- a/src/test/ui/generic-associated-types/self-outlives-lint.stderr
+++ b/src/test/ui/generic-associated-types/self-outlives-lint.stderr
@@ -108,17 +108,6 @@ LL |     type Bar<'b>;
    = note: this bound is currently required to ensure that impls have maximum flexibility
    = note: we are soliciting feedback, see issue #87479 <https://github.com/rust-lang/rust/issues/87479> for more information
 
-error: missing required bound on `Item`
-  --> $DIR/self-outlives-lint.rs:140:5
-   |
-LL |     type Item<'a>;
-   |     ^^^^^^^^^^^^^-
-   |                  |
-   |                  help: add the required where clause: `where Self: 'a`
-   |
-   = note: this bound is currently required to ensure that impls have maximum flexibility
-   = note: we are soliciting feedback, see issue #87479 <https://github.com/rust-lang/rust/issues/87479> for more information
-
 error: missing required bound on `Iterator`
   --> $DIR/self-outlives-lint.rs:142:5
    |
@@ -131,6 +120,17 @@ LL |     type Iterator<'a>: Iterator<Item = Self::Item<'a>>;
    = note: we are soliciting feedback, see issue #87479 <https://github.com/rust-lang/rust/issues/87479> for more information
 
 error: missing required bound on `Item`
+  --> $DIR/self-outlives-lint.rs:140:5
+   |
+LL |     type Item<'a>;
+   |     ^^^^^^^^^^^^^-
+   |                  |
+   |                  help: add the required where clause: `where Self: 'a`
+   |
+   = note: this bound is currently required to ensure that impls have maximum flexibility
+   = note: we are soliciting feedback, see issue #87479 <https://github.com/rust-lang/rust/issues/87479> for more information
+
+error: missing required bound on `Item`
   --> $DIR/self-outlives-lint.rs:148:5
    |
 LL |     type Item<'a>;
diff --git a/src/test/ui/hygiene/panic-location.run.stderr b/src/test/ui/hygiene/panic-location.run.stderr
index 216b31586da..0b23b1cc2f4 100644
--- a/src/test/ui/hygiene/panic-location.run.stderr
+++ b/src/test/ui/hygiene/panic-location.run.stderr
@@ -1,2 +1,2 @@
-thread 'main' panicked at 'capacity overflow', $SRC_DIR/alloc/src/collections/vec_deque/mod.rs:LL:COL
+thread 'main' panicked at 'capacity overflow', library/alloc/src/raw_vec.rs:518:5
 note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
diff --git a/src/test/ui/impl-trait/issue-55872-3.rs b/src/test/ui/impl-trait/issue-55872-3.rs
index 3ffce85e61b..91811df93cd 100644
--- a/src/test/ui/impl-trait/issue-55872-3.rs
+++ b/src/test/ui/impl-trait/issue-55872-3.rs
@@ -12,7 +12,7 @@ pub trait Bar {
 impl<S> Bar for S {
     type E = impl std::marker::Copy;
     fn foo<T>() -> Self::E {
-    //~^ ERROR the trait bound `impl Future<Output = ()>: Copy` is not satisfied [E0277]
+        //~^ ERROR : Copy` is not satisfied [E0277]
         async {}
     }
 }
diff --git a/src/test/ui/impl-trait/issue-55872-3.stderr b/src/test/ui/impl-trait/issue-55872-3.stderr
index 6ab540e8751..c6e10f0f350 100644
--- a/src/test/ui/impl-trait/issue-55872-3.stderr
+++ b/src/test/ui/impl-trait/issue-55872-3.stderr
@@ -1,8 +1,8 @@
-error[E0277]: the trait bound `impl Future<Output = ()>: Copy` is not satisfied
+error[E0277]: the trait bound `[async block@$DIR/issue-55872-3.rs:16:9: 16:17]: Copy` is not satisfied
   --> $DIR/issue-55872-3.rs:14:20
    |
 LL |     fn foo<T>() -> Self::E {
-   |                    ^^^^^^^ the trait `Copy` is not implemented for `impl Future<Output = ()>`
+   |                    ^^^^^^^ the trait `Copy` is not implemented for `[async block@$DIR/issue-55872-3.rs:16:9: 16:17]`
 
 error: aborting due to previous error
 
diff --git a/src/test/ui/impl-trait/issues/issue-104815.rs b/src/test/ui/impl-trait/issues/issue-104815.rs
new file mode 100644
index 00000000000..7a9826a8dff
--- /dev/null
+++ b/src/test/ui/impl-trait/issues/issue-104815.rs
@@ -0,0 +1,66 @@
+// check-pass
+
+struct It;
+
+struct Data {
+    items: Vec<It>,
+}
+
+impl Data {
+    fn new() -> Self {
+        Self {
+            items: vec![It, It],
+        }
+    }
+
+    fn content(&self) -> impl Iterator<Item = &It> {
+        self.items.iter()
+    }
+}
+
+struct Container<'a> {
+    name: String,
+    resolver: Box<dyn Resolver + 'a>,
+}
+
+impl<'a> Container<'a> {
+    fn new<R: Resolver + 'a>(name: &str, resolver: R) -> Self {
+        Self {
+            name: name.to_owned(),
+            resolver: Box::new(resolver),
+        }
+    }
+}
+
+trait Resolver {}
+
+impl<R: Resolver> Resolver for &R {}
+
+impl Resolver for It {}
+
+fn get<'a>(mut items: impl Iterator<Item = &'a It>) -> impl Resolver + 'a {
+    items.next().unwrap()
+}
+
+fn get2<'a, 'b: 'b>(mut items: impl Iterator<Item = &'a It>) -> impl Resolver + 'a {
+    items.next().unwrap()
+}
+
+fn main() {
+    let data = Data::new();
+    let resolver = get(data.content());
+
+    let _ = ["a", "b"]
+        .iter()
+        .map(|&n| Container::new(n, &resolver))
+        .map(|c| c.name)
+        .collect::<Vec<_>>();
+
+    let resolver = get2(data.content());
+
+    let _ = ["a", "b"]
+        .iter()
+        .map(|&n| Container::new(n, &resolver))
+        .map(|c| c.name)
+        .collect::<Vec<_>>();
+}
diff --git a/src/test/ui/impl-trait/issues/issue-78722.rs b/src/test/ui/impl-trait/issues/issue-78722.rs
index 9ee1ba3d3b4..78233f300bd 100644
--- a/src/test/ui/impl-trait/issues/issue-78722.rs
+++ b/src/test/ui/impl-trait/issues/issue-78722.rs
@@ -7,7 +7,7 @@ type F = impl core::future::Future<Output = u8>;
 struct Bug {
     V1: [(); {
         fn concrete_use() -> F {
-            //~^ ERROR expected `impl Future<Output = ()>` to be a future that resolves to `u8`, but it resolves to `()`
+            //~^ ERROR to be a future that resolves to `u8`, but it resolves to `()`
             async {}
         }
         let f: F = async { 1 };
diff --git a/src/test/ui/impl-trait/issues/issue-78722.stderr b/src/test/ui/impl-trait/issues/issue-78722.stderr
index a96994f5a7f..c00df8087e8 100644
--- a/src/test/ui/impl-trait/issues/issue-78722.stderr
+++ b/src/test/ui/impl-trait/issues/issue-78722.stderr
@@ -16,7 +16,7 @@ LL |         let f: F = async { 1 };
 LL |     }],
    |     - value is dropped here
 
-error[E0271]: expected `impl Future<Output = ()>` to be a future that resolves to `u8`, but it resolves to `()`
+error[E0271]: expected `[async block@$DIR/issue-78722.rs:11:13: 11:21]` to be a future that resolves to `u8`, but it resolves to `()`
   --> $DIR/issue-78722.rs:9:30
    |
 LL |         fn concrete_use() -> F {
diff --git a/src/test/ui/inference/deref-suggestion.stderr b/src/test/ui/inference/deref-suggestion.stderr
index d729f2d682a..034005697b4 100644
--- a/src/test/ui/inference/deref-suggestion.stderr
+++ b/src/test/ui/inference/deref-suggestion.stderr
@@ -157,11 +157,11 @@ error[E0308]: `if` and `else` have incompatible types
   --> $DIR/deref-suggestion.rs:69:12
    |
 LL |        let val = if true {
-   |   _______________-
-LL |  |         *a
-   |  |         -- expected because of this
-LL |  |     } else if true {
-   |  |____________^
+   |  ________________-
+LL | |          *a
+   | |          -- expected because of this
+LL | |      } else if true {
+   | | ____________^
 LL | ||
 LL | ||         b
 LL | ||     } else {
@@ -169,7 +169,7 @@ LL | ||         &0
 LL | ||     };
    | ||     ^
    | ||_____|
-   | |______`if` and `else` have incompatible types
+   |  |_____`if` and `else` have incompatible types
    |        expected `i32`, found `&{integer}`
 
 error: aborting due to 13 previous errors
diff --git a/src/test/ui/inference/issue-104649.rs b/src/test/ui/inference/issue-104649.rs
new file mode 100644
index 00000000000..4637b884d44
--- /dev/null
+++ b/src/test/ui/inference/issue-104649.rs
@@ -0,0 +1,32 @@
+type Result<T, E = Error> = ::std::result::Result<T, E>;
+struct Error;
+
+trait ForEach {
+    type Input;
+    fn for_each<F, U>(self, f: F)
+    where
+        F: FnOnce(Self::Input) -> U;
+}
+
+impl<T> ForEach for A<T> {
+    type Input = T;
+    fn for_each<F, U>(self, f: F)
+    where
+        F: FnOnce(Self::Input) -> U,
+    {
+        todo!()
+    }
+}
+
+struct A<T>(T);
+
+fn main() {
+    let a = A(Result::Ok(Result::Ok(()))); //~ ERROR type annotations needed
+    a.for_each(|a: Result<_>| {
+        let f = || match a {
+            Ok(Ok(a)) => {}
+            Ok(Err(a)) => {}
+            Err(a) => {}
+        };
+    });
+}
diff --git a/src/test/ui/inference/issue-104649.stderr b/src/test/ui/inference/issue-104649.stderr
new file mode 100644
index 00000000000..4962b21f9fd
--- /dev/null
+++ b/src/test/ui/inference/issue-104649.stderr
@@ -0,0 +1,14 @@
+error[E0282]: type annotations needed for `A<std::result::Result<std::result::Result<(), E>, Error>>`
+  --> $DIR/issue-104649.rs:24:9
+   |
+LL |     let a = A(Result::Ok(Result::Ok(())));
+   |         ^
+   |
+help: consider giving `a` an explicit type, where the type for type parameter `E` is specified
+   |
+LL |     let a: A<std::result::Result<std::result::Result<(), E>, Error>> = A(Result::Ok(Result::Ok(())));
+   |          +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0282`.
diff --git a/src/test/ui/issues/issue-13497-2.stderr b/src/test/ui/issues/issue-13497-2.stderr
index 6f72b79f2a5..3abeadf9e4b 100644
--- a/src/test/ui/issues/issue-13497-2.stderr
+++ b/src/test/ui/issues/issue-13497-2.stderr
@@ -2,12 +2,12 @@ error[E0515]: cannot return value referencing local variable `rawLines`
   --> $DIR/issue-13497-2.rs:3:5
    |
 LL |        rawLines
-   |   _____^
-   |  |_____|
+   |  ______^
+   | | _____|
    | ||
 LL | ||         .iter().map(|l| l.trim()).collect()
    | ||_______________-___________________________^ returns a value referencing data owned by the current function
-   | |________________|
+   |  |_______________|
    |                  `rawLines` is borrowed here
 
 error: aborting due to previous error
diff --git a/src/test/ui/lang-items/missing-clone-for-suggestion.rs b/src/test/ui/lang-items/missing-clone-for-suggestion.rs
new file mode 100644
index 00000000000..e8290c0098a
--- /dev/null
+++ b/src/test/ui/lang-items/missing-clone-for-suggestion.rs
@@ -0,0 +1,20 @@
+// Avoid panicking if the Clone trait is not found while building error suggestions
+// See #104870
+
+#![feature(no_core, lang_items)]
+#![no_core]
+
+#[lang = "sized"]
+trait Sized {}
+
+#[lang = "copy"]
+trait Copy {}
+
+fn g<T>(x: T) {}
+
+fn f(x: *mut u8) {
+    g(x);
+    g(x); //~ ERROR use of moved value: `x`
+}
+
+fn main() {}
diff --git a/src/test/ui/lang-items/missing-clone-for-suggestion.stderr b/src/test/ui/lang-items/missing-clone-for-suggestion.stderr
new file mode 100644
index 00000000000..35783a1be78
--- /dev/null
+++ b/src/test/ui/lang-items/missing-clone-for-suggestion.stderr
@@ -0,0 +1,21 @@
+error[E0382]: use of moved value: `x`
+  --> $DIR/missing-clone-for-suggestion.rs:17:7
+   |
+LL | fn f(x: *mut u8) {
+   |      - move occurs because `x` has type `*mut u8`, which does not implement the `Copy` trait
+LL |     g(x);
+   |       - value moved here
+LL |     g(x);
+   |       ^ value used here after move
+   |
+note: consider changing this parameter type in function `g` to borrow instead if owning the value isn't necessary
+  --> $DIR/missing-clone-for-suggestion.rs:13:12
+   |
+LL | fn g<T>(x: T) {}
+   |    -       ^ this parameter takes ownership of the value
+   |    |
+   |    in this function
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0382`.
diff --git a/src/test/ui/linkage-attr/link-attr-validation-late.rs b/src/test/ui/linkage-attr/link-attr-validation-late.rs
index b454fbd0ed1..34f720dd2d3 100644
--- a/src/test/ui/linkage-attr/link-attr-validation-late.rs
+++ b/src/test/ui/linkage-attr/link-attr-validation-late.rs
@@ -1,4 +1,3 @@
-#![feature(native_link_modifiers_verbatim)]
 #![feature(link_cfg)]
 
 // Top-level ill-formed
diff --git a/src/test/ui/linkage-attr/link-attr-validation-late.stderr b/src/test/ui/linkage-attr/link-attr-validation-late.stderr
index dd0f1dba2ec..1ad5fbaf7de 100644
--- a/src/test/ui/linkage-attr/link-attr-validation-late.stderr
+++ b/src/test/ui/linkage-attr/link-attr-validation-late.stderr
@@ -1,143 +1,143 @@
 error: unexpected `#[link]` argument, expected one of: name, kind, modifiers, cfg, wasm_import_module, import_name_type
-  --> $DIR/link-attr-validation-late.rs:5:22
+  --> $DIR/link-attr-validation-late.rs:4:22
    |
 LL | #[link(name = "...", "literal")]
    |                      ^^^^^^^^^
 
 error: unexpected `#[link]` argument, expected one of: name, kind, modifiers, cfg, wasm_import_module, import_name_type
-  --> $DIR/link-attr-validation-late.rs:6:22
+  --> $DIR/link-attr-validation-late.rs:5:22
    |
 LL | #[link(name = "...", unknown)]
    |                      ^^^^^^^
 
 error: multiple `name` arguments in a single `#[link]` attribute
-  --> $DIR/link-attr-validation-late.rs:10:22
+  --> $DIR/link-attr-validation-late.rs:9:22
    |
 LL | #[link(name = "foo", name = "bar")]
    |                      ^^^^^^^^^^^^
 
 error: multiple `kind` arguments in a single `#[link]` attribute
-  --> $DIR/link-attr-validation-late.rs:11:38
+  --> $DIR/link-attr-validation-late.rs:10:38
    |
 LL | #[link(name = "...", kind = "dylib", kind = "bar")]
    |                                      ^^^^^^^^^^^^
 
 error: multiple `modifiers` arguments in a single `#[link]` attribute
-  --> $DIR/link-attr-validation-late.rs:12:47
+  --> $DIR/link-attr-validation-late.rs:11:47
    |
 LL | #[link(name = "...", modifiers = "+verbatim", modifiers = "bar")]
    |                                               ^^^^^^^^^^^^^^^^^
 
 error: multiple `cfg` arguments in a single `#[link]` attribute
-  --> $DIR/link-attr-validation-late.rs:13:34
+  --> $DIR/link-attr-validation-late.rs:12:34
    |
 LL | #[link(name = "...", cfg(FALSE), cfg(FALSE))]
    |                                  ^^^^^^^^^^
 
 error: multiple `wasm_import_module` arguments in a single `#[link]` attribute
-  --> $DIR/link-attr-validation-late.rs:14:36
+  --> $DIR/link-attr-validation-late.rs:13:36
    |
 LL | #[link(wasm_import_module = "foo", wasm_import_module = "bar")]
    |                                    ^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 error: link name must be of the form `name = "string"`
-  --> $DIR/link-attr-validation-late.rs:18:8
+  --> $DIR/link-attr-validation-late.rs:17:8
    |
 LL | #[link(name)]
    |        ^^^^
 
 error[E0459]: `#[link]` attribute requires a `name = "string"` argument
-  --> $DIR/link-attr-validation-late.rs:18:1
+  --> $DIR/link-attr-validation-late.rs:17:1
    |
 LL | #[link(name)]
    | ^^^^^^^^^^^^^ missing `name` argument
 
 error: link name must be of the form `name = "string"`
-  --> $DIR/link-attr-validation-late.rs:20:8
+  --> $DIR/link-attr-validation-late.rs:19:8
    |
 LL | #[link(name())]
    |        ^^^^^^
 
 error[E0459]: `#[link]` attribute requires a `name = "string"` argument
-  --> $DIR/link-attr-validation-late.rs:20:1
+  --> $DIR/link-attr-validation-late.rs:19:1
    |
 LL | #[link(name())]
    | ^^^^^^^^^^^^^^^ missing `name` argument
 
 error: link kind must be of the form `kind = "string"`
-  --> $DIR/link-attr-validation-late.rs:22:22
+  --> $DIR/link-attr-validation-late.rs:21:22
    |
 LL | #[link(name = "...", kind)]
    |                      ^^^^
 
 error: link kind must be of the form `kind = "string"`
-  --> $DIR/link-attr-validation-late.rs:23:22
+  --> $DIR/link-attr-validation-late.rs:22:22
    |
 LL | #[link(name = "...", kind())]
    |                      ^^^^^^
 
 error: link modifiers must be of the form `modifiers = "string"`
-  --> $DIR/link-attr-validation-late.rs:24:22
+  --> $DIR/link-attr-validation-late.rs:23:22
    |
 LL | #[link(name = "...", modifiers)]
    |                      ^^^^^^^^^
 
 error: link modifiers must be of the form `modifiers = "string"`
-  --> $DIR/link-attr-validation-late.rs:25:22
+  --> $DIR/link-attr-validation-late.rs:24:22
    |
 LL | #[link(name = "...", modifiers())]
    |                      ^^^^^^^^^^^
 
 error: link cfg must be of the form `cfg(/* predicate */)`
-  --> $DIR/link-attr-validation-late.rs:26:22
+  --> $DIR/link-attr-validation-late.rs:25:22
    |
 LL | #[link(name = "...", cfg)]
    |                      ^^^
 
 error: link cfg must be of the form `cfg(/* predicate */)`
-  --> $DIR/link-attr-validation-late.rs:27:22
+  --> $DIR/link-attr-validation-late.rs:26:22
    |
 LL | #[link(name = "...", cfg = "literal")]
    |                      ^^^^^^^^^^^^^^^
 
 error: link cfg must have a single predicate argument
-  --> $DIR/link-attr-validation-late.rs:28:22
+  --> $DIR/link-attr-validation-late.rs:27:22
    |
 LL | #[link(name = "...", cfg("literal"))]
    |                      ^^^^^^^^^^^^^^
 
 error: wasm import module must be of the form `wasm_import_module = "string"`
-  --> $DIR/link-attr-validation-late.rs:29:22
+  --> $DIR/link-attr-validation-late.rs:28:22
    |
 LL | #[link(name = "...", wasm_import_module)]
    |                      ^^^^^^^^^^^^^^^^^^
 
 error: wasm import module must be of the form `wasm_import_module = "string"`
-  --> $DIR/link-attr-validation-late.rs:30:22
+  --> $DIR/link-attr-validation-late.rs:29:22
    |
 LL | #[link(name = "...", wasm_import_module())]
    |                      ^^^^^^^^^^^^^^^^^^^^
 
 error: invalid linking modifier syntax, expected '+' or '-' prefix before one of: bundle, verbatim, whole-archive, as-needed
-  --> $DIR/link-attr-validation-late.rs:34:34
+  --> $DIR/link-attr-validation-late.rs:33:34
    |
 LL | #[link(name = "...", modifiers = "")]
    |                                  ^^
 
 error: invalid linking modifier syntax, expected '+' or '-' prefix before one of: bundle, verbatim, whole-archive, as-needed
-  --> $DIR/link-attr-validation-late.rs:35:34
+  --> $DIR/link-attr-validation-late.rs:34:34
    |
 LL | #[link(name = "...", modifiers = "no-plus-minus")]
    |                                  ^^^^^^^^^^^^^^^
 
 error: unknown linking modifier `unknown`, expected one of: bundle, verbatim, whole-archive, as-needed
-  --> $DIR/link-attr-validation-late.rs:36:34
+  --> $DIR/link-attr-validation-late.rs:35:34
    |
 LL | #[link(name = "...", modifiers = "+unknown")]
    |                                  ^^^^^^^^^^
 
 error: multiple `verbatim` modifiers in a single `modifiers` argument
-  --> $DIR/link-attr-validation-late.rs:37:34
+  --> $DIR/link-attr-validation-late.rs:36:34
    |
 LL | #[link(name = "...", modifiers = "+verbatim,+verbatim")]
    |                                  ^^^^^^^^^^^^^^^^^^^^^
diff --git a/src/test/ui/lint/suggestions.stderr b/src/test/ui/lint/suggestions.stderr
index f4c0e2141b2..4caee777a13 100644
--- a/src/test/ui/lint/suggestions.stderr
+++ b/src/test/ui/lint/suggestions.stderr
@@ -41,12 +41,12 @@ warning: variable does not need to be mutable
   --> $DIR/suggestions.rs:54:13
    |
 LL |            let mut
-   |   _____________^
-   |  |_____________|
+   |  ______________^
+   | | _____________|
    | ||
 LL | ||             b = 1;
    | ||____________-^
-   |  |____________|
+   | |_____________|
    |               help: remove this `mut`
 
 error: const items should never be `#[no_mangle]`
diff --git a/src/test/ui/lint/unused/issue-88519-unused-paren.rs b/src/test/ui/lint/unused/issue-88519-unused-paren.rs
index be02fcd3f00..ce3d15ac183 100644
--- a/src/test/ui/lint/unused/issue-88519-unused-paren.rs
+++ b/src/test/ui/lint/unused/issue-88519-unused-paren.rs
@@ -51,22 +51,13 @@ mod casts {
 
 mod typeascription {
     fn outside() -> u8 {
-        ({ 0 }): u8
-    }
-    fn inside() -> u8 {
-        ({ 0 }: u8)
+        type_ascribe!(({ 0 }), u8)
     }
     fn outside_match() -> u8 {
-        (match 0 { x => x }): u8
-    }
-    fn inside_match() -> u8 {
-        (match 0 { x => x }: u8)
+        type_ascribe!((match 0 { x => x }), u8)
     }
     fn outside_if() -> u8 {
-        (if false { 0 } else { 0 }): u8
-    }
-    fn inside_if() -> u8 {
-        (if false { 0 } else { 0 }: u8)
+        type_ascribe!((if false { 0 } else { 0 }), u8)
     }
 }
 
diff --git a/src/test/ui/macros/issue-104769-concat_bytes-invalid-literal.rs b/src/test/ui/macros/issue-104769-concat_bytes-invalid-literal.rs
new file mode 100644
index 00000000000..24150376ef0
--- /dev/null
+++ b/src/test/ui/macros/issue-104769-concat_bytes-invalid-literal.rs
@@ -0,0 +1,8 @@
+#![feature(concat_bytes)]
+
+fn main() {
+    concat_bytes!(7Y);
+    //~^ ERROR invalid suffix `Y` for number literal
+    concat_bytes!(888888888888888888888888888888888888888);
+    //~^ ERROR integer literal is too large
+}
diff --git a/src/test/ui/macros/issue-104769-concat_bytes-invalid-literal.stderr b/src/test/ui/macros/issue-104769-concat_bytes-invalid-literal.stderr
new file mode 100644
index 00000000000..8d70faa494d
--- /dev/null
+++ b/src/test/ui/macros/issue-104769-concat_bytes-invalid-literal.stderr
@@ -0,0 +1,16 @@
+error: invalid suffix `Y` for number literal
+  --> $DIR/issue-104769-concat_bytes-invalid-literal.rs:4:19
+   |
+LL |     concat_bytes!(7Y);
+   |                   ^^ invalid suffix `Y`
+   |
+   = help: the suffix must be one of the numeric types (`u32`, `isize`, `f32`, etc.)
+
+error: integer literal is too large
+  --> $DIR/issue-104769-concat_bytes-invalid-literal.rs:6:19
+   |
+LL |     concat_bytes!(888888888888888888888888888888888888888);
+   |                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: aborting due to 2 previous errors
+
diff --git a/src/test/ui/macros/issue-105011.rs b/src/test/ui/macros/issue-105011.rs
new file mode 100644
index 00000000000..da12c381464
--- /dev/null
+++ b/src/test/ui/macros/issue-105011.rs
@@ -0,0 +1,3 @@
+fn main() {
+    println!(""y); //~ ERROR suffixes on string literals are invalid
+}
diff --git a/src/test/ui/macros/issue-105011.stderr b/src/test/ui/macros/issue-105011.stderr
new file mode 100644
index 00000000000..e898af7faa3
--- /dev/null
+++ b/src/test/ui/macros/issue-105011.stderr
@@ -0,0 +1,8 @@
+error: suffixes on string literals are invalid
+  --> $DIR/issue-105011.rs:2:14
+   |
+LL |     println!(""y);
+   |              ^^^ invalid suffix `y`
+
+error: aborting due to previous error
+
diff --git a/src/test/ui/mir/mir_ascription_coercion.rs b/src/test/ui/mir/mir_ascription_coercion.rs
index 0ebd20e97d7..9e04d601987 100644
--- a/src/test/ui/mir/mir_ascription_coercion.rs
+++ b/src/test/ui/mir/mir_ascription_coercion.rs
@@ -6,5 +6,5 @@
 fn main() {
     let x = [1, 2, 3];
     // The RHS should coerce to &[i32]
-    let _y : &[i32] = &x : &[i32; 3];
+    let _y : &[i32] = type_ascribe!(&x, &[i32; 3]);
 }
diff --git a/src/test/ui/nll/user-annotations/issue-57731-ascibed-coupled-types.rs b/src/test/ui/nll/user-annotations/issue-57731-ascibed-coupled-types.rs
index 9b3ec702c75..95c655654ea 100644
--- a/src/test/ui/nll/user-annotations/issue-57731-ascibed-coupled-types.rs
+++ b/src/test/ui/nll/user-annotations/issue-57731-ascibed-coupled-types.rs
@@ -8,17 +8,17 @@ type PairCoupledTypes<T> = (T, T);
 type PairCoupledRegions<'a, T> = (&'a T, &'a T);
 
 fn uncoupled_wilds_rhs<'a>(_x: &'a u32, s: &'static u32) -> &'static u32 {
-    let ((y, _z),) = ((s, _x),): (PairUncoupled<_>,);
+    let ((y, _z),) = type_ascribe!(((s, _x),), (PairUncoupled<_>,));
     y // OK
 }
 
 fn coupled_wilds_rhs<'a>(_x: &'a u32, s: &'static u32) -> &'static u32 {
-    let ((y, _z),) = ((s, _x),): (PairCoupledTypes<_>,);
+    let ((y, _z),) = type_ascribe!(((s, _x),), (PairCoupledTypes<_>,));
     y //~ ERROR lifetime may not live long enough
 }
 
 fn coupled_regions_rhs<'a>(_x: &'a u32, s: &'static u32) -> &'static u32 {
-    let ((y, _z),) = ((s, _x),): (PairCoupledRegions<_>,);
+    let ((y, _z),) = type_ascribe!(((s, _x),), (PairCoupledRegions<_>,));
     y //~ ERROR lifetime may not live long enough
 }
 
diff --git a/src/test/ui/nll/user-annotations/issue-57731-ascibed-coupled-types.stderr b/src/test/ui/nll/user-annotations/issue-57731-ascibed-coupled-types.stderr
index c99f53c5aa4..8601691e88a 100644
--- a/src/test/ui/nll/user-annotations/issue-57731-ascibed-coupled-types.stderr
+++ b/src/test/ui/nll/user-annotations/issue-57731-ascibed-coupled-types.stderr
@@ -3,7 +3,7 @@ error: lifetime may not live long enough
    |
 LL | fn coupled_wilds_rhs<'a>(_x: &'a u32, s: &'static u32) -> &'static u32 {
    |                      -- lifetime `'a` defined here
-LL |     let ((y, _z),) = ((s, _x),): (PairCoupledTypes<_>,);
+LL |     let ((y, _z),) = type_ascribe!(((s, _x),), (PairCoupledTypes<_>,));
 LL |     y
    |     ^ returning this value requires that `'a` must outlive `'static`
 
@@ -12,7 +12,7 @@ error: lifetime may not live long enough
    |
 LL | fn coupled_regions_rhs<'a>(_x: &'a u32, s: &'static u32) -> &'static u32 {
    |                        -- lifetime `'a` defined here
-LL |     let ((y, _z),) = ((s, _x),): (PairCoupledRegions<_>,);
+LL |     let ((y, _z),) = type_ascribe!(((s, _x),), (PairCoupledRegions<_>,));
 LL |     y
    |     ^ returning this value requires that `'a` must outlive `'static`
 
diff --git a/src/test/ui/nll/user-annotations/type_ascription_static_lifetime.rs b/src/test/ui/nll/user-annotations/type_ascription_static_lifetime.rs
index 101b5cfabb3..88d646dee7c 100644
--- a/src/test/ui/nll/user-annotations/type_ascription_static_lifetime.rs
+++ b/src/test/ui/nll/user-annotations/type_ascription_static_lifetime.rs
@@ -3,5 +3,5 @@
 
 fn main() {
     let x = 22_u32;
-    let y: &u32 = &x: &'static u32; //~ ERROR E0597
+    let y: &u32 = type_ascribe!(&x, &'static u32); //~ ERROR E0597
 }
diff --git a/src/test/ui/nll/user-annotations/type_ascription_static_lifetime.stderr b/src/test/ui/nll/user-annotations/type_ascription_static_lifetime.stderr
index 133bbef5231..ccbf3c1d927 100644
--- a/src/test/ui/nll/user-annotations/type_ascription_static_lifetime.stderr
+++ b/src/test/ui/nll/user-annotations/type_ascription_static_lifetime.stderr
@@ -1,10 +1,10 @@
 error[E0597]: `x` does not live long enough
-  --> $DIR/type_ascription_static_lifetime.rs:6:19
+  --> $DIR/type_ascription_static_lifetime.rs:6:33
    |
-LL |     let y: &u32 = &x: &'static u32;
-   |                   ^^--------------
-   |                   |
-   |                   borrowed value does not live long enough
+LL |     let y: &u32 = type_ascribe!(&x, &'static u32);
+   |                   --------------^^---------------
+   |                   |             |
+   |                   |             borrowed value does not live long enough
    |                   type annotation requires that `x` is borrowed for `'static`
 LL | }
    | - `x` dropped here while still borrowed
diff --git a/src/test/ui/pattern/non-structural-match-types.stderr b/src/test/ui/pattern/non-structural-match-types.stderr
index 45e16264973..dea7c4695cc 100644
--- a/src/test/ui/pattern/non-structural-match-types.stderr
+++ b/src/test/ui/pattern/non-structural-match-types.stderr
@@ -4,7 +4,7 @@ error: `[closure@$DIR/non-structural-match-types.rs:9:17: 9:19]` cannot be used
 LL |         const { || {} } => {},
    |         ^^^^^^^^^^^^^^^
 
-error: `impl Future<Output = ()>` cannot be used in patterns
+error: `[async block@$DIR/non-structural-match-types.rs:12:17: 12:25]` cannot be used in patterns
   --> $DIR/non-structural-match-types.rs:12:9
    |
 LL |         const { async {} } => {},
diff --git a/src/test/ui/privacy/effective_visibilities.rs b/src/test/ui/privacy/effective_visibilities.rs
index 8d0602fa79f..ff20e20d332 100644
--- a/src/test/ui/privacy/effective_visibilities.rs
+++ b/src/test/ui/privacy/effective_visibilities.rs
@@ -72,6 +72,5 @@ mod half_public_import {
 
 #[rustc_effective_visibility]
 pub use half_public_import::HalfPublicImport; //~ ERROR Direct: pub, Reexported: pub, Reachable: pub, ReachableThroughImplTrait: pub
-                                              //~^ ERROR Direct: pub, Reexported: pub, Reachable: pub, ReachableThroughImplTrait: pub
 
 fn main() {}
diff --git a/src/test/ui/privacy/effective_visibilities.stderr b/src/test/ui/privacy/effective_visibilities.stderr
index 6a99afe64fe..046b6095f4e 100644
--- a/src/test/ui/privacy/effective_visibilities.stderr
+++ b/src/test/ui/privacy/effective_visibilities.stderr
@@ -124,12 +124,6 @@ error: Direct: pub, Reexported: pub, Reachable: pub, ReachableThroughImplTrait:
 LL | pub use half_public_import::HalfPublicImport;
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
-error: Direct: pub, Reexported: pub, Reachable: pub, ReachableThroughImplTrait: pub
-  --> $DIR/effective_visibilities.rs:74:9
-   |
-LL | pub use half_public_import::HalfPublicImport;
-   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
 error: Direct: pub(crate), Reexported: pub, Reachable: pub, ReachableThroughImplTrait: pub
   --> $DIR/effective_visibilities.rs:14:13
    |
@@ -142,5 +136,5 @@ error: Direct: pub(crate), Reexported: pub, Reachable: pub, ReachableThroughImpl
 LL |             type B;
    |             ^^^^^^
 
-error: aborting due to 24 previous errors
+error: aborting due to 23 previous errors
 
diff --git a/src/test/ui/proc-macro/auxiliary/issue-104884.rs b/src/test/ui/proc-macro/auxiliary/issue-104884.rs
new file mode 100644
index 00000000000..0de59d00573
--- /dev/null
+++ b/src/test/ui/proc-macro/auxiliary/issue-104884.rs
@@ -0,0 +1,23 @@
+// force-host
+// no-prefer-dynamic
+
+#![crate_type = "proc-macro"]
+
+extern crate proc_macro;
+
+use proc_macro::TokenStream;
+
+#[proc_macro_derive(AddImpl)]
+
+pub fn derive(input: TokenStream) -> TokenStream {
+    "use std::cmp::Ordering;
+
+    impl<T> Ord for PriorityQueue<T> {
+        fn cmp(&self, other: &Self) -> Ordering {
+            self.0.cmp(&self.height)
+        }
+    }
+    "
+    .parse()
+    .unwrap()
+}
diff --git a/src/test/ui/proc-macro/issue-104884-trait-impl-sugg-err.rs b/src/test/ui/proc-macro/issue-104884-trait-impl-sugg-err.rs
new file mode 100644
index 00000000000..a0d619c4566
--- /dev/null
+++ b/src/test/ui/proc-macro/issue-104884-trait-impl-sugg-err.rs
@@ -0,0 +1,20 @@
+// aux-build:issue-104884.rs
+
+use std::collections::BinaryHeap;
+
+#[macro_use]
+extern crate issue_104884;
+
+#[derive(PartialEq, Eq, PartialOrd, Ord)]
+struct PriorityQueueEntry<T> {
+    value: T,
+}
+
+#[derive(PartialOrd, AddImpl)]
+//~^ ERROR can't compare `PriorityQueue<T>` with `PriorityQueue<T>`
+//~| ERROR the trait bound `PriorityQueue<T>: Eq` is not satisfied
+//~| ERROR can't compare `T` with `T`
+
+struct PriorityQueue<T>(BinaryHeap<PriorityQueueEntry<T>>);
+
+fn main() {}
diff --git a/src/test/ui/proc-macro/issue-104884-trait-impl-sugg-err.stderr b/src/test/ui/proc-macro/issue-104884-trait-impl-sugg-err.stderr
new file mode 100644
index 00000000000..ac49e04e3c0
--- /dev/null
+++ b/src/test/ui/proc-macro/issue-104884-trait-impl-sugg-err.stderr
@@ -0,0 +1,48 @@
+error[E0277]: can't compare `PriorityQueue<T>` with `PriorityQueue<T>`
+  --> $DIR/issue-104884-trait-impl-sugg-err.rs:13:10
+   |
+LL | #[derive(PartialOrd, AddImpl)]
+   |          ^^^^^^^^^^ no implementation for `PriorityQueue<T> == PriorityQueue<T>`
+   |
+   = help: the trait `PartialEq` is not implemented for `PriorityQueue<T>`
+note: required by a bound in `PartialOrd`
+  --> $SRC_DIR/core/src/cmp.rs:LL:COL
+   |
+LL | pub trait PartialOrd<Rhs: ?Sized = Self>: PartialEq<Rhs> {
+   |                                           ^^^^^^^^^^^^^^ required by this bound in `PartialOrd`
+   = note: this error originates in the derive macro `PartialOrd` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error[E0277]: the trait bound `PriorityQueue<T>: Eq` is not satisfied
+  --> $DIR/issue-104884-trait-impl-sugg-err.rs:13:22
+   |
+LL | #[derive(PartialOrd, AddImpl)]
+   |                      ^^^^^^^ the trait `Eq` is not implemented for `PriorityQueue<T>`
+   |
+note: required by a bound in `Ord`
+  --> $SRC_DIR/core/src/cmp.rs:LL:COL
+   |
+LL | pub trait Ord: Eq + PartialOrd<Self> {
+   |                ^^ required by this bound in `Ord`
+   = note: this error originates in the derive macro `AddImpl` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error[E0277]: can't compare `T` with `T`
+  --> $DIR/issue-104884-trait-impl-sugg-err.rs:13:22
+   |
+LL | #[derive(PartialOrd, AddImpl)]
+   |                      ^^^^^^^ no implementation for `T < T` and `T > T`
+   |
+note: required for `PriorityQueue<T>` to implement `PartialOrd`
+  --> $DIR/issue-104884-trait-impl-sugg-err.rs:13:10
+   |
+LL | #[derive(PartialOrd, AddImpl)]
+   |          ^^^^^^^^^^
+note: required by a bound in `Ord`
+  --> $SRC_DIR/core/src/cmp.rs:LL:COL
+   |
+LL | pub trait Ord: Eq + PartialOrd<Self> {
+   |                     ^^^^^^^^^^^^^^^^ required by this bound in `Ord`
+   = note: this error originates in the derive macro `AddImpl` which comes from the expansion of the derive macro `PartialOrd` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: aborting due to 3 previous errors
+
+For more information about this error, try `rustc --explain E0277`.
diff --git a/src/test/ui/raw-ref-op/raw-ref-temp-deref.rs b/src/test/ui/raw-ref-op/raw-ref-temp-deref.rs
index a814003aebf..2e075a1b9e8 100644
--- a/src/test/ui/raw-ref-op/raw-ref-temp-deref.rs
+++ b/src/test/ui/raw-ref-op/raw-ref-temp-deref.rs
@@ -18,7 +18,7 @@ fn main() {
     let index_deref_ref = &raw const SLICE_REF[1];
 
     let x = 0;
-    let ascribe_ref = &raw const (x: i32);
-    let ascribe_deref = &raw const (*ARRAY_REF: [i32; 2]);
-    let ascribe_index_deref = &raw const (ARRAY_REF[0]: i32);
+    let ascribe_ref = &raw const type_ascribe!(x, i32);
+    let ascribe_deref = &raw const type_ascribe!(*ARRAY_REF, [i32; 2]);
+    let ascribe_index_deref = &raw const type_ascribe!(ARRAY_REF[0], i32);
 }
diff --git a/src/test/ui/raw-ref-op/raw-ref-temp.rs b/src/test/ui/raw-ref-op/raw-ref-temp.rs
index 32df56468da..10e47cb34c5 100644
--- a/src/test/ui/raw-ref-op/raw-ref-temp.rs
+++ b/src/test/ui/raw-ref-op/raw-ref-temp.rs
@@ -8,24 +8,24 @@ const PAIR: (i32, i64) = (1, 2);
 const ARRAY: [i32; 2] = [1, 2];
 
 fn main() {
-    let ref_expr = &raw const 2;                        //~ ERROR cannot take address
-    let mut_ref_expr = &raw mut 3;                      //~ ERROR cannot take address
-    let ref_const = &raw const FOUR;                    //~ ERROR cannot take address
-    let mut_ref_const = &raw mut FOUR;                  //~ ERROR cannot take address
-
-    let field_ref_expr = &raw const (1, 2).0;           //~ ERROR cannot take address
-    let mut_field_ref_expr = &raw mut (1, 2).0;         //~ ERROR cannot take address
-    let field_ref = &raw const PAIR.0;                  //~ ERROR cannot take address
-    let mut_field_ref = &raw mut PAIR.0;                //~ ERROR cannot take address
-
-    let index_ref_expr = &raw const [1, 2][0];          //~ ERROR cannot take address
-    let mut_index_ref_expr = &raw mut [1, 2][0];        //~ ERROR cannot take address
-    let index_ref = &raw const ARRAY[0];                //~ ERROR cannot take address
-    let mut_index_ref = &raw mut ARRAY[1];              //~ ERROR cannot take address
-
-    let ref_ascribe = &raw const (2: i32);              //~ ERROR cannot take address
-    let mut_ref_ascribe = &raw mut (3: i32);            //~ ERROR cannot take address
-
-    let ascribe_field_ref = &raw const (PAIR.0: i32);   //~ ERROR cannot take address
-    let ascribe_index_ref = &raw mut (ARRAY[0]: i32);   //~ ERROR cannot take address
+    let ref_expr = &raw const 2;                                    //~ ERROR cannot take address
+    let mut_ref_expr = &raw mut 3;                                  //~ ERROR cannot take address
+    let ref_const = &raw const FOUR;                                //~ ERROR cannot take address
+    let mut_ref_const = &raw mut FOUR;                              //~ ERROR cannot take address
+
+    let field_ref_expr = &raw const (1, 2).0;                       //~ ERROR cannot take address
+    let mut_field_ref_expr = &raw mut (1, 2).0;                     //~ ERROR cannot take address
+    let field_ref = &raw const PAIR.0;                              //~ ERROR cannot take address
+    let mut_field_ref = &raw mut PAIR.0;                            //~ ERROR cannot take address
+
+    let index_ref_expr = &raw const [1, 2][0];                      //~ ERROR cannot take address
+    let mut_index_ref_expr = &raw mut [1, 2][0];                    //~ ERROR cannot take address
+    let index_ref = &raw const ARRAY[0];                            //~ ERROR cannot take address
+    let mut_index_ref = &raw mut ARRAY[1];                          //~ ERROR cannot take address
+
+    let ref_ascribe = &raw const type_ascribe!(2, i32);             //~ ERROR cannot take address
+    let mut_ref_ascribe = &raw mut type_ascribe!(3, i32);           //~ ERROR cannot take address
+
+    let ascribe_field_ref = &raw const type_ascribe!(PAIR.0, i32);  //~ ERROR cannot take address
+    let ascribe_index_ref = &raw mut type_ascribe!(ARRAY[0], i32);  //~ ERROR cannot take address
 }
diff --git a/src/test/ui/raw-ref-op/raw-ref-temp.stderr b/src/test/ui/raw-ref-op/raw-ref-temp.stderr
index 80dea76d595..b9666162517 100644
--- a/src/test/ui/raw-ref-op/raw-ref-temp.stderr
+++ b/src/test/ui/raw-ref-op/raw-ref-temp.stderr
@@ -73,26 +73,26 @@ LL |     let mut_index_ref = &raw mut ARRAY[1];
 error[E0745]: cannot take address of a temporary
   --> $DIR/raw-ref-temp.rs:26:34
    |
-LL |     let ref_ascribe = &raw const (2: i32);
-   |                                  ^^^^^^^^ temporary value
+LL |     let ref_ascribe = &raw const type_ascribe!(2, i32);
+   |                                  ^^^^^^^^^^^^^^^^^^^^^ temporary value
 
 error[E0745]: cannot take address of a temporary
   --> $DIR/raw-ref-temp.rs:27:36
    |
-LL |     let mut_ref_ascribe = &raw mut (3: i32);
-   |                                    ^^^^^^^^ temporary value
+LL |     let mut_ref_ascribe = &raw mut type_ascribe!(3, i32);
+   |                                    ^^^^^^^^^^^^^^^^^^^^^ temporary value
 
 error[E0745]: cannot take address of a temporary
   --> $DIR/raw-ref-temp.rs:29:40
    |
-LL |     let ascribe_field_ref = &raw const (PAIR.0: i32);
-   |                                        ^^^^^^^^^^^^^ temporary value
+LL |     let ascribe_field_ref = &raw const type_ascribe!(PAIR.0, i32);
+   |                                        ^^^^^^^^^^^^^^^^^^^^^^^^^^ temporary value
 
 error[E0745]: cannot take address of a temporary
   --> $DIR/raw-ref-temp.rs:30:38
    |
-LL |     let ascribe_index_ref = &raw mut (ARRAY[0]: i32);
-   |                                      ^^^^^^^^^^^^^^^ temporary value
+LL |     let ascribe_index_ref = &raw mut type_ascribe!(ARRAY[0], i32);
+   |                                      ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ temporary value
 
 error: aborting due to 16 previous errors
 
diff --git a/src/test/ui/reachable/expr_type.rs b/src/test/ui/reachable/expr_type.rs
index 8d32397b542..1ceb2f85971 100644
--- a/src/test/ui/reachable/expr_type.rs
+++ b/src/test/ui/reachable/expr_type.rs
@@ -6,7 +6,7 @@
 
 fn a() {
     // the cast is unreachable:
-    let x = {return}: !; //~ ERROR unreachable
+    let x = type_ascribe!({return}, !); //~ ERROR unreachable
 }
 
 fn main() { }
diff --git a/src/test/ui/reachable/expr_type.stderr b/src/test/ui/reachable/expr_type.stderr
index c56c64be721..3cb4a32e02f 100644
--- a/src/test/ui/reachable/expr_type.stderr
+++ b/src/test/ui/reachable/expr_type.stderr
@@ -1,10 +1,10 @@
 error: unreachable expression
   --> $DIR/expr_type.rs:9:13
    |
-LL |     let x = {return}: !;
-   |             ^------^^^^
-   |             ||
-   |             |any code following this expression is unreachable
+LL |     let x = type_ascribe!({return}, !);
+   |             ^^^^^^^^^^^^^^^------^^^^^
+   |             |              |
+   |             |              any code following this expression is unreachable
    |             unreachable expression
    |
 note: the lint level is defined here
diff --git a/src/test/ui/return/issue-86188-return-not-in-fn-body.stderr b/src/test/ui/return/issue-86188-return-not-in-fn-body.stderr
index d7eeb3a7290..4f938670e5e 100644
--- a/src/test/ui/return/issue-86188-return-not-in-fn-body.stderr
+++ b/src/test/ui/return/issue-86188-return-not-in-fn-body.stderr
@@ -35,17 +35,17 @@ LL | |     }
 error[E0572]: return statement outside of function body
   --> $DIR/issue-86188-return-not-in-fn-body.rs:36:10
    |
-LL |  / fn main() {
-LL |  |
-LL |  |     [(); return || {
-   |  |__________^
+LL | /  fn main() {
+LL | |
+LL | |      [(); return || {
+   | | __________^
 LL | ||
 LL | ||
 LL | ||         let tx;
 LL | ||     }];
    | ||_____^ the return is part of this body...
-LL |  | }
-   |  |_- ...not the enclosing function body
+LL | |  }
+   | |__- ...not the enclosing function body
 
 error: aborting due to 4 previous errors
 
diff --git a/src/test/ui/span/macro-ty-params.rs b/src/test/ui/span/macro-ty-params.rs
index 0a93105b664..cf28b0255d1 100644
--- a/src/test/ui/span/macro-ty-params.rs
+++ b/src/test/ui/span/macro-ty-params.rs
@@ -9,5 +9,7 @@ macro_rules! foo { () => () }
 fn main() {
     foo::<T>!(); //~ ERROR generic arguments in macro path
     foo::<>!(); //~ ERROR generic arguments in macro path
-    m!(Default<>); //~ ERROR unexpected generic arguments in path
+    m!(Default<>);
+    //~^ ERROR unexpected generic arguments in path
+    //~^^ ERROR generic arguments in macro path
 }
diff --git a/src/test/ui/span/macro-ty-params.stderr b/src/test/ui/span/macro-ty-params.stderr
index 138cd2598a1..7023ef8cd1c 100644
--- a/src/test/ui/span/macro-ty-params.stderr
+++ b/src/test/ui/span/macro-ty-params.stderr
@@ -16,5 +16,11 @@ error: unexpected generic arguments in path
 LL |     m!(Default<>);
    |               ^^
 
-error: aborting due to 3 previous errors
+error: generic arguments in macro path
+  --> $DIR/macro-ty-params.rs:12:15
+   |
+LL |     m!(Default<>);
+   |               ^^
+
+error: aborting due to 4 previous errors
 
diff --git a/src/test/ui/stats/hir-stats.stderr b/src/test/ui/stats/hir-stats.stderr
index 2a0e9497a21..15900bef7f6 100644
--- a/src/test/ui/stats/hir-stats.stderr
+++ b/src/test/ui/stats/hir-stats.stderr
@@ -120,59 +120,59 @@ hir-stats Name                Accumulated Size         Count     Item Size
 hir-stats ----------------------------------------------------------------
 hir-stats ForeignItemRef            24 ( 0.3%)             1            24
 hir-stats Lifetime                  24 ( 0.3%)             1            24
-hir-stats Mod                       32 ( 0.3%)             1            32
+hir-stats Mod                       32 ( 0.4%)             1            32
 hir-stats ExprField                 40 ( 0.4%)             1            40
 hir-stats TraitItemRef              56 ( 0.6%)             2            28
 hir-stats Local                     64 ( 0.7%)             1            64
 hir-stats Param                     64 ( 0.7%)             2            32
 hir-stats InlineAsm                 72 ( 0.8%)             1            72
 hir-stats ImplItemRef               72 ( 0.8%)             2            36
-hir-stats Body                      96 ( 1.0%)             3            32
-hir-stats FieldDef                  96 ( 1.0%)             2            48
-hir-stats Arm                       96 ( 1.0%)             2            48
-hir-stats Stmt                      96 ( 1.0%)             3            32
-hir-stats - Local                     32 ( 0.3%)             1
-hir-stats - Semi                      32 ( 0.3%)             1
-hir-stats - Expr                      32 ( 0.3%)             1
+hir-stats Body                      96 ( 1.1%)             3            32
+hir-stats FieldDef                  96 ( 1.1%)             2            48
+hir-stats Arm                       96 ( 1.1%)             2            48
+hir-stats Stmt                      96 ( 1.1%)             3            32
+hir-stats - Local                     32 ( 0.4%)             1
+hir-stats - Semi                      32 ( 0.4%)             1
+hir-stats - Expr                      32 ( 0.4%)             1
 hir-stats FnDecl                   120 ( 1.3%)             3            40
 hir-stats Attribute                128 ( 1.4%)             4            32
 hir-stats GenericArg               128 ( 1.4%)             4            32
-hir-stats - Type                      32 ( 0.3%)             1
-hir-stats - Lifetime                  96 ( 1.0%)             3
+hir-stats - Type                      32 ( 0.4%)             1
+hir-stats - Lifetime                  96 ( 1.1%)             3
 hir-stats GenericArgs              144 ( 1.6%)             3            48
 hir-stats Variant                  176 ( 1.9%)             2            88
 hir-stats GenericBound             192 ( 2.1%)             4            48
 hir-stats - Trait                    192 ( 2.1%)             4
 hir-stats WherePredicate           192 ( 2.1%)             3            64
 hir-stats - BoundPredicate           192 ( 2.1%)             3
-hir-stats Block                    288 ( 3.1%)             6            48
-hir-stats Pat                      360 ( 3.9%)             5            72
+hir-stats Block                    288 ( 3.2%)             6            48
+hir-stats Pat                      360 ( 4.0%)             5            72
 hir-stats - Wild                      72 ( 0.8%)             1
 hir-stats - Struct                    72 ( 0.8%)             1
 hir-stats - Binding                  216 ( 2.4%)             3
 hir-stats GenericParam             400 ( 4.4%)             5            80
-hir-stats Generics                 560 ( 6.1%)            10            56
-hir-stats Ty                       720 ( 7.9%)            15            48
+hir-stats Generics                 560 ( 6.2%)            10            56
+hir-stats Ty                       720 ( 8.0%)            15            48
 hir-stats - Ptr                       48 ( 0.5%)             1
 hir-stats - Rptr                      48 ( 0.5%)             1
-hir-stats - Path                     624 ( 6.8%)            13
-hir-stats Expr                     768 ( 8.4%)            12            64
+hir-stats - Path                     624 ( 6.9%)            13
+hir-stats Expr                     768 ( 8.5%)            12            64
 hir-stats - Path                      64 ( 0.7%)             1
 hir-stats - Struct                    64 ( 0.7%)             1
 hir-stats - Match                     64 ( 0.7%)             1
 hir-stats - InlineAsm                 64 ( 0.7%)             1
 hir-stats - Lit                      128 ( 1.4%)             2
 hir-stats - Block                    384 ( 4.2%)             6
-hir-stats Item                     960 (10.5%)            12            80
+hir-stats Item                     880 ( 9.7%)            11            80
 hir-stats - Trait                     80 ( 0.9%)             1
 hir-stats - Enum                      80 ( 0.9%)             1
 hir-stats - ExternCrate               80 ( 0.9%)             1
 hir-stats - ForeignMod                80 ( 0.9%)             1
 hir-stats - Impl                      80 ( 0.9%)             1
-hir-stats - Fn                       160 ( 1.7%)             2
-hir-stats - Use                      400 ( 4.4%)             5
-hir-stats Path                   1_280 (14.0%)            32            40
-hir-stats PathSegment            1_920 (20.9%)            40            48
+hir-stats - Fn                       160 ( 1.8%)             2
+hir-stats - Use                      320 ( 3.5%)             4
+hir-stats Path                   1_240 (13.7%)            31            40
+hir-stats PathSegment            1_920 (21.2%)            40            48
 hir-stats ----------------------------------------------------------------
-hir-stats Total                  9_168
+hir-stats Total                  9_048
 hir-stats
diff --git a/src/test/ui/suggestions/expected-boxed-future-isnt-pinned.stderr b/src/test/ui/suggestions/expected-boxed-future-isnt-pinned.stderr
index 918d37e6559..34ff59a9bb0 100644
--- a/src/test/ui/suggestions/expected-boxed-future-isnt-pinned.stderr
+++ b/src/test/ui/suggestions/expected-boxed-future-isnt-pinned.stderr
@@ -87,7 +87,7 @@ LL | |     }
    |       arguments to this function are incorrect
    |
    = note:     expected struct `Pin<Box<dyn Future<Output = i32> + Send>>`
-           found `async` block `impl Future<Output = {integer}>`
+           found `async` block `[async block@$DIR/expected-boxed-future-isnt-pinned.rs:28:5: 30:6]`
 note: function defined here
   --> $SRC_DIR/core/src/future/mod.rs:LL:COL
    |
diff --git a/src/test/ui/suggestions/issue-99240-2.stderr b/src/test/ui/suggestions/issue-99240-2.stderr
index 2af60f59759..260df85653b 100644
--- a/src/test/ui/suggestions/issue-99240-2.stderr
+++ b/src/test/ui/suggestions/issue-99240-2.stderr
@@ -5,12 +5,12 @@ LL |        Unit,
    |        ---- enum variant `Alias::Unit` defined here
 ...
 LL |        Alias::
-   |   _____^
-   |  |_____|
+   |  ______^
+   | | _____|
    | ||
 LL | ||     Unit();
    | ||________^_- call expression requires function
-   | |_________|
+   |  |________|
    | 
    |
 help: `Alias::Unit` is a unit enum variant, and does not take parentheses to be constructed
diff --git a/src/test/ui/suggestions/suggest-remove-refs-3.stderr b/src/test/ui/suggestions/suggest-remove-refs-3.stderr
index 4d07324273c..31cca323d0e 100644
--- a/src/test/ui/suggestions/suggest-remove-refs-3.stderr
+++ b/src/test/ui/suggestions/suggest-remove-refs-3.stderr
@@ -2,8 +2,8 @@ error[E0277]: `&&&&&Enumerate<std::slice::Iter<'_, {integer}>>` is not an iterat
   --> $DIR/suggest-remove-refs-3.rs:6:19
    |
 LL |        for (i, _) in & & &
-   |   ___________________^
-   |  |___________________|
+   |  ____________________^
+   | | ___________________|
    | ||
 LL | ||         & &v
    | ||___________- help: consider removing 5 leading `&`-references
diff --git a/src/test/ui/suggestions/suggest_print_over_printf.rs b/src/test/ui/suggestions/suggest_print_over_printf.rs
new file mode 100644
index 00000000000..124ddec50cb
--- /dev/null
+++ b/src/test/ui/suggestions/suggest_print_over_printf.rs
@@ -0,0 +1,8 @@
+// Suggest print macro when user erroneously uses printf
+
+fn main() {
+    let x = 4;
+    printf("%d", x);
+    //~^ ERROR cannot find function `printf` in this scope
+    //~| HELP you may have meant to use the `print` macro
+}
diff --git a/src/test/ui/suggestions/suggest_print_over_printf.stderr b/src/test/ui/suggestions/suggest_print_over_printf.stderr
new file mode 100644
index 00000000000..1214bec16ce
--- /dev/null
+++ b/src/test/ui/suggestions/suggest_print_over_printf.stderr
@@ -0,0 +1,14 @@
+error[E0425]: cannot find function `printf` in this scope
+  --> $DIR/suggest_print_over_printf.rs:5:5
+   |
+LL |     printf("%d", x);
+   |     ^^^^^^ not found in this scope
+   |
+help: you may have meant to use the `print` macro
+   |
+LL |     print!("%d", x);
+   |     ~~~~~~
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0425`.
diff --git a/src/test/ui/traits/issue-104322.rs b/src/test/ui/traits/issue-104322.rs
new file mode 100644
index 00000000000..dcc27f1f03a
--- /dev/null
+++ b/src/test/ui/traits/issue-104322.rs
@@ -0,0 +1,80 @@
+// build-pass
+//
+// Tests that overflows do not occur in certain situations
+// related to generic diesel code
+
+use mini_diesel::*;
+
+pub trait HandleDelete<K> {}
+
+pub fn handle_delete<D, R>()
+where
+    R: HasTable,
+    R::Table: HandleDelete<D> + 'static,
+{
+}
+
+impl<K, T> HandleDelete<K> for T
+where
+    T: Table + HasTable<Table = T> + 'static,
+    K: 'static,
+    &'static K: Identifiable<Table = T>,
+    T::PrimaryKey: EqAll<<&'static K as Identifiable>::Id>,
+    T::Query: FilterDsl<<T::PrimaryKey as EqAll<<&'static K as Identifiable>::Id>>::Output>,
+    Filter<T::Query, <T::PrimaryKey as EqAll<<&'static K as Identifiable>::Id>>::Output>:
+        IntoUpdateTarget<Table = T>,
+{
+}
+
+mod mini_diesel {
+    pub trait HasTable {
+        type Table: Table;
+    }
+
+    pub trait Identifiable: HasTable {
+        type Id;
+    }
+
+    pub trait EqAll<Rhs> {
+        type Output;
+    }
+
+    pub trait IntoUpdateTarget: HasTable {
+        type WhereClause;
+    }
+
+    pub trait Query {
+        type SqlType;
+    }
+
+    pub trait AsQuery {
+        type Query: Query;
+    }
+    impl<T: Query> AsQuery for T {
+        type Query = Self;
+    }
+
+    pub trait FilterDsl<Predicate> {
+        type Output;
+    }
+
+    impl<T, Predicate> FilterDsl<Predicate> for T
+    where
+        T: Table,
+        T::Query: FilterDsl<Predicate>,
+    {
+        type Output = Filter<T::Query, Predicate>;
+    }
+
+    pub trait QuerySource {
+        type FromClause;
+    }
+
+    pub trait Table: QuerySource + AsQuery + Sized {
+        type PrimaryKey;
+    }
+
+    pub type Filter<Source, Predicate> = <Source as FilterDsl<Predicate>>::Output;
+}
+
+fn main() {}
diff --git a/src/test/ui/traits/predicate_can_apply-hang.rs b/src/test/ui/traits/predicate_can_apply-hang.rs
deleted file mode 100644
index 5f01645da52..00000000000
--- a/src/test/ui/traits/predicate_can_apply-hang.rs
+++ /dev/null
@@ -1,6 +0,0 @@
-fn f<B>(x: Vec<[[[B; 1]; 1]; 1]>) -> impl PartialEq<B> {
-    //~^ ERROR can't compare `Vec<[[[B; 1]; 1]; 1]>` with `B`
-    x
-}
-
-fn main() {}
diff --git a/src/test/ui/traits/predicate_can_apply-hang.stderr b/src/test/ui/traits/predicate_can_apply-hang.stderr
deleted file mode 100644
index 49fe63b412a..00000000000
--- a/src/test/ui/traits/predicate_can_apply-hang.stderr
+++ /dev/null
@@ -1,21 +0,0 @@
-error[E0277]: can't compare `Vec<[[[B; 1]; 1]; 1]>` with `B`
-  --> $DIR/predicate_can_apply-hang.rs:1:38
-   |
-LL | fn f<B>(x: Vec<[[[B; 1]; 1]; 1]>) -> impl PartialEq<B> {
-   |                                      ^^^^^^^^^^^^^^^^^ no implementation for `Vec<[[[B; 1]; 1]; 1]> == B`
-LL |
-LL |     x
-   |     - return type was inferred to be `Vec<[[[B; 1]; 1]; 1]>` here
-   |
-   = help: the trait `PartialEq<B>` is not implemented for `Vec<[[[B; 1]; 1]; 1]>`
-   = help: the following other types implement trait `PartialEq<Rhs>`:
-             <Vec<T, A1> as PartialEq<Vec<U, A2>>>
-             <Vec<T, A> as PartialEq<&[U; N]>>
-             <Vec<T, A> as PartialEq<&[U]>>
-             <Vec<T, A> as PartialEq<&mut [U]>>
-             <Vec<T, A> as PartialEq<[U; N]>>
-             <Vec<T, A> as PartialEq<[U]>>
-
-error: aborting due to previous error
-
-For more information about this error, try `rustc --explain E0277`.
diff --git a/src/test/ui/type-alias-impl-trait/imply_bounds_from_bounds.rs b/src/test/ui/type-alias-impl-trait/imply_bounds_from_bounds.rs
new file mode 100644
index 00000000000..ee9bce15d34
--- /dev/null
+++ b/src/test/ui/type-alias-impl-trait/imply_bounds_from_bounds.rs
@@ -0,0 +1,25 @@
+// check-pass
+
+#![feature(type_alias_impl_trait)]
+
+trait Callable {
+    type Output;
+    fn call() -> Self::Output;
+}
+
+impl<'a> Callable for &'a () {
+    type Output = impl Sized;
+    fn call() -> Self::Output {}
+}
+
+fn test<'a>() -> impl Sized {
+    <&'a () as Callable>::call()
+}
+
+fn want_static<T: 'static>(_: T) {}
+
+fn test2<'a>() {
+    want_static(<&'a () as Callable>::call());
+}
+
+fn main() {}
diff --git a/src/test/ui/type-alias-impl-trait/imply_bounds_from_bounds_param.rs b/src/test/ui/type-alias-impl-trait/imply_bounds_from_bounds_param.rs
new file mode 100644
index 00000000000..ae21a9134a4
--- /dev/null
+++ b/src/test/ui/type-alias-impl-trait/imply_bounds_from_bounds_param.rs
@@ -0,0 +1,38 @@
+#![feature(type_alias_impl_trait)]
+
+trait Callable {
+    type Output;
+    fn call(x: Self) -> Self::Output;
+}
+
+trait PlusOne {
+    fn plus_one(&mut self);
+}
+
+impl<'a> PlusOne for &'a mut i32 {
+    fn plus_one(&mut self) {
+        **self += 1;
+    }
+}
+
+impl<T: PlusOne> Callable for T {
+    type Output = impl PlusOne;
+    fn call(t: T) -> Self::Output { t }
+}
+
+fn test<'a>(y: &'a mut i32) -> impl PlusOne {
+    <&'a mut i32 as Callable>::call(y)
+    //~^ ERROR hidden type for `impl PlusOne` captures lifetime that does not appear in bounds
+}
+
+fn main() {
+    let mut z = 42;
+    let mut thing = test(&mut z);
+    let mut thing2 = test(&mut z);
+    thing.plus_one();
+    assert_eq!(z, 43);
+    thing2.plus_one();
+    assert_eq!(z, 44);
+    thing.plus_one();
+    assert_eq!(z, 45);
+}
diff --git a/src/test/ui/type-alias-impl-trait/imply_bounds_from_bounds_param.stderr b/src/test/ui/type-alias-impl-trait/imply_bounds_from_bounds_param.stderr
new file mode 100644
index 00000000000..0ed8a703b6d
--- /dev/null
+++ b/src/test/ui/type-alias-impl-trait/imply_bounds_from_bounds_param.stderr
@@ -0,0 +1,16 @@
+error[E0700]: hidden type for `impl PlusOne` captures lifetime that does not appear in bounds
+  --> $DIR/imply_bounds_from_bounds_param.rs:24:5
+   |
+LL | fn test<'a>(y: &'a mut i32) -> impl PlusOne {
+   |         -- hidden type `<&'a mut i32 as Callable>::Output` captures the lifetime `'a` as defined here
+LL |     <&'a mut i32 as Callable>::call(y)
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: to declare that `impl PlusOne` captures `'a`, you can add an explicit `'a` lifetime bound
+   |
+LL | fn test<'a>(y: &'a mut i32) -> impl PlusOne + 'a {
+   |                                             ++++
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0700`.
diff --git a/src/test/ui/type-alias-impl-trait/self_implication.rs b/src/test/ui/type-alias-impl-trait/self_implication.rs
new file mode 100644
index 00000000000..4e805ee308f
--- /dev/null
+++ b/src/test/ui/type-alias-impl-trait/self_implication.rs
@@ -0,0 +1,38 @@
+// check-pass
+
+#![feature(type_alias_impl_trait)]
+fn foo() {
+    struct Foo<'a> {
+        x: &'a mut u8,
+    }
+    impl<'a> Foo<'a> {
+        fn foo(&self) -> impl Sized {}
+    }
+    // use site
+    let mut x = 5;
+    let y = Foo { x: &mut x };
+    let z = y.foo();
+    let _a = &x; // invalidate the `&'a mut`in `y`
+    let _b = z; // this should *not* check that `'a` in the type `Foo<'a>::foo::opaque` is live
+}
+
+fn bar() {
+    struct Foo<'a> {
+        x: &'a mut u8,
+    }
+
+    // desugared
+    type FooX<'a> = impl Sized;
+    impl<'a> Foo<'a> {
+        fn foo(&self) -> FooX<'a> {}
+    }
+
+    // use site
+    let mut x = 5;
+    let y = Foo { x: &mut x };
+    let z = y.foo();
+    let _a = &x; // invalidate the `&'a mut`in `y`
+    let _b = z; // this should *not* check that `'a` in the type `Foo<'a>::foo::opaque` is live
+}
+
+fn main() {}
diff --git a/src/test/ui/type/type-ascription-soundness.rs b/src/test/ui/type/type-ascription-soundness.rs
index d583fc2131a..08316cdcd35 100644
--- a/src/test/ui/type/type-ascription-soundness.rs
+++ b/src/test/ui/type/type-ascription-soundness.rs
@@ -4,10 +4,10 @@
 
 fn main() {
     let arr = &[1u8, 2, 3];
-    let ref x = arr: &[u8]; //~ ERROR mismatched types
-    let ref mut x = arr: &[u8]; //~ ERROR mismatched types
-    match arr: &[u8] { //~ ERROR mismatched types
+    let ref x = type_ascribe!(arr, &[u8]);      //~ ERROR mismatched types
+    let ref mut x = type_ascribe!(arr, &[u8]);  //~ ERROR mismatched types
+    match type_ascribe!(arr, &[u8]) {           //~ ERROR mismatched types
         ref x => {}
     }
-    let _len = (arr: &[u8]).len(); //~ ERROR mismatched types
+    let _len = type_ascribe!(arr, &[u8]).len();              //~ ERROR mismatched types
 }
diff --git a/src/test/ui/type/type-ascription-soundness.stderr b/src/test/ui/type/type-ascription-soundness.stderr
index 6ed940823af..522d5b2e375 100644
--- a/src/test/ui/type/type-ascription-soundness.stderr
+++ b/src/test/ui/type/type-ascription-soundness.stderr
@@ -1,35 +1,35 @@
 error[E0308]: mismatched types
-  --> $DIR/type-ascription-soundness.rs:7:17
+  --> $DIR/type-ascription-soundness.rs:7:31
    |
-LL |     let ref x = arr: &[u8];
-   |                 ^^^ expected slice `[u8]`, found array `[u8; 3]`
+LL |     let ref x = type_ascribe!(arr, &[u8]);
+   |                               ^^^ expected slice `[u8]`, found array `[u8; 3]`
    |
    = note: expected reference `&[u8]`
               found reference `&[u8; 3]`
 
 error[E0308]: mismatched types
-  --> $DIR/type-ascription-soundness.rs:8:21
+  --> $DIR/type-ascription-soundness.rs:8:35
    |
-LL |     let ref mut x = arr: &[u8];
-   |                     ^^^ expected slice `[u8]`, found array `[u8; 3]`
+LL |     let ref mut x = type_ascribe!(arr, &[u8]);
+   |                                   ^^^ expected slice `[u8]`, found array `[u8; 3]`
    |
    = note: expected reference `&[u8]`
               found reference `&[u8; 3]`
 
 error[E0308]: mismatched types
-  --> $DIR/type-ascription-soundness.rs:9:11
+  --> $DIR/type-ascription-soundness.rs:9:25
    |
-LL |     match arr: &[u8] {
-   |           ^^^ expected slice `[u8]`, found array `[u8; 3]`
+LL |     match type_ascribe!(arr, &[u8]) {
+   |                         ^^^ expected slice `[u8]`, found array `[u8; 3]`
    |
    = note: expected reference `&[u8]`
               found reference `&[u8; 3]`
 
 error[E0308]: mismatched types
-  --> $DIR/type-ascription-soundness.rs:12:17
+  --> $DIR/type-ascription-soundness.rs:12:30
    |
-LL |     let _len = (arr: &[u8]).len();
-   |                 ^^^ expected slice `[u8]`, found array `[u8; 3]`
+LL |     let _len = type_ascribe!(arr, &[u8]).len();
+   |                              ^^^ expected slice `[u8]`, found array `[u8; 3]`
    |
    = note: expected reference `&[u8]`
               found reference `&[u8; 3]`
diff --git a/src/test/ui/type/type-ascription.rs b/src/test/ui/type/type-ascription.rs
index 7adb074428c..e4a4c89d057 100644
--- a/src/test/ui/type/type-ascription.rs
+++ b/src/test/ui/type/type-ascription.rs
@@ -8,32 +8,32 @@
 
 use std::mem;
 
-const C1: u8 = 10: u8;
-const C2: [u8; 1: usize] = [1];
+const C1: u8 = type_ascribe!(10, u8);
+const C2: [u8; type_ascribe!(1, usize)] = [1];
 
 struct S {
     a: u8
 }
 
 fn main() {
-    assert_eq!(C1.into(): i32, 10);
+    assert_eq!(type_ascribe!(C1.into(), i32), 10);
     assert_eq!(C2[0], 1);
 
-    let s = S { a: 10: u8 };
+    let s = S { a: type_ascribe!(10, u8) };
     let arr = &[1u8, 2, 3];
 
-    let mut v = arr.iter().cloned().collect(): Vec<_>;
+    let mut v = type_ascribe!(arr.iter().cloned().collect(), Vec<_>);
     v.push(4);
     assert_eq!(v, [1, 2, 3, 4]);
 
-    let a = 1: u8;
-    let b = a.into(): u16;
-    assert_eq!(v[a.into(): usize], 2);
+    let a = type_ascribe!(1, u8);
+    let b = type_ascribe!(a.into(), u16);
+    assert_eq!(v[type_ascribe!(a.into(), usize)], 2);
     assert_eq!(mem::size_of_val(&a), 1);
     assert_eq!(mem::size_of_val(&b), 2);
-    assert_eq!(b, 1: u16);
+    assert_eq!(b, type_ascribe!(1, u16));
 
     let mut v = Vec::new();
-    v: Vec<u8> = vec![1, 2, 3]; // Place expression type ascription
+    type_ascribe!(v, Vec<u8>) = vec![1, 2, 3]; // Place expression type ascription
     assert_eq!(v, [1u8, 2, 3]);
 }
diff --git a/src/test/ui/typeck/hang-in-overflow.rs b/src/test/ui/typeck/hang-in-overflow.rs
deleted file mode 100644
index a8330c9b65c..00000000000
--- a/src/test/ui/typeck/hang-in-overflow.rs
+++ /dev/null
@@ -1,19 +0,0 @@
-// normalize-stderr-test "the requirement `.*`" -> "the requirement `...`"
-// normalize-stderr-test "required for `.*` to implement `.*`" -> "required for `...` to implement `...`"
-// normalize-stderr-test: ".*the full type name has been written to.*\n" -> ""
-
-// Currently this fatally aborts instead of hanging.
-// Make sure at least that this doesn't turn into a hang.
-
-fn f() {
-    foo::<_>();
-    //~^ ERROR overflow evaluating the requirement
-}
-
-fn foo<B>()
-where
-    Vec<[[[B; 1]; 1]; 1]>: PartialEq<B>,
-{
-}
-
-fn main() {}
diff --git a/src/test/ui/typeck/hang-in-overflow.stderr b/src/test/ui/typeck/hang-in-overflow.stderr
deleted file mode 100644
index 7a7b85b19b4..00000000000
--- a/src/test/ui/typeck/hang-in-overflow.stderr
+++ /dev/null
@@ -1,22 +0,0 @@
-error[E0275]: overflow evaluating the requirement `...`
-  --> $DIR/hang-in-overflow.rs:9:5
-   |
-LL |     foo::<_>();
-   |     ^^^^^^^^
-   |
-   = help: consider increasing the recursion limit by adding a `#![recursion_limit = "256"]` attribute to your crate (`hang_in_overflow`)
-   = note: required for `...` to implement `...`
-   = note: 127 redundant requirements hidden
-   = note: required for `...` to implement `...`
-note: required by a bound in `foo`
-  --> $DIR/hang-in-overflow.rs:15:28
-   |
-LL | fn foo<B>()
-   |    --- required by a bound in this
-LL | where
-LL |     Vec<[[[B; 1]; 1]; 1]>: PartialEq<B>,
-   |                            ^^^^^^^^^^^^ required by this bound in `foo`
-
-error: aborting due to previous error
-
-For more information about this error, try `rustc --explain E0275`.
diff --git a/src/test/ui/typeck/issue-91267.rs b/src/test/ui/typeck/issue-91267.rs
index f5a37e9cb86..4e39cfab5b4 100644
--- a/src/test/ui/typeck/issue-91267.rs
+++ b/src/test/ui/typeck/issue-91267.rs
@@ -1,5 +1,7 @@
+#![feature(type_ascription)]
+
 fn main() {
-    0: u8<e<5>=e>
+    type_ascribe!(0, u8<e<5>=e>)
     //~^ ERROR: cannot find type `e` in this scope [E0412]
     //~| ERROR: associated type bindings are not allowed here [E0229]
     //~| ERROR: mismatched types [E0308]
diff --git a/src/test/ui/typeck/issue-91267.stderr b/src/test/ui/typeck/issue-91267.stderr
index aac00b9b6a9..72acd9c673b 100644
--- a/src/test/ui/typeck/issue-91267.stderr
+++ b/src/test/ui/typeck/issue-91267.stderr
@@ -1,25 +1,22 @@
 error[E0412]: cannot find type `e` in this scope
-  --> $DIR/issue-91267.rs:2:16
+  --> $DIR/issue-91267.rs:4:30
    |
-LL |     0: u8<e<5>=e>
-   |                ^
-   |                |
-   |                not found in this scope
-   |                help: maybe you meant to write an assignment here: `let e`
+LL |     type_ascribe!(0, u8<e<5>=e>)
+   |                              ^ not found in this scope
 
 error[E0229]: associated type bindings are not allowed here
-  --> $DIR/issue-91267.rs:2:11
+  --> $DIR/issue-91267.rs:4:25
    |
-LL |     0: u8<e<5>=e>
-   |           ^^^^^^ associated type not allowed here
+LL |     type_ascribe!(0, u8<e<5>=e>)
+   |                         ^^^^^^ associated type not allowed here
 
 error[E0308]: mismatched types
-  --> $DIR/issue-91267.rs:2:5
+  --> $DIR/issue-91267.rs:4:5
    |
 LL | fn main() {
    |           - expected `()` because of default return type
-LL |     0: u8<e<5>=e>
-   |     ^^^^^^^^^^^^^ expected `()`, found `u8`
+LL |     type_ascribe!(0, u8<e<5>=e>)
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `()`, found `u8`
 
 error: aborting due to 3 previous errors
 
diff --git a/src/tools/cargo b/src/tools/cargo
-Subproject ba607b23db8398723d659249d9abf5536bc322e
+Subproject e027c4b5d25af2119b1956fac42863b9b324274
diff --git a/src/tools/clippy/CHANGELOG.md b/src/tools/clippy/CHANGELOG.md
index 6f1f73c1fd2..23912bb3ed6 100644
--- a/src/tools/clippy/CHANGELOG.md
+++ b/src/tools/clippy/CHANGELOG.md
@@ -4188,6 +4188,7 @@ Released 2018-09-13
 [`misaligned_transmute`]: https://rust-lang.github.io/rust-clippy/master/index.html#misaligned_transmute
 [`mismatched_target_os`]: https://rust-lang.github.io/rust-clippy/master/index.html#mismatched_target_os
 [`mismatching_type_param_order`]: https://rust-lang.github.io/rust-clippy/master/index.html#mismatching_type_param_order
+[`misnamed_getters`]: https://rust-lang.github.io/rust-clippy/master/index.html#misnamed_getters
 [`misrefactored_assign_op`]: https://rust-lang.github.io/rust-clippy/master/index.html#misrefactored_assign_op
 [`missing_const_for_fn`]: https://rust-lang.github.io/rust-clippy/master/index.html#missing_const_for_fn
 [`missing_docs_in_private_items`]: https://rust-lang.github.io/rust-clippy/master/index.html#missing_docs_in_private_items
@@ -4450,6 +4451,7 @@ Released 2018-09-13
 [`unnecessary_mut_passed`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_mut_passed
 [`unnecessary_operation`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_operation
 [`unnecessary_owned_empty_strings`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_owned_empty_strings
+[`unnecessary_safety_comment`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_safety_comment
 [`unnecessary_safety_doc`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_safety_doc
 [`unnecessary_self_imports`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_self_imports
 [`unnecessary_sort_by`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_sort_by
diff --git a/src/tools/clippy/README.md b/src/tools/clippy/README.md
index f74de7de42b..81254ba8b8b 100644
--- a/src/tools/clippy/README.md
+++ b/src/tools/clippy/README.md
@@ -197,8 +197,8 @@ disallowed-names = ["toto", "tata", "titi"]
 cognitive-complexity-threshold = 30
 ```
 
-See the [list of lints](https://rust-lang.github.io/rust-clippy/master/index.html) for more information about which
-lints can be configured and the meaning of the variables.
+See the [list of configurable lints](https://rust-lang.github.io/rust-clippy/master/index.html#Configuration),
+the lint descriptions contain the names and meanings of these configuration variables.
 
 > **Note**
 >
@@ -224,7 +224,7 @@ in the `Cargo.toml` can be used.
 rust-version = "1.30"
 ```
 
-The MSRV can also be specified as an inner attribute, like below.
+The MSRV can also be specified as an attribute, like below.
 
 ```rust
 #![feature(custom_inner_attributes)]
diff --git a/src/tools/clippy/book/src/SUMMARY.md b/src/tools/clippy/book/src/SUMMARY.md
index 0b945faf9b7..1f0b8db28a1 100644
--- a/src/tools/clippy/book/src/SUMMARY.md
+++ b/src/tools/clippy/book/src/SUMMARY.md
@@ -21,3 +21,4 @@
         - [The Clippy Book](development/infrastructure/book.md)
     - [Proposals](development/proposals/README.md)
         - [Roadmap 2021](development/proposals/roadmap-2021.md)
+        - [Syntax Tree Patterns](development/proposals/syntax-tree-patterns.md)
diff --git a/src/tools/clippy/book/src/configuration.md b/src/tools/clippy/book/src/configuration.md
index 77f1d2e8797..430ff8b739a 100644
--- a/src/tools/clippy/book/src/configuration.md
+++ b/src/tools/clippy/book/src/configuration.md
@@ -11,8 +11,8 @@ disallowed-names = ["toto", "tata", "titi"]
 cognitive-complexity-threshold = 30
 ```
 
-See the [list of lints](https://rust-lang.github.io/rust-clippy/master/index.html) for more information about which
-lints can be configured and the meaning of the variables.
+See the [list of configurable lints](https://rust-lang.github.io/rust-clippy/master/index.html#Configuration),
+the lint descriptions contain the names and meanings of these configuration variables.
 
 To deactivate the "for further information visit *lint-link*" message you can define the `CLIPPY_DISABLE_DOCS_LINKS`
 environment variable.
@@ -72,7 +72,7 @@ minimum supported Rust version (MSRV) in the clippy configuration file.
 msrv = "1.30.0"
 ```
 
-The MSRV can also be specified as an inner attribute, like below.
+The MSRV can also be specified as an attribute, like below.
 
 ```rust
 #![feature(custom_inner_attributes)]
diff --git a/src/tools/clippy/book/src/development/adding_lints.md b/src/tools/clippy/book/src/development/adding_lints.md
index 3c3f368a529..8b4eee8c9d9 100644
--- a/src/tools/clippy/book/src/development/adding_lints.md
+++ b/src/tools/clippy/book/src/development/adding_lints.md
@@ -443,27 +443,27 @@ value is passed to the constructor in `clippy_lints/lib.rs`.
 
 ```rust
 pub struct ManualStrip {
-    msrv: Option<RustcVersion>,
+    msrv: Msrv,
 }
 
 impl ManualStrip {
     #[must_use]
-    pub fn new(msrv: Option<RustcVersion>) -> Self {
+    pub fn new(msrv: Msrv) -> Self {
         Self { msrv }
     }
 }
 ```
 
 The project's MSRV can then be matched against the feature MSRV in the LintPass
-using the `meets_msrv` utility function.
+using the `Msrv::meets` method.
 
 ``` rust
-if !meets_msrv(self.msrv, msrvs::STR_STRIP_PREFIX) {
+if !self.msrv.meets(msrvs::STR_STRIP_PREFIX) {
     return;
 }
 ```
 
-The project's MSRV can also be specified as an inner attribute, which overrides
+The project's MSRV can also be specified as an attribute, which overrides
 the value from `clippy.toml`. This can be accounted for using the
 `extract_msrv_attr!(LintContext)` macro and passing
 `LateContext`/`EarlyContext`.
@@ -483,19 +483,15 @@ have a case for the version below the MSRV and one with the same contents but
 for the MSRV version itself.
 
 ```rust
-#![feature(custom_inner_attributes)]
-
 ...
 
+#[clippy::msrv = "1.44"]
 fn msrv_1_44() {
-    #![clippy::msrv = "1.44"]
-
     /* something that would trigger the lint */
 }
 
+#[clippy::msrv = "1.45"]
 fn msrv_1_45() {
-    #![clippy::msrv = "1.45"]
-
     /* something that would trigger the lint */
 }
 ```
diff --git a/src/tools/clippy/book/src/development/proposals/syntax-tree-patterns.md b/src/tools/clippy/book/src/development/proposals/syntax-tree-patterns.md
new file mode 100644
index 00000000000..c5587c4bf90
--- /dev/null
+++ b/src/tools/clippy/book/src/development/proposals/syntax-tree-patterns.md
@@ -0,0 +1,986 @@
+- Feature Name: syntax-tree-patterns
+- Start Date: 2019-03-12
+- RFC PR: [#3875](https://github.com/rust-lang/rust-clippy/pull/3875)
+
+# Summary
+
+Introduce a domain-specific language (similar to regular expressions) that
+allows to describe lints using *syntax tree patterns*.
+
+
+# Motivation
+
+Finding parts of a syntax tree (AST, HIR, ...) that have certain properties
+(e.g. "*an if that has a block as its condition*") is a major task when writing
+lints. For non-trivial lints, it often requires nested pattern matching of AST /
+HIR nodes. For example, testing that an expression is a boolean literal requires
+the following checks:
+
+```rust
+if let ast::ExprKind::Lit(lit) = &expr.node {
+    if let ast::LitKind::Bool(_) = &lit.node {
+        ...
+    }
+}
+```
+
+Writing this kind of matching code quickly becomes a complex task and the
+resulting code is often hard to comprehend. The code below shows a simplified
+version of the pattern matching required by the `collapsible_if` lint:
+
+```rust
+// simplified version of the collapsible_if lint
+if let ast::ExprKind::If(check, then, None) = &expr.node {
+    if then.stmts.len() == 1 {
+        if let ast::StmtKind::Expr(inner) | ast::StmtKind::Semi(inner) = &then.stmts[0].node {
+            if let ast::ExprKind::If(check_inner, content, None) = &inner.node {
+                ...
+            }
+        }
+    }
+}
+```
+
+The `if_chain` macro can improve readability by flattening the nested if
+statements, but the resulting code is still quite hard to read:
+
+```rust
+// simplified version of the collapsible_if lint
+if_chain! {
+    if let ast::ExprKind::If(check, then, None) = &expr.node;
+    if then.stmts.len() == 1;
+    if let ast::StmtKind::Expr(inner) | ast::StmtKind::Semi(inner) = &then.stmts[0].node;
+    if let ast::ExprKind::If(check_inner, content, None) = &inner.node;
+    then {
+        ...
+    }
+}
+```
+
+The code above matches if expressions that contain only another if expression
+(where both ifs don't have an else branch). While it's easy to explain what the
+lint does, it's hard to see that from looking at the code samples above.
+
+Following the motivation above, the first goal this RFC is to **simplify writing
+and reading lints**.
+
+The second part of the motivation is clippy's dependence on unstable
+compiler-internal data structures. Clippy lints are currently written against
+the compiler's AST / HIR which means that even small changes in these data
+structures might break a lot of lints. The second goal of this RFC is to **make
+lints independant of the compiler's AST / HIR data structures**.
+
+# Approach
+
+A lot of complexity in writing lints currently seems to come from having to
+manually implement the matching logic (see code samples above). It's an
+imparative style that describes *how* to match a syntax tree node instead of
+specifying *what* should be matched against declaratively. In other areas, it's
+common to use declarative patterns to describe desired information and let the
+implementation do the actual matching. A well-known example of this approach are
+[regular expressions](https://en.wikipedia.org/wiki/Regular_expression). Instead
+of writing code that detects certain character sequences, one can describe a
+search pattern using a domain-specific language and search for matches using
+that pattern. The advantage of using a declarative domain-specific language is
+that its limited domain (e.g. matching character sequences in the case of
+regular expressions) allows to express entities in that domain in a very natural
+and expressive way.
+
+While regular expressions are very useful when searching for patterns in flat
+character sequences, they cannot easily be applied to hierarchical data
+structures like syntax trees. This RFC therefore proposes a pattern matching
+system that is inspired by regular expressions and designed for hierarchical
+syntax trees.
+
+# Guide-level explanation
+
+This proposal adds a `pattern!` macro that can be used to specify a syntax tree
+pattern to search for. A simple pattern is shown below:
+
+```rust
+pattern!{
+    my_pattern: Expr =
+        Lit(Bool(false))
+}
+```
+
+This macro call defines a pattern named `my_pattern` that can be matched against
+an `Expr` syntax tree node. The actual pattern (`Lit(Bool(false))` in this case)
+defines which syntax trees should match the pattern. This pattern matches
+expressions that are boolean literals with value `false`.
+
+The pattern can then be used to implement lints in the following way:
+
+```rust
+...
+
+impl EarlyLintPass for MyAwesomeLint {
+    fn check_expr(&mut self, cx: &EarlyContext, expr: &syntax::ast::Expr) {
+
+        if my_pattern(expr).is_some() {
+            cx.span_lint(
+                MY_AWESOME_LINT,
+                expr.span,
+                "This is a match for a simple pattern. Well done!",
+            );
+        }
+
+    }
+}
+```
+
+The `pattern!` macro call expands to a function `my_pattern` that expects a
+syntax tree expression as its argument and returns an `Option` that indicates
+whether the pattern matched.
+
+> Note: The result type is explained in more detail in [a later
+> section](#the-result-type). For now, it's enough to know that the result is
+> `Some` if the pattern matched and `None` otherwise.
+
+## Pattern syntax
+
+The following examples demonstate the pattern syntax:
+
+
+#### Any (`_`)
+
+The simplest pattern is the any pattern. It matches anything and is therefore
+similar to regex's `*`.
+
+```rust
+pattern!{
+    // matches any expression
+    my_pattern: Expr =
+        _
+}
+```
+
+#### Node (`<node-name>(<args>)`)
+
+Nodes are used to match a specific variant of an AST node. A node has a name and
+a number of arguments that depends on the node type. For example, the `Lit` node
+has a single argument that describes the type of the literal. As another
+example, the `If` node has three arguments describing the if's condition, then
+block and else block.
+
+```rust
+pattern!{
+    // matches any expression that is a literal
+    my_pattern: Expr =
+        Lit(_)
+}
+
+pattern!{
+    // matches any expression that is a boolean literal
+    my_pattern: Expr =
+        Lit(Bool(_))
+}
+
+pattern!{
+    // matches if expressions that have a boolean literal in their condition
+    // Note: The `_?` syntax here means that the else branch is optional and can be anything.
+    //       This is discussed in more detail in the section `Repetition`.
+    my_pattern: Expr =
+        If( Lit(Bool(_)) , _, _?)
+}
+```
+
+
+#### Literal (`<lit>`)
+
+A pattern can also contain Rust literals. These literals match themselves.
+
+```rust
+pattern!{
+    // matches the boolean literal false
+    my_pattern: Expr =
+        Lit(Bool(false))
+}
+
+pattern!{
+    // matches the character literal 'x'
+    my_pattern: Expr =
+        Lit(Char('x'))
+}
+```
+
+#### Alternations (`a | b`)
+
+```rust
+pattern!{
+    // matches if the literal is a boolean or integer literal
+    my_pattern: Lit =
+        Bool(_) | Int(_)
+}
+
+pattern!{
+    // matches if the expression is a char literal with value 'x' or 'y'
+    my_pattern: Expr =
+        Lit( Char('x' | 'y') )
+}
+```
+
+#### Empty (`()`)
+
+The empty pattern represents an empty sequence or the `None` variant of an
+optional.
+
+```rust
+pattern!{
+    // matches if the expression is an empty array
+    my_pattern: Expr =
+        Array( () )
+}
+
+pattern!{
+    // matches if expressions that don't have an else clause
+    my_pattern: Expr =
+        If(_, _, ())
+}
+```
+
+#### Sequence (`<a> <b>`)
+
+```rust
+pattern!{
+    // matches the array [true, false]
+    my_pattern: Expr =
+        Array( Lit(Bool(true)) Lit(Bool(false)) )
+}
+```
+
+#### Repetition (`<a>*`, `<a>+`, `<a>?`, `<a>{n}`, `<a>{n,m}`, `<a>{n,}`)
+
+Elements may be repeated. The syntax for specifying repetitions is identical to
+[regex's syntax](https://docs.rs/regex/1.1.2/regex/#repetitions).
+
+```rust
+pattern!{
+    // matches arrays that contain 2 'x's as their last or second-last elements
+    // Examples:
+    //     ['x', 'x']                         match
+    //     ['x', 'x', 'y']                    match
+    //     ['a', 'b', 'c', 'x', 'x', 'y']     match
+    //     ['x', 'x', 'y', 'z']               no match
+    my_pattern: Expr =
+        Array( _* Lit(Char('x')){2} _? )
+}
+
+pattern!{
+    // matches if expressions that **may or may not** have an else block
+    // Attn: `If(_, _, _)` matches only ifs that **have** an else block
+    //
+    //              | if with else block | if witout else block
+    // If(_, _, _)  |       match        |       no match
+    // If(_, _, _?) |       match        |        match
+    // If(_, _, ()) |      no match      |        match
+    my_pattern: Expr =
+        If(_, _, _?)
+}
+```
+
+#### Named submatch (`<a>#<name>`)
+
+```rust
+pattern!{
+    // matches character literals and gives the literal the name foo
+    my_pattern: Expr =
+        Lit(Char(_)#foo)
+}
+
+pattern!{
+    // matches character literals and gives the char the name bar
+    my_pattern: Expr =
+        Lit(Char(_#bar))
+}
+
+pattern!{
+    // matches character literals and gives the expression the name baz
+    my_pattern: Expr =
+        Lit(Char(_))#baz
+}
+```
+
+The reason for using named submatches is described in the section [The result
+type](#the-result-type).
+
+### Summary
+
+The following table gives an summary of the pattern syntax:
+
+| Syntax                  | Concept          | Examples                                   |
+|-------------------------|------------------|--------------------------------------------|
+|`_`                      | Any              | `_`                                        |
+|`<node-name>(<args>)`    | Node             | `Lit(Bool(true))`, `If(_, _, _)`           |
+|`<lit>`                  | Literal          | `'x'`, `false`, `101`                      |
+|`<a> \| <b>`             | Alternation      | `Char(_) \| Bool(_)`                       |
+|`()`                     | Empty            | `Array( () )`                              |
+|`<a> <b>`                | Sequence         | `Tuple( Lit(Bool(_)) Lit(Int(_)) Lit(_) )` |
+|`<a>*` <br> `<a>+` <br> `<a>?` <br> `<a>{n}` <br> `<a>{n,m}` <br> `<a>{n,}` | Repetition <br> <br> <br> <br> <br><br> | `Array( _* )`, <br> `Block( Semi(_)+ )`, <br> `If(_, _, Block(_)?)`, <br> `Array( Lit(_){10} )`, <br> `Lit(_){5,10}`, <br> `Lit(Bool(_)){10,}` |
+|`<a>#<name>`             | Named submatch   | `Lit(Int(_))#foo` `Lit(Int(_#bar))`        |
+
+
+## The result type
+
+A lot of lints require checks that go beyond what the pattern syntax described
+above can express. For example, a lint might want to check whether a node was
+created as part of a macro expansion or whether there's no comment above a node.
+Another example would be a lint that wants to match two nodes that have the same
+value (as needed by lints like `almost_swapped`). Instead of allowing users to
+write these checks into the pattern directly (which might make patterns hard to
+read), the proposed solution allows users to assign names to parts of a pattern
+expression. When matching a pattern against a syntax tree node, the return value
+will contain references to all nodes that were matched by these named
+subpatterns. This is similar to capture groups in regular expressions.
+
+For example, given the following pattern
+
+```rust
+pattern!{
+    // matches character literals
+    my_pattern: Expr =
+        Lit(Char(_#val_inner)#val)#val_outer
+}
+```
+
+one could get references to the nodes that matched the subpatterns in the
+following way:
+
+```rust
+...
+fn check_expr(expr: &syntax::ast::Expr) {
+    if let Some(result) = my_pattern(expr) {
+        result.val_inner  // type: &char
+        result.val        // type: &syntax::ast::Lit
+        result.val_outer  // type: &syntax::ast::Expr
+    }
+}
+```
+
+The types in the `result` struct depend on the pattern. For example, the
+following pattern
+
+```rust
+pattern!{
+    // matches arrays of character literals
+    my_pattern_seq: Expr =
+        Array( Lit(_)*#foo )
+}
+```
+
+matches arrays that consist of any number of literal expressions. Because those
+expressions are named `foo`, the result struct contains a `foo` attribute which
+is a vector of expressions:
+
+```rust
+...
+if let Some(result) = my_pattern_seq(expr) {
+    result.foo        // type: Vec<&syntax::ast::Expr>
+}
+```
+
+Another result type occurs when a name is only defined in one branch of an
+alternation:
+
+```rust
+pattern!{
+    // matches if expression is a boolean or integer literal
+    my_pattern_alt: Expr =
+        Lit( Bool(_#bar) | Int(_) )
+}
+```
+
+In the pattern above, the `bar` name is only defined if the pattern matches a
+boolean literal. If it matches an integer literal, the name isn't set. To
+account for this, the result struct's `bar` attribute is an option type:
+
+```rust
+...
+if let Some(result) = my_pattern_alt(expr) {
+    result.bar        // type: Option<&bool>
+}
+```
+
+It's also possible to use a name in multiple alternation branches if they have
+compatible types:
+
+```rust
+pattern!{
+    // matches if expression is a boolean or integer literal
+    my_pattern_mult: Expr =
+        Lit(_#baz) | Array( Lit(_#baz) )
+}
+...
+if let Some(result) = my_pattern_mult(expr) {
+    result.baz        // type: &syntax::ast::Lit
+}
+```
+
+Named submatches are a **flat** namespace and this is intended. In the example
+above, two different sub-structures are assigned to a flat name. I expect that
+for most lints, a flat namespace is sufficient and easier to work with than a
+hierarchical one.
+
+#### Two stages
+
+Using named subpatterns, users can write lints in two stages. First, a coarse
+selection of possible matches is produced by the pattern syntax. In the second
+stage, the named subpattern references can be used to do additional tests like
+asserting that a node hasn't been created as part of a macro expansion.
+
+## Implementing clippy lints using patterns
+
+As a "real-world" example, I re-implemented the `collapsible_if` lint using
+patterns. The code can be found
+[here](https://github.com/fkohlgrueber/rust-clippy-pattern/blob/039b07ecccaf96d6aa7504f5126720d2c9cceddd/clippy_lints/src/collapsible_if.rs#L88-L163).
+The pattern-based version passes all test cases that were written for
+`collapsible_if`.
+
+
+# Reference-level explanation
+
+## Overview
+
+The following diagram shows the dependencies between the main parts of the
+proposed solution:
+
+```
+                          Pattern syntax
+                                |
+                                |  parsing / lowering
+                                v
+                           PatternTree
+                                ^
+                                |
+                                |
+                          IsMatch trait
+                                |
+                                |
+             +---------------+-----------+---------+
+             |               |           |         |
+             v               v           v         v
+        syntax::ast     rustc::hir      syn       ...
+```
+
+The pattern syntax described in the previous section is parsed / lowered into
+the so-called *PatternTree* data structure that represents a valid syntax tree
+pattern. Matching a *PatternTree* against an actual syntax tree (e.g. rust ast /
+hir or the syn ast, ...) is done using the *IsMatch* trait.
+
+The *PatternTree* and the *IsMatch* trait are introduced in more detail in the
+following sections.
+
+## PatternTree
+
+The core data structure of this RFC is the **PatternTree**.
+
+It's a data structure similar to rust's AST / HIR, but with the following
+differences:
+
+- The PatternTree doesn't contain parsing information like `Span`s
+- The PatternTree can represent alternatives, sequences and optionals
+
+The code below shows a simplified version of the current PatternTree:
+
+> Note: The current implementation can be found
+> [here](https://github.com/fkohlgrueber/pattern-matching/blob/dfb3bc9fbab69cec7c91e72564a63ebaa2ede638/pattern-match/src/pattern_tree.rs#L50-L96).
+
+
+```rust
+pub enum Expr {
+    Lit(Alt<Lit>),
+    Array(Seq<Expr>),
+    Block_(Alt<BlockType>),
+    If(Alt<Expr>, Alt<BlockType>, Opt<Expr>),
+    IfLet(
+        Alt<BlockType>,
+        Opt<Expr>,
+    ),
+}
+
+pub enum Lit {
+    Char(Alt<char>),
+    Bool(Alt<bool>),
+    Int(Alt<u128>),
+}
+
+pub enum Stmt {
+    Expr(Alt<Expr>),
+    Semi(Alt<Expr>),
+}
+
+pub enum BlockType {
+    Block(Seq<Stmt>),
+}
+```
+
+The `Alt`, `Seq` and `Opt` structs look like these:
+
+> Note: The current implementation can be found
+> [here](https://github.com/fkohlgrueber/pattern-matching/blob/dfb3bc9fbab69cec7c91e72564a63ebaa2ede638/pattern-match/src/matchers.rs#L35-L60).
+
+```rust
+pub enum Alt<T> {
+    Any,
+    Elmt(Box<T>),
+    Alt(Box<Self>, Box<Self>),
+    Named(Box<Self>, ...)
+}
+
+pub enum Opt<T> {
+    Any,  // anything, but not None
+    Elmt(Box<T>),
+    None,
+    Alt(Box<Self>, Box<Self>),
+    Named(Box<Self>, ...)
+}
+
+pub enum Seq<T> {
+    Any,
+    Empty,
+    Elmt(Box<T>),
+    Repeat(Box<Self>, RepeatRange),
+    Seq(Box<Self>, Box<Self>),
+    Alt(Box<Self>, Box<Self>),
+    Named(Box<Self>, ...)
+}
+
+pub struct RepeatRange {
+    pub start: usize,
+    pub end: Option<usize>  // exclusive
+}
+```
+
+## Parsing / Lowering
+
+The input of a `pattern!` macro call is parsed into a `ParseTree` first and then
+lowered to a `PatternTree`.
+
+Valid patterns depend on the *PatternTree* definitions. For example, the pattern
+`Lit(Bool(_)*)` isn't valid because the parameter type of the `Lit` variant of
+the `Expr` enum is `Any<Lit>` and therefore doesn't support repetition (`*`). As
+another example, `Array( Lit(_)* )` is a valid pattern because the parameter of
+`Array` is of type `Seq<Expr>` which allows sequences and repetitions.
+
+> Note: names in the pattern syntax correspond to *PatternTree* enum
+> **variants**. For example, the `Lit` in the pattern above refers to the `Lit`
+> variant of the `Expr` enum (`Expr::Lit`), not the `Lit` enum.
+
+## The IsMatch Trait
+
+The pattern syntax and the *PatternTree* are independant of specific syntax tree
+implementations (rust ast / hir, syn, ...). When looking at the different
+pattern examples in the previous sections, it can be seen that the patterns
+don't contain any information specific to a certain syntax tree implementation.
+In contrast, clippy lints currently match against ast / hir syntax tree nodes
+and therefore directly depend on their implementation.
+
+The connection between the *PatternTree* and specific syntax tree
+implementations is the `IsMatch` trait. It defines how to match *PatternTree*
+nodes against specific syntax tree nodes. A simplified implementation of the
+`IsMatch` trait is shown below:
+
+```rust
+pub trait IsMatch<O> {
+    fn is_match(&self, other: &'o O) -> bool;
+}
+```
+
+This trait needs to be implemented on each enum of the *PatternTree* (for the
+corresponding syntax tree types). For example, the `IsMatch` implementation for
+matching `ast::LitKind` against the *PatternTree's* `Lit` enum might look like
+this:
+
+```rust
+impl IsMatch<ast::LitKind> for Lit {
+    fn is_match(&self, other: &ast::LitKind) -> bool {
+        match (self, other) {
+            (Lit::Char(i), ast::LitKind::Char(j)) => i.is_match(j),
+            (Lit::Bool(i), ast::LitKind::Bool(j)) => i.is_match(j),
+            (Lit::Int(i), ast::LitKind::Int(j, _)) => i.is_match(j),
+            _ => false,
+        }
+    }
+}
+```
+
+All `IsMatch` implementations for matching the current *PatternTree* against
+`syntax::ast` can be found
+[here](https://github.com/fkohlgrueber/pattern-matching/blob/dfb3bc9fbab69cec7c91e72564a63ebaa2ede638/pattern-match/src/ast_match.rs).
+
+
+# Drawbacks
+
+#### Performance
+
+The pattern matching code is currently not optimized for performance, so it
+might be slower than hand-written matching code. Additionally, the two-stage
+approach (matching against the coarse pattern first and checking for additional
+properties later) might be slower than the current practice of checking for
+structure and additional properties in one pass. For example, the following lint
+
+```rust
+pattern!{
+    pat_if_without_else: Expr =
+        If(
+            _,
+            Block(
+                Expr( If(_, _, ())#inner )
+                | Semi( If(_, _, ())#inner )
+            )#then,
+            ()
+        )
+}
+...
+fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &ast::Expr) {
+    if let Some(result) = pat_if_without_else(expr) {
+        if !block_starts_with_comment(cx, result.then) {
+            ...
+        }
+}
+```
+
+first matches against the pattern and then checks that the `then` block doesn't
+start with a comment. Using clippy's current approach, it's possible to check
+for these conditions earlier:
+
+```rust
+fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &ast::Expr) {
+    if_chain! {
+        if let ast::ExprKind::If(ref check, ref then, None) = expr.node;
+        if !block_starts_with_comment(cx, then);
+        if let Some(inner) = expr_block(then);
+        if let ast::ExprKind::If(ref check_inner, ref content, None) = inner.node;
+        then {
+            ...
+        }
+    }
+}
+```
+
+Whether or not this causes performance regressions depends on actual patterns.
+If it turns out to be a problem, the pattern matching algorithms could be
+extended to allow "early filtering" (see the [Early Filtering](#early-filtering)
+section in Future Possibilities).
+
+That being said, I don't see any conceptual limitations regarding pattern
+matching performance.
+
+#### Applicability
+
+Even though I'd expect that a lot of lints can be written using the proposed
+pattern syntax, it's unlikely that all lints can be expressed using patterns. I
+suspect that there will still be lints that need to be implemented by writing
+custom pattern matching code. This would lead to mix within clippy's codebase
+where some lints are implemented using patterns and others aren't. This
+inconsistency might be considered a drawback.
+
+
+# Rationale and alternatives
+
+Specifying lints using syntax tree patterns has a couple of advantages compared
+to the current approach of manually writing matching code. First, syntax tree
+patterns allow users to describe patterns in a simple and expressive way. This
+makes it easier to write new lints for both novices and experts and also makes
+reading / modifying existing lints simpler.
+
+Another advantage is that lints are independent of specific syntax tree
+implementations (e.g. AST / HIR, ...). When these syntax tree implementations
+change, only the `IsMatch` trait implementations need to be adapted and existing
+lints can remain unchanged. This also means that if the `IsMatch` trait
+implementations were integrated into the compiler, updating the `IsMatch`
+implementations would be required for the compiler to compile successfully. This
+could reduce the number of times clippy breaks because of changes in the
+compiler. Another advantage of the pattern's independence is that converting an
+`EarlyLintPass` lint into a `LatePassLint` wouldn't require rewriting the whole
+pattern matching code. In fact, the pattern might work just fine without any
+adaptions.
+
+
+## Alternatives
+
+### Rust-like pattern syntax
+
+The proposed pattern syntax requires users to know the structure of the
+`PatternTree` (which is very similar to the AST's / HIR's structure) and also
+the pattern syntax. An alternative would be to introduce a pattern syntax that
+is similar to actual Rust syntax (probably like the `quote!` macro). For
+example, a pattern that matches `if` expressions that have `false` in their
+condition could look like this:
+
+```rust
+if false {
+    #[*]
+}
+```
+
+#### Problems
+
+Extending Rust syntax (which is quite complex by itself) with additional syntax
+needed for specifying patterns (alternations, sequences, repetisions, named
+submatches, ...) might become difficult to read and really hard to parse
+properly.
+
+For example, a pattern that matches a binary operation that has `0` on both
+sides might look like this:
+
+```
+0 #[*:BinOpKind] 0
+```
+
+Now consider this slightly more complex example:
+
+```
+1 + 0 #[*:BinOpKind] 0
+```
+
+The parser would need to know the precedence of `#[*:BinOpKind]` because it
+affects the structure of the resulting AST. `1 + 0 + 0` is parsed as `(1 + 0) +
+0` while `1 + 0 * 0` is parsed as `1 + (0 * 0)`. Since the pattern could be any
+`BinOpKind`, the precedence cannot be known in advance.
+
+Another example of a problem would be named submatches. Take a look at this
+pattern:
+
+```rust
+fn test() {
+    1 #foo
+}
+```
+
+Which node is `#foo` referring to? `int`, `ast::Lit`, `ast::Expr`, `ast::Stmt`?
+Naming subpatterns in a rust-like syntax is difficult because a lot of AST nodes
+don't have a syntactic element that can be used to put the name tag on. In these
+situations, the only sensible option would be to assign the name tag to the
+outermost node (`ast::Stmt` in the example above), because the information of
+all child nodes can be retrieved through the outermost node. The problem with
+this then would be that accessing inner nodes (like `ast::Lit`) would again
+require manual pattern matching.
+
+In general, Rust syntax contains a lot of code structure implicitly. This
+structure is reconstructed during parsing (e.g. binary operations are
+reconstructed using operator precedence and left-to-right) and is one of the
+reasons why parsing is a complex task. The advantage of this approach is that
+writing code is simpler for users.
+
+When writing *syntax tree patterns*, each element of the hierarchy might have
+alternatives, repetitions, etc.. Respecting that while still allowing
+human-friendly syntax that contains structure implicitly seems to be really
+complex, if not impossible.
+
+Developing such a syntax would also require to maintain a custom parser that is
+at least as complex as the Rust parser itself. Additionally, future changes in
+the Rust syntax might be incompatible with such a syntax.
+
+In summary, I think that developing such a syntax would introduce a lot of
+complexity to solve a relatively minor problem.
+
+The issue of users not knowing about the *PatternTree* structure could be solved
+by a tool that, given a rust program, generates a pattern that matches only this
+program (similar to the clippy author lint).
+
+For some simple cases (like the first example above), it might be possible to
+successfully mix Rust and pattern syntax. This space could be further explored
+in a future extension.
+
+# Prior art
+
+The pattern syntax is heavily inspired by regular expressions (repetitions,
+alternatives, sequences, ...).
+
+From what I've seen until now, other linters also implement lints that directly
+work on syntax tree data structures, just like clippy does currently. I would
+therefore consider the pattern syntax to be *new*, but please correct me if I'm
+wrong.
+
+# Unresolved questions
+
+#### How to handle multiple matches?
+
+When matching a syntax tree node against a pattern, there are possibly multiple
+ways in which the pattern can be matched. A simple example of this would be the
+following pattern:
+
+```rust
+pattern!{
+    my_pattern: Expr =
+        Array( _* Lit(_)+#literals)
+}
+```
+
+This pattern matches arrays that end with at least one literal. Now given the
+array `[x, 1, 2]`, should `1` be matched as part of the `_*` or the `Lit(_)+`
+part of the pattern? The difference is important because the named submatch
+`#literals` would contain 1 or 2 elements depending how the pattern is matched.
+In regular expressions, this problem is solved by matching "greedy" by default
+and "non-greedy" optionally.
+
+I haven't looked much into this yet because I don't know how relevant it is for
+most lints. The current implementation simply returns the first match it finds.
+
+# Future possibilities
+
+#### Implement rest of Rust Syntax
+
+The current project only implements a small part of the Rust syntax. In the
+future, this should incrementally be extended to more syntax to allow
+implementing more lints. Implementing more of the Rust syntax requires extending
+the `PatternTree` and `IsMatch` implementations, but should be relatively
+straight-forward.
+
+#### Early filtering
+
+As described in the *Drawbacks/Performance* section, allowing additional checks
+during the pattern matching might be beneficial.
+
+The pattern below shows how this could look like:
+
+```rust
+pattern!{
+    pat_if_without_else: Expr =
+        If(
+            _,
+            Block(
+                Expr( If(_, _, ())#inner )
+                | Semi( If(_, _, ())#inner )
+            )#then,
+            ()
+        )
+    where
+        !in_macro(#then.span);
+}
+```
+
+The difference compared to the currently proposed two-stage filtering is that
+using early filtering, the condition (`!in_macro(#then.span)` in this case)
+would be evaluated as soon as the `Block(_)#then` was matched.
+
+Another idea in this area would be to introduce a syntax for backreferences.
+They could be used to require that multiple parts of a pattern should match the
+same value. For example, the `assign_op_pattern` lint that searches for `a = a
+op b` and recommends changing it to `a op= b` requires that both occurrances of
+`a` are the same. Using `=#...` as syntax for backreferences, the lint could be
+implemented like this:
+
+```rust
+pattern!{
+    assign_op_pattern: Expr =
+        Assign(_#target, Binary(_, =#target, _)
+}
+```
+
+#### Match descendant
+
+A lot of lints currently implement custom visitors that check whether any
+subtree (which might not be a direct descendant) of the current node matches
+some properties. This cannot be expressed with the proposed pattern syntax.
+Extending the pattern syntax to allow patterns like "a function that contains at
+least two return statements" could be a practical addition.
+
+#### Negation operator for alternatives
+
+For patterns like "a literal that is not a boolean literal" one currently needs
+to list all alternatives except the boolean case. Introducing a negation
+operator that allows to write `Lit(!Bool(_))` might be a good idea. This pattern
+would be eqivalent to `Lit( Char(_) | Int(_) )` (given that currently only three
+literal types are implemented).
+
+#### Functional composition
+
+Patterns currently don't have any concept of composition. This leads to
+repetitions within patterns. For example, one of the collapsible-if patterns
+currently has to be written like this:
+
+```rust
+pattern!{
+    pat_if_else: Expr =
+        If(
+            _,
+            _,
+            Block_(
+                Block(
+                    Expr((If(_, _, _?) | IfLet(_, _?))#else_) |
+                    Semi((If(_, _, _?) | IfLet(_, _?))#else_)
+                )#block_inner
+            )#block
+        ) |
+        IfLet(
+            _,
+            Block_(
+                Block(
+                    Expr((If(_, _, _?) | IfLet(_, _?))#else_) |
+                    Semi((If(_, _, _?) | IfLet(_, _?))#else_)
+                )#block_inner
+            )#block
+        )
+}
+```
+
+If patterns supported defining functions of subpatterns, the code could be
+simplified as follows:
+
+```rust
+pattern!{
+    fn expr_or_semi(expr: Expr) -> Stmt {
+        Expr(expr) | Semi(expr)
+    }
+    fn if_or_if_let(then: Block, else: Opt<Expr>) -> Expr {
+        If(_, then, else) | IfLet(then, else)
+    }
+    pat_if_else: Expr =
+        if_or_if_let(
+            _,
+            Block_(
+                Block(
+                    expr_or_semi( if_or_if_let(_, _?)#else_ )
+                )#block_inner
+            )#block
+        )
+}
+```
+
+Additionally, common patterns like `expr_or_semi` could be shared between
+different lints.
+
+#### Clippy Pattern Author
+
+Another improvement could be to create a tool that, given some valid Rust
+syntax, generates a pattern that matches this syntax exactly. This would make
+starting to write a pattern easier. A user could take a look at the patterns
+generated for a couple of Rust code examples and use that information to write a
+pattern that matches all of them.
+
+This is similar to clippy's author lint.
+
+#### Supporting other syntaxes
+
+Most of the proposed system is language-agnostic. For example, the pattern
+syntax could also be used to describe patterns for other programming languages.
+
+In order to support other languages' syntaxes, one would need to implement
+another `PatternTree` that sufficiently describes the languages' AST and
+implement `IsMatch` for this `PatternTree` and the languages' AST.
+
+One aspect of this is that it would even be possible to write lints that work on
+the pattern syntax itself. For example, when writing the following pattern
+
+
+```rust
+pattern!{
+    my_pattern: Expr =
+        Array( Lit(Bool(false)) Lit(Bool(false)) )
+}
+```
+
+a lint that works on the pattern syntax's AST could suggest using this pattern
+instead:
+
+```rust
+pattern!{
+    my_pattern: Expr =
+        Array( Lit(Bool(false)){2} )
+}
+```
+
+In the future, clippy could use this system to also provide lints for custom
+syntaxes like those found in macros.
diff --git a/src/tools/clippy/clippy_dev/src/new_lint.rs b/src/tools/clippy/clippy_dev/src/new_lint.rs
index 9e15f1504fa..ec7f1dd0d84 100644
--- a/src/tools/clippy/clippy_dev/src/new_lint.rs
+++ b/src/tools/clippy/clippy_dev/src/new_lint.rs
@@ -120,7 +120,7 @@ fn add_lint(lint: &LintData<'_>, enable_msrv: bool) -> io::Result<()> {
 
     let new_lint = if enable_msrv {
         format!(
-            "store.register_{lint_pass}_pass(move |{ctor_arg}| Box::new({module_name}::{camel_name}::new(msrv)));\n    ",
+            "store.register_{lint_pass}_pass(move |{ctor_arg}| Box::new({module_name}::{camel_name}::new(msrv())));\n    ",
             lint_pass = lint.pass,
             ctor_arg = if lint.pass == "late" { "_" } else { "" },
             module_name = lint.name,
@@ -238,10 +238,9 @@ fn get_lint_file_contents(lint: &LintData<'_>, enable_msrv: bool) -> String {
     result.push_str(&if enable_msrv {
         formatdoc!(
             r#"
-            use clippy_utils::msrvs;
+            use clippy_utils::msrvs::{{self, Msrv}};
             {pass_import}
             use rustc_lint::{{{context_import}, {pass_type}, LintContext}};
-            use rustc_semver::RustcVersion;
             use rustc_session::{{declare_tool_lint, impl_lint_pass}};
 
         "#
@@ -263,12 +262,12 @@ fn get_lint_file_contents(lint: &LintData<'_>, enable_msrv: bool) -> String {
         formatdoc!(
             r#"
             pub struct {name_camel} {{
-                msrv: Option<RustcVersion>,
+                msrv: Msrv,
             }}
 
             impl {name_camel} {{
                 #[must_use]
-                pub fn new(msrv: Option<RustcVersion>) -> Self {{
+                pub fn new(msrv: Msrv) -> Self {{
                     Self {{ msrv }}
                 }}
             }}
@@ -357,15 +356,14 @@ fn create_lint_for_ty(lint: &LintData<'_>, enable_msrv: bool, ty: &str) -> io::R
         let _ = writedoc!(
             lint_file_contents,
             r#"
-                use clippy_utils::{{meets_msrv, msrvs}};
+                use clippy_utils::msrvs::{{self, Msrv}};
                 use rustc_lint::{{{context_import}, LintContext}};
-                use rustc_semver::RustcVersion;
 
                 use super::{name_upper};
 
                 // TODO: Adjust the parameters as necessary
-                pub(super) fn check(cx: &{context_import}, msrv: Option<RustcVersion>) {{
-                    if !meets_msrv(msrv, todo!("Add a new entry in `clippy_utils/src/msrvs`")) {{
+                pub(super) fn check(cx: &{context_import}, msrv: &Msrv) {{
+                    if !msrv.meets(todo!("Add a new entry in `clippy_utils/src/msrvs`")) {{
                         return;
                     }}
                     todo!();
diff --git a/src/tools/clippy/clippy_lints/src/almost_complete_letter_range.rs b/src/tools/clippy/clippy_lints/src/almost_complete_letter_range.rs
index df92579a85d..52beaf504a4 100644
--- a/src/tools/clippy/clippy_lints/src/almost_complete_letter_range.rs
+++ b/src/tools/clippy/clippy_lints/src/almost_complete_letter_range.rs
@@ -1,11 +1,10 @@
 use clippy_utils::diagnostics::span_lint_and_then;
+use clippy_utils::msrvs::{self, Msrv};
 use clippy_utils::source::{trim_span, walk_span_to_context};
-use clippy_utils::{meets_msrv, msrvs};
 use rustc_ast::ast::{Expr, ExprKind, LitKind, Pat, PatKind, RangeEnd, RangeLimits};
 use rustc_errors::Applicability;
 use rustc_lint::{EarlyContext, EarlyLintPass, LintContext};
 use rustc_middle::lint::in_external_macro;
-use rustc_semver::RustcVersion;
 use rustc_session::{declare_tool_lint, impl_lint_pass};
 use rustc_span::Span;
 
@@ -33,10 +32,10 @@ declare_clippy_lint! {
 impl_lint_pass!(AlmostCompleteLetterRange => [ALMOST_COMPLETE_LETTER_RANGE]);
 
 pub struct AlmostCompleteLetterRange {
-    msrv: Option<RustcVersion>,
+    msrv: Msrv,
 }
 impl AlmostCompleteLetterRange {
-    pub fn new(msrv: Option<RustcVersion>) -> Self {
+    pub fn new(msrv: Msrv) -> Self {
         Self { msrv }
     }
 }
@@ -46,7 +45,7 @@ impl EarlyLintPass for AlmostCompleteLetterRange {
             let ctxt = e.span.ctxt();
             let sugg = if let Some(start) = walk_span_to_context(start.span, ctxt)
                 && let Some(end) = walk_span_to_context(end.span, ctxt)
-                && meets_msrv(self.msrv, msrvs::RANGE_INCLUSIVE)
+                && self.msrv.meets(msrvs::RANGE_INCLUSIVE)
             {
                 Some((trim_span(cx.sess().source_map(), start.between(end)), "..="))
             } else {
@@ -60,7 +59,7 @@ impl EarlyLintPass for AlmostCompleteLetterRange {
         if let PatKind::Range(Some(start), Some(end), kind) = &p.kind
             && matches!(kind.node, RangeEnd::Excluded)
         {
-            let sugg = if meets_msrv(self.msrv, msrvs::RANGE_INCLUSIVE) {
+            let sugg = if self.msrv.meets(msrvs::RANGE_INCLUSIVE) {
                 "..="
             } else {
                 "..."
diff --git a/src/tools/clippy/clippy_lints/src/approx_const.rs b/src/tools/clippy/clippy_lints/src/approx_const.rs
index 724490fb495..ccf82f132f4 100644
--- a/src/tools/clippy/clippy_lints/src/approx_const.rs
+++ b/src/tools/clippy/clippy_lints/src/approx_const.rs
@@ -1,5 +1,5 @@
 use clippy_utils::diagnostics::span_lint_and_help;
-use clippy_utils::{meets_msrv, msrvs};
+use clippy_utils::msrvs::{self, Msrv};
 use rustc_ast::ast::{FloatTy, LitFloatType, LitKind};
 use rustc_hir::{Expr, ExprKind};
 use rustc_lint::{LateContext, LateLintPass};
@@ -63,12 +63,12 @@ const KNOWN_CONSTS: [(f64, &str, usize, Option<RustcVersion>); 19] = [
 ];
 
 pub struct ApproxConstant {
-    msrv: Option<RustcVersion>,
+    msrv: Msrv,
 }
 
 impl ApproxConstant {
     #[must_use]
-    pub fn new(msrv: Option<RustcVersion>) -> Self {
+    pub fn new(msrv: Msrv) -> Self {
         Self { msrv }
     }
 
@@ -87,7 +87,7 @@ impl ApproxConstant {
         let s = s.as_str();
         if s.parse::<f64>().is_ok() {
             for &(constant, name, min_digits, msrv) in &KNOWN_CONSTS {
-                if is_approx_const(constant, s, min_digits) && msrv.map_or(true, |msrv| meets_msrv(self.msrv, msrv)) {
+                if is_approx_const(constant, s, min_digits) && msrv.map_or(true, |msrv| self.msrv.meets(msrv)) {
                     span_lint_and_help(
                         cx,
                         APPROX_CONSTANT,
diff --git a/src/tools/clippy/clippy_lints/src/attrs.rs b/src/tools/clippy/clippy_lints/src/attrs.rs
index ecf8e83375d..0710ac0bb0a 100644
--- a/src/tools/clippy/clippy_lints/src/attrs.rs
+++ b/src/tools/clippy/clippy_lints/src/attrs.rs
@@ -2,11 +2,10 @@
 
 use clippy_utils::diagnostics::{span_lint, span_lint_and_help, span_lint_and_sugg, span_lint_and_then};
 use clippy_utils::macros::{is_panic, macro_backtrace};
-use clippy_utils::msrvs;
+use clippy_utils::msrvs::{self, Msrv};
 use clippy_utils::source::{first_line_of_span, is_present_in_source, snippet_opt, without_block_comments};
-use clippy_utils::{extract_msrv_attr, meets_msrv};
 use if_chain::if_chain;
-use rustc_ast::{AttrKind, AttrStyle, Attribute, Lit, LitKind, MetaItemKind, NestedMetaItem};
+use rustc_ast::{AttrKind, AttrStyle, Attribute, LitKind, MetaItemKind, MetaItemLit, NestedMetaItem};
 use rustc_errors::Applicability;
 use rustc_hir::{
     Block, Expr, ExprKind, ImplItem, ImplItemKind, Item, ItemKind, StmtKind, TraitFn, TraitItem, TraitItemKind,
@@ -14,7 +13,6 @@ use rustc_hir::{
 use rustc_lint::{EarlyContext, EarlyLintPass, LateContext, LateLintPass, Level, LintContext};
 use rustc_middle::lint::in_external_macro;
 use rustc_middle::ty;
-use rustc_semver::RustcVersion;
 use rustc_session::{declare_lint_pass, declare_tool_lint, impl_lint_pass};
 use rustc_span::source_map::Span;
 use rustc_span::symbol::Symbol;
@@ -576,7 +574,7 @@ fn check_attrs(cx: &LateContext<'_>, span: Span, name: Symbol, attrs: &[Attribut
     }
 }
 
-fn check_semver(cx: &LateContext<'_>, span: Span, lit: &Lit) {
+fn check_semver(cx: &LateContext<'_>, span: Span, lit: &MetaItemLit) {
     if let LitKind::Str(is, _) = lit.kind {
         if Version::parse(is.as_str()).is_ok() {
             return;
@@ -599,7 +597,7 @@ fn is_word(nmi: &NestedMetaItem, expected: Symbol) -> bool {
 }
 
 pub struct EarlyAttributes {
-    pub msrv: Option<RustcVersion>,
+    pub msrv: Msrv,
 }
 
 impl_lint_pass!(EarlyAttributes => [
@@ -614,7 +612,7 @@ impl EarlyLintPass for EarlyAttributes {
     }
 
     fn check_attribute(&mut self, cx: &EarlyContext<'_>, attr: &Attribute) {
-        check_deprecated_cfg_attr(cx, attr, self.msrv);
+        check_deprecated_cfg_attr(cx, attr, &self.msrv);
         check_mismatched_target_os(cx, attr);
     }
 
@@ -654,9 +652,9 @@ fn check_empty_line_after_outer_attr(cx: &EarlyContext<'_>, item: &rustc_ast::It
     }
 }
 
-fn check_deprecated_cfg_attr(cx: &EarlyContext<'_>, attr: &Attribute, msrv: Option<RustcVersion>) {
+fn check_deprecated_cfg_attr(cx: &EarlyContext<'_>, attr: &Attribute, msrv: &Msrv) {
     if_chain! {
-        if meets_msrv(msrv, msrvs::TOOL_ATTRIBUTES);
+        if msrv.meets(msrvs::TOOL_ATTRIBUTES);
         // check cfg_attr
         if attr.has_name(sym::cfg_attr);
         if let Some(items) = attr.meta_item_list();
diff --git a/src/tools/clippy/clippy_lints/src/casts/cast_abs_to_unsigned.rs b/src/tools/clippy/clippy_lints/src/casts/cast_abs_to_unsigned.rs
index 3f1edabe6c5..44226298333 100644
--- a/src/tools/clippy/clippy_lints/src/casts/cast_abs_to_unsigned.rs
+++ b/src/tools/clippy/clippy_lints/src/casts/cast_abs_to_unsigned.rs
@@ -1,11 +1,10 @@
 use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::msrvs::{self, Msrv};
 use clippy_utils::sugg::Sugg;
-use clippy_utils::{meets_msrv, msrvs};
 use rustc_errors::Applicability;
 use rustc_hir::{Expr, ExprKind};
 use rustc_lint::LateContext;
 use rustc_middle::ty::{self, Ty};
-use rustc_semver::RustcVersion;
 
 use super::CAST_ABS_TO_UNSIGNED;
 
@@ -15,9 +14,9 @@ pub(super) fn check(
     cast_expr: &Expr<'_>,
     cast_from: Ty<'_>,
     cast_to: Ty<'_>,
-    msrv: Option<RustcVersion>,
+    msrv: &Msrv,
 ) {
-    if meets_msrv(msrv, msrvs::UNSIGNED_ABS)
+    if msrv.meets(msrvs::UNSIGNED_ABS)
         && let ty::Int(from) = cast_from.kind()
         && let ty::Uint(to) = cast_to.kind()
         && let ExprKind::MethodCall(method_path, receiver, ..) = cast_expr.kind
diff --git a/src/tools/clippy/clippy_lints/src/casts/cast_lossless.rs b/src/tools/clippy/clippy_lints/src/casts/cast_lossless.rs
index 13c403234da..cf07e050ccc 100644
--- a/src/tools/clippy/clippy_lints/src/casts/cast_lossless.rs
+++ b/src/tools/clippy/clippy_lints/src/casts/cast_lossless.rs
@@ -1,12 +1,12 @@
 use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::in_constant;
+use clippy_utils::msrvs::{self, Msrv};
 use clippy_utils::source::snippet_opt;
 use clippy_utils::ty::is_isize_or_usize;
-use clippy_utils::{in_constant, meets_msrv, msrvs};
 use rustc_errors::Applicability;
 use rustc_hir::{Expr, ExprKind};
 use rustc_lint::LateContext;
 use rustc_middle::ty::{self, FloatTy, Ty};
-use rustc_semver::RustcVersion;
 
 use super::{utils, CAST_LOSSLESS};
 
@@ -16,7 +16,7 @@ pub(super) fn check(
     cast_op: &Expr<'_>,
     cast_from: Ty<'_>,
     cast_to: Ty<'_>,
-    msrv: Option<RustcVersion>,
+    msrv: &Msrv,
 ) {
     if !should_lint(cx, expr, cast_from, cast_to, msrv) {
         return;
@@ -57,13 +57,7 @@ pub(super) fn check(
     );
 }
 
-fn should_lint(
-    cx: &LateContext<'_>,
-    expr: &Expr<'_>,
-    cast_from: Ty<'_>,
-    cast_to: Ty<'_>,
-    msrv: Option<RustcVersion>,
-) -> bool {
+fn should_lint(cx: &LateContext<'_>, expr: &Expr<'_>, cast_from: Ty<'_>, cast_to: Ty<'_>, msrv: &Msrv) -> bool {
     // Do not suggest using From in consts/statics until it is valid to do so (see #2267).
     if in_constant(cx, expr.hir_id) {
         return false;
@@ -89,7 +83,7 @@ fn should_lint(
             };
             !is_isize_or_usize(cast_from) && from_nbits < to_nbits
         },
-        (false, true) if matches!(cast_from.kind(), ty::Bool) && meets_msrv(msrv, msrvs::FROM_BOOL) => true,
+        (false, true) if matches!(cast_from.kind(), ty::Bool) && msrv.meets(msrvs::FROM_BOOL) => true,
         (_, _) => {
             matches!(cast_from.kind(), ty::Float(FloatTy::F32)) && matches!(cast_to.kind(), ty::Float(FloatTy::F64))
         },
diff --git a/src/tools/clippy/clippy_lints/src/casts/cast_possible_truncation.rs b/src/tools/clippy/clippy_lints/src/casts/cast_possible_truncation.rs
index adbcfd3189b..a6376484914 100644
--- a/src/tools/clippy/clippy_lints/src/casts/cast_possible_truncation.rs
+++ b/src/tools/clippy/clippy_lints/src/casts/cast_possible_truncation.rs
@@ -118,12 +118,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, cast_expr: &Expr<'_>,
             };
             let to_nbits = utils::int_ty_to_nbits(cast_to, cx.tcx);
 
-            let cast_from_ptr_size = def.repr().int.map_or(true, |ty| {
-                matches!(
-                    ty,
-                    IntegerType::Pointer(_),
-                )
-            });
+            let cast_from_ptr_size = def.repr().int.map_or(true, |ty| matches!(ty, IntegerType::Pointer(_),));
             let suffix = match (cast_from_ptr_size, is_isize_or_usize(cast_to)) {
                 (false, false) if from_nbits > to_nbits => "",
                 (true, false) if from_nbits > to_nbits => "",
diff --git a/src/tools/clippy/clippy_lints/src/casts/cast_slice_different_sizes.rs b/src/tools/clippy/clippy_lints/src/casts/cast_slice_different_sizes.rs
index d31d10d22b9..e862f13e69f 100644
--- a/src/tools/clippy/clippy_lints/src/casts/cast_slice_different_sizes.rs
+++ b/src/tools/clippy/clippy_lints/src/casts/cast_slice_different_sizes.rs
@@ -1,16 +1,16 @@
-use clippy_utils::{diagnostics::span_lint_and_then, meets_msrv, msrvs, source};
+use clippy_utils::msrvs::{self, Msrv};
+use clippy_utils::{diagnostics::span_lint_and_then, source};
 use if_chain::if_chain;
 use rustc_ast::Mutability;
 use rustc_hir::{Expr, ExprKind, Node};
 use rustc_lint::LateContext;
 use rustc_middle::ty::{self, layout::LayoutOf, Ty, TypeAndMut};
-use rustc_semver::RustcVersion;
 
 use super::CAST_SLICE_DIFFERENT_SIZES;
 
-pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'tcx>, msrv: Option<RustcVersion>) {
+pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'tcx>, msrv: &Msrv) {
     // suggestion is invalid if `ptr::slice_from_raw_parts` does not exist
-    if !meets_msrv(msrv, msrvs::PTR_SLICE_RAW_PARTS) {
+    if !msrv.meets(msrvs::PTR_SLICE_RAW_PARTS) {
         return;
     }
 
diff --git a/src/tools/clippy/clippy_lints/src/casts/cast_slice_from_raw_parts.rs b/src/tools/clippy/clippy_lints/src/casts/cast_slice_from_raw_parts.rs
index 284ef165998..627b795d6ed 100644
--- a/src/tools/clippy/clippy_lints/src/casts/cast_slice_from_raw_parts.rs
+++ b/src/tools/clippy/clippy_lints/src/casts/cast_slice_from_raw_parts.rs
@@ -1,12 +1,12 @@
 use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::msrvs::{self, Msrv};
 use clippy_utils::source::snippet_with_applicability;
-use clippy_utils::{match_def_path, meets_msrv, msrvs, paths};
+use clippy_utils::{match_def_path, paths};
 use if_chain::if_chain;
 use rustc_errors::Applicability;
 use rustc_hir::{def_id::DefId, Expr, ExprKind};
 use rustc_lint::LateContext;
 use rustc_middle::ty::{self, Ty};
-use rustc_semver::RustcVersion;
 
 use super::CAST_SLICE_FROM_RAW_PARTS;
 
@@ -25,15 +25,9 @@ fn raw_parts_kind(cx: &LateContext<'_>, did: DefId) -> Option<RawPartsKind> {
     }
 }
 
-pub(super) fn check(
-    cx: &LateContext<'_>,
-    expr: &Expr<'_>,
-    cast_expr: &Expr<'_>,
-    cast_to: Ty<'_>,
-    msrv: Option<RustcVersion>,
-) {
+pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, cast_expr: &Expr<'_>, cast_to: Ty<'_>, msrv: &Msrv) {
     if_chain! {
-        if meets_msrv(msrv, msrvs::PTR_SLICE_RAW_PARTS);
+        if msrv.meets(msrvs::PTR_SLICE_RAW_PARTS);
         if let ty::RawPtr(ptrty) = cast_to.kind();
         if let ty::Slice(_) = ptrty.ty.kind();
         if let ExprKind::Call(fun, [ptr_arg, len_arg]) = cast_expr.peel_blocks().kind;
diff --git a/src/tools/clippy/clippy_lints/src/casts/mod.rs b/src/tools/clippy/clippy_lints/src/casts/mod.rs
index 7148b5e6ebf..c6d505c4a18 100644
--- a/src/tools/clippy/clippy_lints/src/casts/mod.rs
+++ b/src/tools/clippy/clippy_lints/src/casts/mod.rs
@@ -21,11 +21,11 @@ mod ptr_as_ptr;
 mod unnecessary_cast;
 mod utils;
 
-use clippy_utils::{is_hir_ty_cfg_dependant, meets_msrv, msrvs};
+use clippy_utils::is_hir_ty_cfg_dependant;
+use clippy_utils::msrvs::{self, Msrv};
 use rustc_hir::{Expr, ExprKind};
 use rustc_lint::{LateContext, LateLintPass, LintContext};
 use rustc_middle::lint::in_external_macro;
-use rustc_semver::RustcVersion;
 use rustc_session::{declare_tool_lint, impl_lint_pass};
 
 declare_clippy_lint! {
@@ -648,12 +648,12 @@ declare_clippy_lint! {
 }
 
 pub struct Casts {
-    msrv: Option<RustcVersion>,
+    msrv: Msrv,
 }
 
 impl Casts {
     #[must_use]
-    pub fn new(msrv: Option<RustcVersion>) -> Self {
+    pub fn new(msrv: Msrv) -> Self {
         Self { msrv }
     }
 }
@@ -686,7 +686,7 @@ impl_lint_pass!(Casts => [
 impl<'tcx> LateLintPass<'tcx> for Casts {
     fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
         if !in_external_macro(cx.sess(), expr.span) {
-            ptr_as_ptr::check(cx, expr, self.msrv);
+            ptr_as_ptr::check(cx, expr, &self.msrv);
         }
 
         if expr.span.from_expansion() {
@@ -705,7 +705,7 @@ impl<'tcx> LateLintPass<'tcx> for Casts {
             if unnecessary_cast::check(cx, expr, cast_expr, cast_from, cast_to) {
                 return;
             }
-            cast_slice_from_raw_parts::check(cx, expr, cast_expr, cast_to, self.msrv);
+            cast_slice_from_raw_parts::check(cx, expr, cast_expr, cast_to, &self.msrv);
             as_ptr_cast_mut::check(cx, expr, cast_expr, cast_to);
             fn_to_numeric_cast_any::check(cx, expr, cast_expr, cast_from, cast_to);
             fn_to_numeric_cast::check(cx, expr, cast_expr, cast_from, cast_to);
@@ -717,16 +717,16 @@ impl<'tcx> LateLintPass<'tcx> for Casts {
                     cast_possible_wrap::check(cx, expr, cast_from, cast_to);
                     cast_precision_loss::check(cx, expr, cast_from, cast_to);
                     cast_sign_loss::check(cx, expr, cast_expr, cast_from, cast_to);
-                    cast_abs_to_unsigned::check(cx, expr, cast_expr, cast_from, cast_to, self.msrv);
+                    cast_abs_to_unsigned::check(cx, expr, cast_expr, cast_from, cast_to, &self.msrv);
                     cast_nan_to_int::check(cx, expr, cast_expr, cast_from, cast_to);
                 }
-                cast_lossless::check(cx, expr, cast_expr, cast_from, cast_to, self.msrv);
+                cast_lossless::check(cx, expr, cast_expr, cast_from, cast_to, &self.msrv);
                 cast_enum_constructor::check(cx, expr, cast_expr, cast_from);
             }
 
             as_underscore::check(cx, expr, cast_to_hir);
 
-            if meets_msrv(self.msrv, msrvs::BORROW_AS_PTR) {
+            if self.msrv.meets(msrvs::BORROW_AS_PTR) {
                 borrow_as_ptr::check(cx, expr, cast_expr, cast_to_hir);
             }
         }
@@ -734,8 +734,8 @@ impl<'tcx> LateLintPass<'tcx> for Casts {
         cast_ref_to_mut::check(cx, expr);
         cast_ptr_alignment::check(cx, expr);
         char_lit_as_u8::check(cx, expr);
-        ptr_as_ptr::check(cx, expr, self.msrv);
-        cast_slice_different_sizes::check(cx, expr, self.msrv);
+        ptr_as_ptr::check(cx, expr, &self.msrv);
+        cast_slice_different_sizes::check(cx, expr, &self.msrv);
     }
 
     extract_msrv_attr!(LateContext);
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 b9509ca656f..15ffb00da88 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
@@ -1,19 +1,18 @@
 use std::borrow::Cow;
 
 use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::msrvs::{self, Msrv};
 use clippy_utils::sugg::Sugg;
-use clippy_utils::{meets_msrv, msrvs};
 use if_chain::if_chain;
 use rustc_errors::Applicability;
 use rustc_hir::{Expr, ExprKind, Mutability, TyKind};
 use rustc_lint::LateContext;
 use rustc_middle::ty::{self, TypeAndMut};
-use rustc_semver::RustcVersion;
 
 use super::PTR_AS_PTR;
 
-pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, msrv: Option<RustcVersion>) {
-    if !meets_msrv(msrv, msrvs::POINTER_CAST) {
+pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, msrv: &Msrv) {
+    if !msrv.meets(msrvs::POINTER_CAST) {
         return;
     }
 
diff --git a/src/tools/clippy/clippy_lints/src/casts/unnecessary_cast.rs b/src/tools/clippy/clippy_lints/src/casts/unnecessary_cast.rs
index c8596987e4d..7e23318076c 100644
--- a/src/tools/clippy/clippy_lints/src/casts/unnecessary_cast.rs
+++ b/src/tools/clippy/clippy_lints/src/casts/unnecessary_cast.rs
@@ -1,7 +1,7 @@
 use clippy_utils::diagnostics::span_lint_and_sugg;
-use clippy_utils::get_parent_expr;
 use clippy_utils::numeric_literal::NumericLiteral;
 use clippy_utils::source::snippet_opt;
+use clippy_utils::{get_parent_expr, path_to_local};
 use if_chain::if_chain;
 use rustc_ast::{LitFloatType, LitIntType, LitKind};
 use rustc_errors::Applicability;
@@ -75,13 +75,26 @@ pub(super) fn check<'tcx>(
     }
 
     if cast_from.kind() == cast_to.kind() && !in_external_macro(cx.sess(), expr.span) {
+        if let Some(id) = path_to_local(cast_expr)
+            && let Some(span) = cx.tcx.hir().opt_span(id)
+            && span.ctxt() != cast_expr.span.ctxt()
+        {
+            // Binding context is different than the identifiers context.
+            // Weird macro wizardry could be involved here.
+            return false;
+        }
+
         span_lint_and_sugg(
             cx,
             UNNECESSARY_CAST,
             expr.span,
             &format!("casting to the same type is unnecessary (`{cast_from}` -> `{cast_to}`)"),
             "try",
-            cast_str,
+            if get_parent_expr(cx, expr).map_or(false, |e| matches!(e.kind, ExprKind::AddrOf(..))) {
+                format!("{{ {cast_str} }}")
+            } else {
+                cast_str
+            },
             Applicability::MachineApplicable,
         );
         return true;
diff --git a/src/tools/clippy/clippy_lints/src/checked_conversions.rs b/src/tools/clippy/clippy_lints/src/checked_conversions.rs
index 78e9921f036..9102a89e377 100644
--- a/src/tools/clippy/clippy_lints/src/checked_conversions.rs
+++ b/src/tools/clippy/clippy_lints/src/checked_conversions.rs
@@ -1,14 +1,14 @@
 //! lint on manually implemented checked conversions that could be transformed into `try_from`
 
 use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::msrvs::{self, Msrv};
 use clippy_utils::source::snippet_with_applicability;
-use clippy_utils::{in_constant, is_integer_literal, meets_msrv, msrvs, SpanlessEq};
+use clippy_utils::{in_constant, is_integer_literal, SpanlessEq};
 use if_chain::if_chain;
 use rustc_errors::Applicability;
 use rustc_hir::{BinOp, BinOpKind, Expr, ExprKind, QPath, TyKind};
 use rustc_lint::{LateContext, LateLintPass, LintContext};
 use rustc_middle::lint::in_external_macro;
-use rustc_semver::RustcVersion;
 use rustc_session::{declare_tool_lint, impl_lint_pass};
 
 declare_clippy_lint! {
@@ -37,12 +37,12 @@ declare_clippy_lint! {
 }
 
 pub struct CheckedConversions {
-    msrv: Option<RustcVersion>,
+    msrv: Msrv,
 }
 
 impl CheckedConversions {
     #[must_use]
-    pub fn new(msrv: Option<RustcVersion>) -> Self {
+    pub fn new(msrv: Msrv) -> Self {
         Self { msrv }
     }
 }
@@ -51,7 +51,7 @@ impl_lint_pass!(CheckedConversions => [CHECKED_CONVERSIONS]);
 
 impl<'tcx> LateLintPass<'tcx> for CheckedConversions {
     fn check_expr(&mut self, cx: &LateContext<'_>, item: &Expr<'_>) {
-        if !meets_msrv(self.msrv, msrvs::TRY_FROM) {
+        if !self.msrv.meets(msrvs::TRY_FROM) {
             return;
         }
 
diff --git a/src/tools/clippy/clippy_lints/src/collapsible_if.rs b/src/tools/clippy/clippy_lints/src/collapsible_if.rs
index 90430b71a0e..b38e09dc09f 100644
--- a/src/tools/clippy/clippy_lints/src/collapsible_if.rs
+++ b/src/tools/clippy/clippy_lints/src/collapsible_if.rs
@@ -160,11 +160,13 @@ fn check_collapsible_no_if_let(cx: &EarlyContext<'_>, expr: &ast::Expr, check: &
         if let ast::ExprKind::If(ref check_inner, ref content, None) = inner.kind;
         // Prevent triggering on `if c { if let a = b { .. } }`.
         if !matches!(check_inner.kind, ast::ExprKind::Let(..));
-        if expr.span.ctxt() == inner.span.ctxt();
+        let ctxt = expr.span.ctxt();
+        if inner.span.ctxt() == ctxt;
         then {
             span_lint_and_then(cx, COLLAPSIBLE_IF, expr.span, "this `if` statement can be collapsed", |diag| {
-                let lhs = Sugg::ast(cx, check, "..");
-                let rhs = Sugg::ast(cx, check_inner, "..");
+                let mut app = Applicability::MachineApplicable;
+                let lhs = Sugg::ast(cx, check, "..", ctxt, &mut app);
+                let rhs = Sugg::ast(cx, check_inner, "..", ctxt, &mut app);
                 diag.span_suggestion(
                     expr.span,
                     "collapse nested if block",
@@ -173,7 +175,7 @@ fn check_collapsible_no_if_let(cx: &EarlyContext<'_>, expr: &ast::Expr, check: &
                         lhs.and(&rhs),
                         snippet_block(cx, content.span, "..", Some(expr.span)),
                     ),
-                    Applicability::MachineApplicable, // snippet
+                    app, // snippet
                 );
             });
         }
diff --git a/src/tools/clippy/clippy_lints/src/declared_lints.rs b/src/tools/clippy/clippy_lints/src/declared_lints.rs
index 0d3fc43a644..e4d76f07d6b 100644
--- a/src/tools/clippy/clippy_lints/src/declared_lints.rs
+++ b/src/tools/clippy/clippy_lints/src/declared_lints.rs
@@ -177,6 +177,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[
     crate::from_raw_with_void_ptr::FROM_RAW_WITH_VOID_PTR_INFO,
     crate::from_str_radix_10::FROM_STR_RADIX_10_INFO,
     crate::functions::DOUBLE_MUST_USE_INFO,
+    crate::functions::MISNAMED_GETTERS_INFO,
     crate::functions::MUST_USE_CANDIDATE_INFO,
     crate::functions::MUST_USE_UNIT_INFO,
     crate::functions::NOT_UNSAFE_PTR_ARG_DEREF_INFO,
@@ -583,6 +584,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[
     crate::types::TYPE_COMPLEXITY_INFO,
     crate::types::VEC_BOX_INFO,
     crate::undocumented_unsafe_blocks::UNDOCUMENTED_UNSAFE_BLOCKS_INFO,
+    crate::undocumented_unsafe_blocks::UNNECESSARY_SAFETY_COMMENT_INFO,
     crate::unicode::INVISIBLE_CHARACTERS_INFO,
     crate::unicode::NON_ASCII_LITERAL_INFO,
     crate::unicode::UNICODE_NOT_NFC_INFO,
diff --git a/src/tools/clippy/clippy_lints/src/dereference.rs b/src/tools/clippy/clippy_lints/src/dereference.rs
index 47ea98956be..38329659e02 100644
--- a/src/tools/clippy/clippy_lints/src/dereference.rs
+++ b/src/tools/clippy/clippy_lints/src/dereference.rs
@@ -1,12 +1,13 @@
 use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_hir_and_then};
 use clippy_utils::mir::{enclosing_mir, expr_local, local_assignments, used_exactly_once, PossibleBorrowerMap};
+use clippy_utils::msrvs::{self, Msrv};
 use clippy_utils::source::{snippet_with_applicability, snippet_with_context};
 use clippy_utils::sugg::has_enclosing_paren;
 use clippy_utils::ty::{expr_sig, is_copy, peel_mid_ty_refs, ty_sig, variant_of_res};
 use clippy_utils::{
-    fn_def_id, get_parent_expr, get_parent_expr_for_hir, is_lint_allowed, meets_msrv, msrvs, path_to_local,
-    walk_to_expr_usage,
+    fn_def_id, get_parent_expr, get_parent_expr_for_hir, is_lint_allowed, path_to_local, walk_to_expr_usage,
 };
+
 use rustc_ast::util::parser::{PREC_POSTFIX, PREC_PREFIX};
 use rustc_data_structures::fx::FxIndexMap;
 use rustc_data_structures::graph::iterate::{CycleDetector, TriColorDepthFirstSearch};
@@ -28,7 +29,6 @@ use rustc_middle::ty::{
     self, Binder, BoundVariableKind, Clause, EarlyBinder, FnSig, GenericArgKind, List, ParamTy, PredicateKind,
     ProjectionPredicate, Ty, TyCtxt, TypeVisitable, TypeckResults,
 };
-use rustc_semver::RustcVersion;
 use rustc_session::{declare_tool_lint, impl_lint_pass};
 use rustc_span::{symbol::sym, Span, Symbol};
 use rustc_trait_selection::infer::InferCtxtExt as _;
@@ -181,12 +181,12 @@ pub struct Dereferencing<'tcx> {
     possible_borrowers: Vec<(LocalDefId, PossibleBorrowerMap<'tcx, 'tcx>)>,
 
     // `IntoIterator` for arrays requires Rust 1.53.
-    msrv: Option<RustcVersion>,
+    msrv: Msrv,
 }
 
 impl<'tcx> Dereferencing<'tcx> {
     #[must_use]
-    pub fn new(msrv: Option<RustcVersion>) -> Self {
+    pub fn new(msrv: Msrv) -> Self {
         Self {
             msrv,
             ..Dereferencing::default()
@@ -286,26 +286,27 @@ impl<'tcx> LateLintPass<'tcx> for Dereferencing<'tcx> {
         match (self.state.take(), kind) {
             (None, kind) => {
                 let expr_ty = typeck.expr_ty(expr);
-                let (position, adjustments) = walk_parents(cx, &mut self.possible_borrowers, expr, self.msrv);
+                let (position, adjustments) = walk_parents(cx, &mut self.possible_borrowers, expr, &self.msrv);
                 match kind {
                     RefOp::Deref => {
+                        let sub_ty = typeck.expr_ty(sub_expr);
                         if let Position::FieldAccess {
                             name,
                             of_union: false,
                         } = position
-                            && !ty_contains_field(typeck.expr_ty(sub_expr), name)
+                            && !ty_contains_field(sub_ty, name)
                         {
                             self.state = Some((
                                 State::ExplicitDerefField { name },
                                 StateData { span: expr.span, hir_id: expr.hir_id, position },
                             ));
-                        } else if position.is_deref_stable() {
+                        } else if position.is_deref_stable() && sub_ty.is_ref() {
                             self.state = Some((
                                 State::ExplicitDeref { mutability: None },
                                 StateData { span: expr.span, hir_id: expr.hir_id, position },
                             ));
                         }
-                    }
+                    },
                     RefOp::Method(target_mut)
                         if !is_lint_allowed(cx, EXPLICIT_DEREF_METHODS, expr.hir_id)
                             && position.lint_explicit_deref() =>
@@ -320,7 +321,7 @@ impl<'tcx> LateLintPass<'tcx> for Dereferencing<'tcx> {
                             StateData {
                                 span: expr.span,
                                 hir_id: expr.hir_id,
-                                position
+                                position,
                             },
                         ));
                     },
@@ -394,7 +395,11 @@ impl<'tcx> LateLintPass<'tcx> for Dereferencing<'tcx> {
                                     msg,
                                     snip_expr,
                                 }),
-                                StateData { span: expr.span, hir_id: expr.hir_id, position },
+                                StateData {
+                                    span: expr.span,
+                                    hir_id: expr.hir_id,
+                                    position,
+                                },
                             ));
                         } else if position.is_deref_stable()
                             // Auto-deref doesn't combine with other adjustments
@@ -406,7 +411,7 @@ impl<'tcx> LateLintPass<'tcx> for Dereferencing<'tcx> {
                                 StateData {
                                     span: expr.span,
                                     hir_id: expr.hir_id,
-                                    position
+                                    position,
                                 },
                             ));
                         }
@@ -698,7 +703,7 @@ fn walk_parents<'tcx>(
     cx: &LateContext<'tcx>,
     possible_borrowers: &mut Vec<(LocalDefId, PossibleBorrowerMap<'tcx, 'tcx>)>,
     e: &'tcx Expr<'_>,
-    msrv: Option<RustcVersion>,
+    msrv: &Msrv,
 ) -> (Position, &'tcx [Adjustment<'tcx>]) {
     let mut adjustments = [].as_slice();
     let mut precedence = 0i8;
@@ -862,7 +867,11 @@ fn walk_parents<'tcx>(
                             } && impl_ty.is_ref()
                             && let infcx = cx.tcx.infer_ctxt().build()
                             && infcx
-                                .type_implements_trait(trait_id, [impl_ty.into()].into_iter().chain(subs.iter().copied()), cx.param_env)
+                                .type_implements_trait(
+                                    trait_id,
+                                    [impl_ty.into()].into_iter().chain(subs.iter().copied()),
+                                    cx.param_env,
+                                )
                                 .must_apply_modulo_regions()
                         {
                             return Some(Position::MethodReceiverRefImpl)
@@ -1078,7 +1087,7 @@ fn needless_borrow_impl_arg_position<'tcx>(
     param_ty: ParamTy,
     mut expr: &Expr<'tcx>,
     precedence: i8,
-    msrv: Option<RustcVersion>,
+    msrv: &Msrv,
 ) -> Position {
     let destruct_trait_def_id = cx.tcx.lang_items().destruct_trait();
     let sized_trait_def_id = cx.tcx.lang_items().sized_trait();
@@ -1178,7 +1187,7 @@ fn needless_borrow_impl_arg_position<'tcx>(
                 && let ty::Param(param_ty) = trait_predicate.self_ty().kind()
                 && let GenericArgKind::Type(ty) = substs_with_referent_ty[param_ty.index as usize].unpack()
                 && ty.is_array()
-                && !meets_msrv(msrv, msrvs::ARRAY_INTO_ITERATOR)
+                && !msrv.meets(msrvs::ARRAY_INTO_ITERATOR)
             {
                 return false;
             }
diff --git a/src/tools/clippy/clippy_lints/src/derive.rs b/src/tools/clippy/clippy_lints/src/derive.rs
index d870e0ceef4..9e596ca8157 100644
--- a/src/tools/clippy/clippy_lints/src/derive.rs
+++ b/src/tools/clippy/clippy_lints/src/derive.rs
@@ -14,8 +14,8 @@ use rustc_lint::{LateContext, LateLintPass};
 use rustc_middle::hir::nested_filter;
 use rustc_middle::traits::Reveal;
 use rustc_middle::ty::{
-    self, Binder, BoundConstness, Clause, GenericParamDefKind, ImplPolarity, ParamEnv, PredicateKind, TraitPredicate, TraitRef,
-    Ty, TyCtxt,
+    self, Binder, BoundConstness, Clause, GenericParamDefKind, ImplPolarity, ParamEnv, PredicateKind, TraitPredicate,
+    TraitRef, Ty, TyCtxt,
 };
 use rustc_session::{declare_lint_pass, declare_tool_lint};
 use rustc_span::source_map::Span;
diff --git a/src/tools/clippy/clippy_lints/src/disallowed_types.rs b/src/tools/clippy/clippy_lints/src/disallowed_types.rs
index aee3d8c4f08..1f56d0118a4 100644
--- a/src/tools/clippy/clippy_lints/src/disallowed_types.rs
+++ b/src/tools/clippy/clippy_lints/src/disallowed_types.rs
@@ -106,7 +106,9 @@ impl<'tcx> LateLintPass<'tcx> for DisallowedTypes {
 
     fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'tcx>) {
         if let ItemKind::Use(path, UseKind::Single) = &item.kind {
-            self.check_res_emit(cx, &path.res, item.span);
+            for res in &path.res {
+                self.check_res_emit(cx, res, item.span);
+            }
         }
     }
 
diff --git a/src/tools/clippy/clippy_lints/src/doc.rs b/src/tools/clippy/clippy_lints/src/doc.rs
index ae5f9424b23..cdc23a4d227 100644
--- a/src/tools/clippy/clippy_lints/src/doc.rs
+++ b/src/tools/clippy/clippy_lints/src/doc.rs
@@ -253,7 +253,7 @@ declare_clippy_lint! {
     /// ```
     #[clippy::version = "1.66.0"]
     pub UNNECESSARY_SAFETY_DOC,
-    style,
+    restriction,
     "`pub fn` or `pub trait` with `# Safety` docs"
 }
 
diff --git a/src/tools/clippy/clippy_lints/src/eta_reduction.rs b/src/tools/clippy/clippy_lints/src/eta_reduction.rs
index f34cbee0355..3543910c3b5 100644
--- a/src/tools/clippy/clippy_lints/src/eta_reduction.rs
+++ b/src/tools/clippy/clippy_lints/src/eta_reduction.rs
@@ -11,7 +11,7 @@ use rustc_hir::{Closure, Expr, ExprKind, Param, PatKind, Unsafety};
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_middle::ty::adjustment::{Adjust, Adjustment, AutoBorrow};
 use rustc_middle::ty::binding::BindingMode;
-use rustc_middle::ty::{self, Ty, TypeVisitable};
+use rustc_middle::ty::{self, EarlyBinder, SubstsRef, Ty, TypeVisitable};
 use rustc_session::{declare_lint_pass, declare_tool_lint};
 use rustc_span::symbol::sym;
 
@@ -125,7 +125,12 @@ impl<'tcx> LateLintPass<'tcx> for EtaReduction {
                     if let Some(mut snippet) = snippet_opt(cx, callee.span) {
                         if let Some(fn_mut_id) = cx.tcx.lang_items().fn_mut_trait()
                             && let args = cx.tcx.erase_late_bound_regions(substs.as_closure().sig()).inputs()
-                            && implements_trait(cx, callee_ty.peel_refs(), fn_mut_id, &args.iter().copied().map(Into::into).collect::<Vec<_>>())
+                            && implements_trait(
+                                   cx,
+                                   callee_ty.peel_refs(),
+                                   fn_mut_id,
+                                   &args.iter().copied().map(Into::into).collect::<Vec<_>>(),
+                               )
                             && path_to_local(callee).map_or(false, |l| local_used_after_expr(cx, l, expr))
                         {
                                 // Mutable closure is used after current expr; we cannot consume it.
@@ -152,7 +157,7 @@ impl<'tcx> LateLintPass<'tcx> for EtaReduction {
             if check_sig(cx, closure_ty, call_ty);
             then {
                 span_lint_and_then(cx, REDUNDANT_CLOSURE_FOR_METHOD_CALLS, expr.span, "redundant closure", |diag| {
-                    let name = get_ufcs_type_name(cx, method_def_id);
+                    let name = get_ufcs_type_name(cx, method_def_id, substs);
                     diag.span_suggestion(
                         expr.span,
                         "replace the closure with the method itself",
@@ -222,7 +227,7 @@ fn check_sig<'tcx>(cx: &LateContext<'tcx>, closure_ty: Ty<'tcx>, call_ty: Ty<'tc
     cx.tcx.erase_late_bound_regions(closure_sig) == cx.tcx.erase_late_bound_regions(call_sig)
 }
 
-fn get_ufcs_type_name(cx: &LateContext<'_>, method_def_id: DefId) -> String {
+fn get_ufcs_type_name<'tcx>(cx: &LateContext<'tcx>, method_def_id: DefId, substs: SubstsRef<'tcx>) -> String {
     let assoc_item = cx.tcx.associated_item(method_def_id);
     let def_id = assoc_item.container_id(cx.tcx);
     match assoc_item.container {
@@ -231,6 +236,15 @@ fn get_ufcs_type_name(cx: &LateContext<'_>, method_def_id: DefId) -> String {
             let ty = cx.tcx.type_of(def_id);
             match ty.kind() {
                 ty::Adt(adt, _) => cx.tcx.def_path_str(adt.did()),
+                ty::Array(..)
+                | ty::Dynamic(..)
+                | ty::Never
+                | ty::RawPtr(_)
+                | ty::Ref(..)
+                | ty::Slice(_)
+                | ty::Tuple(_) => {
+                    format!("<{}>", EarlyBinder(ty).subst(cx.tcx, substs))
+                },
                 _ => ty.to_string(),
             }
         },
diff --git a/src/tools/clippy/clippy_lints/src/exit.rs b/src/tools/clippy/clippy_lints/src/exit.rs
index 407dd1b3957..9c8b0d076df 100644
--- a/src/tools/clippy/clippy_lints/src/exit.rs
+++ b/src/tools/clippy/clippy_lints/src/exit.rs
@@ -7,21 +7,34 @@ use rustc_session::{declare_lint_pass, declare_tool_lint};
 
 declare_clippy_lint! {
     /// ### What it does
-    /// `exit()`  terminates the program and doesn't provide a
-    /// stack trace.
+    /// Detects calls to the `exit()` function which terminates the program.
     ///
     /// ### Why is this bad?
-    /// Ideally a program is terminated by finishing
+    /// Exit terminates the program at the location it is called. For unrecoverable
+    /// errors `panics` should be used to provide a stacktrace and potentualy other
+    /// information. A normal termination or one with an error code should happen in
     /// the main function.
     ///
     /// ### Example
-    /// ```ignore
+    /// ```
     /// std::process::exit(0)
     /// ```
+    ///
+    /// Use instead:
+    ///
+    /// ```ignore
+    /// // To provide a stacktrace and additional information
+    /// panic!("message");
+    ///
+    /// // or a main method with a return
+    /// fn main() -> Result<(), i32> {
+    ///     Ok(())
+    /// }
+    /// ```
     #[clippy::version = "1.41.0"]
     pub EXIT,
     restriction,
-    "`std::process::exit` is called, terminating the program"
+    "detects `std::process::exit` calls"
 }
 
 declare_lint_pass!(Exit => [EXIT]);
diff --git a/src/tools/clippy/clippy_lints/src/format_args.rs b/src/tools/clippy/clippy_lints/src/format_args.rs
index f0fe845d330..111b624f529 100644
--- a/src/tools/clippy/clippy_lints/src/format_args.rs
+++ b/src/tools/clippy/clippy_lints/src/format_args.rs
@@ -1,19 +1,22 @@
 use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then};
-use clippy_utils::macros::FormatParamKind::{Implicit, Named, Numbered, Starred};
+use clippy_utils::is_diag_trait_item;
+use clippy_utils::macros::FormatParamKind::{Implicit, Named, NamedInline, Numbered, Starred};
 use clippy_utils::macros::{
     is_format_macro, is_panic, root_macro_call, Count, FormatArg, FormatArgsExpn, FormatParam, FormatParamUsage,
 };
+use clippy_utils::msrvs::{self, Msrv};
 use clippy_utils::source::snippet_opt;
 use clippy_utils::ty::{implements_trait, is_type_diagnostic_item};
-use clippy_utils::{is_diag_trait_item, meets_msrv, msrvs};
 use if_chain::if_chain;
 use itertools::Itertools;
-use rustc_errors::Applicability;
+use rustc_errors::{
+    Applicability,
+    SuggestionStyle::{CompletelyHidden, ShowCode},
+};
 use rustc_hir::{Expr, ExprKind, HirId, QPath};
 use rustc_lint::{LateContext, LateLintPass, LintContext};
 use rustc_middle::ty::adjustment::{Adjust, Adjustment};
 use rustc_middle::ty::Ty;
-use rustc_semver::RustcVersion;
 use rustc_session::{declare_tool_lint, impl_lint_pass};
 use rustc_span::def_id::DefId;
 use rustc_span::edition::Edition::Edition2021;
@@ -103,19 +106,25 @@ declare_clippy_lint! {
     /// format!("{var:.prec$}");
     /// ```
     ///
-    /// ### Known Problems
-    ///
-    /// There may be a false positive if the format string is expanded from certain proc macros:
-    ///
-    /// ```ignore
-    /// println!(indoc!("{}"), var);
+    /// If allow-mixed-uninlined-format-args is set to false in clippy.toml,
+    /// the following code will also trigger the lint:
+    /// ```rust
+    /// # let var = 42;
+    /// format!("{} {}", var, 1+2);
+    /// ```
+    /// Use instead:
+    /// ```rust
+    /// # let var = 42;
+    /// format!("{var} {}", 1+2);
     /// ```
     ///
+    /// ### Known Problems
+    ///
     /// If a format string contains a numbered argument that cannot be inlined
     /// nothing will be suggested, e.g. `println!("{0}={1}", var, 1+2)`.
     #[clippy::version = "1.65.0"]
     pub UNINLINED_FORMAT_ARGS,
-    pedantic,
+    style,
     "using non-inlined variables in `format!` calls"
 }
 
@@ -158,13 +167,17 @@ impl_lint_pass!(FormatArgs => [
 ]);
 
 pub struct FormatArgs {
-    msrv: Option<RustcVersion>,
+    msrv: Msrv,
+    ignore_mixed: bool,
 }
 
 impl FormatArgs {
     #[must_use]
-    pub fn new(msrv: Option<RustcVersion>) -> Self {
-        Self { msrv }
+    pub fn new(msrv: Msrv, allow_mixed_uninlined_format_args: bool) -> Self {
+        Self {
+            msrv,
+            ignore_mixed: allow_mixed_uninlined_format_args,
+        }
     }
 }
 
@@ -188,8 +201,8 @@ impl<'tcx> LateLintPass<'tcx> for FormatArgs {
                 check_format_in_format_args(cx, outermost_expn_data.call_site, name, arg.param.value);
                 check_to_string_in_format_args(cx, name, arg.param.value);
             }
-            if meets_msrv(self.msrv, msrvs::FORMAT_ARGS_CAPTURE) {
-                check_uninlined_args(cx, &format_args, outermost_expn_data.call_site, macro_def_id);
+            if self.msrv.meets(msrvs::FORMAT_ARGS_CAPTURE) {
+                check_uninlined_args(cx, &format_args, outermost_expn_data.call_site, macro_def_id, self.ignore_mixed);
             }
         }
     }
@@ -267,7 +280,13 @@ fn check_unused_format_specifier(cx: &LateContext<'_>, arg: &FormatArg<'_>) {
     }
 }
 
-fn check_uninlined_args(cx: &LateContext<'_>, args: &FormatArgsExpn<'_>, call_site: Span, def_id: DefId) {
+fn check_uninlined_args(
+    cx: &LateContext<'_>,
+    args: &FormatArgsExpn<'_>,
+    call_site: Span,
+    def_id: DefId,
+    ignore_mixed: bool,
+) {
     if args.format_string.span.from_expansion() {
         return;
     }
@@ -282,14 +301,13 @@ fn check_uninlined_args(cx: &LateContext<'_>, args: &FormatArgsExpn<'_>, call_si
     // we cannot remove any other arguments in the format string,
     // because the index numbers might be wrong after inlining.
     // Example of an un-inlinable format:  print!("{}{1}", foo, 2)
-    if !args.params().all(|p| check_one_arg(args, &p, &mut fixes)) || fixes.is_empty() {
+    if !args.params().all(|p| check_one_arg(args, &p, &mut fixes, ignore_mixed)) || fixes.is_empty() {
         return;
     }
 
-    // Temporarily ignore multiline spans: https://github.com/rust-lang/rust/pull/102729#discussion_r988704308
-    if fixes.iter().any(|(span, _)| cx.sess().source_map().is_multiline(*span)) {
-        return;
-    }
+    // multiline span display suggestion is sometimes broken: https://github.com/rust-lang/rust/pull/102729#discussion_r988704308
+    // in those cases, make the code suggestion hidden
+    let multiline_fix = fixes.iter().any(|(span, _)| cx.sess().source_map().is_multiline(*span));
 
     span_lint_and_then(
         cx,
@@ -297,12 +315,22 @@ fn check_uninlined_args(cx: &LateContext<'_>, args: &FormatArgsExpn<'_>, call_si
         call_site,
         "variables can be used directly in the `format!` string",
         |diag| {
-            diag.multipart_suggestion("change this to", fixes, Applicability::MachineApplicable);
+            diag.multipart_suggestion_with_style(
+                "change this to",
+                fixes,
+                Applicability::MachineApplicable,
+                if multiline_fix { CompletelyHidden } else { ShowCode },
+            );
         },
     );
 }
 
-fn check_one_arg(args: &FormatArgsExpn<'_>, param: &FormatParam<'_>, fixes: &mut Vec<(Span, String)>) -> bool {
+fn check_one_arg(
+    args: &FormatArgsExpn<'_>,
+    param: &FormatParam<'_>,
+    fixes: &mut Vec<(Span, String)>,
+    ignore_mixed: bool,
+) -> bool {
     if matches!(param.kind, Implicit | Starred | Named(_) | Numbered)
         && let ExprKind::Path(QPath::Resolved(None, path)) = param.value.kind
         && let [segment] = path.segments
@@ -317,8 +345,10 @@ fn check_one_arg(args: &FormatArgsExpn<'_>, param: &FormatParam<'_>, fixes: &mut
         fixes.push((arg_span, String::new()));
         true  // successful inlining, continue checking
     } else {
-        // if we can't inline a numbered argument, we can't continue
-        param.kind != Numbered
+        // Do not continue inlining (return false) in case
+        // * if we can't inline a numbered argument, e.g. `print!("{0} ...", foo.bar, ...)`
+        // * if allow_mixed_uninlined_format_args is false and this arg hasn't been inlined already
+        param.kind != Numbered && (!ignore_mixed || matches!(param.kind, NamedInline(_)))
     }
 }
 
diff --git a/src/tools/clippy/clippy_lints/src/from_over_into.rs b/src/tools/clippy/clippy_lints/src/from_over_into.rs
index 8b24a4962fb..0634b2798fe 100644
--- a/src/tools/clippy/clippy_lints/src/from_over_into.rs
+++ b/src/tools/clippy/clippy_lints/src/from_over_into.rs
@@ -1,7 +1,8 @@
 use clippy_utils::diagnostics::span_lint_and_then;
 use clippy_utils::macros::span_is_local;
+use clippy_utils::msrvs::{self, Msrv};
+use clippy_utils::path_def_id;
 use clippy_utils::source::snippet_opt;
-use clippy_utils::{meets_msrv, msrvs, path_def_id};
 use rustc_errors::Applicability;
 use rustc_hir::intravisit::{walk_path, Visitor};
 use rustc_hir::{
@@ -10,7 +11,6 @@ use rustc_hir::{
 };
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_middle::hir::nested_filter::OnlyBodies;
-use rustc_semver::RustcVersion;
 use rustc_session::{declare_tool_lint, impl_lint_pass};
 use rustc_span::symbol::{kw, sym};
 use rustc_span::{Span, Symbol};
@@ -49,12 +49,12 @@ declare_clippy_lint! {
 }
 
 pub struct FromOverInto {
-    msrv: Option<RustcVersion>,
+    msrv: Msrv,
 }
 
 impl FromOverInto {
     #[must_use]
-    pub fn new(msrv: Option<RustcVersion>) -> Self {
+    pub fn new(msrv: Msrv) -> Self {
         FromOverInto { msrv }
     }
 }
@@ -63,7 +63,7 @@ impl_lint_pass!(FromOverInto => [FROM_OVER_INTO]);
 
 impl<'tcx> LateLintPass<'tcx> for FromOverInto {
     fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) {
-        if !meets_msrv(self.msrv, msrvs::RE_REBALANCING_COHERENCE) || !span_is_local(item.span) {
+        if !self.msrv.meets(msrvs::RE_REBALANCING_COHERENCE) || !span_is_local(item.span) {
             return;
         }
 
@@ -126,7 +126,7 @@ impl<'a, 'tcx> Visitor<'tcx> for SelfFinder<'a, 'tcx> {
         self.cx.tcx.hir()
     }
 
-    fn visit_path(&mut self, path: &'tcx Path<'tcx>, _id: HirId) {
+    fn visit_path(&mut self, path: &Path<'tcx>, _id: HirId) {
         for segment in path.segments {
             match segment.ident.name {
                 kw::SelfLower => self.lower.push(segment.ident.span),
diff --git a/src/tools/clippy/clippy_lints/src/functions/misnamed_getters.rs b/src/tools/clippy/clippy_lints/src/functions/misnamed_getters.rs
new file mode 100644
index 00000000000..27acad45ccf
--- /dev/null
+++ b/src/tools/clippy/clippy_lints/src/functions/misnamed_getters.rs
@@ -0,0 +1,125 @@
+use clippy_utils::diagnostics::span_lint_and_then;
+use clippy_utils::source::snippet;
+use rustc_errors::Applicability;
+use rustc_hir::{intravisit::FnKind, Body, ExprKind, FnDecl, HirId, ImplicitSelfKind, Unsafety};
+use rustc_lint::LateContext;
+use rustc_middle::ty;
+use rustc_span::Span;
+
+use std::iter;
+
+use super::MISNAMED_GETTERS;
+
+pub fn check_fn(
+    cx: &LateContext<'_>,
+    kind: FnKind<'_>,
+    decl: &FnDecl<'_>,
+    body: &Body<'_>,
+    span: Span,
+    _hir_id: HirId,
+) {
+    let FnKind::Method(ref ident, sig) = kind else {
+            return;
+        };
+
+    // Takes only &(mut) self
+    if decl.inputs.len() != 1 {
+        return;
+    }
+
+    let name = ident.name.as_str();
+
+    let name = match decl.implicit_self {
+        ImplicitSelfKind::MutRef => {
+            let Some(name) = name.strip_suffix("_mut") else {
+                    return;
+                };
+            name
+        },
+        ImplicitSelfKind::Imm | ImplicitSelfKind::Mut | ImplicitSelfKind::ImmRef => name,
+        ImplicitSelfKind::None => return,
+    };
+
+    let name = if sig.header.unsafety == Unsafety::Unsafe {
+        name.strip_suffix("_unchecked").unwrap_or(name)
+    } else {
+        name
+    };
+
+    // Body must be &(mut) <self_data>.name
+    // self_data is not neccessarilly self, to also lint sub-getters, etc…
+
+    let block_expr = if_chain! {
+        if let ExprKind::Block(block,_) = body.value.kind;
+        if block.stmts.is_empty();
+        if let Some(block_expr) = block.expr;
+        then {
+            block_expr
+        } else {
+            return;
+        }
+    };
+    let expr_span = block_expr.span;
+
+    // Accept &<expr>, &mut <expr> and <expr>
+    let expr = if let ExprKind::AddrOf(_, _, tmp) = block_expr.kind {
+        tmp
+    } else {
+        block_expr
+    };
+    let (self_data, used_ident) = if_chain! {
+        if let ExprKind::Field(self_data, ident) = expr.kind;
+        if ident.name.as_str() != name;
+        then {
+            (self_data, ident)
+        } else {
+            return;
+        }
+    };
+
+    let mut used_field = None;
+    let mut correct_field = None;
+    let typeck_results = cx.typeck_results();
+    for adjusted_type in iter::once(typeck_results.expr_ty(self_data))
+        .chain(typeck_results.expr_adjustments(self_data).iter().map(|adj| adj.target))
+    {
+        let ty::Adt(def,_) = adjusted_type.kind() else {
+            continue;
+        };
+
+        for f in def.all_fields() {
+            if f.name.as_str() == name {
+                correct_field = Some(f);
+            }
+            if f.name == used_ident.name {
+                used_field = Some(f);
+            }
+        }
+    }
+
+    let Some(used_field) = used_field else {
+        // Can happen if the field access is a tuple. We don't lint those because the getter name could not start with a number.
+        return;
+    };
+
+    let Some(correct_field) = correct_field else {
+        // There is no field corresponding to the getter name.
+        // FIXME: This can be a false positive if the correct field is reachable trought deeper autodereferences than used_field is
+        return;
+    };
+
+    if cx.tcx.type_of(used_field.did) == cx.tcx.type_of(correct_field.did) {
+        let left_span = block_expr.span.until(used_ident.span);
+        let snippet = snippet(cx, left_span, "..");
+        let sugg = format!("{snippet}{name}");
+        span_lint_and_then(
+            cx,
+            MISNAMED_GETTERS,
+            span,
+            "getter function appears to return the wrong field",
+            |diag| {
+                diag.span_suggestion(expr_span, "consider using", sugg, Applicability::MaybeIncorrect);
+            },
+        );
+    }
+}
diff --git a/src/tools/clippy/clippy_lints/src/functions/mod.rs b/src/tools/clippy/clippy_lints/src/functions/mod.rs
index ae0e0833446..91e6ffe6447 100644
--- a/src/tools/clippy/clippy_lints/src/functions/mod.rs
+++ b/src/tools/clippy/clippy_lints/src/functions/mod.rs
@@ -1,3 +1,4 @@
+mod misnamed_getters;
 mod must_use;
 mod not_unsafe_ptr_arg_deref;
 mod result;
@@ -260,6 +261,48 @@ declare_clippy_lint! {
     "function returning `Result` with large `Err` type"
 }
 
+declare_clippy_lint! {
+    /// ### What it does
+    /// Checks for getter methods that return a field that doesn't correspond
+    /// to the name of the method, when there is a field's whose name matches that of the method.
+    ///
+    /// ### Why is this bad?
+    /// It is most likely that such a  method is a bug caused by a typo or by copy-pasting.
+    ///
+    /// ### Example
+
+    /// ```rust
+    /// struct A {
+    ///     a: String,
+    ///     b: String,
+    /// }
+    ///
+    /// impl A {
+    ///     fn a(&self) -> &str{
+    ///         &self.b
+    ///     }
+    /// }
+
+    /// ```
+    /// Use instead:
+    /// ```rust
+    /// struct A {
+    ///     a: String,
+    ///     b: String,
+    /// }
+    ///
+    /// impl A {
+    ///     fn a(&self) -> &str{
+    ///         &self.a
+    ///     }
+    /// }
+    /// ```
+    #[clippy::version = "1.67.0"]
+    pub MISNAMED_GETTERS,
+    suspicious,
+    "getter method returning the wrong field"
+}
+
 #[derive(Copy, Clone)]
 pub struct Functions {
     too_many_arguments_threshold: u64,
@@ -286,6 +329,7 @@ impl_lint_pass!(Functions => [
     MUST_USE_CANDIDATE,
     RESULT_UNIT_ERR,
     RESULT_LARGE_ERR,
+    MISNAMED_GETTERS,
 ]);
 
 impl<'tcx> LateLintPass<'tcx> for Functions {
@@ -301,6 +345,7 @@ impl<'tcx> LateLintPass<'tcx> for Functions {
         too_many_arguments::check_fn(cx, kind, decl, span, hir_id, self.too_many_arguments_threshold);
         too_many_lines::check_fn(cx, kind, span, body, self.too_many_lines_threshold);
         not_unsafe_ptr_arg_deref::check_fn(cx, kind, decl, body, hir_id);
+        misnamed_getters::check_fn(cx, kind, decl, body, span, hir_id);
     }
 
     fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::Item<'_>) {
diff --git a/src/tools/clippy/clippy_lints/src/functions/result.rs b/src/tools/clippy/clippy_lints/src/functions/result.rs
index f7e30b051a6..23da145d038 100644
--- a/src/tools/clippy/clippy_lints/src/functions/result.rs
+++ b/src/tools/clippy/clippy_lints/src/functions/result.rs
@@ -94,7 +94,9 @@ fn check_result_large_err<'tcx>(cx: &LateContext<'tcx>, err_ty: Ty<'tcx>, hir_ty
         if let hir::ItemKind::Enum(ref def, _) = item.kind;
         then {
             let variants_size = AdtVariantInfo::new(cx, *adt, subst);
-            if variants_size[0].size >= large_err_threshold {
+            if let Some((first_variant, variants)) = variants_size.split_first()
+                && first_variant.size >= large_err_threshold
+            {
                 span_lint_and_then(
                     cx,
                     RESULT_LARGE_ERR,
@@ -102,11 +104,11 @@ fn check_result_large_err<'tcx>(cx: &LateContext<'tcx>, err_ty: Ty<'tcx>, hir_ty
                     "the `Err`-variant returned from this function is very large",
                     |diag| {
                         diag.span_label(
-                            def.variants[variants_size[0].ind].span,
+                            def.variants[first_variant.ind].span,
                             format!("the largest variant contains at least {} bytes", variants_size[0].size),
                         );
 
-                        for variant in &variants_size[1..] {
+                        for variant in variants {
                             if variant.size >= large_err_threshold {
                                 let variant_def = &def.variants[variant.ind];
                                 diag.span_label(
diff --git a/src/tools/clippy/clippy_lints/src/future_not_send.rs b/src/tools/clippy/clippy_lints/src/future_not_send.rs
index a9425a40f88..61934a91426 100644
--- a/src/tools/clippy/clippy_lints/src/future_not_send.rs
+++ b/src/tools/clippy/clippy_lints/src/future_not_send.rs
@@ -91,7 +91,9 @@ impl<'tcx> LateLintPass<'tcx> for FutureNotSend {
                                 infcx
                                     .err_ctxt()
                                     .maybe_note_obligation_cause_for_async_await(db, &obligation);
-                                if let PredicateKind::Clause(Clause::Trait(trait_pred)) = obligation.predicate.kind().skip_binder() {
+                                if let PredicateKind::Clause(Clause::Trait(trait_pred)) =
+                                    obligation.predicate.kind().skip_binder()
+                                {
                                     db.note(&format!(
                                         "`{}` doesn't implement `{}`",
                                         trait_pred.self_ty(),
diff --git a/src/tools/clippy/clippy_lints/src/if_then_some_else_none.rs b/src/tools/clippy/clippy_lints/src/if_then_some_else_none.rs
index 0d6718c168a..9cadaaa493e 100644
--- a/src/tools/clippy/clippy_lints/src/if_then_some_else_none.rs
+++ b/src/tools/clippy/clippy_lints/src/if_then_some_else_none.rs
@@ -1,14 +1,12 @@
 use clippy_utils::diagnostics::span_lint_and_help;
 use clippy_utils::eager_or_lazy::switch_to_eager_eval;
+use clippy_utils::msrvs::{self, Msrv};
 use clippy_utils::source::snippet_with_macro_callsite;
-use clippy_utils::{
-    contains_return, higher, is_else_clause, is_res_lang_ctor, meets_msrv, msrvs, path_res, peel_blocks,
-};
+use clippy_utils::{contains_return, higher, is_else_clause, is_res_lang_ctor, path_res, peel_blocks};
 use rustc_hir::LangItem::{OptionNone, OptionSome};
 use rustc_hir::{Expr, ExprKind, Stmt, StmtKind};
 use rustc_lint::{LateContext, LateLintPass, LintContext};
 use rustc_middle::lint::in_external_macro;
-use rustc_semver::RustcVersion;
 use rustc_session::{declare_tool_lint, impl_lint_pass};
 
 declare_clippy_lint! {
@@ -47,12 +45,12 @@ declare_clippy_lint! {
 }
 
 pub struct IfThenSomeElseNone {
-    msrv: Option<RustcVersion>,
+    msrv: Msrv,
 }
 
 impl IfThenSomeElseNone {
     #[must_use]
-    pub fn new(msrv: Option<RustcVersion>) -> Self {
+    pub fn new(msrv: Msrv) -> Self {
         Self { msrv }
     }
 }
@@ -61,7 +59,7 @@ impl_lint_pass!(IfThenSomeElseNone => [IF_THEN_SOME_ELSE_NONE]);
 
 impl<'tcx> LateLintPass<'tcx> for IfThenSomeElseNone {
     fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) {
-        if !meets_msrv(self.msrv, msrvs::BOOL_THEN) {
+        if !self.msrv.meets(msrvs::BOOL_THEN) {
             return;
         }
 
@@ -94,7 +92,7 @@ impl<'tcx> LateLintPass<'tcx> for IfThenSomeElseNone {
             } else {
                 format!("{{ /* snippet */ {arg_snip} }}")
             };
-            let method_name = if switch_to_eager_eval(cx, expr) && meets_msrv(self.msrv, msrvs::BOOL_THEN_SOME) {
+            let method_name = if switch_to_eager_eval(cx, expr) && self.msrv.meets(msrvs::BOOL_THEN_SOME) {
                 "then_some"
             } else {
                 method_body.insert_str(0, "|| ");
diff --git a/src/tools/clippy/clippy_lints/src/index_refutable_slice.rs b/src/tools/clippy/clippy_lints/src/index_refutable_slice.rs
index 0d5099bde6d..cf35b1f175c 100644
--- a/src/tools/clippy/clippy_lints/src/index_refutable_slice.rs
+++ b/src/tools/clippy/clippy_lints/src/index_refutable_slice.rs
@@ -1,8 +1,9 @@
 use clippy_utils::consts::{constant, Constant};
 use clippy_utils::diagnostics::span_lint_and_then;
 use clippy_utils::higher::IfLet;
+use clippy_utils::msrvs::{self, Msrv};
 use clippy_utils::ty::is_copy;
-use clippy_utils::{is_expn_of, is_lint_allowed, meets_msrv, msrvs, path_to_local};
+use clippy_utils::{is_expn_of, is_lint_allowed, path_to_local};
 use if_chain::if_chain;
 use rustc_data_structures::fx::{FxHashSet, FxIndexMap};
 use rustc_errors::Applicability;
@@ -11,7 +12,6 @@ use rustc_hir::intravisit::{self, Visitor};
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_middle::hir::nested_filter;
 use rustc_middle::ty;
-use rustc_semver::RustcVersion;
 use rustc_session::{declare_tool_lint, impl_lint_pass};
 use rustc_span::{symbol::Ident, Span};
 
@@ -47,18 +47,17 @@ declare_clippy_lint! {
     /// ```
     #[clippy::version = "1.59.0"]
     pub INDEX_REFUTABLE_SLICE,
-    nursery,
+    pedantic,
     "avoid indexing on slices which could be destructed"
 }
 
-#[derive(Copy, Clone)]
 pub struct IndexRefutableSlice {
     max_suggested_slice: u64,
-    msrv: Option<RustcVersion>,
+    msrv: Msrv,
 }
 
 impl IndexRefutableSlice {
-    pub fn new(max_suggested_slice_pattern_length: u64, msrv: Option<RustcVersion>) -> Self {
+    pub fn new(max_suggested_slice_pattern_length: u64, msrv: Msrv) -> Self {
         Self {
             max_suggested_slice: max_suggested_slice_pattern_length,
             msrv,
@@ -74,7 +73,7 @@ impl<'tcx> LateLintPass<'tcx> for IndexRefutableSlice {
             if !expr.span.from_expansion() || is_expn_of(expr.span, "if_chain").is_some();
             if let Some(IfLet {let_pat, if_then, ..}) = IfLet::hir(cx, expr);
             if !is_lint_allowed(cx, INDEX_REFUTABLE_SLICE, expr.hir_id);
-            if meets_msrv(self.msrv, msrvs::SLICE_PATTERNS);
+            if self.msrv.meets(msrvs::SLICE_PATTERNS);
 
             let found_slices = find_slice_values(cx, let_pat);
             if !found_slices.is_empty();
diff --git a/src/tools/clippy/clippy_lints/src/instant_subtraction.rs b/src/tools/clippy/clippy_lints/src/instant_subtraction.rs
index 60754b224fc..dd1b23e7d9d 100644
--- a/src/tools/clippy/clippy_lints/src/instant_subtraction.rs
+++ b/src/tools/clippy/clippy_lints/src/instant_subtraction.rs
@@ -1,13 +1,11 @@
-use clippy_utils::{
-    diagnostics::{self, span_lint_and_sugg},
-    meets_msrv, msrvs, source,
-    sugg::Sugg,
-    ty,
-};
+use clippy_utils::diagnostics::{self, span_lint_and_sugg};
+use clippy_utils::msrvs::{self, Msrv};
+use clippy_utils::source;
+use clippy_utils::sugg::Sugg;
+use clippy_utils::ty;
 use rustc_errors::Applicability;
 use rustc_hir::{BinOpKind, Expr, ExprKind};
 use rustc_lint::{LateContext, LateLintPass};
-use rustc_semver::RustcVersion;
 use rustc_session::{declare_tool_lint, impl_lint_pass};
 use rustc_span::{source_map::Spanned, sym};
 
@@ -68,12 +66,12 @@ declare_clippy_lint! {
 }
 
 pub struct InstantSubtraction {
-    msrv: Option<RustcVersion>,
+    msrv: Msrv,
 }
 
 impl InstantSubtraction {
     #[must_use]
-    pub fn new(msrv: Option<RustcVersion>) -> Self {
+    pub fn new(msrv: Msrv) -> Self {
         Self { msrv }
     }
 }
@@ -101,7 +99,7 @@ impl LateLintPass<'_> for InstantSubtraction {
                 } else {
                     if_chain! {
                         if !expr.span.from_expansion();
-                        if meets_msrv(self.msrv, msrvs::TRY_FROM);
+                        if self.msrv.meets(msrvs::TRY_FROM);
 
                         if is_an_instant(cx, lhs);
                         if is_a_duration(cx, rhs);
diff --git a/src/tools/clippy/clippy_lints/src/lib.rs b/src/tools/clippy/clippy_lints/src/lib.rs
index 601990cd6a3..7b17d8a156d 100644
--- a/src/tools/clippy/clippy_lints/src/lib.rs
+++ b/src/tools/clippy/clippy_lints/src/lib.rs
@@ -52,10 +52,9 @@ extern crate declare_clippy_lint;
 use std::io;
 use std::path::PathBuf;
 
-use clippy_utils::parse_msrv;
+use clippy_utils::msrvs::Msrv;
 use rustc_data_structures::fx::FxHashSet;
 use rustc_lint::{Lint, LintId};
-use rustc_semver::RustcVersion;
 use rustc_session::Session;
 
 #[cfg(feature = "internal")]
@@ -322,48 +321,10 @@ pub use crate::utils::conf::{lookup_conf_file, Conf};
 /// Used in `./src/driver.rs`.
 pub fn register_pre_expansion_lints(store: &mut rustc_lint::LintStore, sess: &Session, conf: &Conf) {
     // NOTE: Do not add any more pre-expansion passes. These should be removed eventually.
+    let msrv = Msrv::read(&conf.msrv, sess);
+    let msrv = move || msrv.clone();
 
-    let msrv = conf.msrv.as_ref().and_then(|s| {
-        parse_msrv(s, None, None).or_else(|| {
-            sess.err(format!(
-                "error reading Clippy's configuration file. `{s}` is not a valid Rust version"
-            ));
-            None
-        })
-    });
-
-    store.register_pre_expansion_pass(move || Box::new(attrs::EarlyAttributes { msrv }));
-}
-
-fn read_msrv(conf: &Conf, sess: &Session) -> Option<RustcVersion> {
-    let cargo_msrv = std::env::var("CARGO_PKG_RUST_VERSION")
-        .ok()
-        .and_then(|v| parse_msrv(&v, None, None));
-    let clippy_msrv = conf.msrv.as_ref().and_then(|s| {
-        parse_msrv(s, None, None).or_else(|| {
-            sess.err(format!(
-                "error reading Clippy's configuration file. `{s}` is not a valid Rust version"
-            ));
-            None
-        })
-    });
-
-    if let Some(cargo_msrv) = cargo_msrv {
-        if let Some(clippy_msrv) = clippy_msrv {
-            // if both files have an msrv, let's compare them and emit a warning if they differ
-            if clippy_msrv != cargo_msrv {
-                sess.warn(format!(
-                    "the MSRV in `clippy.toml` and `Cargo.toml` differ; using `{clippy_msrv}` from `clippy.toml`"
-                ));
-            }
-
-            Some(clippy_msrv)
-        } else {
-            Some(cargo_msrv)
-        }
-    } else {
-        clippy_msrv
-    }
+    store.register_pre_expansion_pass(move || Box::new(attrs::EarlyAttributes { msrv: msrv() }));
 }
 
 #[doc(hidden)]
@@ -595,43 +556,44 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
     store.register_late_pass(|_| Box::new(non_octal_unix_permissions::NonOctalUnixPermissions));
     store.register_early_pass(|| Box::new(unnecessary_self_imports::UnnecessarySelfImports));
 
-    let msrv = read_msrv(conf, sess);
+    let msrv = Msrv::read(&conf.msrv, sess);
+    let msrv = move || msrv.clone();
     let avoid_breaking_exported_api = conf.avoid_breaking_exported_api;
     let allow_expect_in_tests = conf.allow_expect_in_tests;
     let allow_unwrap_in_tests = conf.allow_unwrap_in_tests;
-    store.register_late_pass(move |_| Box::new(approx_const::ApproxConstant::new(msrv)));
+    store.register_late_pass(move |_| Box::new(approx_const::ApproxConstant::new(msrv())));
     store.register_late_pass(move |_| {
         Box::new(methods::Methods::new(
             avoid_breaking_exported_api,
-            msrv,
+            msrv(),
             allow_expect_in_tests,
             allow_unwrap_in_tests,
         ))
     });
-    store.register_late_pass(move |_| Box::new(matches::Matches::new(msrv)));
+    store.register_late_pass(move |_| Box::new(matches::Matches::new(msrv())));
     let matches_for_let_else = conf.matches_for_let_else;
-    store.register_late_pass(move |_| Box::new(manual_let_else::ManualLetElse::new(msrv, matches_for_let_else)));
-    store.register_early_pass(move || Box::new(manual_non_exhaustive::ManualNonExhaustiveStruct::new(msrv)));
-    store.register_late_pass(move |_| Box::new(manual_non_exhaustive::ManualNonExhaustiveEnum::new(msrv)));
-    store.register_late_pass(move |_| Box::new(manual_strip::ManualStrip::new(msrv)));
-    store.register_early_pass(move || Box::new(redundant_static_lifetimes::RedundantStaticLifetimes::new(msrv)));
-    store.register_early_pass(move || Box::new(redundant_field_names::RedundantFieldNames::new(msrv)));
-    store.register_late_pass(move |_| Box::new(checked_conversions::CheckedConversions::new(msrv)));
-    store.register_late_pass(move |_| Box::new(mem_replace::MemReplace::new(msrv)));
-    store.register_late_pass(move |_| Box::new(ranges::Ranges::new(msrv)));
-    store.register_late_pass(move |_| Box::new(from_over_into::FromOverInto::new(msrv)));
-    store.register_late_pass(move |_| Box::new(use_self::UseSelf::new(msrv)));
-    store.register_late_pass(move |_| Box::new(missing_const_for_fn::MissingConstForFn::new(msrv)));
+    store.register_late_pass(move |_| Box::new(manual_let_else::ManualLetElse::new(msrv(), matches_for_let_else)));
+    store.register_early_pass(move || Box::new(manual_non_exhaustive::ManualNonExhaustiveStruct::new(msrv())));
+    store.register_late_pass(move |_| Box::new(manual_non_exhaustive::ManualNonExhaustiveEnum::new(msrv())));
+    store.register_late_pass(move |_| Box::new(manual_strip::ManualStrip::new(msrv())));
+    store.register_early_pass(move || Box::new(redundant_static_lifetimes::RedundantStaticLifetimes::new(msrv())));
+    store.register_early_pass(move || Box::new(redundant_field_names::RedundantFieldNames::new(msrv())));
+    store.register_late_pass(move |_| Box::new(checked_conversions::CheckedConversions::new(msrv())));
+    store.register_late_pass(move |_| Box::new(mem_replace::MemReplace::new(msrv())));
+    store.register_late_pass(move |_| Box::new(ranges::Ranges::new(msrv())));
+    store.register_late_pass(move |_| Box::new(from_over_into::FromOverInto::new(msrv())));
+    store.register_late_pass(move |_| Box::new(use_self::UseSelf::new(msrv())));
+    store.register_late_pass(move |_| Box::new(missing_const_for_fn::MissingConstForFn::new(msrv())));
     store.register_late_pass(move |_| Box::new(needless_question_mark::NeedlessQuestionMark));
-    store.register_late_pass(move |_| Box::new(casts::Casts::new(msrv)));
-    store.register_early_pass(move || Box::new(unnested_or_patterns::UnnestedOrPatterns::new(msrv)));
+    store.register_late_pass(move |_| Box::new(casts::Casts::new(msrv())));
+    store.register_early_pass(move || Box::new(unnested_or_patterns::UnnestedOrPatterns::new(msrv())));
     store.register_late_pass(|_| Box::new(size_of_in_element_count::SizeOfInElementCount));
     store.register_late_pass(|_| Box::new(same_name_method::SameNameMethod));
     let max_suggested_slice_pattern_length = conf.max_suggested_slice_pattern_length;
     store.register_late_pass(move |_| {
         Box::new(index_refutable_slice::IndexRefutableSlice::new(
             max_suggested_slice_pattern_length,
-            msrv,
+            msrv(),
         ))
     });
     store.register_late_pass(|_| Box::<shadow::Shadow>::default());
@@ -648,7 +610,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
     store.register_late_pass(|_| Box::new(borrow_deref_ref::BorrowDerefRef));
     store.register_late_pass(|_| Box::new(no_effect::NoEffect));
     store.register_late_pass(|_| Box::new(temporary_assignment::TemporaryAssignment));
-    store.register_late_pass(move |_| Box::new(transmute::Transmute::new(msrv)));
+    store.register_late_pass(move |_| Box::new(transmute::Transmute::new(msrv())));
     let cognitive_complexity_threshold = conf.cognitive_complexity_threshold;
     store.register_late_pass(move |_| {
         Box::new(cognitive_complexity::CognitiveComplexity::new(
@@ -806,7 +768,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
     store.register_late_pass(move |_| Box::new(wildcard_imports::WildcardImports::new(warn_on_all_wildcard_imports)));
     store.register_late_pass(|_| Box::<redundant_pub_crate::RedundantPubCrate>::default());
     store.register_late_pass(|_| Box::new(unnamed_address::UnnamedAddress));
-    store.register_late_pass(move |_| Box::new(dereference::Dereferencing::new(msrv)));
+    store.register_late_pass(move |_| Box::new(dereference::Dereferencing::new(msrv())));
     store.register_late_pass(|_| Box::new(option_if_let_else::OptionIfLetElse));
     store.register_late_pass(|_| Box::new(future_not_send::FutureNotSend));
     store.register_late_pass(|_| Box::new(if_let_mutex::IfLetMutex));
@@ -840,7 +802,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
     store.register_late_pass(|_| Box::<vec_init_then_push::VecInitThenPush>::default());
     store.register_late_pass(|_| Box::new(redundant_slicing::RedundantSlicing));
     store.register_late_pass(|_| Box::new(from_str_radix_10::FromStrRadix10));
-    store.register_late_pass(move |_| Box::new(if_then_some_else_none::IfThenSomeElseNone::new(msrv)));
+    store.register_late_pass(move |_| Box::new(if_then_some_else_none::IfThenSomeElseNone::new(msrv())));
     store.register_late_pass(|_| Box::new(bool_assert_comparison::BoolAssertComparison));
     store.register_early_pass(move || Box::new(module_style::ModStyle));
     store.register_late_pass(|_| Box::new(unused_async::UnusedAsync));
@@ -865,14 +827,15 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
         ))
     });
     store.register_late_pass(move |_| Box::new(undocumented_unsafe_blocks::UndocumentedUnsafeBlocks));
-    store.register_late_pass(move |_| Box::new(format_args::FormatArgs::new(msrv)));
+    let allow_mixed_uninlined = conf.allow_mixed_uninlined_format_args;
+    store.register_late_pass(move |_| Box::new(format_args::FormatArgs::new(msrv(), allow_mixed_uninlined)));
     store.register_late_pass(|_| Box::new(trailing_empty_array::TrailingEmptyArray));
     store.register_early_pass(|| Box::new(octal_escapes::OctalEscapes));
     store.register_late_pass(|_| Box::new(needless_late_init::NeedlessLateInit));
     store.register_late_pass(|_| Box::new(return_self_not_must_use::ReturnSelfNotMustUse));
     store.register_late_pass(|_| Box::new(init_numbered_fields::NumberedFields));
     store.register_early_pass(|| Box::new(single_char_lifetime_names::SingleCharLifetimeNames));
-    store.register_late_pass(move |_| Box::new(manual_bits::ManualBits::new(msrv)));
+    store.register_late_pass(move |_| Box::new(manual_bits::ManualBits::new(msrv())));
     store.register_late_pass(|_| Box::new(default_union_representation::DefaultUnionRepresentation));
     store.register_late_pass(|_| Box::<only_used_in_recursion::OnlyUsedInRecursion>::default());
     let allow_dbg_in_tests = conf.allow_dbg_in_tests;
@@ -896,20 +859,20 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
     store.register_late_pass(|_| Box::new(rc_clone_in_vec_init::RcCloneInVecInit));
     store.register_early_pass(|| Box::<duplicate_mod::DuplicateMod>::default());
     store.register_early_pass(|| Box::new(unused_rounding::UnusedRounding));
-    store.register_early_pass(move || Box::new(almost_complete_letter_range::AlmostCompleteLetterRange::new(msrv)));
+    store.register_early_pass(move || Box::new(almost_complete_letter_range::AlmostCompleteLetterRange::new(msrv())));
     store.register_late_pass(|_| Box::new(swap_ptr_to_ref::SwapPtrToRef));
     store.register_late_pass(|_| Box::new(mismatching_type_param_order::TypeParamMismatch));
     store.register_late_pass(|_| Box::new(read_zero_byte_vec::ReadZeroByteVec));
     store.register_late_pass(|_| Box::new(default_instead_of_iter_empty::DefaultIterEmpty));
-    store.register_late_pass(move |_| Box::new(manual_rem_euclid::ManualRemEuclid::new(msrv)));
-    store.register_late_pass(move |_| Box::new(manual_retain::ManualRetain::new(msrv)));
+    store.register_late_pass(move |_| Box::new(manual_rem_euclid::ManualRemEuclid::new(msrv())));
+    store.register_late_pass(move |_| Box::new(manual_retain::ManualRetain::new(msrv())));
     let verbose_bit_mask_threshold = conf.verbose_bit_mask_threshold;
     store.register_late_pass(move |_| Box::new(operators::Operators::new(verbose_bit_mask_threshold)));
     store.register_late_pass(|_| Box::new(invalid_utf8_in_unchecked::InvalidUtf8InUnchecked));
     store.register_late_pass(|_| Box::<std_instead_of_core::StdReexports>::default());
-    store.register_late_pass(move |_| Box::new(instant_subtraction::InstantSubtraction::new(msrv)));
+    store.register_late_pass(move |_| Box::new(instant_subtraction::InstantSubtraction::new(msrv())));
     store.register_late_pass(|_| Box::new(partialeq_to_none::PartialeqToNone));
-    store.register_late_pass(move |_| Box::new(manual_clamp::ManualClamp::new(msrv)));
+    store.register_late_pass(move |_| Box::new(manual_clamp::ManualClamp::new(msrv())));
     store.register_late_pass(|_| Box::new(manual_string_new::ManualStringNew));
     store.register_late_pass(|_| Box::new(unused_peekable::UnusedPeekable));
     store.register_early_pass(|| Box::new(multi_assignments::MultiAssignments));
@@ -920,7 +883,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
     store.register_late_pass(|_| Box::new(missing_trait_methods::MissingTraitMethods));
     store.register_late_pass(|_| Box::new(from_raw_with_void_ptr::FromRawWithVoidPtr));
     store.register_late_pass(|_| Box::new(suspicious_xor_used_as_pow::ConfusingXorAndPow));
-    store.register_late_pass(move |_| Box::new(manual_is_ascii_check::ManualIsAsciiCheck::new(msrv)));
+    store.register_late_pass(move |_| Box::new(manual_is_ascii_check::ManualIsAsciiCheck::new(msrv())));
     // add lints here, do not remove this comment, it's used in `new_lint`
 }
 
diff --git a/src/tools/clippy/clippy_lints/src/lifetimes.rs b/src/tools/clippy/clippy_lints/src/lifetimes.rs
index 220941dcd5d..7cf1a6b8084 100644
--- a/src/tools/clippy/clippy_lints/src/lifetimes.rs
+++ b/src/tools/clippy/clippy_lints/src/lifetimes.rs
@@ -3,7 +3,7 @@ use clippy_utils::trait_ref_of_method;
 use rustc_data_structures::fx::{FxHashMap, FxHashSet};
 use rustc_hir::intravisit::nested_filter::{self as hir_nested_filter, NestedFilter};
 use rustc_hir::intravisit::{
-    walk_fn_decl, walk_generic_param, walk_generics, walk_impl_item_ref, walk_item, walk_param_bound,
+    walk_fn_decl, walk_generic_arg, walk_generic_param, walk_generics, walk_impl_item_ref, walk_item, walk_param_bound,
     walk_poly_trait_ref, walk_trait_ref, walk_ty, Visitor,
 };
 use rustc_hir::lang_items;
@@ -481,7 +481,7 @@ impl<'a, 'tcx> Visitor<'tcx> for RefVisitor<'a, 'tcx> {
                 sub_visitor.visit_fn_decl(decl);
                 self.nested_elision_site_lts.append(&mut sub_visitor.all_lts());
             },
-            TyKind::TraitObject(bounds, ref lt, _) => {
+            TyKind::TraitObject(bounds, lt, _) => {
                 if !lt.is_elided() {
                     self.unelided_trait_object_lifetime = true;
                 }
@@ -497,14 +497,7 @@ impl<'a, 'tcx> Visitor<'tcx> for RefVisitor<'a, 'tcx> {
         if let GenericArg::Lifetime(l) = generic_arg && let LifetimeName::Param(def_id) = l.res {
             self.lifetime_generic_arg_spans.entry(def_id).or_insert(l.ident.span);
         }
-        // Replace with `walk_generic_arg` if/when https://github.com/rust-lang/rust/pull/103692 lands.
-        // walk_generic_arg(self, generic_arg);
-        match generic_arg {
-            GenericArg::Lifetime(lt) => self.visit_lifetime(lt),
-            GenericArg::Type(ty) => self.visit_ty(ty),
-            GenericArg::Const(ct) => self.visit_anon_const(&ct.value),
-            GenericArg::Infer(inf) => self.visit_infer(inf),
-        }
+        walk_generic_arg(self, generic_arg);
     }
 }
 
diff --git a/src/tools/clippy/clippy_lints/src/macro_use.rs b/src/tools/clippy/clippy_lints/src/macro_use.rs
index 594f6af76b3..e2e6a87a301 100644
--- a/src/tools/clippy/clippy_lints/src/macro_use.rs
+++ b/src/tools/clippy/clippy_lints/src/macro_use.rs
@@ -94,7 +94,10 @@ impl<'tcx> LateLintPass<'tcx> for MacroUseImports {
             let hir_id = item.hir_id();
             let attrs = cx.tcx.hir().attrs(hir_id);
             if let Some(mac_attr) = attrs.iter().find(|attr| attr.has_name(sym::macro_use));
-            if let Res::Def(DefKind::Mod, id) = path.res;
+            if let Some(id) = path.res.iter().find_map(|res| match res {
+                Res::Def(DefKind::Mod, id) => Some(id),
+                _ => None,
+            });
             if !id.is_local();
             then {
                 for kid in cx.tcx.module_children(id).iter() {
diff --git a/src/tools/clippy/clippy_lints/src/manual_bits.rs b/src/tools/clippy/clippy_lints/src/manual_bits.rs
index 6655c92b1da..462d73cf0b9 100644
--- a/src/tools/clippy/clippy_lints/src/manual_bits.rs
+++ b/src/tools/clippy/clippy_lints/src/manual_bits.rs
@@ -1,12 +1,12 @@
 use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::get_parent_expr;
+use clippy_utils::msrvs::{self, Msrv};
 use clippy_utils::source::snippet_with_applicability;
-use clippy_utils::{get_parent_expr, meets_msrv, msrvs};
 use rustc_ast::ast::LitKind;
 use rustc_errors::Applicability;
 use rustc_hir::{BinOpKind, Expr, ExprKind, GenericArg, QPath};
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_middle::ty::{self, Ty};
-use rustc_semver::RustcVersion;
 use rustc_session::{declare_tool_lint, impl_lint_pass};
 use rustc_span::sym;
 
@@ -34,12 +34,12 @@ declare_clippy_lint! {
 
 #[derive(Clone)]
 pub struct ManualBits {
-    msrv: Option<RustcVersion>,
+    msrv: Msrv,
 }
 
 impl ManualBits {
     #[must_use]
-    pub fn new(msrv: Option<RustcVersion>) -> Self {
+    pub fn new(msrv: Msrv) -> Self {
         Self { msrv }
     }
 }
@@ -48,7 +48,7 @@ impl_lint_pass!(ManualBits => [MANUAL_BITS]);
 
 impl<'tcx> LateLintPass<'tcx> for ManualBits {
     fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
-        if !meets_msrv(self.msrv, msrvs::MANUAL_BITS) {
+        if !self.msrv.meets(msrvs::MANUAL_BITS) {
             return;
         }
 
diff --git a/src/tools/clippy/clippy_lints/src/manual_clamp.rs b/src/tools/clippy/clippy_lints/src/manual_clamp.rs
index 02dc8755dd6..bb6d628af3b 100644
--- a/src/tools/clippy/clippy_lints/src/manual_clamp.rs
+++ b/src/tools/clippy/clippy_lints/src/manual_clamp.rs
@@ -1,28 +1,25 @@
+use clippy_utils::diagnostics::{span_lint_and_then, span_lint_hir_and_then};
+use clippy_utils::higher::If;
+use clippy_utils::msrvs::{self, Msrv};
+use clippy_utils::sugg::Sugg;
+use clippy_utils::ty::implements_trait;
+use clippy_utils::visitors::is_const_evaluatable;
+use clippy_utils::MaybePath;
+use clippy_utils::{
+    eq_expr_value, is_diag_trait_item, is_trait_method, path_res, path_to_local_id, peel_blocks, peel_blocks_with_stmt,
+};
 use itertools::Itertools;
+use rustc_errors::Applicability;
 use rustc_errors::Diagnostic;
 use rustc_hir::{
     def::Res, Arm, BinOpKind, Block, Expr, ExprKind, Guard, HirId, PatKind, PathSegment, PrimTy, QPath, StmtKind,
 };
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_middle::ty::Ty;
-use rustc_semver::RustcVersion;
 use rustc_session::{declare_tool_lint, impl_lint_pass};
 use rustc_span::{symbol::sym, Span};
 use std::ops::Deref;
 
-use clippy_utils::{
-    diagnostics::{span_lint_and_then, span_lint_hir_and_then},
-    eq_expr_value,
-    higher::If,
-    is_diag_trait_item, is_trait_method, meets_msrv, msrvs, path_res, path_to_local_id, peel_blocks,
-    peel_blocks_with_stmt,
-    sugg::Sugg,
-    ty::implements_trait,
-    visitors::is_const_evaluatable,
-    MaybePath,
-};
-use rustc_errors::Applicability;
-
 declare_clippy_lint! {
     /// ### What it does
     /// Identifies good opportunities for a clamp function from std or core, and suggests using it.
@@ -87,11 +84,11 @@ declare_clippy_lint! {
 impl_lint_pass!(ManualClamp => [MANUAL_CLAMP]);
 
 pub struct ManualClamp {
-    msrv: Option<RustcVersion>,
+    msrv: Msrv,
 }
 
 impl ManualClamp {
-    pub fn new(msrv: Option<RustcVersion>) -> Self {
+    pub fn new(msrv: Msrv) -> Self {
         Self { msrv }
     }
 }
@@ -114,7 +111,7 @@ struct InputMinMax<'tcx> {
 
 impl<'tcx> LateLintPass<'tcx> for ManualClamp {
     fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) {
-        if !meets_msrv(self.msrv, msrvs::CLAMP) {
+        if !self.msrv.meets(msrvs::CLAMP) {
             return;
         }
         if !expr.span.from_expansion() {
@@ -130,7 +127,7 @@ impl<'tcx> LateLintPass<'tcx> for ManualClamp {
     }
 
     fn check_block(&mut self, cx: &LateContext<'tcx>, block: &'tcx Block<'tcx>) {
-        if !meets_msrv(self.msrv, msrvs::CLAMP) {
+        if !self.msrv.meets(msrvs::CLAMP) {
             return;
         }
         for suggestion in is_two_if_pattern(cx, block) {
diff --git a/src/tools/clippy/clippy_lints/src/manual_is_ascii_check.rs b/src/tools/clippy/clippy_lints/src/manual_is_ascii_check.rs
index bb8c142f8e4..5ab049d8d13 100644
--- a/src/tools/clippy/clippy_lints/src/manual_is_ascii_check.rs
+++ b/src/tools/clippy/clippy_lints/src/manual_is_ascii_check.rs
@@ -1,15 +1,12 @@
+use clippy_utils::msrvs::{self, Msrv};
+use clippy_utils::{diagnostics::span_lint_and_sugg, in_constant, macros::root_macro_call, source::snippet};
 use rustc_ast::LitKind::{Byte, Char};
 use rustc_errors::Applicability;
 use rustc_hir::{Expr, ExprKind, PatKind, RangeEnd};
 use rustc_lint::{LateContext, LateLintPass};
-use rustc_semver::RustcVersion;
 use rustc_session::{declare_tool_lint, impl_lint_pass};
 use rustc_span::{def_id::DefId, sym};
 
-use clippy_utils::{
-    diagnostics::span_lint_and_sugg, in_constant, macros::root_macro_call, meets_msrv, msrvs, source::snippet,
-};
-
 declare_clippy_lint! {
     /// ### What it does
     /// Suggests to use dedicated built-in methods,
@@ -45,12 +42,12 @@ declare_clippy_lint! {
 impl_lint_pass!(ManualIsAsciiCheck => [MANUAL_IS_ASCII_CHECK]);
 
 pub struct ManualIsAsciiCheck {
-    msrv: Option<RustcVersion>,
+    msrv: Msrv,
 }
 
 impl ManualIsAsciiCheck {
     #[must_use]
-    pub fn new(msrv: Option<RustcVersion>) -> Self {
+    pub fn new(msrv: Msrv) -> Self {
         Self { msrv }
     }
 }
@@ -70,11 +67,11 @@ enum CharRange {
 
 impl<'tcx> LateLintPass<'tcx> for ManualIsAsciiCheck {
     fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
-        if !meets_msrv(self.msrv, msrvs::IS_ASCII_DIGIT) {
+        if !self.msrv.meets(msrvs::IS_ASCII_DIGIT) {
             return;
         }
 
-        if in_constant(cx, expr.hir_id) && !meets_msrv(self.msrv, msrvs::IS_ASCII_DIGIT_CONST) {
+        if in_constant(cx, expr.hir_id) && !self.msrv.meets(msrvs::IS_ASCII_DIGIT_CONST) {
             return;
         }
 
diff --git a/src/tools/clippy/clippy_lints/src/manual_let_else.rs b/src/tools/clippy/clippy_lints/src/manual_let_else.rs
index 1846596fa4c..874d36ca9f4 100644
--- a/src/tools/clippy/clippy_lints/src/manual_let_else.rs
+++ b/src/tools/clippy/clippy_lints/src/manual_let_else.rs
@@ -1,16 +1,16 @@
 use clippy_utils::diagnostics::span_lint_and_then;
 use clippy_utils::higher::IfLetOrMatch;
-use clippy_utils::source::snippet_opt;
+use clippy_utils::msrvs::{self, Msrv};
+use clippy_utils::peel_blocks;
+use clippy_utils::source::snippet_with_context;
 use clippy_utils::ty::is_type_diagnostic_item;
 use clippy_utils::visitors::{for_each_expr, Descend};
-use clippy_utils::{meets_msrv, msrvs, peel_blocks};
 use if_chain::if_chain;
 use rustc_data_structures::fx::FxHashSet;
 use rustc_errors::Applicability;
 use rustc_hir::{Expr, ExprKind, MatchSource, Pat, PatKind, QPath, Stmt, StmtKind};
 use rustc_lint::{LateContext, LateLintPass, LintContext};
 use rustc_middle::lint::in_external_macro;
-use rustc_semver::RustcVersion;
 use rustc_session::{declare_tool_lint, impl_lint_pass};
 use rustc_span::symbol::sym;
 use rustc_span::Span;
@@ -50,13 +50,13 @@ declare_clippy_lint! {
 }
 
 pub struct ManualLetElse {
-    msrv: Option<RustcVersion>,
+    msrv: Msrv,
     matches_behaviour: MatchLintBehaviour,
 }
 
 impl ManualLetElse {
     #[must_use]
-    pub fn new(msrv: Option<RustcVersion>, matches_behaviour: MatchLintBehaviour) -> Self {
+    pub fn new(msrv: Msrv, matches_behaviour: MatchLintBehaviour) -> Self {
         Self {
             msrv,
             matches_behaviour,
@@ -69,7 +69,7 @@ impl_lint_pass!(ManualLetElse => [MANUAL_LET_ELSE]);
 impl<'tcx> LateLintPass<'tcx> for ManualLetElse {
     fn check_stmt(&mut self, cx: &LateContext<'_>, stmt: &'tcx Stmt<'tcx>) {
         let if_let_or_match = if_chain! {
-            if meets_msrv(self.msrv, msrvs::LET_ELSE);
+            if self.msrv.meets(msrvs::LET_ELSE);
             if !in_external_macro(cx.sess(), stmt.span);
             if let StmtKind::Local(local) = stmt.kind;
             if let Some(init) = local.init;
@@ -141,20 +141,18 @@ fn emit_manual_let_else(cx: &LateContext<'_>, span: Span, expr: &Expr<'_>, pat:
             // * unused binding collision detection with existing ones
             // * putting patterns with at the top level | inside ()
             // for this to be machine applicable.
-            let app = Applicability::HasPlaceholders;
+            let mut app = Applicability::HasPlaceholders;
+            let (sn_pat, _) = snippet_with_context(cx, pat.span, span.ctxt(), "", &mut app);
+            let (sn_expr, _) = snippet_with_context(cx, expr.span, span.ctxt(), "", &mut app);
+            let (sn_else, _) = snippet_with_context(cx, else_body.span, span.ctxt(), "", &mut app);
 
-            if let Some(sn_pat) = snippet_opt(cx, pat.span) &&
-                let Some(sn_expr) = snippet_opt(cx, expr.span) &&
-                let Some(sn_else) = snippet_opt(cx, else_body.span)
-            {
-                let else_bl = if matches!(else_body.kind, ExprKind::Block(..)) {
-                    sn_else
-                } else {
-                    format!("{{ {sn_else} }}")
-                };
-                let sugg = format!("let {sn_pat} = {sn_expr} else {else_bl};");
-                diag.span_suggestion(span, "consider writing", sugg, app);
-            }
+            let else_bl = if matches!(else_body.kind, ExprKind::Block(..)) {
+                sn_else.into_owned()
+            } else {
+                format!("{{ {sn_else} }}")
+            };
+            let sugg = format!("let {sn_pat} = {sn_expr} else {else_bl};");
+            diag.span_suggestion(span, "consider writing", sugg, app);
         },
     );
 }
diff --git a/src/tools/clippy/clippy_lints/src/manual_non_exhaustive.rs b/src/tools/clippy/clippy_lints/src/manual_non_exhaustive.rs
index 4877cee0cc1..bca193be9e7 100644
--- a/src/tools/clippy/clippy_lints/src/manual_non_exhaustive.rs
+++ b/src/tools/clippy/clippy_lints/src/manual_non_exhaustive.rs
@@ -1,6 +1,7 @@
 use clippy_utils::diagnostics::{span_lint_and_then, span_lint_hir_and_then};
+use clippy_utils::is_doc_hidden;
+use clippy_utils::msrvs::{self, Msrv};
 use clippy_utils::source::snippet_opt;
-use clippy_utils::{is_doc_hidden, meets_msrv, msrvs};
 use rustc_ast::ast::{self, VisibilityKind};
 use rustc_data_structures::fx::FxHashSet;
 use rustc_errors::Applicability;
@@ -8,7 +9,6 @@ use rustc_hir::def::{CtorKind, CtorOf, DefKind, Res};
 use rustc_hir::{self as hir, Expr, ExprKind, QPath};
 use rustc_lint::{EarlyContext, EarlyLintPass, LateContext, LateLintPass, LintContext};
 use rustc_middle::ty::DefIdTree;
-use rustc_semver::RustcVersion;
 use rustc_session::{declare_tool_lint, impl_lint_pass};
 use rustc_span::def_id::{DefId, LocalDefId};
 use rustc_span::{sym, Span};
@@ -63,12 +63,12 @@ declare_clippy_lint! {
 
 #[expect(clippy::module_name_repetitions)]
 pub struct ManualNonExhaustiveStruct {
-    msrv: Option<RustcVersion>,
+    msrv: Msrv,
 }
 
 impl ManualNonExhaustiveStruct {
     #[must_use]
-    pub fn new(msrv: Option<RustcVersion>) -> Self {
+    pub fn new(msrv: Msrv) -> Self {
         Self { msrv }
     }
 }
@@ -77,14 +77,14 @@ impl_lint_pass!(ManualNonExhaustiveStruct => [MANUAL_NON_EXHAUSTIVE]);
 
 #[expect(clippy::module_name_repetitions)]
 pub struct ManualNonExhaustiveEnum {
-    msrv: Option<RustcVersion>,
+    msrv: Msrv,
     constructed_enum_variants: FxHashSet<(DefId, DefId)>,
     potential_enums: Vec<(LocalDefId, LocalDefId, Span, Span)>,
 }
 
 impl ManualNonExhaustiveEnum {
     #[must_use]
-    pub fn new(msrv: Option<RustcVersion>) -> Self {
+    pub fn new(msrv: Msrv) -> Self {
         Self {
             msrv,
             constructed_enum_variants: FxHashSet::default(),
@@ -97,7 +97,7 @@ impl_lint_pass!(ManualNonExhaustiveEnum => [MANUAL_NON_EXHAUSTIVE]);
 
 impl EarlyLintPass for ManualNonExhaustiveStruct {
     fn check_item(&mut self, cx: &EarlyContext<'_>, item: &ast::Item) {
-        if !meets_msrv(self.msrv, msrvs::NON_EXHAUSTIVE) {
+        if !self.msrv.meets(msrvs::NON_EXHAUSTIVE) {
             return;
         }
 
@@ -149,7 +149,7 @@ impl EarlyLintPass for ManualNonExhaustiveStruct {
 
 impl<'tcx> LateLintPass<'tcx> for ManualNonExhaustiveEnum {
     fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::Item<'_>) {
-        if !meets_msrv(self.msrv, msrvs::NON_EXHAUSTIVE) {
+        if !self.msrv.meets(msrvs::NON_EXHAUSTIVE) {
             return;
         }
 
diff --git a/src/tools/clippy/clippy_lints/src/manual_rem_euclid.rs b/src/tools/clippy/clippy_lints/src/manual_rem_euclid.rs
index 6f25a2ed8e4..8d447c37150 100644
--- a/src/tools/clippy/clippy_lints/src/manual_rem_euclid.rs
+++ b/src/tools/clippy/clippy_lints/src/manual_rem_euclid.rs
@@ -1,12 +1,12 @@
 use clippy_utils::consts::{constant_full_int, FullInt};
 use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::msrvs::{self, Msrv};
 use clippy_utils::source::snippet_with_applicability;
-use clippy_utils::{in_constant, meets_msrv, msrvs, path_to_local};
+use clippy_utils::{in_constant, path_to_local};
 use rustc_errors::Applicability;
 use rustc_hir::{BinOpKind, Expr, ExprKind, Node, TyKind};
 use rustc_lint::{LateContext, LateLintPass, LintContext};
 use rustc_middle::lint::in_external_macro;
-use rustc_semver::RustcVersion;
 use rustc_session::{declare_tool_lint, impl_lint_pass};
 
 declare_clippy_lint! {
@@ -34,12 +34,12 @@ declare_clippy_lint! {
 }
 
 pub struct ManualRemEuclid {
-    msrv: Option<RustcVersion>,
+    msrv: Msrv,
 }
 
 impl ManualRemEuclid {
     #[must_use]
-    pub fn new(msrv: Option<RustcVersion>) -> Self {
+    pub fn new(msrv: Msrv) -> Self {
         Self { msrv }
     }
 }
@@ -48,11 +48,11 @@ impl_lint_pass!(ManualRemEuclid => [MANUAL_REM_EUCLID]);
 
 impl<'tcx> LateLintPass<'tcx> for ManualRemEuclid {
     fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
-        if !meets_msrv(self.msrv, msrvs::REM_EUCLID) {
+        if !self.msrv.meets(msrvs::REM_EUCLID) {
             return;
         }
 
-        if in_constant(cx, expr.hir_id) && !meets_msrv(self.msrv, msrvs::REM_EUCLID_CONST) {
+        if in_constant(cx, expr.hir_id) && !self.msrv.meets(msrvs::REM_EUCLID_CONST) {
             return;
         }
 
diff --git a/src/tools/clippy/clippy_lints/src/manual_retain.rs b/src/tools/clippy/clippy_lints/src/manual_retain.rs
index d6438ca7fec..c1e6c82487d 100644
--- a/src/tools/clippy/clippy_lints/src/manual_retain.rs
+++ b/src/tools/clippy/clippy_lints/src/manual_retain.rs
@@ -1,8 +1,8 @@
 use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::msrvs::{self, Msrv};
 use clippy_utils::source::snippet;
 use clippy_utils::ty::{is_type_diagnostic_item, is_type_lang_item};
 use clippy_utils::{get_parent_expr, match_def_path, paths, SpanlessEq};
-use clippy_utils::{meets_msrv, msrvs};
 use rustc_errors::Applicability;
 use rustc_hir as hir;
 use rustc_hir::def_id::DefId;
@@ -50,12 +50,12 @@ declare_clippy_lint! {
 }
 
 pub struct ManualRetain {
-    msrv: Option<RustcVersion>,
+    msrv: Msrv,
 }
 
 impl ManualRetain {
     #[must_use]
-    pub fn new(msrv: Option<RustcVersion>) -> Self {
+    pub fn new(msrv: Msrv) -> Self {
         Self { msrv }
     }
 }
@@ -71,9 +71,9 @@ impl<'tcx> LateLintPass<'tcx> for ManualRetain {
             && let hir::ExprKind::MethodCall(_, target_expr, [], _) = &collect_expr.kind
             && let Some(collect_def_id) = cx.typeck_results().type_dependent_def_id(collect_expr.hir_id)
             && match_def_path(cx, collect_def_id, &paths::CORE_ITER_COLLECT) {
-            check_into_iter(cx, parent_expr, left_expr, target_expr, self.msrv);
-            check_iter(cx, parent_expr, left_expr, target_expr, self.msrv);
-            check_to_owned(cx, parent_expr, left_expr, target_expr, self.msrv);
+            check_into_iter(cx, parent_expr, left_expr, target_expr, &self.msrv);
+            check_iter(cx, parent_expr, left_expr, target_expr, &self.msrv);
+            check_to_owned(cx, parent_expr, left_expr, target_expr, &self.msrv);
         }
     }
 
@@ -85,7 +85,7 @@ fn check_into_iter(
     parent_expr: &hir::Expr<'_>,
     left_expr: &hir::Expr<'_>,
     target_expr: &hir::Expr<'_>,
-    msrv: Option<RustcVersion>,
+    msrv: &Msrv,
 ) {
     if let hir::ExprKind::MethodCall(_, into_iter_expr, [_], _) = &target_expr.kind
         && let Some(filter_def_id) = cx.typeck_results().type_dependent_def_id(target_expr.hir_id)
@@ -104,7 +104,7 @@ fn check_iter(
     parent_expr: &hir::Expr<'_>,
     left_expr: &hir::Expr<'_>,
     target_expr: &hir::Expr<'_>,
-    msrv: Option<RustcVersion>,
+    msrv: &Msrv,
 ) {
     if let hir::ExprKind::MethodCall(_, filter_expr, [], _) = &target_expr.kind
         && let Some(copied_def_id) = cx.typeck_results().type_dependent_def_id(target_expr.hir_id)
@@ -127,9 +127,9 @@ fn check_to_owned(
     parent_expr: &hir::Expr<'_>,
     left_expr: &hir::Expr<'_>,
     target_expr: &hir::Expr<'_>,
-    msrv: Option<RustcVersion>,
+    msrv: &Msrv,
 ) {
-    if meets_msrv(msrv,  msrvs::STRING_RETAIN)
+    if msrv.meets(msrvs::STRING_RETAIN)
         && let hir::ExprKind::MethodCall(_, filter_expr, [], _) = &target_expr.kind
         && let Some(to_owned_def_id) = cx.typeck_results().type_dependent_def_id(target_expr.hir_id)
         && match_def_path(cx, to_owned_def_id, &paths::TO_OWNED_METHOD)
@@ -215,10 +215,10 @@ fn match_acceptable_def_path(cx: &LateContext<'_>, collect_def_id: DefId) -> boo
         .any(|&method| match_def_path(cx, collect_def_id, method))
 }
 
-fn match_acceptable_type(cx: &LateContext<'_>, expr: &hir::Expr<'_>, msrv: Option<RustcVersion>) -> bool {
+fn match_acceptable_type(cx: &LateContext<'_>, expr: &hir::Expr<'_>, msrv: &Msrv) -> bool {
     let expr_ty = cx.typeck_results().expr_ty(expr).peel_refs();
     ACCEPTABLE_TYPES.iter().any(|(ty, acceptable_msrv)| {
         is_type_diagnostic_item(cx, expr_ty, *ty)
-            && acceptable_msrv.map_or(true, |acceptable_msrv| meets_msrv(msrv, acceptable_msrv))
+            && acceptable_msrv.map_or(true, |acceptable_msrv| msrv.meets(acceptable_msrv))
     })
 }
diff --git a/src/tools/clippy/clippy_lints/src/manual_strip.rs b/src/tools/clippy/clippy_lints/src/manual_strip.rs
index 0976940afac..de166b9765f 100644
--- a/src/tools/clippy/clippy_lints/src/manual_strip.rs
+++ b/src/tools/clippy/clippy_lints/src/manual_strip.rs
@@ -1,8 +1,9 @@
 use clippy_utils::consts::{constant, Constant};
 use clippy_utils::diagnostics::{multispan_sugg, span_lint_and_then};
+use clippy_utils::msrvs::{self, Msrv};
 use clippy_utils::source::snippet;
 use clippy_utils::usage::mutated_variables;
-use clippy_utils::{eq_expr_value, higher, match_def_path, meets_msrv, msrvs, paths};
+use clippy_utils::{eq_expr_value, higher, match_def_path, paths};
 use if_chain::if_chain;
 use rustc_ast::ast::LitKind;
 use rustc_hir::def::Res;
@@ -11,7 +12,6 @@ use rustc_hir::BinOpKind;
 use rustc_hir::{BorrowKind, Expr, ExprKind};
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_middle::ty;
-use rustc_semver::RustcVersion;
 use rustc_session::{declare_tool_lint, impl_lint_pass};
 use rustc_span::source_map::Spanned;
 use rustc_span::Span;
@@ -48,12 +48,12 @@ declare_clippy_lint! {
 }
 
 pub struct ManualStrip {
-    msrv: Option<RustcVersion>,
+    msrv: Msrv,
 }
 
 impl ManualStrip {
     #[must_use]
-    pub fn new(msrv: Option<RustcVersion>) -> Self {
+    pub fn new(msrv: Msrv) -> Self {
         Self { msrv }
     }
 }
@@ -68,7 +68,7 @@ enum StripKind {
 
 impl<'tcx> LateLintPass<'tcx> for ManualStrip {
     fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
-        if !meets_msrv(self.msrv, msrvs::STR_STRIP_PREFIX) {
+        if !self.msrv.meets(msrvs::STR_STRIP_PREFIX) {
             return;
         }
 
diff --git a/src/tools/clippy/clippy_lints/src/matches/mod.rs b/src/tools/clippy/clippy_lints/src/matches/mod.rs
index 7d8171ead89..7b15a307fec 100644
--- a/src/tools/clippy/clippy_lints/src/matches/mod.rs
+++ b/src/tools/clippy/clippy_lints/src/matches/mod.rs
@@ -23,13 +23,13 @@ mod single_match;
 mod try_err;
 mod wild_in_or_pats;
 
+use clippy_utils::msrvs::{self, Msrv};
 use clippy_utils::source::{snippet_opt, walk_span_to_context};
-use clippy_utils::{higher, in_constant, is_span_match, meets_msrv, msrvs};
+use clippy_utils::{higher, in_constant, is_span_match};
 use rustc_hir::{Arm, Expr, ExprKind, Local, MatchSource, Pat};
 use rustc_lexer::{tokenize, TokenKind};
 use rustc_lint::{LateContext, LateLintPass, LintContext};
 use rustc_middle::lint::in_external_macro;
-use rustc_semver::RustcVersion;
 use rustc_session::{declare_tool_lint, impl_lint_pass};
 use rustc_span::{Span, SpanData, SyntaxContext};
 
@@ -930,13 +930,13 @@ declare_clippy_lint! {
 
 #[derive(Default)]
 pub struct Matches {
-    msrv: Option<RustcVersion>,
+    msrv: Msrv,
     infallible_destructuring_match_linted: bool,
 }
 
 impl Matches {
     #[must_use]
-    pub fn new(msrv: Option<RustcVersion>) -> Self {
+    pub fn new(msrv: Msrv) -> Self {
         Self {
             msrv,
             ..Matches::default()
@@ -1000,9 +1000,7 @@ impl<'tcx> LateLintPass<'tcx> for Matches {
 
             if !from_expansion && !contains_cfg_arm(cx, expr, ex, arms) {
                 if source == MatchSource::Normal {
-                    if !(meets_msrv(self.msrv, msrvs::MATCHES_MACRO)
-                        && match_like_matches::check_match(cx, expr, ex, arms))
-                    {
+                    if !(self.msrv.meets(msrvs::MATCHES_MACRO) && match_like_matches::check_match(cx, expr, ex, arms)) {
                         match_same_arms::check(cx, arms);
                     }
 
@@ -1034,7 +1032,7 @@ impl<'tcx> LateLintPass<'tcx> for Matches {
             collapsible_match::check_if_let(cx, if_let.let_pat, if_let.if_then, if_let.if_else);
             if !from_expansion {
                 if let Some(else_expr) = if_let.if_else {
-                    if meets_msrv(self.msrv, msrvs::MATCHES_MACRO) {
+                    if self.msrv.meets(msrvs::MATCHES_MACRO) {
                         match_like_matches::check_if_let(
                             cx,
                             expr,
diff --git a/src/tools/clippy/clippy_lints/src/matches/try_err.rs b/src/tools/clippy/clippy_lints/src/matches/try_err.rs
index c6cba81d871..704c34c32bf 100644
--- a/src/tools/clippy/clippy_lints/src/matches/try_err.rs
+++ b/src/tools/clippy/clippy_lints/src/matches/try_err.rs
@@ -1,7 +1,7 @@
 use clippy_utils::diagnostics::span_lint_and_sugg;
 use clippy_utils::source::snippet_with_applicability;
 use clippy_utils::ty::is_type_diagnostic_item;
-use clippy_utils::{get_parent_expr, is_res_lang_ctor, match_def_path, path_res, paths};
+use clippy_utils::{get_parent_expr, is_res_lang_ctor, path_res};
 use if_chain::if_chain;
 use rustc_errors::Applicability;
 use rustc_hir::LangItem::ResultErr;
@@ -107,7 +107,7 @@ fn result_error_type<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Option<Ty<'t
 fn poll_result_error_type<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Option<Ty<'tcx>> {
     if_chain! {
         if let ty::Adt(def, subst) = ty.kind();
-        if match_def_path(cx, def.did(), &paths::POLL);
+        if cx.tcx.lang_items().get(LangItem::Poll) == Some(def.did());
         let ready_ty = subst.type_at(0);
 
         if let ty::Adt(ready_def, ready_subst) = ready_ty.kind();
@@ -124,7 +124,7 @@ fn poll_result_error_type<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Option<
 fn poll_option_result_error_type<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Option<Ty<'tcx>> {
     if_chain! {
         if let ty::Adt(def, subst) = ty.kind();
-        if match_def_path(cx, def.did(), &paths::POLL);
+        if cx.tcx.lang_items().get(LangItem::Poll) == Some(def.did());
         let ready_ty = subst.type_at(0);
 
         if let ty::Adt(ready_def, ready_subst) = ready_ty.kind();
diff --git a/src/tools/clippy/clippy_lints/src/mem_replace.rs b/src/tools/clippy/clippy_lints/src/mem_replace.rs
index 0c4d9f100f7..35024ec1224 100644
--- a/src/tools/clippy/clippy_lints/src/mem_replace.rs
+++ b/src/tools/clippy/clippy_lints/src/mem_replace.rs
@@ -1,14 +1,14 @@
 use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_sugg, span_lint_and_then};
+use clippy_utils::msrvs::{self, Msrv};
 use clippy_utils::source::{snippet, snippet_with_applicability};
 use clippy_utils::ty::is_non_aggregate_primitive_type;
-use clippy_utils::{is_default_equivalent, is_res_lang_ctor, meets_msrv, msrvs, path_res};
+use clippy_utils::{is_default_equivalent, is_res_lang_ctor, path_res};
 use if_chain::if_chain;
 use rustc_errors::Applicability;
 use rustc_hir::LangItem::OptionNone;
 use rustc_hir::{BorrowKind, Expr, ExprKind, Mutability, QPath};
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_middle::lint::in_external_macro;
-use rustc_semver::RustcVersion;
 use rustc_session::{declare_tool_lint, impl_lint_pass};
 use rustc_span::source_map::Span;
 use rustc_span::symbol::sym;
@@ -227,12 +227,12 @@ fn check_replace_with_default(cx: &LateContext<'_>, src: &Expr<'_>, dest: &Expr<
 }
 
 pub struct MemReplace {
-    msrv: Option<RustcVersion>,
+    msrv: Msrv,
 }
 
 impl MemReplace {
     #[must_use]
-    pub fn new(msrv: Option<RustcVersion>) -> Self {
+    pub fn new(msrv: Msrv) -> Self {
         Self { msrv }
     }
 }
@@ -248,7 +248,7 @@ impl<'tcx> LateLintPass<'tcx> for MemReplace {
             then {
                 check_replace_option_with_none(cx, src, dest, expr.span);
                 check_replace_with_uninit(cx, src, dest, expr.span);
-                if meets_msrv(self.msrv, msrvs::MEM_TAKE) {
+                if self.msrv.meets(msrvs::MEM_TAKE) {
                     check_replace_with_default(cx, src, dest, expr.span);
                 }
             }
diff --git a/src/tools/clippy/clippy_lints/src/methods/cloned_instead_of_copied.rs b/src/tools/clippy/clippy_lints/src/methods/cloned_instead_of_copied.rs
index e9aeab2d5b6..4e6ec61f6a8 100644
--- a/src/tools/clippy/clippy_lints/src/methods/cloned_instead_of_copied.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/cloned_instead_of_copied.rs
@@ -1,25 +1,25 @@
 use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::is_trait_method;
+use clippy_utils::msrvs::{self, Msrv};
 use clippy_utils::ty::{get_iterator_item_ty, is_copy};
-use clippy_utils::{is_trait_method, meets_msrv, msrvs};
 use rustc_errors::Applicability;
 use rustc_hir::Expr;
 use rustc_lint::LateContext;
 use rustc_middle::ty;
-use rustc_semver::RustcVersion;
 use rustc_span::{sym, Span};
 
 use super::CLONED_INSTEAD_OF_COPIED;
 
-pub fn check(cx: &LateContext<'_>, expr: &Expr<'_>, recv: &Expr<'_>, span: Span, msrv: Option<RustcVersion>) {
+pub fn check(cx: &LateContext<'_>, expr: &Expr<'_>, recv: &Expr<'_>, span: Span, msrv: &Msrv) {
     let recv_ty = cx.typeck_results().expr_ty_adjusted(recv);
     let inner_ty = match recv_ty.kind() {
         // `Option<T>` -> `T`
         ty::Adt(adt, subst)
-            if cx.tcx.is_diagnostic_item(sym::Option, adt.did()) && meets_msrv(msrv, msrvs::OPTION_COPIED) =>
+            if cx.tcx.is_diagnostic_item(sym::Option, adt.did()) && msrv.meets(msrvs::OPTION_COPIED) =>
         {
             subst.type_at(0)
         },
-        _ if is_trait_method(cx, expr, sym::Iterator) && meets_msrv(msrv, msrvs::ITERATOR_COPIED) => {
+        _ if is_trait_method(cx, expr, sym::Iterator) && msrv.meets(msrvs::ITERATOR_COPIED) => {
             match get_iterator_item_ty(cx, recv_ty) {
                 // <T as Iterator>::Item
                 Some(ty) => ty,
diff --git a/src/tools/clippy/clippy_lints/src/methods/err_expect.rs b/src/tools/clippy/clippy_lints/src/methods/err_expect.rs
index 720d9a68c85..ae03da0d3f9 100644
--- a/src/tools/clippy/clippy_lints/src/methods/err_expect.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/err_expect.rs
@@ -1,27 +1,27 @@
 use super::ERR_EXPECT;
 use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::msrvs::{self, Msrv};
 use clippy_utils::ty::has_debug_impl;
-use clippy_utils::{meets_msrv, msrvs, ty::is_type_diagnostic_item};
+use clippy_utils::ty::is_type_diagnostic_item;
 use rustc_errors::Applicability;
 use rustc_lint::LateContext;
 use rustc_middle::ty;
 use rustc_middle::ty::Ty;
-use rustc_semver::RustcVersion;
 use rustc_span::{sym, Span};
 
 pub(super) fn check(
     cx: &LateContext<'_>,
     _expr: &rustc_hir::Expr<'_>,
     recv: &rustc_hir::Expr<'_>,
-    msrv: Option<RustcVersion>,
     expect_span: Span,
     err_span: Span,
+    msrv: &Msrv,
 ) {
     if_chain! {
         if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(recv), sym::Result);
         // Test the version to make sure the lint can be showed (expect_err has been
         // introduced in rust 1.17.0 : https://github.com/rust-lang/rust/pull/38982)
-        if meets_msrv(msrv, msrvs::EXPECT_ERR);
+        if msrv.meets(msrvs::EXPECT_ERR);
 
         // Grabs the `Result<T, E>` type
         let result_type = cx.typeck_results().expr_ty(recv);
diff --git a/src/tools/clippy/clippy_lints/src/methods/filter_map_next.rs b/src/tools/clippy/clippy_lints/src/methods/filter_map_next.rs
index ddf8a1f09b8..175e04f8ac0 100644
--- a/src/tools/clippy/clippy_lints/src/methods/filter_map_next.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/filter_map_next.rs
@@ -1,10 +1,10 @@
 use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg};
+use clippy_utils::is_trait_method;
+use clippy_utils::msrvs::{self, Msrv};
 use clippy_utils::source::snippet;
-use clippy_utils::{is_trait_method, meets_msrv, msrvs};
 use rustc_errors::Applicability;
 use rustc_hir as hir;
 use rustc_lint::LateContext;
-use rustc_semver::RustcVersion;
 use rustc_span::sym;
 
 use super::FILTER_MAP_NEXT;
@@ -14,10 +14,10 @@ pub(super) fn check<'tcx>(
     expr: &'tcx hir::Expr<'_>,
     recv: &'tcx hir::Expr<'_>,
     arg: &'tcx hir::Expr<'_>,
-    msrv: Option<RustcVersion>,
+    msrv: &Msrv,
 ) {
     if is_trait_method(cx, expr, sym::Iterator) {
-        if !meets_msrv(msrv, msrvs::ITERATOR_FIND_MAP) {
+        if !msrv.meets(msrvs::ITERATOR_FIND_MAP) {
             return;
         }
 
diff --git a/src/tools/clippy/clippy_lints/src/methods/is_digit_ascii_radix.rs b/src/tools/clippy/clippy_lints/src/methods/is_digit_ascii_radix.rs
index 304024e8066..301aff5ae6a 100644
--- a/src/tools/clippy/clippy_lints/src/methods/is_digit_ascii_radix.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/is_digit_ascii_radix.rs
@@ -1,23 +1,22 @@
 //! Lint for `c.is_digit(10)`
 
 use super::IS_DIGIT_ASCII_RADIX;
+use clippy_utils::msrvs::{self, Msrv};
 use clippy_utils::{
-    consts::constant_full_int, consts::FullInt, diagnostics::span_lint_and_sugg, meets_msrv, msrvs,
-    source::snippet_with_applicability,
+    consts::constant_full_int, consts::FullInt, diagnostics::span_lint_and_sugg, source::snippet_with_applicability,
 };
 use rustc_errors::Applicability;
 use rustc_hir::Expr;
 use rustc_lint::LateContext;
-use rustc_semver::RustcVersion;
 
 pub(super) fn check<'tcx>(
     cx: &LateContext<'tcx>,
     expr: &'tcx Expr<'_>,
     self_arg: &'tcx Expr<'_>,
     radix: &'tcx Expr<'_>,
-    msrv: Option<RustcVersion>,
+    msrv: &Msrv,
 ) {
-    if !meets_msrv(msrv, msrvs::IS_ASCII_DIGIT) {
+    if !msrv.meets(msrvs::IS_ASCII_DIGIT) {
         return;
     }
 
diff --git a/src/tools/clippy/clippy_lints/src/methods/map_clone.rs b/src/tools/clippy/clippy_lints/src/methods/map_clone.rs
index 6bc783c6d50..52cc1e0464b 100644
--- a/src/tools/clippy/clippy_lints/src/methods/map_clone.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/map_clone.rs
@@ -1,7 +1,8 @@
 use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::msrvs::{self, Msrv};
 use clippy_utils::source::snippet_with_applicability;
 use clippy_utils::ty::{is_copy, is_type_diagnostic_item};
-use clippy_utils::{is_diag_trait_item, meets_msrv, msrvs, peel_blocks};
+use clippy_utils::{is_diag_trait_item, peel_blocks};
 use if_chain::if_chain;
 use rustc_errors::Applicability;
 use rustc_hir as hir;
@@ -9,19 +10,12 @@ use rustc_lint::LateContext;
 use rustc_middle::mir::Mutability;
 use rustc_middle::ty;
 use rustc_middle::ty::adjustment::Adjust;
-use rustc_semver::RustcVersion;
 use rustc_span::symbol::Ident;
 use rustc_span::{sym, Span};
 
 use super::MAP_CLONE;
 
-pub(super) fn check(
-    cx: &LateContext<'_>,
-    e: &hir::Expr<'_>,
-    recv: &hir::Expr<'_>,
-    arg: &hir::Expr<'_>,
-    msrv: Option<RustcVersion>,
-) {
+pub(super) fn check(cx: &LateContext<'_>, e: &hir::Expr<'_>, recv: &hir::Expr<'_>, arg: &hir::Expr<'_>, msrv: &Msrv) {
     if_chain! {
         if let Some(method_id) = cx.typeck_results().type_dependent_def_id(e.hir_id);
         if cx.tcx.impl_of_method(method_id)
@@ -97,10 +91,10 @@ fn lint_needless_cloning(cx: &LateContext<'_>, root: Span, receiver: Span) {
     );
 }
 
-fn lint_explicit_closure(cx: &LateContext<'_>, replace: Span, root: Span, is_copy: bool, msrv: Option<RustcVersion>) {
+fn lint_explicit_closure(cx: &LateContext<'_>, replace: Span, root: Span, is_copy: bool, msrv: &Msrv) {
     let mut applicability = Applicability::MachineApplicable;
 
-    let (message, sugg_method) = if is_copy && meets_msrv(msrv, msrvs::ITERATOR_COPIED) {
+    let (message, sugg_method) = if is_copy && msrv.meets(msrvs::ITERATOR_COPIED) {
         ("you are using an explicit closure for copying elements", "copied")
     } else {
         ("you are using an explicit closure for cloning elements", "cloned")
diff --git a/src/tools/clippy/clippy_lints/src/methods/map_unwrap_or.rs b/src/tools/clippy/clippy_lints/src/methods/map_unwrap_or.rs
index 74fdead216b..3122f72ee91 100644
--- a/src/tools/clippy/clippy_lints/src/methods/map_unwrap_or.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/map_unwrap_or.rs
@@ -1,12 +1,11 @@
 use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg};
+use clippy_utils::msrvs::{self, Msrv};
 use clippy_utils::source::snippet;
 use clippy_utils::ty::is_type_diagnostic_item;
 use clippy_utils::usage::mutated_variables;
-use clippy_utils::{meets_msrv, msrvs};
 use rustc_errors::Applicability;
 use rustc_hir as hir;
 use rustc_lint::LateContext;
-use rustc_semver::RustcVersion;
 use rustc_span::symbol::sym;
 
 use super::MAP_UNWRAP_OR;
@@ -19,13 +18,13 @@ pub(super) fn check<'tcx>(
     recv: &'tcx hir::Expr<'_>,
     map_arg: &'tcx hir::Expr<'_>,
     unwrap_arg: &'tcx hir::Expr<'_>,
-    msrv: Option<RustcVersion>,
+    msrv: &Msrv,
 ) -> bool {
     // lint if the caller of `map()` is an `Option`
     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);
 
-    if is_result && !meets_msrv(msrv, msrvs::RESULT_MAP_OR_ELSE) {
+    if is_result && !msrv.meets(msrvs::RESULT_MAP_OR_ELSE) {
         return false;
     }
 
diff --git a/src/tools/clippy/clippy_lints/src/methods/mod.rs b/src/tools/clippy/clippy_lints/src/methods/mod.rs
index 38165ab4fb2..d2913680cbb 100644
--- a/src/tools/clippy/clippy_lints/src/methods/mod.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/mod.rs
@@ -104,8 +104,9 @@ mod zst_offset;
 use bind_instead_of_map::BindInsteadOfMap;
 use clippy_utils::consts::{constant, Constant};
 use clippy_utils::diagnostics::{span_lint, span_lint_and_help};
+use clippy_utils::msrvs::{self, Msrv};
 use clippy_utils::ty::{contains_ty_adt_constructor_opaque, implements_trait, is_copy, is_type_diagnostic_item};
-use clippy_utils::{contains_return, is_bool, is_trait_method, iter_input_pats, meets_msrv, msrvs, return_ty};
+use clippy_utils::{contains_return, is_bool, is_trait_method, iter_input_pats, return_ty};
 use if_chain::if_chain;
 use rustc_hir as hir;
 use rustc_hir::{Expr, ExprKind, TraitItem, TraitItemKind};
@@ -113,7 +114,6 @@ use rustc_hir_analysis::hir_ty_to_ty;
 use rustc_lint::{LateContext, LateLintPass, LintContext};
 use rustc_middle::lint::in_external_macro;
 use rustc_middle::ty::{self, TraitRef, Ty};
-use rustc_semver::RustcVersion;
 use rustc_session::{declare_tool_lint, impl_lint_pass};
 use rustc_span::{sym, Span};
 
@@ -3163,7 +3163,7 @@ declare_clippy_lint! {
 
 pub struct Methods {
     avoid_breaking_exported_api: bool,
-    msrv: Option<RustcVersion>,
+    msrv: Msrv,
     allow_expect_in_tests: bool,
     allow_unwrap_in_tests: bool,
 }
@@ -3172,7 +3172,7 @@ impl Methods {
     #[must_use]
     pub fn new(
         avoid_breaking_exported_api: bool,
-        msrv: Option<RustcVersion>,
+        msrv: Msrv,
         allow_expect_in_tests: bool,
         allow_unwrap_in_tests: bool,
     ) -> Self {
@@ -3325,7 +3325,7 @@ impl<'tcx> LateLintPass<'tcx> for Methods {
                 single_char_add_str::check(cx, expr, receiver, args);
                 into_iter_on_ref::check(cx, expr, method_span, method_call.ident.name, receiver);
                 single_char_pattern::check(cx, expr, method_call.ident.name, receiver, args);
-                unnecessary_to_owned::check(cx, expr, method_call.ident.name, receiver, args, self.msrv);
+                unnecessary_to_owned::check(cx, expr, method_call.ident.name, receiver, args, &self.msrv);
             },
             hir::ExprKind::Binary(op, lhs, rhs) if op.node == hir::BinOpKind::Eq || op.node == hir::BinOpKind::Ne => {
                 let mut info = BinaryExprInfo {
@@ -3501,7 +3501,7 @@ impl Methods {
                 ("as_mut", []) => useless_asref::check(cx, expr, "as_mut", recv),
                 ("as_ref", []) => useless_asref::check(cx, expr, "as_ref", recv),
                 ("assume_init", []) => uninit_assumed_init::check(cx, expr, recv),
-                ("cloned", []) => cloned_instead_of_copied::check(cx, expr, recv, span, self.msrv),
+                ("cloned", []) => cloned_instead_of_copied::check(cx, expr, recv, span, &self.msrv),
                 ("collect", []) if is_trait_method(cx, expr, sym::Iterator) => {
                     needless_collect::check(cx, span, expr, recv, call_span);
                     match method_call(recv) {
@@ -3512,7 +3512,7 @@ impl Methods {
                             map_collect_result_unit::check(cx, expr, m_recv, m_arg);
                         },
                         Some(("take", take_self_arg, [take_arg], _, _)) => {
-                            if meets_msrv(self.msrv, msrvs::STR_REPEAT) {
+                            if self.msrv.meets(msrvs::STR_REPEAT) {
                                 manual_str_repeat::check(cx, expr, recv, take_self_arg, take_arg);
                             }
                         },
@@ -3539,7 +3539,7 @@ impl Methods {
                 },
                 ("expect", [_]) => match method_call(recv) {
                     Some(("ok", recv, [], _, _)) => ok_expect::check(cx, expr, recv),
-                    Some(("err", recv, [], err_span, _)) => err_expect::check(cx, expr, recv, self.msrv, span, err_span),
+                    Some(("err", recv, [], err_span, _)) => err_expect::check(cx, expr, recv, span, err_span, &self.msrv),
                     _ => expect_used::check(cx, expr, recv, false, self.allow_expect_in_tests),
                 },
                 ("expect_err", [_]) => expect_used::check(cx, expr, recv, true, self.allow_expect_in_tests),
@@ -3578,7 +3578,7 @@ impl Methods {
                     unit_hash::check(cx, expr, recv, arg);
                 },
                 ("is_file", []) => filetype_is_file::check(cx, expr, recv),
-                ("is_digit", [radix]) => is_digit_ascii_radix::check(cx, expr, recv, radix, self.msrv),
+                ("is_digit", [radix]) => is_digit_ascii_radix::check(cx, expr, recv, radix, &self.msrv),
                 ("is_none", []) => check_is_some_is_none(cx, expr, recv, false),
                 ("is_some", []) => check_is_some_is_none(cx, expr, recv, true),
                 ("iter" | "iter_mut" | "into_iter", []) => {
@@ -3601,7 +3601,7 @@ impl Methods {
                 },
                 (name @ ("map" | "map_err"), [m_arg]) => {
                     if name == "map" {
-                        map_clone::check(cx, expr, recv, m_arg, self.msrv);
+                        map_clone::check(cx, expr, recv, m_arg, &self.msrv);
                         if let Some((map_name @ ("iter" | "into_iter"), recv2, _, _, _)) = method_call(recv) {
                             iter_kv_map::check(cx, map_name, expr, recv2, m_arg);
                         }
@@ -3610,8 +3610,8 @@ impl Methods {
                     }
                     if let Some((name, recv2, args, span2,_)) = method_call(recv) {
                         match (name, args) {
-                            ("as_mut", []) => option_as_ref_deref::check(cx, expr, recv2, m_arg, true, self.msrv),
-                            ("as_ref", []) => option_as_ref_deref::check(cx, expr, recv2, m_arg, false, self.msrv),
+                            ("as_mut", []) => option_as_ref_deref::check(cx, expr, recv2, m_arg, true, &self.msrv),
+                            ("as_ref", []) => option_as_ref_deref::check(cx, expr, recv2, m_arg, false, &self.msrv),
                             ("filter", [f_arg]) => {
                                 filter_map::check(cx, expr, recv2, f_arg, span2, recv, m_arg, span, false);
                             },
@@ -3632,7 +3632,7 @@ impl Methods {
                         match (name2, args2) {
                             ("cloned", []) => iter_overeager_cloned::check(cx, expr, recv, recv2, false, false),
                             ("filter", [arg]) => filter_next::check(cx, expr, recv2, arg),
-                            ("filter_map", [arg]) => filter_map_next::check(cx, expr, recv2, arg, self.msrv),
+                            ("filter_map", [arg]) => filter_map_next::check(cx, expr, recv2, arg, &self.msrv),
                             ("iter", []) => iter_next_slice::check(cx, expr, recv2),
                             ("skip", [arg]) => iter_skip_next::check(cx, expr, recv2, arg),
                             ("skip_while", [_]) => skip_while_next::check(cx, expr),
@@ -3680,10 +3680,10 @@ impl Methods {
                     vec_resize_to_zero::check(cx, expr, count_arg, default_arg, span);
                 },
                 ("seek", [arg]) => {
-                    if meets_msrv(self.msrv, msrvs::SEEK_FROM_CURRENT) {
+                    if self.msrv.meets(msrvs::SEEK_FROM_CURRENT) {
                         seek_from_current::check(cx, expr, recv, arg);
                     }
-                    if meets_msrv(self.msrv, msrvs::SEEK_REWIND) {
+                    if self.msrv.meets(msrvs::SEEK_REWIND) {
                         seek_to_start_instead_of_rewind::check(cx, expr, recv, arg, span);
                     }
                 },
@@ -3699,7 +3699,7 @@ impl Methods {
                 ("splitn" | "rsplitn", [count_arg, pat_arg]) => {
                     if let Some((Constant::Int(count), _)) = constant(cx, cx.typeck_results(), count_arg) {
                         suspicious_splitn::check(cx, name, expr, recv, count);
-                        str_splitn::check(cx, name, expr, recv, pat_arg, count, self.msrv);
+                        str_splitn::check(cx, name, expr, recv, pat_arg, count, &self.msrv);
                     }
                 },
                 ("splitn_mut" | "rsplitn_mut", [count_arg, _]) => {
@@ -3717,7 +3717,7 @@ impl Methods {
                 },
                 ("take", []) => needless_option_take::check(cx, expr, recv),
                 ("then", [arg]) => {
-                    if !meets_msrv(self.msrv, msrvs::BOOL_THEN_SOME) {
+                    if !self.msrv.meets(msrvs::BOOL_THEN_SOME) {
                         return;
                     }
                     unnecessary_lazy_eval::check(cx, expr, recv, arg, "then_some");
@@ -3760,7 +3760,7 @@ impl Methods {
                 },
                 ("unwrap_or_else", [u_arg]) => match method_call(recv) {
                     Some(("map", recv, [map_arg], _, _))
-                        if map_unwrap_or::check(cx, expr, recv, map_arg, u_arg, self.msrv) => {},
+                        if map_unwrap_or::check(cx, expr, recv, map_arg, u_arg, &self.msrv) => {},
                     _ => {
                         unwrap_or_else_default::check(cx, expr, recv, u_arg);
                         unnecessary_lazy_eval::check(cx, expr, recv, u_arg, "unwrap_or");
diff --git a/src/tools/clippy/clippy_lints/src/methods/option_as_ref_deref.rs b/src/tools/clippy/clippy_lints/src/methods/option_as_ref_deref.rs
index e6eb64bcbde..3e33f919337 100644
--- a/src/tools/clippy/clippy_lints/src/methods/option_as_ref_deref.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/option_as_ref_deref.rs
@@ -1,13 +1,13 @@
 use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::msrvs::{self, Msrv};
 use clippy_utils::source::snippet;
 use clippy_utils::ty::is_type_diagnostic_item;
-use clippy_utils::{match_def_path, meets_msrv, msrvs, path_to_local_id, paths, peel_blocks};
+use clippy_utils::{match_def_path, path_to_local_id, paths, peel_blocks};
 use if_chain::if_chain;
 use rustc_errors::Applicability;
 use rustc_hir as hir;
 use rustc_lint::LateContext;
 use rustc_middle::ty;
-use rustc_semver::RustcVersion;
 use rustc_span::sym;
 
 use super::OPTION_AS_REF_DEREF;
@@ -19,9 +19,9 @@ pub(super) fn check(
     as_ref_recv: &hir::Expr<'_>,
     map_arg: &hir::Expr<'_>,
     is_mut: bool,
-    msrv: Option<RustcVersion>,
+    msrv: &Msrv,
 ) {
-    if !meets_msrv(msrv, msrvs::OPTION_AS_DEREF) {
+    if !msrv.meets(msrvs::OPTION_AS_DEREF) {
         return;
     }
 
diff --git a/src/tools/clippy/clippy_lints/src/methods/option_map_unwrap_or.rs b/src/tools/clippy/clippy_lints/src/methods/option_map_unwrap_or.rs
index 30421a6dd5a..910ee14855e 100644
--- a/src/tools/clippy/clippy_lints/src/methods/option_map_unwrap_or.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/option_map_unwrap_or.rs
@@ -97,7 +97,7 @@ struct UnwrapVisitor<'a, 'tcx> {
 impl<'a, 'tcx> Visitor<'tcx> for UnwrapVisitor<'a, 'tcx> {
     type NestedFilter = nested_filter::All;
 
-    fn visit_path(&mut self, path: &'tcx Path<'_>, _id: HirId) {
+    fn visit_path(&mut self, path: &Path<'tcx>, _id: HirId) {
         self.identifiers.insert(ident(path));
         walk_path(self, path);
     }
@@ -116,7 +116,7 @@ struct MapExprVisitor<'a, 'tcx> {
 impl<'a, 'tcx> Visitor<'tcx> for MapExprVisitor<'a, 'tcx> {
     type NestedFilter = nested_filter::All;
 
-    fn visit_path(&mut self, path: &'tcx Path<'_>, _id: HirId) {
+    fn visit_path(&mut self, path: &Path<'tcx>, _id: HirId) {
         if self.identifiers.contains(&ident(path)) {
             self.found_identifier = true;
             return;
diff --git a/src/tools/clippy/clippy_lints/src/methods/str_splitn.rs b/src/tools/clippy/clippy_lints/src/methods/str_splitn.rs
index 1acac59144c..3c01ce1fecd 100644
--- a/src/tools/clippy/clippy_lints/src/methods/str_splitn.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/str_splitn.rs
@@ -1,9 +1,10 @@
 use clippy_utils::consts::{constant, Constant};
 use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then};
+use clippy_utils::msrvs::{self, Msrv};
 use clippy_utils::source::snippet_with_context;
 use clippy_utils::usage::local_used_after_expr;
 use clippy_utils::visitors::{for_each_expr_with_closures, Descend};
-use clippy_utils::{is_diag_item_method, match_def_path, meets_msrv, msrvs, path_to_local_id, paths};
+use clippy_utils::{is_diag_item_method, match_def_path, path_to_local_id, paths};
 use core::ops::ControlFlow;
 use if_chain::if_chain;
 use rustc_errors::Applicability;
@@ -12,7 +13,6 @@ use rustc_hir::{
 };
 use rustc_lint::LateContext;
 use rustc_middle::ty;
-use rustc_semver::RustcVersion;
 use rustc_span::{sym, Span, Symbol, SyntaxContext};
 
 use super::{MANUAL_SPLIT_ONCE, NEEDLESS_SPLITN};
@@ -24,7 +24,7 @@ pub(super) fn check(
     self_arg: &Expr<'_>,
     pat_arg: &Expr<'_>,
     count: u128,
-    msrv: Option<RustcVersion>,
+    msrv: &Msrv,
 ) {
     if count < 2 || !cx.typeck_results().expr_ty_adjusted(self_arg).peel_refs().is_str() {
         return;
@@ -34,7 +34,7 @@ pub(super) fn check(
         IterUsageKind::Nth(n) => count > n + 1,
         IterUsageKind::NextTuple => count > 2,
     };
-    let manual = count == 2 && meets_msrv(msrv, msrvs::STR_SPLIT_ONCE);
+    let manual = count == 2 && msrv.meets(msrvs::STR_SPLIT_ONCE);
 
     match parse_iter_usage(cx, expr.span.ctxt(), cx.tcx.hir().parent_iter(expr.hir_id)) {
         Some(usage) if needless(usage.kind) => lint_needless(cx, method_name, expr, self_arg, pat_arg),
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 7ff13b95626..17b0507682a 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
@@ -1,13 +1,11 @@
 use super::implicit_clone::is_clone_like;
 use super::unnecessary_iter_cloned::{self, is_into_iter};
 use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::msrvs::{self, Msrv};
 use clippy_utils::source::snippet_opt;
 use clippy_utils::ty::{get_iterator_item_ty, implements_trait, is_copy, peel_mid_ty_refs};
 use clippy_utils::visitors::find_all_ret_expressions;
-use clippy_utils::{
-    fn_def_id, get_parent_expr, is_diag_item_method, is_diag_trait_item, return_ty,
-};
-use clippy_utils::{meets_msrv, msrvs};
+use clippy_utils::{fn_def_id, get_parent_expr, is_diag_item_method, is_diag_trait_item, return_ty};
 use rustc_errors::Applicability;
 use rustc_hir::{def_id::DefId, BorrowKind, Expr, ExprKind, ItemKind, Node};
 use rustc_hir_typeck::{FnCtxt, Inherited};
@@ -16,14 +14,9 @@ use rustc_lint::LateContext;
 use rustc_middle::mir::Mutability;
 use rustc_middle::ty::adjustment::{Adjust, Adjustment, OverloadedDeref};
 use rustc_middle::ty::subst::{GenericArg, GenericArgKind, SubstsRef};
-use rustc_middle::ty::EarlyBinder;
-use rustc_middle::ty::{self, Clause, ParamTy, PredicateKind, ProjectionPredicate, TraitPredicate, Ty};
-use rustc_semver::RustcVersion;
+use rustc_middle::ty::{self, Clause, EarlyBinder, ParamTy, PredicateKind, ProjectionPredicate, TraitPredicate, Ty};
 use rustc_span::{sym, Symbol};
-use rustc_trait_selection::traits::{
-    query::evaluate_obligation::InferCtxtExt as _, Obligation, ObligationCause,
-};
-use std::cmp::max;
+use rustc_trait_selection::traits::{query::evaluate_obligation::InferCtxtExt as _, Obligation, ObligationCause};
 
 use super::UNNECESSARY_TO_OWNED;
 
@@ -33,7 +26,7 @@ pub fn check<'tcx>(
     method_name: Symbol,
     receiver: &'tcx Expr<'_>,
     args: &'tcx [Expr<'_>],
-    msrv: Option<RustcVersion>,
+    msrv: &Msrv,
 ) {
     if_chain! {
         if let Some(method_def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id);
@@ -204,7 +197,7 @@ fn check_into_iter_call_arg(
     expr: &Expr<'_>,
     method_name: Symbol,
     receiver: &Expr<'_>,
-    msrv: Option<RustcVersion>,
+    msrv: &Msrv,
 ) -> bool {
     if_chain! {
         if let Some(parent) = get_parent_expr(cx, expr);
@@ -219,7 +212,7 @@ fn check_into_iter_call_arg(
             if unnecessary_iter_cloned::check_for_loop_iter(cx, parent, method_name, receiver, true) {
                 return true;
             }
-            let cloned_or_copied = if is_copy(cx, item_ty) && meets_msrv(msrv, msrvs::ITERATOR_COPIED) {
+            let cloned_or_copied = if is_copy(cx, item_ty) && msrv.meets(msrvs::ITERATOR_COPIED) {
                 "copied"
             } else {
                 "cloned"
@@ -267,11 +260,22 @@ fn check_other_call_arg<'tcx>(
         if let Some(as_ref_trait_id) = cx.tcx.get_diagnostic_item(sym::AsRef);
         if trait_predicate.def_id() == deref_trait_id || trait_predicate.def_id() == as_ref_trait_id;
         let receiver_ty = cx.typeck_results().expr_ty(receiver);
-        if can_change_type(cx, maybe_arg, receiver_ty);
         // We can't add an `&` when the trait is `Deref` because `Target = &T` won't match
         // `Target = T`.
-        if n_refs > 0 || is_copy(cx, receiver_ty) || trait_predicate.def_id() != deref_trait_id;
-        let n_refs = max(n_refs, usize::from(!is_copy(cx, receiver_ty)));
+        if let Some((n_refs, receiver_ty)) = if n_refs > 0 || is_copy(cx, receiver_ty) {
+            Some((n_refs, receiver_ty))
+        } else if trait_predicate.def_id() != deref_trait_id {
+            Some((1, cx.tcx.mk_ref(
+                cx.tcx.lifetimes.re_erased,
+                ty::TypeAndMut {
+                    ty: receiver_ty,
+                    mutbl: Mutability::Not,
+                },
+            )))
+        } else {
+            None
+        };
+        if can_change_type(cx, maybe_arg, receiver_ty);
         if let Some(receiver_snippet) = snippet_opt(cx, receiver.span);
         then {
             span_lint_and_sugg(
@@ -345,13 +349,13 @@ fn get_input_traits_and_projections<'tcx>(
                 if trait_predicate.trait_ref.self_ty() == input {
                     trait_predicates.push(trait_predicate);
                 }
-            }
+            },
             PredicateKind::Clause(Clause::Projection(projection_predicate)) => {
                 if projection_predicate.projection_ty.self_ty() == input {
                     projection_predicates.push(projection_predicate);
                 }
-            }
-            _ => {}
+            },
+            _ => {},
         }
     }
     (trait_predicates, projection_predicates)
@@ -403,10 +407,12 @@ fn can_change_type<'a>(cx: &LateContext<'a>, mut expr: &'a Expr<'a>, mut ty: Ty<
 
                         let mut trait_predicates = cx.tcx.param_env(callee_def_id)
                             .caller_bounds().iter().filter(|predicate| {
-                            if let PredicateKind::Clause(Clause::Trait(trait_predicate)) =  predicate.kind().skip_binder()
-                                && trait_predicate.trait_ref.self_ty() == *param_ty {
-                                    true
-                                } else {
+                            if let PredicateKind::Clause(Clause::Trait(trait_predicate))
+                                    = predicate.kind().skip_binder()
+                                && trait_predicate.trait_ref.self_ty() == *param_ty
+                            {
+                                true
+                            } else {
                                 false
                             }
                         });
@@ -466,12 +472,7 @@ fn is_cloned_or_copied(cx: &LateContext<'_>, method_name: Symbol, method_def_id:
 
 /// Returns true if the named method can be used to convert the receiver to its "owned"
 /// representation.
-fn is_to_owned_like<'a>(
-    cx: &LateContext<'a>,
-    call_expr: &Expr<'a>,
-    method_name: Symbol,
-    method_def_id: DefId,
-) -> bool {
+fn is_to_owned_like<'a>(cx: &LateContext<'a>, call_expr: &Expr<'a>, method_name: Symbol, method_def_id: DefId) -> bool {
     is_clone_like(cx, method_name.as_str(), method_def_id)
         || is_cow_into_owned(cx, method_name, method_def_id)
         || is_to_string_on_string_like(cx, call_expr, method_name, method_def_id)
diff --git a/src/tools/clippy/clippy_lints/src/missing_const_for_fn.rs b/src/tools/clippy/clippy_lints/src/missing_const_for_fn.rs
index 71cc0d0a81c..5bc04bc17fb 100644
--- a/src/tools/clippy/clippy_lints/src/missing_const_for_fn.rs
+++ b/src/tools/clippy/clippy_lints/src/missing_const_for_fn.rs
@@ -1,9 +1,8 @@
 use clippy_utils::diagnostics::span_lint;
+use clippy_utils::msrvs::{self, Msrv};
 use clippy_utils::qualify_min_const_fn::is_min_const_fn;
 use clippy_utils::ty::has_drop;
-use clippy_utils::{
-    fn_has_unsatisfiable_preds, is_entrypoint_fn, is_from_proc_macro, meets_msrv, msrvs, trait_ref_of_method,
-};
+use clippy_utils::{fn_has_unsatisfiable_preds, is_entrypoint_fn, is_from_proc_macro, trait_ref_of_method};
 use rustc_hir as hir;
 use rustc_hir::def_id::CRATE_DEF_ID;
 use rustc_hir::intravisit::FnKind;
@@ -11,7 +10,6 @@ use rustc_hir::{Body, Constness, FnDecl, GenericParamKind, HirId};
 use rustc_hir_analysis::hir_ty_to_ty;
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_middle::lint::in_external_macro;
-use rustc_semver::RustcVersion;
 use rustc_session::{declare_tool_lint, impl_lint_pass};
 use rustc_span::Span;
 
@@ -75,12 +73,12 @@ declare_clippy_lint! {
 impl_lint_pass!(MissingConstForFn => [MISSING_CONST_FOR_FN]);
 
 pub struct MissingConstForFn {
-    msrv: Option<RustcVersion>,
+    msrv: Msrv,
 }
 
 impl MissingConstForFn {
     #[must_use]
-    pub fn new(msrv: Option<RustcVersion>) -> Self {
+    pub fn new(msrv: Msrv) -> Self {
         Self { msrv }
     }
 }
@@ -95,7 +93,7 @@ impl<'tcx> LateLintPass<'tcx> for MissingConstForFn {
         span: Span,
         hir_id: HirId,
     ) {
-        if !meets_msrv(self.msrv, msrvs::CONST_IF_MATCH) {
+        if !self.msrv.meets(msrvs::CONST_IF_MATCH) {
             return;
         }
 
@@ -152,7 +150,7 @@ impl<'tcx> LateLintPass<'tcx> for MissingConstForFn {
 
         let mir = cx.tcx.optimized_mir(def_id);
 
-        if let Err((span, err)) = is_min_const_fn(cx.tcx, mir, self.msrv) {
+        if let Err((span, err)) = is_min_const_fn(cx.tcx, mir, &self.msrv) {
             if cx.tcx.is_const_fn_raw(def_id.to_def_id()) {
                 cx.tcx.sess.span_err(span, err.as_ref());
             }
diff --git a/src/tools/clippy/clippy_lints/src/missing_enforced_import_rename.rs b/src/tools/clippy/clippy_lints/src/missing_enforced_import_rename.rs
index 4712846939e..773174679db 100644
--- a/src/tools/clippy/clippy_lints/src/missing_enforced_import_rename.rs
+++ b/src/tools/clippy/clippy_lints/src/missing_enforced_import_rename.rs
@@ -66,35 +66,38 @@ impl LateLintPass<'_> for ImportRename {
     }
 
     fn check_item(&mut self, cx: &LateContext<'_>, item: &Item<'_>) {
-        if_chain! {
-            if let ItemKind::Use(path, UseKind::Single) = &item.kind;
-            if let Res::Def(_, id) = path.res;
-            if let Some(name) = self.renames.get(&id);
-            // Remove semicolon since it is not present for nested imports
-            let span_without_semi = cx.sess().source_map().span_until_char(item.span, ';');
-            if let Some(snip) = snippet_opt(cx, span_without_semi);
-            if let Some(import) = match snip.split_once(" as ") {
-                None => Some(snip.as_str()),
-                Some((import, rename)) => {
-                    if rename.trim() == name.as_str() {
-                        None
-                    } else {
-                        Some(import.trim())
+        if let ItemKind::Use(path, UseKind::Single) = &item.kind {
+            for &res in &path.res {
+                if_chain! {
+                    if let Res::Def(_, id) = res;
+                    if let Some(name) = self.renames.get(&id);
+                    // Remove semicolon since it is not present for nested imports
+                    let span_without_semi = cx.sess().source_map().span_until_char(item.span, ';');
+                    if let Some(snip) = snippet_opt(cx, span_without_semi);
+                    if let Some(import) = match snip.split_once(" as ") {
+                        None => Some(snip.as_str()),
+                        Some((import, rename)) => {
+                            if rename.trim() == name.as_str() {
+                                None
+                            } else {
+                                Some(import.trim())
+                            }
+                        },
+                    };
+                    then {
+                        span_lint_and_sugg(
+                            cx,
+                            MISSING_ENFORCED_IMPORT_RENAMES,
+                            span_without_semi,
+                            "this import should be renamed",
+                            "try",
+                            format!(
+                                "{import} as {name}",
+                            ),
+                            Applicability::MachineApplicable,
+                        );
                     }
-                },
-            };
-            then {
-                span_lint_and_sugg(
-                    cx,
-                    MISSING_ENFORCED_IMPORT_RENAMES,
-                    span_without_semi,
-                    "this import should be renamed",
-                    "try",
-                    format!(
-                        "{import} as {name}",
-                    ),
-                    Applicability::MachineApplicable,
-                );
+                }
             }
         }
     }
diff --git a/src/tools/clippy/clippy_lints/src/needless_pass_by_value.rs b/src/tools/clippy/clippy_lints/src/needless_pass_by_value.rs
index 75e12715458..2f0b7ce16e5 100644
--- a/src/tools/clippy/clippy_lints/src/needless_pass_by_value.rs
+++ b/src/tools/clippy/clippy_lints/src/needless_pass_by_value.rs
@@ -1,7 +1,9 @@
 use clippy_utils::diagnostics::{multispan_sugg, span_lint_and_then};
 use clippy_utils::ptr::get_spans;
 use clippy_utils::source::{snippet, snippet_opt};
-use clippy_utils::ty::{implements_trait, implements_trait_with_env, is_copy, is_type_diagnostic_item, is_type_lang_item};
+use clippy_utils::ty::{
+    implements_trait, implements_trait_with_env, is_copy, is_type_diagnostic_item, is_type_lang_item,
+};
 use clippy_utils::{get_trait_def_id, is_self, paths};
 use if_chain::if_chain;
 use rustc_ast::ast::Attribute;
@@ -124,7 +126,9 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessPassByValue {
             .filter_map(|obligation| {
                 // Note that we do not want to deal with qualified predicates here.
                 match obligation.predicate.kind().no_bound_vars() {
-                    Some(ty::PredicateKind::Clause(ty::Clause::Trait(pred))) if pred.def_id() != sized_trait => Some(pred),
+                    Some(ty::PredicateKind::Clause(ty::Clause::Trait(pred))) if pred.def_id() != sized_trait => {
+                        Some(pred)
+                    },
                     _ => None,
                 }
             })
diff --git a/src/tools/clippy/clippy_lints/src/no_effect.rs b/src/tools/clippy/clippy_lints/src/no_effect.rs
index 819646bb678..79c1ae4861e 100644
--- a/src/tools/clippy/clippy_lints/src/no_effect.rs
+++ b/src/tools/clippy/clippy_lints/src/no_effect.rs
@@ -6,7 +6,8 @@ use clippy_utils::ty::has_drop;
 use rustc_errors::Applicability;
 use rustc_hir::def::{DefKind, Res};
 use rustc_hir::{is_range_literal, BinOpKind, BlockCheckMode, Expr, ExprKind, PatKind, Stmt, StmtKind, UnsafeSource};
-use rustc_lint::{LateContext, LateLintPass};
+use rustc_lint::{LateContext, LateLintPass, LintContext};
+use rustc_middle::lint::in_external_macro;
 use rustc_session::{declare_lint_pass, declare_tool_lint};
 use std::ops::Deref;
 
@@ -159,8 +160,11 @@ fn has_no_effect(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
 fn check_unnecessary_operation(cx: &LateContext<'_>, stmt: &Stmt<'_>) {
     if_chain! {
         if let StmtKind::Semi(expr) = stmt.kind;
+        let ctxt = stmt.span.ctxt();
+        if expr.span.ctxt() == ctxt;
         if let Some(reduced) = reduce_expression(cx, expr);
-        if !&reduced.iter().any(|e| e.span.from_expansion());
+        if !in_external_macro(cx.sess(), stmt.span);
+        if reduced.iter().all(|e| e.span.ctxt() == ctxt);
         then {
             if let ExprKind::Index(..) = &expr.kind {
                 let snippet = if let (Some(arr), Some(func)) =
diff --git a/src/tools/clippy/clippy_lints/src/ptr.rs b/src/tools/clippy/clippy_lints/src/ptr.rs
index 92920bbad6e..e395ff54cb1 100644
--- a/src/tools/clippy/clippy_lints/src/ptr.rs
+++ b/src/tools/clippy/clippy_lints/src/ptr.rs
@@ -490,7 +490,7 @@ fn check_fn_args<'cx, 'tcx: 'cx>(
                         ty_name: name.ident.name,
                         method_renames,
                         ref_prefix: RefPrefix {
-                            lt: lt.clone(),
+                            lt: *lt,
                             mutability,
                         },
                         deref_ty,
@@ -693,9 +693,10 @@ fn matches_preds<'tcx>(
             cx.tcx,
             ObligationCause::dummy(),
             cx.param_env,
-            cx.tcx.mk_predicate(Binder::dummy(
-                PredicateKind::Clause(Clause::Projection(p.with_self_ty(cx.tcx, ty))),
-            )),
+            cx.tcx
+                .mk_predicate(Binder::dummy(PredicateKind::Clause(Clause::Projection(
+                    p.with_self_ty(cx.tcx, ty),
+                )))),
         )),
         ExistentialPredicate::AutoTrait(p) => infcx
             .type_implements_trait(p, [ty], cx.param_env)
diff --git a/src/tools/clippy/clippy_lints/src/ranges.rs b/src/tools/clippy/clippy_lints/src/ranges.rs
index c6fbb5e805a..0a1b9d173cf 100644
--- a/src/tools/clippy/clippy_lints/src/ranges.rs
+++ b/src/tools/clippy/clippy_lints/src/ranges.rs
@@ -1,16 +1,16 @@
 use clippy_utils::consts::{constant, Constant};
 use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg, span_lint_and_then};
 use clippy_utils::higher;
+use clippy_utils::msrvs::{self, Msrv};
 use clippy_utils::source::{snippet, snippet_opt, snippet_with_applicability};
 use clippy_utils::sugg::Sugg;
-use clippy_utils::{get_parent_expr, in_constant, is_integer_const, meets_msrv, msrvs, path_to_local};
+use clippy_utils::{get_parent_expr, in_constant, is_integer_const, path_to_local};
 use if_chain::if_chain;
 use rustc_ast::ast::RangeLimits;
 use rustc_errors::Applicability;
 use rustc_hir::{BinOpKind, Expr, ExprKind, HirId};
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_middle::ty;
-use rustc_semver::RustcVersion;
 use rustc_session::{declare_tool_lint, impl_lint_pass};
 use rustc_span::source_map::{Span, Spanned};
 use std::cmp::Ordering;
@@ -161,12 +161,12 @@ declare_clippy_lint! {
 }
 
 pub struct Ranges {
-    msrv: Option<RustcVersion>,
+    msrv: Msrv,
 }
 
 impl Ranges {
     #[must_use]
-    pub fn new(msrv: Option<RustcVersion>) -> Self {
+    pub fn new(msrv: Msrv) -> Self {
         Self { msrv }
     }
 }
@@ -181,7 +181,7 @@ impl_lint_pass!(Ranges => [
 impl<'tcx> LateLintPass<'tcx> for Ranges {
     fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
         if let ExprKind::Binary(ref op, l, r) = expr.kind {
-            if meets_msrv(self.msrv, msrvs::RANGE_CONTAINS) {
+            if self.msrv.meets(msrvs::RANGE_CONTAINS) {
                 check_possible_range_contains(cx, op.node, l, r, expr, expr.span);
             }
         }
diff --git a/src/tools/clippy/clippy_lints/src/redundant_closure_call.rs b/src/tools/clippy/clippy_lints/src/redundant_closure_call.rs
index 8e675d34a18..2a42e73488f 100644
--- a/src/tools/clippy/clippy_lints/src/redundant_closure_call.rs
+++ b/src/tools/clippy/clippy_lints/src/redundant_closure_call.rs
@@ -81,8 +81,8 @@ impl EarlyLintPass for RedundantClosureCall {
                         "try not to call a closure in the expression where it is declared",
                         |diag| {
                             if fn_decl.inputs.is_empty() {
-                                let app = Applicability::MachineApplicable;
-                                let mut hint = Sugg::ast(cx, body, "..");
+                                let mut app = Applicability::MachineApplicable;
+                                let mut hint = Sugg::ast(cx, body, "..", closure.span.ctxt(), &mut app);
 
                                 if asyncness.is_async() {
                                     // `async x` is a syntax error, so it becomes `async { x }`
diff --git a/src/tools/clippy/clippy_lints/src/redundant_field_names.rs b/src/tools/clippy/clippy_lints/src/redundant_field_names.rs
index 40b03068f6c..61bff4a0e38 100644
--- a/src/tools/clippy/clippy_lints/src/redundant_field_names.rs
+++ b/src/tools/clippy/clippy_lints/src/redundant_field_names.rs
@@ -1,10 +1,9 @@
 use clippy_utils::diagnostics::span_lint_and_sugg;
-use clippy_utils::{meets_msrv, msrvs};
+use clippy_utils::msrvs::{self, Msrv};
 use rustc_ast::ast::{Expr, ExprKind};
 use rustc_errors::Applicability;
 use rustc_lint::{EarlyContext, EarlyLintPass, LintContext};
 use rustc_middle::lint::in_external_macro;
-use rustc_semver::RustcVersion;
 use rustc_session::{declare_tool_lint, impl_lint_pass};
 
 declare_clippy_lint! {
@@ -37,12 +36,12 @@ declare_clippy_lint! {
 }
 
 pub struct RedundantFieldNames {
-    msrv: Option<RustcVersion>,
+    msrv: Msrv,
 }
 
 impl RedundantFieldNames {
     #[must_use]
-    pub fn new(msrv: Option<RustcVersion>) -> Self {
+    pub fn new(msrv: Msrv) -> Self {
         Self { msrv }
     }
 }
@@ -51,7 +50,7 @@ impl_lint_pass!(RedundantFieldNames => [REDUNDANT_FIELD_NAMES]);
 
 impl EarlyLintPass for RedundantFieldNames {
     fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &Expr) {
-        if !meets_msrv(self.msrv, msrvs::FIELD_INIT_SHORTHAND) {
+        if !self.msrv.meets(msrvs::FIELD_INIT_SHORTHAND) {
             return;
         }
 
diff --git a/src/tools/clippy/clippy_lints/src/redundant_pub_crate.rs b/src/tools/clippy/clippy_lints/src/redundant_pub_crate.rs
index 833dc4913b4..d612d249c2f 100644
--- a/src/tools/clippy/clippy_lints/src/redundant_pub_crate.rs
+++ b/src/tools/clippy/clippy_lints/src/redundant_pub_crate.rs
@@ -84,7 +84,7 @@ impl<'tcx> LateLintPass<'tcx> for RedundantPubCrate {
 
 fn is_not_macro_export<'tcx>(item: &'tcx Item<'tcx>) -> bool {
     if let ItemKind::Use(path, _) = item.kind {
-        if let Res::Def(DefKind::Macro(MacroKind::Bang), _) = path.res {
+        if path.res.iter().all(|res| matches!(res, Res::Def(DefKind::Macro(MacroKind::Bang), _))) {
             return false;
         }
     } else if let ItemKind::Macro(..) = item.kind {
diff --git a/src/tools/clippy/clippy_lints/src/redundant_static_lifetimes.rs b/src/tools/clippy/clippy_lints/src/redundant_static_lifetimes.rs
index 60ba62c4a43..3aa2490bc44 100644
--- a/src/tools/clippy/clippy_lints/src/redundant_static_lifetimes.rs
+++ b/src/tools/clippy/clippy_lints/src/redundant_static_lifetimes.rs
@@ -1,10 +1,9 @@
 use clippy_utils::diagnostics::span_lint_and_then;
+use clippy_utils::msrvs::{self, Msrv};
 use clippy_utils::source::snippet;
-use clippy_utils::{meets_msrv, msrvs};
 use rustc_ast::ast::{Item, ItemKind, Ty, TyKind};
 use rustc_errors::Applicability;
 use rustc_lint::{EarlyContext, EarlyLintPass};
-use rustc_semver::RustcVersion;
 use rustc_session::{declare_tool_lint, impl_lint_pass};
 
 declare_clippy_lint! {
@@ -34,12 +33,12 @@ declare_clippy_lint! {
 }
 
 pub struct RedundantStaticLifetimes {
-    msrv: Option<RustcVersion>,
+    msrv: Msrv,
 }
 
 impl RedundantStaticLifetimes {
     #[must_use]
-    pub fn new(msrv: Option<RustcVersion>) -> Self {
+    pub fn new(msrv: Msrv) -> Self {
         Self { msrv }
     }
 }
@@ -96,7 +95,7 @@ impl RedundantStaticLifetimes {
 
 impl EarlyLintPass for RedundantStaticLifetimes {
     fn check_item(&mut self, cx: &EarlyContext<'_>, item: &Item) {
-        if !meets_msrv(self.msrv, msrvs::STATIC_IN_CONST) {
+        if !self.msrv.meets(msrvs::STATIC_IN_CONST) {
             return;
         }
 
diff --git a/src/tools/clippy/clippy_lints/src/returns.rs b/src/tools/clippy/clippy_lints/src/returns.rs
index 2b2a41d1601..81143d7799e 100644
--- a/src/tools/clippy/clippy_lints/src/returns.rs
+++ b/src/tools/clippy/clippy_lints/src/returns.rs
@@ -12,6 +12,7 @@ use rustc_middle::lint::in_external_macro;
 use rustc_middle::ty::subst::GenericArgKind;
 use rustc_session::{declare_lint_pass, declare_tool_lint};
 use rustc_span::source_map::Span;
+use rustc_span::{BytePos, Pos};
 
 declare_clippy_lint! {
     /// ### What it does
@@ -209,13 +210,14 @@ fn check_final_expr<'tcx>(
             if cx.tcx.hir().attrs(expr.hir_id).is_empty() {
                 let borrows = inner.map_or(false, |inner| last_statement_borrows(cx, inner));
                 if !borrows {
-                    emit_return_lint(
-                        cx,
-                        peeled_drop_expr.span,
-                        semi_spans,
-                        inner.as_ref().map(|i| i.span),
-                        replacement,
-                    );
+                    // check if expr return nothing
+                    let ret_span = if inner.is_none() && replacement == RetReplacement::Empty {
+                        extend_span_to_previous_non_ws(cx, peeled_drop_expr.span)
+                    } else {
+                        peeled_drop_expr.span
+                    };
+
+                    emit_return_lint(cx, ret_span, semi_spans, inner.as_ref().map(|i| i.span), replacement);
                 }
             }
         },
@@ -289,3 +291,16 @@ fn last_statement_borrows<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>)
     })
     .is_some()
 }
+
+// Go backwards while encountering whitespace and extend the given Span to that point.
+fn extend_span_to_previous_non_ws(cx: &LateContext<'_>, sp: Span) -> Span {
+    if let Ok(prev_source) = cx.sess().source_map().span_to_prev_source(sp) {
+        let ws = [' ', '\t', '\n'];
+        if let Some(non_ws_pos) = prev_source.rfind(|c| !ws.contains(&c)) {
+            let len = prev_source.len() - non_ws_pos - 1;
+            return sp.with_lo(sp.lo() - BytePos::from_usize(len));
+        }
+    }
+
+    sp
+}
diff --git a/src/tools/clippy/clippy_lints/src/single_component_path_imports.rs b/src/tools/clippy/clippy_lints/src/single_component_path_imports.rs
index 2036e85db7e..d46f6a6352c 100644
--- a/src/tools/clippy/clippy_lints/src/single_component_path_imports.rs
+++ b/src/tools/clippy/clippy_lints/src/single_component_path_imports.rs
@@ -149,7 +149,7 @@ impl SingleComponentPathImports {
 
                 // keep track of `use some_module;` usages
                 if segments.len() == 1 {
-                    if let UseTreeKind::Simple(None, _, _) = use_tree.kind {
+                    if let UseTreeKind::Simple(None) = use_tree.kind {
                         let name = segments[0].ident.name;
                         if !macros.contains(&name) {
                             single_use_usages.push(SingleUse {
@@ -169,7 +169,7 @@ impl SingleComponentPathImports {
                         for tree in trees {
                             let segments = &tree.0.prefix.segments;
                             if segments.len() == 1 {
-                                if let UseTreeKind::Simple(None, _, _) = tree.0.kind {
+                                if let UseTreeKind::Simple(None) = tree.0.kind {
                                     let name = segments[0].ident.name;
                                     if !macros.contains(&name) {
                                         single_use_usages.push(SingleUse {
diff --git a/src/tools/clippy/clippy_lints/src/transmute/mod.rs b/src/tools/clippy/clippy_lints/src/transmute/mod.rs
index 424a6e9264e..83e651aba8e 100644
--- a/src/tools/clippy/clippy_lints/src/transmute/mod.rs
+++ b/src/tools/clippy/clippy_lints/src/transmute/mod.rs
@@ -16,10 +16,10 @@ mod utils;
 mod wrong_transmute;
 
 use clippy_utils::in_constant;
+use clippy_utils::msrvs::Msrv;
 use if_chain::if_chain;
 use rustc_hir::{Expr, ExprKind, QPath};
 use rustc_lint::{LateContext, LateLintPass};
-use rustc_semver::RustcVersion;
 use rustc_session::{declare_tool_lint, impl_lint_pass};
 use rustc_span::symbol::sym;
 
@@ -410,7 +410,7 @@ declare_clippy_lint! {
 }
 
 pub struct Transmute {
-    msrv: Option<RustcVersion>,
+    msrv: Msrv,
 }
 impl_lint_pass!(Transmute => [
     CROSSPOINTER_TRANSMUTE,
@@ -431,7 +431,7 @@ impl_lint_pass!(Transmute => [
 ]);
 impl Transmute {
     #[must_use]
-    pub fn new(msrv: Option<RustcVersion>) -> Self {
+    pub fn new(msrv: Msrv) -> Self {
         Self { msrv }
     }
 }
@@ -461,7 +461,7 @@ impl<'tcx> LateLintPass<'tcx> for Transmute {
                 let linted = wrong_transmute::check(cx, e, from_ty, to_ty)
                     | crosspointer_transmute::check(cx, e, from_ty, to_ty)
                     | transmuting_null::check(cx, e, arg, to_ty)
-                    | transmute_ptr_to_ref::check(cx, e, from_ty, to_ty, arg, path, self.msrv)
+                    | transmute_ptr_to_ref::check(cx, e, from_ty, to_ty, arg, path, &self.msrv)
                     | transmute_int_to_char::check(cx, e, from_ty, to_ty, arg, const_context)
                     | transmute_ref_to_ref::check(cx, e, from_ty, to_ty, arg, const_context)
                     | transmute_ptr_to_ptr::check(cx, e, from_ty, to_ty, arg)
diff --git a/src/tools/clippy/clippy_lints/src/transmute/transmute_ptr_to_ref.rs b/src/tools/clippy/clippy_lints/src/transmute/transmute_ptr_to_ref.rs
index 12d0b866e1c..3dde4eee671 100644
--- a/src/tools/clippy/clippy_lints/src/transmute/transmute_ptr_to_ref.rs
+++ b/src/tools/clippy/clippy_lints/src/transmute/transmute_ptr_to_ref.rs
@@ -1,12 +1,12 @@
 use super::TRANSMUTE_PTR_TO_REF;
 use clippy_utils::diagnostics::span_lint_and_then;
+use clippy_utils::msrvs::{self, Msrv};
 use clippy_utils::source::snippet_with_applicability;
-use clippy_utils::{meets_msrv, msrvs, sugg};
+use clippy_utils::sugg;
 use rustc_errors::Applicability;
 use rustc_hir::{self as hir, Expr, GenericArg, Mutability, Path, TyKind};
 use rustc_lint::LateContext;
 use rustc_middle::ty::{self, Ty, TypeVisitable};
-use rustc_semver::RustcVersion;
 
 /// Checks for `transmute_ptr_to_ref` lint.
 /// Returns `true` if it's triggered, otherwise returns `false`.
@@ -17,7 +17,7 @@ pub(super) fn check<'tcx>(
     to_ty: Ty<'tcx>,
     arg: &'tcx Expr<'_>,
     path: &'tcx Path<'_>,
-    msrv: Option<RustcVersion>,
+    msrv: &Msrv,
 ) -> bool {
     match (&from_ty.kind(), &to_ty.kind()) {
         (ty::RawPtr(from_ptr_ty), ty::Ref(_, to_ref_ty, mutbl)) => {
@@ -37,7 +37,7 @@ pub(super) fn check<'tcx>(
 
                     let sugg = if let Some(ty) = get_explicit_type(path) {
                         let ty_snip = snippet_with_applicability(cx, ty.span, "..", &mut app);
-                        if meets_msrv(msrv, msrvs::POINTER_CAST) {
+                        if msrv.meets(msrvs::POINTER_CAST) {
                             format!("{deref}{}.cast::<{ty_snip}>()", arg.maybe_par())
                         } else if from_ptr_ty.has_erased_regions() {
                             sugg::make_unop(deref, arg.as_ty(format!("{cast} () as {cast} {ty_snip}"))).to_string()
@@ -46,7 +46,7 @@ pub(super) fn check<'tcx>(
                         }
                     } else if from_ptr_ty.ty == *to_ref_ty {
                         if from_ptr_ty.has_erased_regions() {
-                            if meets_msrv(msrv, msrvs::POINTER_CAST) {
+                            if msrv.meets(msrvs::POINTER_CAST) {
                                 format!("{deref}{}.cast::<{to_ref_ty}>()", arg.maybe_par())
                             } else {
                                 sugg::make_unop(deref, arg.as_ty(format!("{cast} () as {cast} {to_ref_ty}")))
diff --git a/src/tools/clippy/clippy_lints/src/undocumented_unsafe_blocks.rs b/src/tools/clippy/clippy_lints/src/undocumented_unsafe_blocks.rs
index e8f15a44473..2e1b6d8d4ea 100644
--- a/src/tools/clippy/clippy_lints/src/undocumented_unsafe_blocks.rs
+++ b/src/tools/clippy/clippy_lints/src/undocumented_unsafe_blocks.rs
@@ -1,6 +1,10 @@
+use std::ops::ControlFlow;
+
 use clippy_utils::diagnostics::span_lint_and_help;
 use clippy_utils::source::walk_span_to_context;
+use clippy_utils::visitors::{for_each_expr_with_closures, Descend};
 use clippy_utils::{get_parent_node, is_lint_allowed};
+use hir::HirId;
 use rustc_data_structures::sync::Lrc;
 use rustc_hir as hir;
 use rustc_hir::{Block, BlockCheckMode, ItemKind, Node, UnsafeSource};
@@ -59,11 +63,39 @@ declare_clippy_lint! {
     restriction,
     "creating an unsafe block without explaining why it is safe"
 }
+declare_clippy_lint! {
+    /// ### What it does
+    /// Checks for `// SAFETY: ` comments on safe code.
+    ///
+    /// ### Why is this bad?
+    /// Safe code has no safety requirements, so there is no need to
+    /// describe safety invariants.
+    ///
+    /// ### Example
+    /// ```rust
+    /// use std::ptr::NonNull;
+    /// let a = &mut 42;
+    ///
+    /// // SAFETY: references are guaranteed to be non-null.
+    /// let ptr = NonNull::new(a).unwrap();
+    /// ```
+    /// Use instead:
+    /// ```rust
+    /// use std::ptr::NonNull;
+    /// let a = &mut 42;
+    ///
+    /// let ptr = NonNull::new(a).unwrap();
+    /// ```
+    #[clippy::version = "1.67.0"]
+    pub UNNECESSARY_SAFETY_COMMENT,
+    restriction,
+    "annotating safe code with a safety comment"
+}
 
-declare_lint_pass!(UndocumentedUnsafeBlocks => [UNDOCUMENTED_UNSAFE_BLOCKS]);
+declare_lint_pass!(UndocumentedUnsafeBlocks => [UNDOCUMENTED_UNSAFE_BLOCKS, UNNECESSARY_SAFETY_COMMENT]);
 
-impl LateLintPass<'_> for UndocumentedUnsafeBlocks {
-    fn check_block(&mut self, cx: &LateContext<'_>, block: &'_ Block<'_>) {
+impl<'tcx> LateLintPass<'tcx> for UndocumentedUnsafeBlocks {
+    fn check_block(&mut self, cx: &LateContext<'tcx>, block: &'tcx Block<'tcx>) {
         if block.rules == BlockCheckMode::UnsafeBlock(UnsafeSource::UserProvided)
             && !in_external_macro(cx.tcx.sess, block.span)
             && !is_lint_allowed(cx, UNDOCUMENTED_UNSAFE_BLOCKS, block.hir_id)
@@ -87,35 +119,175 @@ impl LateLintPass<'_> for UndocumentedUnsafeBlocks {
                 "consider adding a safety comment on the preceding line",
             );
         }
+
+        if let Some(tail) = block.expr
+            && !is_lint_allowed(cx, UNNECESSARY_SAFETY_COMMENT, tail.hir_id)
+            && !in_external_macro(cx.tcx.sess, tail.span)
+            && let HasSafetyComment::Yes(pos) = stmt_has_safety_comment(cx, tail.span, tail.hir_id)
+            && let Some(help_span) = expr_has_unnecessary_safety_comment(cx, tail, pos)
+        {
+            span_lint_and_help(
+                cx,
+                UNNECESSARY_SAFETY_COMMENT,
+                tail.span,
+                "expression has unnecessary safety comment",
+                Some(help_span),
+                "consider removing the safety comment",
+            );
+        }
     }
 
-    fn check_item(&mut self, cx: &LateContext<'_>, item: &hir::Item<'_>) {
-        if let hir::ItemKind::Impl(imple) = item.kind
-            && imple.unsafety == hir::Unsafety::Unsafe
-            && !in_external_macro(cx.tcx.sess, item.span)
-            && !is_lint_allowed(cx, UNDOCUMENTED_UNSAFE_BLOCKS, item.hir_id())
-            && !is_unsafe_from_proc_macro(cx, item.span)
-            && !item_has_safety_comment(cx, item)
+    fn check_stmt(&mut self, cx: &LateContext<'tcx>, stmt: &hir::Stmt<'tcx>) {
+        let (
+            hir::StmtKind::Local(&hir::Local { init: Some(expr), .. })
+            | hir::StmtKind::Expr(expr)
+            | hir::StmtKind::Semi(expr)
+        ) = stmt.kind else { return };
+        if !is_lint_allowed(cx, UNNECESSARY_SAFETY_COMMENT, stmt.hir_id)
+            && !in_external_macro(cx.tcx.sess, stmt.span)
+            && let HasSafetyComment::Yes(pos) = stmt_has_safety_comment(cx, stmt.span, stmt.hir_id)
+            && let Some(help_span) = expr_has_unnecessary_safety_comment(cx, expr, pos)
         {
+            span_lint_and_help(
+                cx,
+                UNNECESSARY_SAFETY_COMMENT,
+                stmt.span,
+                "statement has unnecessary safety comment",
+                Some(help_span),
+                "consider removing the safety comment",
+            );
+        }
+    }
+
+    fn check_item(&mut self, cx: &LateContext<'_>, item: &hir::Item<'_>) {
+        if in_external_macro(cx.tcx.sess, item.span) {
+            return;
+        }
+
+        let mk_spans = |pos: BytePos| {
             let source_map = cx.tcx.sess.source_map();
+            let span = Span::new(pos, pos, SyntaxContext::root(), None);
+            let help_span = source_map.span_extend_to_next_char(span, '\n', true);
             let span = if source_map.is_multiline(item.span) {
                 source_map.span_until_char(item.span, '\n')
             } else {
                 item.span
             };
+            (span, help_span)
+        };
 
-            span_lint_and_help(
-                cx,
-                UNDOCUMENTED_UNSAFE_BLOCKS,
-                span,
-                "unsafe impl missing a safety comment",
-                None,
-                "consider adding a safety comment on the preceding line",
-            );
+        let item_has_safety_comment = item_has_safety_comment(cx, item);
+        match (&item.kind, item_has_safety_comment) {
+            // lint unsafe impl without safety comment
+            (hir::ItemKind::Impl(impl_), HasSafetyComment::No) if impl_.unsafety == hir::Unsafety::Unsafe => {
+                if !is_lint_allowed(cx, UNDOCUMENTED_UNSAFE_BLOCKS, item.hir_id())
+                    && !is_unsafe_from_proc_macro(cx, item.span)
+                {
+                    let source_map = cx.tcx.sess.source_map();
+                    let span = if source_map.is_multiline(item.span) {
+                        source_map.span_until_char(item.span, '\n')
+                    } else {
+                        item.span
+                    };
+
+                    span_lint_and_help(
+                        cx,
+                        UNDOCUMENTED_UNSAFE_BLOCKS,
+                        span,
+                        "unsafe impl missing a safety comment",
+                        None,
+                        "consider adding a safety comment on the preceding line",
+                    );
+                }
+            },
+            // lint safe impl with unnecessary safety comment
+            (hir::ItemKind::Impl(impl_), HasSafetyComment::Yes(pos)) if impl_.unsafety == hir::Unsafety::Normal => {
+                if !is_lint_allowed(cx, UNNECESSARY_SAFETY_COMMENT, item.hir_id()) {
+                    let (span, help_span) = mk_spans(pos);
+
+                    span_lint_and_help(
+                        cx,
+                        UNNECESSARY_SAFETY_COMMENT,
+                        span,
+                        "impl has unnecessary safety comment",
+                        Some(help_span),
+                        "consider removing the safety comment",
+                    );
+                }
+            },
+            (hir::ItemKind::Impl(_), _) => {},
+            // const and static items only need a safety comment if their body is an unsafe block, lint otherwise
+            (&hir::ItemKind::Const(.., body) | &hir::ItemKind::Static(.., body), HasSafetyComment::Yes(pos)) => {
+                if !is_lint_allowed(cx, UNNECESSARY_SAFETY_COMMENT, body.hir_id) {
+                    let body = cx.tcx.hir().body(body);
+                    if !matches!(
+                        body.value.kind, hir::ExprKind::Block(block, _)
+                        if block.rules == BlockCheckMode::UnsafeBlock(UnsafeSource::UserProvided)
+                    ) {
+                        let (span, help_span) = mk_spans(pos);
+
+                        span_lint_and_help(
+                            cx,
+                            UNNECESSARY_SAFETY_COMMENT,
+                            span,
+                            &format!("{} has unnecessary safety comment", item.kind.descr()),
+                            Some(help_span),
+                            "consider removing the safety comment",
+                        );
+                    }
+                }
+            },
+            // Aside from unsafe impls and consts/statics with an unsafe block, items in general
+            // do not have safety invariants that need to be documented, so lint those.
+            (_, HasSafetyComment::Yes(pos)) => {
+                if !is_lint_allowed(cx, UNNECESSARY_SAFETY_COMMENT, item.hir_id()) {
+                    let (span, help_span) = mk_spans(pos);
+
+                    span_lint_and_help(
+                        cx,
+                        UNNECESSARY_SAFETY_COMMENT,
+                        span,
+                        &format!("{} has unnecessary safety comment", item.kind.descr()),
+                        Some(help_span),
+                        "consider removing the safety comment",
+                    );
+                }
+            },
+            _ => (),
         }
     }
 }
 
+fn expr_has_unnecessary_safety_comment<'tcx>(
+    cx: &LateContext<'tcx>,
+    expr: &'tcx hir::Expr<'tcx>,
+    comment_pos: BytePos,
+) -> Option<Span> {
+    // this should roughly be the reverse of `block_parents_have_safety_comment`
+    if for_each_expr_with_closures(cx, expr, |expr| match expr.kind {
+        hir::ExprKind::Block(
+            Block {
+                rules: BlockCheckMode::UnsafeBlock(UnsafeSource::UserProvided),
+                ..
+            },
+            _,
+        ) => ControlFlow::Break(()),
+        // statements will be handled by check_stmt itself again
+        hir::ExprKind::Block(..) => ControlFlow::Continue(Descend::No),
+        _ => ControlFlow::Continue(Descend::Yes),
+    })
+    .is_some()
+    {
+        return None;
+    }
+
+    let source_map = cx.tcx.sess.source_map();
+    let span = Span::new(comment_pos, comment_pos, SyntaxContext::root(), None);
+    let help_span = source_map.span_extend_to_next_char(span, '\n', true);
+
+    Some(help_span)
+}
+
 fn is_unsafe_from_proc_macro(cx: &LateContext<'_>, span: Span) -> bool {
     let source_map = cx.sess().source_map();
     let file_pos = source_map.lookup_byte_offset(span.lo());
@@ -170,85 +342,134 @@ fn block_has_safety_comment(cx: &LateContext<'_>, span: Span) -> bool {
     // won't work. This is to avoid dealing with where such a comment should be place relative to
     // attributes and doc comments.
 
-    span_from_macro_expansion_has_safety_comment(cx, span) || span_in_body_has_safety_comment(cx, span)
+    matches!(
+        span_from_macro_expansion_has_safety_comment(cx, span),
+        HasSafetyComment::Yes(_)
+    ) || span_in_body_has_safety_comment(cx, span)
+}
+
+enum HasSafetyComment {
+    Yes(BytePos),
+    No,
+    Maybe,
 }
 
 /// Checks if the lines immediately preceding the item contain a safety comment.
 #[allow(clippy::collapsible_match)]
-fn item_has_safety_comment(cx: &LateContext<'_>, item: &hir::Item<'_>) -> bool {
-    if span_from_macro_expansion_has_safety_comment(cx, item.span) {
-        return true;
+fn item_has_safety_comment(cx: &LateContext<'_>, item: &hir::Item<'_>) -> HasSafetyComment {
+    match span_from_macro_expansion_has_safety_comment(cx, item.span) {
+        HasSafetyComment::Maybe => (),
+        has_safety_comment => return has_safety_comment,
     }
 
-    if item.span.ctxt() == SyntaxContext::root() {
-        if let Some(parent_node) = get_parent_node(cx.tcx, item.hir_id()) {
-            let comment_start = match parent_node {
-                Node::Crate(parent_mod) => {
-                    comment_start_before_impl_in_mod(cx, parent_mod, parent_mod.spans.inner_span, item)
-                },
-                Node::Item(parent_item) => {
-                    if let ItemKind::Mod(parent_mod) = &parent_item.kind {
-                        comment_start_before_impl_in_mod(cx, parent_mod, parent_item.span, item)
-                    } else {
-                        // Doesn't support impls in this position. Pretend a comment was found.
-                        return true;
-                    }
-                },
-                Node::Stmt(stmt) => {
-                    if let Some(stmt_parent) = get_parent_node(cx.tcx, stmt.hir_id) {
-                        match stmt_parent {
-                            Node::Block(block) => walk_span_to_context(block.span, SyntaxContext::root()).map(Span::lo),
-                            _ => {
-                                // Doesn't support impls in this position. Pretend a comment was found.
-                                return true;
-                            },
-                        }
-                    } else {
-                        // Problem getting the parent node. Pretend a comment was found.
-                        return true;
-                    }
-                },
-                _ => {
+    if item.span.ctxt() != SyntaxContext::root() {
+        return HasSafetyComment::No;
+    }
+    if let Some(parent_node) = get_parent_node(cx.tcx, item.hir_id()) {
+        let comment_start = match parent_node {
+            Node::Crate(parent_mod) => {
+                comment_start_before_item_in_mod(cx, parent_mod, parent_mod.spans.inner_span, item)
+            },
+            Node::Item(parent_item) => {
+                if let ItemKind::Mod(parent_mod) = &parent_item.kind {
+                    comment_start_before_item_in_mod(cx, parent_mod, parent_item.span, item)
+                } else {
                     // Doesn't support impls in this position. Pretend a comment was found.
-                    return true;
-                },
-            };
+                    return HasSafetyComment::Maybe;
+                }
+            },
+            Node::Stmt(stmt) => {
+                if let Some(Node::Block(block)) = get_parent_node(cx.tcx, stmt.hir_id) {
+                    walk_span_to_context(block.span, SyntaxContext::root()).map(Span::lo)
+                } else {
+                    // Problem getting the parent node. Pretend a comment was found.
+                    return HasSafetyComment::Maybe;
+                }
+            },
+            _ => {
+                // Doesn't support impls in this position. Pretend a comment was found.
+                return HasSafetyComment::Maybe;
+            },
+        };
 
-            let source_map = cx.sess().source_map();
-            if let Some(comment_start) = comment_start
-                && let Ok(unsafe_line) = source_map.lookup_line(item.span.lo())
-                && let Ok(comment_start_line) = source_map.lookup_line(comment_start)
-                && Lrc::ptr_eq(&unsafe_line.sf, &comment_start_line.sf)
-                && let Some(src) = unsafe_line.sf.src.as_deref()
-            {
-                unsafe_line.sf.lines(|lines| {
-                    comment_start_line.line < unsafe_line.line && text_has_safety_comment(
+        let source_map = cx.sess().source_map();
+        if let Some(comment_start) = comment_start
+            && let Ok(unsafe_line) = source_map.lookup_line(item.span.lo())
+            && let Ok(comment_start_line) = source_map.lookup_line(comment_start)
+            && Lrc::ptr_eq(&unsafe_line.sf, &comment_start_line.sf)
+            && let Some(src) = unsafe_line.sf.src.as_deref()
+        {
+            return unsafe_line.sf.lines(|lines| {
+                if comment_start_line.line >= unsafe_line.line {
+                    HasSafetyComment::No
+                } else {
+                    match text_has_safety_comment(
                         src,
                         &lines[comment_start_line.line + 1..=unsafe_line.line],
                         unsafe_line.sf.start_pos.to_usize(),
-                    )
-                })
-            } else {
-                // Problem getting source text. Pretend a comment was found.
-                true
-            }
-        } else {
-            // No parent node. Pretend a comment was found.
-            true
+                    ) {
+                        Some(b) => HasSafetyComment::Yes(b),
+                        None => HasSafetyComment::No,
+                    }
+                }
+            });
+        }
+    }
+    HasSafetyComment::Maybe
+}
+
+/// Checks if the lines immediately preceding the item contain a safety comment.
+#[allow(clippy::collapsible_match)]
+fn stmt_has_safety_comment(cx: &LateContext<'_>, span: Span, hir_id: HirId) -> HasSafetyComment {
+    match span_from_macro_expansion_has_safety_comment(cx, span) {
+        HasSafetyComment::Maybe => (),
+        has_safety_comment => return has_safety_comment,
+    }
+
+    if span.ctxt() != SyntaxContext::root() {
+        return HasSafetyComment::No;
+    }
+
+    if let Some(parent_node) = get_parent_node(cx.tcx, hir_id) {
+        let comment_start = match parent_node {
+            Node::Block(block) => walk_span_to_context(block.span, SyntaxContext::root()).map(Span::lo),
+            _ => return HasSafetyComment::Maybe,
+        };
+
+        let source_map = cx.sess().source_map();
+        if let Some(comment_start) = comment_start
+            && let Ok(unsafe_line) = source_map.lookup_line(span.lo())
+            && let Ok(comment_start_line) = source_map.lookup_line(comment_start)
+            && Lrc::ptr_eq(&unsafe_line.sf, &comment_start_line.sf)
+            && let Some(src) = unsafe_line.sf.src.as_deref()
+        {
+            return unsafe_line.sf.lines(|lines| {
+                if comment_start_line.line >= unsafe_line.line {
+                    HasSafetyComment::No
+                } else {
+                    match text_has_safety_comment(
+                        src,
+                        &lines[comment_start_line.line + 1..=unsafe_line.line],
+                        unsafe_line.sf.start_pos.to_usize(),
+                    ) {
+                        Some(b) => HasSafetyComment::Yes(b),
+                        None => HasSafetyComment::No,
+                    }
+                }
+            });
         }
-    } else {
-        false
     }
+    HasSafetyComment::Maybe
 }
 
-fn comment_start_before_impl_in_mod(
+fn comment_start_before_item_in_mod(
     cx: &LateContext<'_>,
     parent_mod: &hir::Mod<'_>,
     parent_mod_span: Span,
-    imple: &hir::Item<'_>,
+    item: &hir::Item<'_>,
 ) -> Option<BytePos> {
     parent_mod.item_ids.iter().enumerate().find_map(|(idx, item_id)| {
-        if *item_id == imple.item_id() {
+        if *item_id == item.item_id() {
             if idx == 0 {
                 // mod A { /* comment */ unsafe impl T {} ... }
                 // ^------------------------------------------^ returns the start of this span
@@ -270,11 +491,11 @@ fn comment_start_before_impl_in_mod(
     })
 }
 
-fn span_from_macro_expansion_has_safety_comment(cx: &LateContext<'_>, span: Span) -> bool {
+fn span_from_macro_expansion_has_safety_comment(cx: &LateContext<'_>, span: Span) -> HasSafetyComment {
     let source_map = cx.sess().source_map();
     let ctxt = span.ctxt();
     if ctxt == SyntaxContext::root() {
-        false
+        HasSafetyComment::Maybe
     } else {
         // From a macro expansion. Get the text from the start of the macro declaration to start of the
         // unsafe block.
@@ -286,15 +507,22 @@ fn span_from_macro_expansion_has_safety_comment(cx: &LateContext<'_>, span: Span
             && let Some(src) = unsafe_line.sf.src.as_deref()
         {
             unsafe_line.sf.lines(|lines| {
-                macro_line.line < unsafe_line.line && text_has_safety_comment(
-                    src,
-                    &lines[macro_line.line + 1..=unsafe_line.line],
-                    unsafe_line.sf.start_pos.to_usize(),
-                )
+                if macro_line.line < unsafe_line.line {
+                    match text_has_safety_comment(
+                        src,
+                        &lines[macro_line.line + 1..=unsafe_line.line],
+                        unsafe_line.sf.start_pos.to_usize(),
+                    ) {
+                        Some(b) => HasSafetyComment::Yes(b),
+                        None => HasSafetyComment::No,
+                    }
+                } else {
+                    HasSafetyComment::No
+                }
             })
         } else {
             // Problem getting source text. Pretend a comment was found.
-            true
+            HasSafetyComment::Maybe
         }
     }
 }
@@ -333,7 +561,7 @@ fn span_in_body_has_safety_comment(cx: &LateContext<'_>, span: Span) -> bool {
                     src,
                     &lines[body_line.line + 1..=unsafe_line.line],
                     unsafe_line.sf.start_pos.to_usize(),
-                )
+                ).is_some()
             })
         } else {
             // Problem getting source text. Pretend a comment was found.
@@ -345,30 +573,34 @@ fn span_in_body_has_safety_comment(cx: &LateContext<'_>, span: Span) -> bool {
 }
 
 /// Checks if the given text has a safety comment for the immediately proceeding line.
-fn text_has_safety_comment(src: &str, line_starts: &[BytePos], offset: usize) -> bool {
+fn text_has_safety_comment(src: &str, line_starts: &[BytePos], offset: usize) -> Option<BytePos> {
     let mut lines = line_starts
         .array_windows::<2>()
         .rev()
         .map_while(|[start, end]| {
             let start = start.to_usize() - offset;
             let end = end.to_usize() - offset;
-            src.get(start..end).map(|text| (start, text.trim_start()))
+            let text = src.get(start..end)?;
+            let trimmed = text.trim_start();
+            Some((start + (text.len() - trimmed.len()), trimmed))
         })
         .filter(|(_, text)| !text.is_empty());
 
     let Some((line_start, line)) = lines.next() else {
-        return false;
+        return None;
     };
     // Check for a sequence of line comments.
     if line.starts_with("//") {
-        let mut line = line;
+        let (mut line, mut line_start) = (line, line_start);
         loop {
             if line.to_ascii_uppercase().contains("SAFETY:") {
-                return true;
+                return Some(BytePos(
+                    u32::try_from(line_start).unwrap() + u32::try_from(offset).unwrap(),
+                ));
             }
             match lines.next() {
-                Some((_, x)) if x.starts_with("//") => line = x,
-                _ => return false,
+                Some((s, x)) if x.starts_with("//") => (line, line_start) = (x, s),
+                _ => return None,
             }
         }
     }
@@ -377,16 +609,19 @@ fn text_has_safety_comment(src: &str, line_starts: &[BytePos], offset: usize) ->
     let (mut line_start, mut line) = (line_start, line);
     loop {
         if line.starts_with("/*") {
-            let src = src[line_start..line_starts.last().unwrap().to_usize() - offset].trim_start();
+            let src = &src[line_start..line_starts.last().unwrap().to_usize() - offset];
             let mut tokens = tokenize(src);
-            return src[..tokens.next().unwrap().len as usize]
+            return (src[..tokens.next().unwrap().len as usize]
                 .to_ascii_uppercase()
                 .contains("SAFETY:")
-                && tokens.all(|t| t.kind == TokenKind::Whitespace);
+                && tokens.all(|t| t.kind == TokenKind::Whitespace))
+            .then_some(BytePos(
+                u32::try_from(line_start).unwrap() + u32::try_from(offset).unwrap(),
+            ));
         }
         match lines.next() {
             Some(x) => (line_start, line) = x,
-            None => return false,
+            None => return None,
         }
     }
 }
diff --git a/src/tools/clippy/clippy_lints/src/unnecessary_self_imports.rs b/src/tools/clippy/clippy_lints/src/unnecessary_self_imports.rs
index bc0dd263d88..397633f533b 100644
--- a/src/tools/clippy/clippy_lints/src/unnecessary_self_imports.rs
+++ b/src/tools/clippy/clippy_lints/src/unnecessary_self_imports.rs
@@ -57,7 +57,7 @@ impl EarlyLintPass for UnnecessarySelfImports {
                             format!(
                                 "{}{};",
                                 last_segment.ident,
-                                if let UseTreeKind::Simple(Some(alias), ..) = self_tree.kind { format!(" as {alias}") } else { String::new() },
+                                if let UseTreeKind::Simple(Some(alias)) = self_tree.kind { format!(" as {alias}") } else { String::new() },
                             ),
                             Applicability::MaybeIncorrect,
                         );
diff --git a/src/tools/clippy/clippy_lints/src/unnested_or_patterns.rs b/src/tools/clippy/clippy_lints/src/unnested_or_patterns.rs
index bb6fb38e969..7355260ae4a 100644
--- a/src/tools/clippy/clippy_lints/src/unnested_or_patterns.rs
+++ b/src/tools/clippy/clippy_lints/src/unnested_or_patterns.rs
@@ -2,14 +2,14 @@
 
 use clippy_utils::ast_utils::{eq_field_pat, eq_id, eq_maybe_qself, eq_pat, eq_path};
 use clippy_utils::diagnostics::span_lint_and_then;
-use clippy_utils::{meets_msrv, msrvs, over};
+use clippy_utils::msrvs::{self, Msrv};
+use clippy_utils::over;
 use rustc_ast::mut_visit::*;
 use rustc_ast::ptr::P;
 use rustc_ast::{self as ast, Mutability, Pat, PatKind, PatKind::*, DUMMY_NODE_ID};
 use rustc_ast_pretty::pprust;
 use rustc_errors::Applicability;
 use rustc_lint::{EarlyContext, EarlyLintPass};
-use rustc_semver::RustcVersion;
 use rustc_session::{declare_tool_lint, impl_lint_pass};
 use rustc_span::DUMMY_SP;
 
@@ -45,14 +45,13 @@ declare_clippy_lint! {
     "unnested or-patterns, e.g., `Foo(Bar) | Foo(Baz) instead of `Foo(Bar | Baz)`"
 }
 
-#[derive(Clone, Copy)]
 pub struct UnnestedOrPatterns {
-    msrv: Option<RustcVersion>,
+    msrv: Msrv,
 }
 
 impl UnnestedOrPatterns {
     #[must_use]
-    pub fn new(msrv: Option<RustcVersion>) -> Self {
+    pub fn new(msrv: Msrv) -> Self {
         Self { msrv }
     }
 }
@@ -61,13 +60,13 @@ impl_lint_pass!(UnnestedOrPatterns => [UNNESTED_OR_PATTERNS]);
 
 impl EarlyLintPass for UnnestedOrPatterns {
     fn check_arm(&mut self, cx: &EarlyContext<'_>, a: &ast::Arm) {
-        if meets_msrv(self.msrv, msrvs::OR_PATTERNS) {
+        if self.msrv.meets(msrvs::OR_PATTERNS) {
             lint_unnested_or_patterns(cx, &a.pat);
         }
     }
 
     fn check_expr(&mut self, cx: &EarlyContext<'_>, e: &ast::Expr) {
-        if meets_msrv(self.msrv, msrvs::OR_PATTERNS) {
+        if self.msrv.meets(msrvs::OR_PATTERNS) {
             if let ast::ExprKind::Let(pat, _, _) = &e.kind {
                 lint_unnested_or_patterns(cx, pat);
             }
@@ -75,13 +74,13 @@ impl EarlyLintPass for UnnestedOrPatterns {
     }
 
     fn check_param(&mut self, cx: &EarlyContext<'_>, p: &ast::Param) {
-        if meets_msrv(self.msrv, msrvs::OR_PATTERNS) {
+        if self.msrv.meets(msrvs::OR_PATTERNS) {
             lint_unnested_or_patterns(cx, &p.pat);
         }
     }
 
     fn check_local(&mut self, cx: &EarlyContext<'_>, l: &ast::Local) {
-        if meets_msrv(self.msrv, msrvs::OR_PATTERNS) {
+        if self.msrv.meets(msrvs::OR_PATTERNS) {
             lint_unnested_or_patterns(cx, &l.pat);
         }
     }
diff --git a/src/tools/clippy/clippy_lints/src/unsafe_removed_from_name.rs b/src/tools/clippy/clippy_lints/src/unsafe_removed_from_name.rs
index 95258652768..7ee785804f0 100644
--- a/src/tools/clippy/clippy_lints/src/unsafe_removed_from_name.rs
+++ b/src/tools/clippy/clippy_lints/src/unsafe_removed_from_name.rs
@@ -39,7 +39,7 @@ impl EarlyLintPass for UnsafeNameRemoval {
 
 fn check_use_tree(use_tree: &UseTree, cx: &EarlyContext<'_>, span: Span) {
     match use_tree.kind {
-        UseTreeKind::Simple(Some(new_name), ..) => {
+        UseTreeKind::Simple(Some(new_name)) => {
             let old_name = use_tree
                 .prefix
                 .segments
@@ -48,7 +48,7 @@ fn check_use_tree(use_tree: &UseTree, cx: &EarlyContext<'_>, span: Span) {
                 .ident;
             unsafe_to_safe_check(old_name, new_name, cx, span);
         },
-        UseTreeKind::Simple(None, ..) | UseTreeKind::Glob => {},
+        UseTreeKind::Simple(None) | UseTreeKind::Glob => {},
         UseTreeKind::Nested(ref nested_use_tree) => {
             for (use_tree, _) in nested_use_tree {
                 check_use_tree(use_tree, cx, span);
diff --git a/src/tools/clippy/clippy_lints/src/unused_rounding.rs b/src/tools/clippy/clippy_lints/src/unused_rounding.rs
index aac6719a8dc..097568cd1f7 100644
--- a/src/tools/clippy/clippy_lints/src/unused_rounding.rs
+++ b/src/tools/clippy/clippy_lints/src/unused_rounding.rs
@@ -1,4 +1,5 @@
 use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::source::snippet;
 use rustc_ast::ast::{Expr, ExprKind, MethodCall};
 use rustc_errors::Applicability;
 use rustc_lint::{EarlyContext, EarlyLintPass};
@@ -29,22 +30,16 @@ declare_clippy_lint! {
 }
 declare_lint_pass!(UnusedRounding => [UNUSED_ROUNDING]);
 
-fn is_useless_rounding(expr: &Expr) -> Option<(&str, String)> {
+fn is_useless_rounding<'a>(cx: &EarlyContext<'_>, expr: &'a Expr) -> Option<(&'a str, String)> {
     if let ExprKind::MethodCall(box MethodCall { seg:name_ident, receiver, .. }) = &expr.kind
         && let method_name = name_ident.ident.name.as_str()
         && (method_name == "ceil" || method_name == "round" || method_name == "floor")
         && let ExprKind::Lit(token_lit) = &receiver.kind
-        && token_lit.is_semantic_float() {
-            let mut f_str = token_lit.symbol.to_string();
-            let f = f_str.trim_end_matches('_').parse::<f64>().unwrap();
-            if let Some(suffix) = token_lit.suffix {
-                f_str.push_str(suffix.as_str());
-            }
-            if f.fract() == 0.0 {
-                Some((method_name, f_str))
-            } else {
-                None
-            }
+        && token_lit.is_semantic_float()
+        && let Ok(f) = token_lit.symbol.as_str().replace('_', "").parse::<f64>() {
+            (f.fract() == 0.0).then(||
+                (method_name, snippet(cx, receiver.span, "..").to_string())
+            )
         } else {
             None
         }
@@ -52,7 +47,7 @@ fn is_useless_rounding(expr: &Expr) -> Option<(&str, String)> {
 
 impl EarlyLintPass for UnusedRounding {
     fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &Expr) {
-        if let Some((method_name, float)) = is_useless_rounding(expr) {
+        if let Some((method_name, float)) = is_useless_rounding(cx, expr) {
             span_lint_and_sugg(
                 cx,
                 UNUSED_ROUNDING,
diff --git a/src/tools/clippy/clippy_lints/src/use_self.rs b/src/tools/clippy/clippy_lints/src/use_self.rs
index e2860db71a5..4c755d812a0 100644
--- a/src/tools/clippy/clippy_lints/src/use_self.rs
+++ b/src/tools/clippy/clippy_lints/src/use_self.rs
@@ -1,6 +1,7 @@
 use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::is_from_proc_macro;
+use clippy_utils::msrvs::{self, Msrv};
 use clippy_utils::ty::same_type_and_consts;
-use clippy_utils::{is_from_proc_macro, meets_msrv, msrvs};
 use if_chain::if_chain;
 use rustc_data_structures::fx::FxHashSet;
 use rustc_errors::Applicability;
@@ -14,7 +15,6 @@ use rustc_hir::{
 };
 use rustc_hir_analysis::hir_ty_to_ty;
 use rustc_lint::{LateContext, LateLintPass};
-use rustc_semver::RustcVersion;
 use rustc_session::{declare_tool_lint, impl_lint_pass};
 use rustc_span::Span;
 
@@ -57,13 +57,13 @@ declare_clippy_lint! {
 
 #[derive(Default)]
 pub struct UseSelf {
-    msrv: Option<RustcVersion>,
+    msrv: Msrv,
     stack: Vec<StackItem>,
 }
 
 impl UseSelf {
     #[must_use]
-    pub fn new(msrv: Option<RustcVersion>) -> Self {
+    pub fn new(msrv: Msrv) -> Self {
         Self {
             msrv,
             ..Self::default()
@@ -199,7 +199,7 @@ impl<'tcx> LateLintPass<'tcx> for UseSelf {
     fn check_ty(&mut self, cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>) {
         if_chain! {
             if !hir_ty.span.from_expansion();
-            if meets_msrv(self.msrv, msrvs::TYPE_ALIAS_ENUM_VARIANTS);
+            if self.msrv.meets(msrvs::TYPE_ALIAS_ENUM_VARIANTS);
             if let Some(&StackItem::Check {
                 impl_id,
                 in_body,
@@ -228,7 +228,7 @@ impl<'tcx> LateLintPass<'tcx> for UseSelf {
     fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) {
         if_chain! {
             if !expr.span.from_expansion();
-            if meets_msrv(self.msrv, msrvs::TYPE_ALIAS_ENUM_VARIANTS);
+            if self.msrv.meets(msrvs::TYPE_ALIAS_ENUM_VARIANTS);
             if let Some(&StackItem::Check { impl_id, .. }) = self.stack.last();
             if cx.typeck_results().expr_ty(expr) == cx.tcx.type_of(impl_id);
             then {} else { return; }
@@ -248,7 +248,7 @@ impl<'tcx> LateLintPass<'tcx> for UseSelf {
     fn check_pat(&mut self, cx: &LateContext<'_>, pat: &Pat<'_>) {
         if_chain! {
             if !pat.span.from_expansion();
-            if meets_msrv(self.msrv, msrvs::TYPE_ALIAS_ENUM_VARIANTS);
+            if self.msrv.meets(msrvs::TYPE_ALIAS_ENUM_VARIANTS);
             if let Some(&StackItem::Check { impl_id, .. }) = self.stack.last();
             // get the path from the pattern
             if let PatKind::Path(QPath::Resolved(_, path))
diff --git a/src/tools/clippy/clippy_lints/src/utils/conf.rs b/src/tools/clippy/clippy_lints/src/utils/conf.rs
index b37d4239477..b6dc8cd7ab1 100644
--- a/src/tools/clippy/clippy_lints/src/utils/conf.rs
+++ b/src/tools/clippy/clippy_lints/src/utils/conf.rs
@@ -402,6 +402,10 @@ define_Conf! {
     /// A list of paths to types that should be treated like `Arc`, i.e. ignored but
     /// for the generic parameters for determining interior mutability
     (ignore_interior_mutability: Vec<String> = Vec::from(["bytes::Bytes".into()])),
+    /// Lint: UNINLINED_FORMAT_ARGS.
+    ///
+    /// Whether to allow mixed uninlined format args, e.g. `format!("{} {}", a, foo.bar)`
+    (allow_mixed_uninlined_format_args: bool = true),
 }
 
 /// Search for the configuration file.
diff --git a/src/tools/clippy/clippy_lints/src/utils/internal_lints/lint_without_lint_pass.rs b/src/tools/clippy/clippy_lints/src/utils/internal_lints/lint_without_lint_pass.rs
index 1aebb8b3104..786d9608c85 100644
--- a/src/tools/clippy/clippy_lints/src/utils/internal_lints/lint_without_lint_pass.rs
+++ b/src/tools/clippy/clippy_lints/src/utils/internal_lints/lint_without_lint_pass.rs
@@ -330,7 +330,7 @@ struct LintCollector<'a, 'tcx> {
 impl<'a, 'tcx> Visitor<'tcx> for LintCollector<'a, 'tcx> {
     type NestedFilter = nested_filter::All;
 
-    fn visit_path(&mut self, path: &'tcx Path<'_>, _: HirId) {
+    fn visit_path(&mut self, path: &Path<'_>, _: HirId) {
         if path.segments.len() == 1 {
             self.output.insert(path.segments[0].ident.name);
         }
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 d06a616e4b3..857abe77e21 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
@@ -1019,7 +1019,7 @@ impl<'a, 'hir> intravisit::Visitor<'hir> for ApplicabilityResolver<'a, 'hir> {
         self.cx.tcx.hir()
     }
 
-    fn visit_path(&mut self, path: &'hir hir::Path<'hir>, _id: hir::HirId) {
+    fn visit_path(&mut self, path: &hir::Path<'hir>, _id: hir::HirId) {
         for (index, enum_value) in paths::APPLICABILITY_VALUES.iter().enumerate() {
             if match_path(path, enum_value) {
                 self.add_new_index(index);
diff --git a/src/tools/clippy/clippy_lints/src/utils/internal_lints/msrv_attr_impl.rs b/src/tools/clippy/clippy_lints/src/utils/internal_lints/msrv_attr_impl.rs
index 1e994e3f2b1..9876a8a765c 100644
--- a/src/tools/clippy/clippy_lints/src/utils/internal_lints/msrv_attr_impl.rs
+++ b/src/tools/clippy/clippy_lints/src/utils/internal_lints/msrv_attr_impl.rs
@@ -41,7 +41,7 @@ impl LateLintPass<'_> for MsrvAttrImpl {
                     .type_of(f.did)
                     .walk()
                     .filter(|t| matches!(t.unpack(), GenericArgKind::Type(_)))
-                    .any(|t| match_type(cx, t.expect_ty(), &paths::RUSTC_VERSION))
+                    .any(|t| match_type(cx, t.expect_ty(), &paths::MSRV))
             });
             if !items.iter().any(|item| item.ident.name == sym!(enter_lint_attrs));
             then {
diff --git a/src/tools/clippy/clippy_lints/src/wildcard_imports.rs b/src/tools/clippy/clippy_lints/src/wildcard_imports.rs
index be98344470b..e4d1ee195c4 100644
--- a/src/tools/clippy/clippy_lints/src/wildcard_imports.rs
+++ b/src/tools/clippy/clippy_lints/src/wildcard_imports.rs
@@ -176,7 +176,8 @@ impl LateLintPass<'_> for WildcardImports {
                     format!("{import_source_snippet}::{imports_string}")
                 };
 
-                let (lint, message) = if let Res::Def(DefKind::Enum, _) = use_path.res {
+                // Glob imports always have a single resolution.
+                let (lint, message) = if let Res::Def(DefKind::Enum, _) = use_path.res[0] {
                     (ENUM_GLOB_USE, "usage of wildcard import for enum variants")
                 } else {
                     (WILDCARD_IMPORTS, "usage of wildcard import")
diff --git a/src/tools/clippy/clippy_utils/src/ast_utils.rs b/src/tools/clippy/clippy_utils/src/ast_utils.rs
index 6bcf0bbd7eb..49e5f283db0 100644
--- a/src/tools/clippy/clippy_utils/src/ast_utils.rs
+++ b/src/tools/clippy/clippy_utils/src/ast_utils.rs
@@ -566,7 +566,7 @@ pub fn eq_use_tree_kind(l: &UseTreeKind, r: &UseTreeKind) -> bool {
     use UseTreeKind::*;
     match (l, r) {
         (Glob, Glob) => true,
-        (Simple(l, _, _), Simple(r, _, _)) => both(l, r, |l, r| eq_id(*l, *r)),
+        (Simple(l), Simple(r)) => both(l, r, |l, r| eq_id(*l, *r)),
         (Nested(l), Nested(r)) => over(l, r, |(l, _), (r, _)| eq_use_tree(l, r)),
         _ => false,
     }
diff --git a/src/tools/clippy/clippy_utils/src/attrs.rs b/src/tools/clippy/clippy_utils/src/attrs.rs
index cd8575c90e8..7987a233bdc 100644
--- a/src/tools/clippy/clippy_utils/src/attrs.rs
+++ b/src/tools/clippy/clippy_utils/src/attrs.rs
@@ -125,19 +125,19 @@ fn parse_attrs<F: FnMut(u64)>(sess: &Session, attrs: &[ast::Attribute], name: &'
     }
 }
 
-pub fn get_unique_inner_attr(sess: &Session, attrs: &[ast::Attribute], name: &'static str) -> Option<ast::Attribute> {
-    let mut unique_attr = None;
+pub fn get_unique_attr<'a>(
+    sess: &'a Session,
+    attrs: &'a [ast::Attribute],
+    name: &'static str,
+) -> Option<&'a ast::Attribute> {
+    let mut unique_attr: Option<&ast::Attribute> = None;
     for attr in get_attr(sess, attrs, name) {
-        match attr.style {
-            ast::AttrStyle::Inner if unique_attr.is_none() => unique_attr = Some(attr.clone()),
-            ast::AttrStyle::Inner => {
-                sess.struct_span_err(attr.span, &format!("`{name}` is defined multiple times"))
-                    .span_note(unique_attr.as_ref().unwrap().span, "first definition found here")
-                    .emit();
-            },
-            ast::AttrStyle::Outer => {
-                sess.span_err(attr.span, format!("`{name}` cannot be an outer attribute"));
-            },
+        if let Some(duplicate) = unique_attr {
+            sess.struct_span_err(attr.span, &format!("`{name}` is defined multiple times"))
+                .span_note(duplicate.span, "first definition found here")
+                .emit();
+        } else {
+            unique_attr = Some(attr);
         }
     }
     unique_attr
diff --git a/src/tools/clippy/clippy_utils/src/eager_or_lazy.rs b/src/tools/clippy/clippy_utils/src/eager_or_lazy.rs
index f74f7dadfa9..96711936968 100644
--- a/src/tools/clippy/clippy_utils/src/eager_or_lazy.rs
+++ b/src/tools/clippy/clippy_utils/src/eager_or_lazy.rs
@@ -91,6 +91,16 @@ fn fn_eagerness(cx: &LateContext<'_>, fn_id: DefId, name: Symbol, have_one_arg:
     }
 }
 
+fn res_has_significant_drop(res: Res, cx: &LateContext<'_>, e: &Expr<'_>) -> bool {
+    if let Res::Def(DefKind::Ctor(..) | DefKind::Variant, _) | Res::SelfCtor(_) = res {
+        cx.typeck_results()
+            .expr_ty(e)
+            .has_significant_drop(cx.tcx, cx.param_env)
+    } else {
+        false
+    }
+}
+
 #[expect(clippy::too_many_lines)]
 fn expr_eagerness<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> EagernessSuggestion {
     struct V<'cx, 'tcx> {
@@ -113,13 +123,8 @@ fn expr_eagerness<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> EagernessS
                     },
                     args,
                 ) => match self.cx.qpath_res(path, hir_id) {
-                    Res::Def(DefKind::Ctor(..) | DefKind::Variant, _) | Res::SelfCtor(_) => {
-                        if self
-                            .cx
-                            .typeck_results()
-                            .expr_ty(e)
-                            .has_significant_drop(self.cx.tcx, self.cx.param_env)
-                        {
+                    res @ (Res::Def(DefKind::Ctor(..) | DefKind::Variant, _) | Res::SelfCtor(_)) => {
+                        if res_has_significant_drop(res, self.cx, e) {
                             self.eagerness = ForceNoChange;
                             return;
                         }
@@ -147,6 +152,12 @@ fn expr_eagerness<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> EagernessS
                     self.eagerness |= NoChange;
                     return;
                 },
+                ExprKind::Path(ref path) => {
+                    if res_has_significant_drop(self.cx.qpath_res(path, e.hir_id), self.cx, e) {
+                        self.eagerness = ForceNoChange;
+                        return;
+                    }
+                },
                 ExprKind::MethodCall(name, ..) => {
                     self.eagerness |= self
                         .cx
@@ -206,7 +217,6 @@ fn expr_eagerness<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> EagernessS
                 | ExprKind::Match(..)
                 | ExprKind::Closure { .. }
                 | ExprKind::Field(..)
-                | ExprKind::Path(_)
                 | ExprKind::AddrOf(..)
                 | ExprKind::Struct(..)
                 | ExprKind::Repeat(..)
diff --git a/src/tools/clippy/clippy_utils/src/lib.rs b/src/tools/clippy/clippy_utils/src/lib.rs
index 9e2682925a2..90192f46cbf 100644
--- a/src/tools/clippy/clippy_utils/src/lib.rs
+++ b/src/tools/clippy/clippy_utils/src/lib.rs
@@ -105,8 +105,6 @@ use rustc_middle::ty::{
     layout::IntegerExt, BorrowKind, ClosureKind, DefIdTree, Ty, TyCtxt, TypeAndMut, TypeVisitable, UpvarCapture,
 };
 use rustc_middle::ty::{FloatTy, IntTy, UintTy};
-use rustc_semver::RustcVersion;
-use rustc_session::Session;
 use rustc_span::hygiene::{ExpnKind, MacroKind};
 use rustc_span::source_map::SourceMap;
 use rustc_span::sym;
@@ -118,36 +116,17 @@ use crate::consts::{constant, Constant};
 use crate::ty::{can_partially_move_ty, expr_sig, is_copy, is_recursively_primitive_type, ty_is_fn_once_param};
 use crate::visitors::for_each_expr;
 
-pub fn parse_msrv(msrv: &str, sess: Option<&Session>, span: Option<Span>) -> Option<RustcVersion> {
-    if let Ok(version) = RustcVersion::parse(msrv) {
-        return Some(version);
-    } else if let Some(sess) = sess {
-        if let Some(span) = span {
-            sess.span_err(span, format!("`{msrv}` is not a valid Rust version"));
-        }
-    }
-    None
-}
-
-pub fn meets_msrv(msrv: Option<RustcVersion>, lint_msrv: RustcVersion) -> bool {
-    msrv.map_or(true, |msrv| msrv.meets(lint_msrv))
-}
-
 #[macro_export]
 macro_rules! extract_msrv_attr {
     ($context:ident) => {
         fn enter_lint_attrs(&mut self, cx: &rustc_lint::$context<'_>, attrs: &[rustc_ast::ast::Attribute]) {
             let sess = rustc_lint::LintContext::sess(cx);
-            match $crate::get_unique_inner_attr(sess, attrs, "msrv") {
-                Some(msrv_attr) => {
-                    if let Some(msrv) = msrv_attr.value_str() {
-                        self.msrv = $crate::parse_msrv(&msrv.to_string(), Some(sess), Some(msrv_attr.span));
-                    } else {
-                        sess.span_err(msrv_attr.span, "bad clippy attribute");
-                    }
-                },
-                _ => (),
-            }
+            self.msrv.enter_lint_attrs(sess, attrs);
+        }
+
+        fn exit_lint_attrs(&mut self, cx: &rustc_lint::$context<'_>, attrs: &[rustc_ast::ast::Attribute]) {
+            let sess = rustc_lint::LintContext::sess(cx);
+            self.msrv.exit_lint_attrs(sess, attrs);
         }
     };
 }
diff --git a/src/tools/clippy/clippy_utils/src/msrvs.rs b/src/tools/clippy/clippy_utils/src/msrvs.rs
index 79b19e6fb3e..12a512f78a6 100644
--- a/src/tools/clippy/clippy_utils/src/msrvs.rs
+++ b/src/tools/clippy/clippy_utils/src/msrvs.rs
@@ -1,4 +1,11 @@
+use std::sync::OnceLock;
+
+use rustc_ast::Attribute;
 use rustc_semver::RustcVersion;
+use rustc_session::Session;
+use rustc_span::Span;
+
+use crate::attrs::get_unique_attr;
 
 macro_rules! msrv_aliases {
     ($($major:literal,$minor:literal,$patch:literal {
@@ -40,3 +47,97 @@ msrv_aliases! {
     1,16,0 { STR_REPEAT }
     1,55,0 { SEEK_REWIND }
 }
+
+fn parse_msrv(msrv: &str, sess: Option<&Session>, span: Option<Span>) -> Option<RustcVersion> {
+    if let Ok(version) = RustcVersion::parse(msrv) {
+        return Some(version);
+    } else if let Some(sess) = sess {
+        if let Some(span) = span {
+            sess.span_err(span, format!("`{msrv}` is not a valid Rust version"));
+        }
+    }
+    None
+}
+
+/// Tracks the current MSRV from `clippy.toml`, `Cargo.toml` or set via `#[clippy::msrv]`
+#[derive(Debug, Clone, Default)]
+pub struct Msrv {
+    stack: Vec<RustcVersion>,
+}
+
+impl Msrv {
+    fn new(initial: Option<RustcVersion>) -> Self {
+        Self {
+            stack: Vec::from_iter(initial),
+        }
+    }
+
+    fn read_inner(conf_msrv: &Option<String>, sess: &Session) -> Self {
+        let cargo_msrv = std::env::var("CARGO_PKG_RUST_VERSION")
+            .ok()
+            .and_then(|v| parse_msrv(&v, None, None));
+        let clippy_msrv = conf_msrv.as_ref().and_then(|s| {
+            parse_msrv(s, None, None).or_else(|| {
+                sess.err(format!(
+                    "error reading Clippy's configuration file. `{s}` is not a valid Rust version"
+                ));
+                None
+            })
+        });
+
+        // if both files have an msrv, let's compare them and emit a warning if they differ
+        if let Some(cargo_msrv) = cargo_msrv
+            && let Some(clippy_msrv) = clippy_msrv
+            && clippy_msrv != cargo_msrv
+        {
+            sess.warn(format!(
+                "the MSRV in `clippy.toml` and `Cargo.toml` differ; using `{clippy_msrv}` from `clippy.toml`"
+            ));
+        }
+
+        Self::new(clippy_msrv.or(cargo_msrv))
+    }
+
+    /// Set the initial MSRV from the Clippy config file or from Cargo due to the `rust-version`
+    /// field in `Cargo.toml`
+    ///
+    /// Returns a `&'static Msrv` as `Copy` types are more easily passed to the
+    /// `register_{late,early}_pass` callbacks
+    pub fn read(conf_msrv: &Option<String>, sess: &Session) -> &'static Self {
+        static PARSED: OnceLock<Msrv> = OnceLock::new();
+
+        PARSED.get_or_init(|| Self::read_inner(conf_msrv, sess))
+    }
+
+    pub fn current(&self) -> Option<RustcVersion> {
+        self.stack.last().copied()
+    }
+
+    pub fn meets(&self, required: RustcVersion) -> bool {
+        self.current().map_or(true, |version| version.meets(required))
+    }
+
+    fn parse_attr(sess: &Session, attrs: &[Attribute]) -> Option<RustcVersion> {
+        if let Some(msrv_attr) = get_unique_attr(sess, attrs, "msrv") {
+            if let Some(msrv) = msrv_attr.value_str() {
+                return parse_msrv(&msrv.to_string(), Some(sess), Some(msrv_attr.span));
+            }
+
+            sess.span_err(msrv_attr.span, "bad clippy attribute");
+        }
+
+        None
+    }
+
+    pub fn enter_lint_attrs(&mut self, sess: &Session, attrs: &[Attribute]) {
+        if let Some(version) = Self::parse_attr(sess, attrs) {
+            self.stack.push(version);
+        }
+    }
+
+    pub fn exit_lint_attrs(&mut self, sess: &Session, attrs: &[Attribute]) {
+        if Self::parse_attr(sess, attrs).is_some() {
+            self.stack.pop();
+        }
+    }
+}
diff --git a/src/tools/clippy/clippy_utils/src/paths.rs b/src/tools/clippy/clippy_utils/src/paths.rs
index 6c09c146082..6417f0f3c71 100644
--- a/src/tools/clippy/clippy_utils/src/paths.rs
+++ b/src/tools/clippy/clippy_utils/src/paths.rs
@@ -60,6 +60,8 @@ pub const LATE_LINT_PASS: [&str; 3] = ["rustc_lint", "passes", "LateLintPass"];
 #[cfg(feature = "internal")]
 pub const LINT: [&str; 2] = ["rustc_lint_defs", "Lint"];
 pub const MEM_SWAP: [&str; 3] = ["core", "mem", "swap"];
+#[cfg(feature = "internal")]
+pub const MSRV: [&str; 3] = ["clippy_utils", "msrvs", "Msrv"];
 pub const OPEN_OPTIONS: [&str; 3] = ["std", "fs", "OpenOptions"];
 pub const OS_STRING_AS_OS_STR: [&str; 5] = ["std", "ffi", "os_str", "OsString", "as_os_str"];
 pub const OS_STR_TO_OS_STRING: [&str; 5] = ["std", "ffi", "os_str", "OsStr", "to_os_string"];
@@ -72,7 +74,6 @@ pub const PEEKABLE: [&str; 5] = ["core", "iter", "adapters", "peekable", "Peekab
 pub const PERMISSIONS: [&str; 3] = ["std", "fs", "Permissions"];
 #[cfg_attr(not(unix), allow(clippy::invalid_paths))]
 pub const PERMISSIONS_FROM_MODE: [&str; 6] = ["std", "os", "unix", "fs", "PermissionsExt", "from_mode"];
-pub const POLL: [&str; 4] = ["core", "task", "poll", "Poll"];
 pub const PTR_COPY: [&str; 3] = ["core", "intrinsics", "copy"];
 pub const PTR_COPY_NONOVERLAPPING: [&str; 3] = ["core", "intrinsics", "copy_nonoverlapping"];
 pub const PTR_EQ: [&str; 3] = ["core", "ptr", "eq"];
@@ -101,8 +102,6 @@ pub const REGEX_BYTES_NEW: [&str; 4] = ["regex", "re_bytes", "Regex", "new"];
 pub const REGEX_BYTES_SET_NEW: [&str; 5] = ["regex", "re_set", "bytes", "RegexSet", "new"];
 pub const REGEX_NEW: [&str; 4] = ["regex", "re_unicode", "Regex", "new"];
 pub const REGEX_SET_NEW: [&str; 5] = ["regex", "re_set", "unicode", "RegexSet", "new"];
-#[cfg(feature = "internal")]
-pub const RUSTC_VERSION: [&str; 2] = ["rustc_semver", "RustcVersion"];
 pub const SERDE_DESERIALIZE: [&str; 3] = ["serde", "de", "Deserialize"];
 pub const SERDE_DE_VISITOR: [&str; 3] = ["serde", "de", "Visitor"];
 pub const SLICE_FROM_RAW_PARTS: [&str; 4] = ["core", "slice", "raw", "from_raw_parts"];
diff --git a/src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs b/src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs
index b8c2dd5ab9e..480e8e55cf3 100644
--- a/src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs
+++ b/src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs
@@ -3,6 +3,7 @@
 // of terminologies might not be relevant in the context of Clippy. Note that its behavior might
 // differ from the time of `rustc` even if the name stays the same.
 
+use crate::msrvs::Msrv;
 use rustc_hir as hir;
 use rustc_hir::def_id::DefId;
 use rustc_middle::mir::{
@@ -18,20 +19,22 @@ use std::borrow::Cow;
 
 type McfResult = Result<(), (Span, Cow<'static, str>)>;
 
-pub fn is_min_const_fn<'tcx>(tcx: TyCtxt<'tcx>, body: &Body<'tcx>, msrv: Option<RustcVersion>) -> McfResult {
+pub fn is_min_const_fn<'tcx>(tcx: TyCtxt<'tcx>, body: &Body<'tcx>, msrv: &Msrv) -> McfResult {
     let def_id = body.source.def_id();
     let mut current = def_id;
     loop {
         let predicates = tcx.predicates_of(current);
         for (predicate, _) in predicates.predicates {
             match predicate.kind().skip_binder() {
-                ty::PredicateKind::Clause(ty::Clause::RegionOutlives(_))
-                | ty::PredicateKind::Clause(ty::Clause::TypeOutlives(_))
+                ty::PredicateKind::Clause(
+                    ty::Clause::RegionOutlives(_)
+                    | ty::Clause::TypeOutlives(_)
+                    | ty::Clause::Projection(_)
+                    | ty::Clause::Trait(..),
+                )
                 | ty::PredicateKind::WellFormed(_)
-                | ty::PredicateKind::Clause(ty::Clause::Projection(_))
                 | ty::PredicateKind::ConstEvaluatable(..)
                 | ty::PredicateKind::ConstEquate(..)
-                | ty::PredicateKind::Clause(ty::Clause::Trait(..))
                 | ty::PredicateKind::TypeWellFormedFromEnv(..) => continue,
                 ty::PredicateKind::ObjectSafe(_) => panic!("object safe predicate on function: {predicate:#?}"),
                 ty::PredicateKind::ClosureKind(..) => panic!("closure kind predicate on function: {predicate:#?}"),
@@ -281,7 +284,7 @@ fn check_terminator<'tcx>(
     tcx: TyCtxt<'tcx>,
     body: &Body<'tcx>,
     terminator: &Terminator<'tcx>,
-    msrv: Option<RustcVersion>,
+    msrv: &Msrv,
 ) -> McfResult {
     let span = terminator.source_info.span;
     match &terminator.kind {
@@ -365,7 +368,7 @@ fn check_terminator<'tcx>(
     }
 }
 
-fn is_const_fn(tcx: TyCtxt<'_>, def_id: DefId, msrv: Option<RustcVersion>) -> bool {
+fn is_const_fn(tcx: TyCtxt<'_>, def_id: DefId, msrv: &Msrv) -> bool {
     tcx.is_const_fn(def_id)
         && tcx.lookup_const_stability(def_id).map_or(true, |const_stab| {
             if let rustc_attr::StabilityLevel::Stable { since, .. } = const_stab.level {
@@ -384,15 +387,12 @@ fn is_const_fn(tcx: TyCtxt<'_>, def_id: DefId, msrv: Option<RustcVersion>) -> bo
 
                 let since = rustc_span::Symbol::intern(short_version);
 
-                crate::meets_msrv(
-                    msrv,
-                    RustcVersion::parse(since.as_str()).unwrap_or_else(|err| {
-                        panic!("`rustc_attr::StabilityLevel::Stable::since` is ill-formatted: `{since}`, {err:?}")
-                    }),
-                )
+                msrv.meets(RustcVersion::parse(since.as_str()).unwrap_or_else(|err| {
+                    panic!("`rustc_attr::StabilityLevel::Stable::since` is ill-formatted: `{since}`, {err:?}")
+                }))
             } else {
                 // Unstable const fn with the feature enabled.
-                msrv.is_none()
+                msrv.current().is_none()
             }
         })
 }
diff --git a/src/tools/clippy/clippy_utils/src/source.rs b/src/tools/clippy/clippy_utils/src/source.rs
index eacfa91ba55..cd5dcfdaca3 100644
--- a/src/tools/clippy/clippy_utils/src/source.rs
+++ b/src/tools/clippy/clippy_utils/src/source.rs
@@ -5,6 +5,7 @@
 use rustc_errors::Applicability;
 use rustc_hir::{Expr, ExprKind};
 use rustc_lint::{LateContext, LintContext};
+use rustc_session::Session;
 use rustc_span::hygiene;
 use rustc_span::source_map::{original_sp, SourceMap};
 use rustc_span::{BytePos, Pos, Span, SpanData, SyntaxContext, DUMMY_SP};
@@ -205,10 +206,19 @@ pub fn snippet_with_applicability<'a, T: LintContext>(
     default: &'a str,
     applicability: &mut Applicability,
 ) -> Cow<'a, str> {
+    snippet_with_applicability_sess(cx.sess(), span, default, applicability)
+}
+
+fn snippet_with_applicability_sess<'a>(
+    sess: &Session,
+    span: Span,
+    default: &'a str,
+    applicability: &mut Applicability,
+) -> Cow<'a, str> {
     if *applicability != Applicability::Unspecified && span.from_expansion() {
         *applicability = Applicability::MaybeIncorrect;
     }
-    snippet_opt(cx, span).map_or_else(
+    snippet_opt_sess(sess, span).map_or_else(
         || {
             if *applicability == Applicability::MachineApplicable {
                 *applicability = Applicability::HasPlaceholders;
@@ -226,8 +236,12 @@ pub fn snippet_with_macro_callsite<'a, T: LintContext>(cx: &T, span: Span, defau
 }
 
 /// Converts a span to a code snippet. Returns `None` if not available.
-pub fn snippet_opt<T: LintContext>(cx: &T, span: Span) -> Option<String> {
-    cx.sess().source_map().span_to_snippet(span).ok()
+pub fn snippet_opt(cx: &impl LintContext, span: Span) -> Option<String> {
+    snippet_opt_sess(cx.sess(), span)
+}
+
+fn snippet_opt_sess(sess: &Session, span: Span) -> Option<String> {
+    sess.source_map().span_to_snippet(span).ok()
 }
 
 /// Converts a span (from a block) to a code snippet if available, otherwise use default.
@@ -277,8 +291,8 @@ pub fn snippet_block<'a, T: LintContext>(
 
 /// Same as `snippet_block`, but adapts the applicability level by the rules of
 /// `snippet_with_applicability`.
-pub fn snippet_block_with_applicability<'a, T: LintContext>(
-    cx: &T,
+pub fn snippet_block_with_applicability<'a>(
+    cx: &impl LintContext,
     span: Span,
     default: &'a str,
     indent_relative_to: Option<Span>,
@@ -299,7 +313,17 @@ pub fn snippet_block_with_applicability<'a, T: LintContext>(
 ///
 /// This will also return whether or not the snippet is a macro call.
 pub fn snippet_with_context<'a>(
-    cx: &LateContext<'_>,
+    cx: &impl LintContext,
+    span: Span,
+    outer: SyntaxContext,
+    default: &'a str,
+    applicability: &mut Applicability,
+) -> (Cow<'a, str>, bool) {
+    snippet_with_context_sess(cx.sess(), span, outer, default, applicability)
+}
+
+fn snippet_with_context_sess<'a>(
+    sess: &Session,
     span: Span,
     outer: SyntaxContext,
     default: &'a str,
@@ -318,7 +342,7 @@ pub fn snippet_with_context<'a>(
     );
 
     (
-        snippet_with_applicability(cx, span, default, applicability),
+        snippet_with_applicability_sess(sess, span, default, applicability),
         is_macro_call,
     )
 }
diff --git a/src/tools/clippy/clippy_utils/src/sugg.rs b/src/tools/clippy/clippy_utils/src/sugg.rs
index 3cacdb49377..b66604f33db 100644
--- a/src/tools/clippy/clippy_utils/src/sugg.rs
+++ b/src/tools/clippy/clippy_utils/src/sugg.rs
@@ -176,25 +176,28 @@ impl<'a> Sugg<'a> {
     }
 
     /// Prepare a suggestion from an expression.
-    pub fn ast(cx: &EarlyContext<'_>, expr: &ast::Expr, default: &'a str) -> Self {
+    pub fn ast(
+        cx: &EarlyContext<'_>,
+        expr: &ast::Expr,
+        default: &'a str,
+        ctxt: SyntaxContext,
+        app: &mut Applicability,
+    ) -> Self {
         use rustc_ast::ast::RangeLimits;
 
-        let snippet_without_expansion = |cx, span: Span, default| {
-            if span.from_expansion() {
-                snippet_with_macro_callsite(cx, span, default)
-            } else {
-                snippet(cx, span, default)
-            }
-        };
-
+        #[expect(clippy::match_wildcard_for_single_variants)]
         match expr.kind {
+            _ if expr.span.ctxt() != ctxt => Sugg::NonParen(snippet_with_context(cx, expr.span, ctxt, default, app).0),
             ast::ExprKind::AddrOf(..)
             | ast::ExprKind::Box(..)
             | ast::ExprKind::Closure { .. }
             | ast::ExprKind::If(..)
             | ast::ExprKind::Let(..)
             | ast::ExprKind::Unary(..)
-            | ast::ExprKind::Match(..) => Sugg::MaybeParen(snippet_without_expansion(cx, expr.span, default)),
+            | ast::ExprKind::Match(..) => match snippet_with_context(cx, expr.span, ctxt, default, app) {
+                (snip, false) => Sugg::MaybeParen(snip),
+                (snip, true) => Sugg::NonParen(snip),
+            },
             ast::ExprKind::Async(..)
             | ast::ExprKind::Block(..)
             | ast::ExprKind::Break(..)
@@ -224,45 +227,49 @@ impl<'a> Sugg<'a> {
             | ast::ExprKind::Array(..)
             | ast::ExprKind::While(..)
             | ast::ExprKind::Await(..)
-            | ast::ExprKind::Err => Sugg::NonParen(snippet_without_expansion(cx, expr.span, default)),
+            | ast::ExprKind::Err => Sugg::NonParen(snippet_with_context(cx, expr.span, ctxt, default, app).0),
             ast::ExprKind::Range(ref lhs, ref rhs, RangeLimits::HalfOpen) => Sugg::BinOp(
                 AssocOp::DotDot,
-                lhs.as_ref()
-                    .map_or("".into(), |lhs| snippet_without_expansion(cx, lhs.span, default)),
-                rhs.as_ref()
-                    .map_or("".into(), |rhs| snippet_without_expansion(cx, rhs.span, default)),
+                lhs.as_ref().map_or("".into(), |lhs| {
+                    snippet_with_context(cx, lhs.span, ctxt, default, app).0
+                }),
+                rhs.as_ref().map_or("".into(), |rhs| {
+                    snippet_with_context(cx, rhs.span, ctxt, default, app).0
+                }),
             ),
             ast::ExprKind::Range(ref lhs, ref rhs, RangeLimits::Closed) => Sugg::BinOp(
                 AssocOp::DotDotEq,
-                lhs.as_ref()
-                    .map_or("".into(), |lhs| snippet_without_expansion(cx, lhs.span, default)),
-                rhs.as_ref()
-                    .map_or("".into(), |rhs| snippet_without_expansion(cx, rhs.span, default)),
+                lhs.as_ref().map_or("".into(), |lhs| {
+                    snippet_with_context(cx, lhs.span, ctxt, default, app).0
+                }),
+                rhs.as_ref().map_or("".into(), |rhs| {
+                    snippet_with_context(cx, rhs.span, ctxt, default, app).0
+                }),
             ),
             ast::ExprKind::Assign(ref lhs, ref rhs, _) => Sugg::BinOp(
                 AssocOp::Assign,
-                snippet_without_expansion(cx, lhs.span, default),
-                snippet_without_expansion(cx, rhs.span, default),
+                snippet_with_context(cx, lhs.span, ctxt, default, app).0,
+                snippet_with_context(cx, rhs.span, ctxt, default, app).0,
             ),
             ast::ExprKind::AssignOp(op, ref lhs, ref rhs) => Sugg::BinOp(
                 astbinop2assignop(op),
-                snippet_without_expansion(cx, lhs.span, default),
-                snippet_without_expansion(cx, rhs.span, default),
+                snippet_with_context(cx, lhs.span, ctxt, default, app).0,
+                snippet_with_context(cx, rhs.span, ctxt, default, app).0,
             ),
             ast::ExprKind::Binary(op, ref lhs, ref rhs) => Sugg::BinOp(
                 AssocOp::from_ast_binop(op.node),
-                snippet_without_expansion(cx, lhs.span, default),
-                snippet_without_expansion(cx, rhs.span, default),
+                snippet_with_context(cx, lhs.span, ctxt, default, app).0,
+                snippet_with_context(cx, rhs.span, ctxt, default, app).0,
             ),
             ast::ExprKind::Cast(ref lhs, ref ty) => Sugg::BinOp(
                 AssocOp::As,
-                snippet_without_expansion(cx, lhs.span, default),
-                snippet_without_expansion(cx, ty.span, default),
+                snippet_with_context(cx, lhs.span, ctxt, default, app).0,
+                snippet_with_context(cx, ty.span, ctxt, default, app).0,
             ),
             ast::ExprKind::Type(ref lhs, ref ty) => Sugg::BinOp(
                 AssocOp::Colon,
-                snippet_without_expansion(cx, lhs.span, default),
-                snippet_without_expansion(cx, ty.span, default),
+                snippet_with_context(cx, lhs.span, ctxt, default, app).0,
+                snippet_with_context(cx, ty.span, ctxt, default, app).0,
             ),
         }
     }
diff --git a/src/tools/clippy/clippy_utils/src/ty.rs b/src/tools/clippy/clippy_utils/src/ty.rs
index f4459e3e663..bfb2d472a39 100644
--- a/src/tools/clippy/clippy_utils/src/ty.rs
+++ b/src/tools/clippy/clippy_utils/src/ty.rs
@@ -9,7 +9,10 @@ use rustc_hir as hir;
 use rustc_hir::def::{CtorKind, CtorOf, DefKind, Res};
 use rustc_hir::def_id::DefId;
 use rustc_hir::{Expr, FnDecl, LangItem, TyKind, Unsafety};
-use rustc_infer::infer::{TyCtxtInferExt, type_variable::{TypeVariableOrigin, TypeVariableOriginKind}};
+use rustc_infer::infer::{
+    type_variable::{TypeVariableOrigin, TypeVariableOriginKind},
+    TyCtxtInferExt,
+};
 use rustc_lint::LateContext;
 use rustc_middle::mir::interpret::{ConstValue, Scalar};
 use rustc_middle::ty::{
@@ -22,7 +25,7 @@ use rustc_span::symbol::Ident;
 use rustc_span::{sym, Span, Symbol, DUMMY_SP};
 use rustc_target::abi::{Size, VariantIdx};
 use rustc_trait_selection::infer::InferCtxtExt;
-use rustc_trait_selection::traits::query::normalize::AtExt;
+use rustc_trait_selection::traits::query::normalize::QueryNormalizeExt;
 use std::iter;
 
 use crate::{match_def_path, path_res, paths};
@@ -189,7 +192,13 @@ pub fn implements_trait<'tcx>(
     trait_id: DefId,
     ty_params: &[GenericArg<'tcx>],
 ) -> bool {
-    implements_trait_with_env(cx.tcx, cx.param_env, ty, trait_id, ty_params.iter().map(|&arg| Some(arg)))
+    implements_trait_with_env(
+        cx.tcx,
+        cx.param_env,
+        ty,
+        trait_id,
+        ty_params.iter().map(|&arg| Some(arg)),
+    )
 }
 
 /// Same as `implements_trait` but allows using a `ParamEnv` different from the lint context.
@@ -212,7 +221,11 @@ pub fn implements_trait_with_env<'tcx>(
         kind: TypeVariableOriginKind::MiscVariable,
         span: DUMMY_SP,
     };
-    let ty_params = tcx.mk_substs(ty_params.into_iter().map(|arg| arg.unwrap_or_else(|| infcx.next_ty_var(orig).into())));
+    let ty_params = tcx.mk_substs(
+        ty_params
+            .into_iter()
+            .map(|arg| arg.unwrap_or_else(|| infcx.next_ty_var(orig).into())),
+    );
     infcx
         .type_implements_trait(trait_id, [ty.into()].into_iter().chain(ty_params), param_env)
         .must_apply_modulo_regions()
@@ -283,7 +296,7 @@ fn is_normalizable_helper<'tcx>(
     cache.insert(ty, false);
     let infcx = cx.tcx.infer_ctxt().build();
     let cause = rustc_middle::traits::ObligationCause::dummy();
-    let result = if infcx.at(&cause, param_env).normalize(ty).is_ok() {
+    let result = if infcx.at(&cause, param_env).query_normalize(ty).is_ok() {
         match ty.kind() {
             ty::Adt(def, substs) => def.variants().iter().all(|variant| {
                 variant
@@ -712,7 +725,9 @@ fn sig_for_projection<'tcx>(cx: &LateContext<'tcx>, ty: ProjectionTy<'tcx>) -> O
                 }
                 inputs = Some(i);
             },
-            PredicateKind::Clause(ty::Clause::Projection(p)) if Some(p.projection_ty.item_def_id) == lang_items.fn_once_output() => {
+            PredicateKind::Clause(ty::Clause::Projection(p))
+                if Some(p.projection_ty.item_def_id) == lang_items.fn_once_output() =>
+            {
                 if output.is_some() {
                     // Multiple different fn trait impls. Is this even allowed?
                     return None;
@@ -992,14 +1007,12 @@ pub fn make_projection<'tcx>(
 
             debug_assert!(
                 generic_count == substs.len(),
-                "wrong number of substs for `{:?}`: found `{}` expected `{}`.\n\
+                "wrong number of substs for `{:?}`: found `{}` expected `{generic_count}`.\n\
                     note: the expected parameters are: {:#?}\n\
-                    the given arguments are: `{:#?}`",
+                    the given arguments are: `{substs:#?}`",
                 assoc_item.def_id,
                 substs.len(),
-                generic_count,
                 params.map(ty::GenericParamDefKind::descr).collect::<Vec<_>>(),
-                substs,
             );
 
             if let Some((idx, (param, arg))) = params
@@ -1017,14 +1030,11 @@ pub fn make_projection<'tcx>(
             {
                 debug_assert!(
                     false,
-                    "mismatched subst type at index {}: expected a {}, found `{:?}`\n\
+                    "mismatched subst type at index {idx}: expected a {}, found `{arg:?}`\n\
                         note: the expected parameters are {:#?}\n\
-                        the given arguments are {:#?}",
-                    idx,
+                        the given arguments are {substs:#?}",
                     param.descr(),
-                    arg,
-                    params.map(ty::GenericParamDefKind::descr).collect::<Vec<_>>(),
-                    substs,
+                    params.map(ty::GenericParamDefKind::descr).collect::<Vec<_>>()
                 );
             }
         }
diff --git a/src/tools/clippy/clippy_utils/src/usage.rs b/src/tools/clippy/clippy_utils/src/usage.rs
index 797722cfc1f..ab3976a13bd 100644
--- a/src/tools/clippy/clippy_utils/src/usage.rs
+++ b/src/tools/clippy/clippy_utils/src/usage.rs
@@ -128,7 +128,7 @@ impl<'a, 'tcx> intravisit::Visitor<'tcx> for BindingUsageFinder<'a, 'tcx> {
         }
     }
 
-    fn visit_path(&mut self, path: &'tcx hir::Path<'tcx>, _: hir::HirId) {
+    fn visit_path(&mut self, path: &hir::Path<'tcx>, _: hir::HirId) {
         if let hir::def::Res::Local(id) = path.res {
             if self.binding_ids.contains(&id) {
                 self.usage_found = true;
diff --git a/src/tools/clippy/clippy_utils/src/visitors.rs b/src/tools/clippy/clippy_utils/src/visitors.rs
index d4294f18fd5..863fb60fcfc 100644
--- a/src/tools/clippy/clippy_utils/src/visitors.rs
+++ b/src/tools/clippy/clippy_utils/src/visitors.rs
@@ -170,22 +170,22 @@ where
         cb: F,
     }
 
-    struct WithStmtGuarg<'a, F> {
+    struct WithStmtGuard<'a, F> {
         val: &'a mut RetFinder<F>,
         prev_in_stmt: bool,
     }
 
     impl<F> RetFinder<F> {
-        fn inside_stmt(&mut self, in_stmt: bool) -> WithStmtGuarg<'_, F> {
+        fn inside_stmt(&mut self, in_stmt: bool) -> WithStmtGuard<'_, F> {
             let prev_in_stmt = std::mem::replace(&mut self.in_stmt, in_stmt);
-            WithStmtGuarg {
+            WithStmtGuard {
                 val: self,
                 prev_in_stmt,
             }
         }
     }
 
-    impl<F> std::ops::Deref for WithStmtGuarg<'_, F> {
+    impl<F> std::ops::Deref for WithStmtGuard<'_, F> {
         type Target = RetFinder<F>;
 
         fn deref(&self) -> &Self::Target {
@@ -193,13 +193,13 @@ where
         }
     }
 
-    impl<F> std::ops::DerefMut for WithStmtGuarg<'_, F> {
+    impl<F> std::ops::DerefMut for WithStmtGuard<'_, F> {
         fn deref_mut(&mut self) -> &mut Self::Target {
             self.val
         }
     }
 
-    impl<F> Drop for WithStmtGuarg<'_, F> {
+    impl<F> Drop for WithStmtGuard<'_, F> {
         fn drop(&mut self) {
             self.val.in_stmt = self.prev_in_stmt;
         }
diff --git a/src/tools/clippy/lintcheck/src/main.rs b/src/tools/clippy/lintcheck/src/main.rs
index ee8ab7c1d7c..bd49f096072 100644
--- a/src/tools/clippy/lintcheck/src/main.rs
+++ b/src/tools/clippy/lintcheck/src/main.rs
@@ -120,8 +120,8 @@ impl ClippyWarning {
             format!("$CARGO_HOME/{}", stripped.display())
         } else {
             format!(
-                "target/lintcheck/sources/{}-{}/{}",
-                crate_name, crate_version, span.file_name
+                "target/lintcheck/sources/{crate_name}-{crate_version}/{}",
+                span.file_name
             )
         };
 
@@ -322,13 +322,13 @@ impl Crate {
 
         if config.max_jobs == 1 {
             println!(
-                "{}/{} {}% Linting {} {}",
-                index, total_crates_to_lint, perc, &self.name, &self.version
+                "{index}/{total_crates_to_lint} {perc}% Linting {} {}",
+                &self.name, &self.version
             );
         } else {
             println!(
-                "{}/{} {}% Linting {} {} in target dir {:?}",
-                index, total_crates_to_lint, perc, &self.name, &self.version, thread_index
+                "{index}/{total_crates_to_lint} {perc}% Linting {} {} in target dir {thread_index:?}",
+                &self.name, &self.version
             );
         }
 
@@ -398,8 +398,7 @@ impl Crate {
             .output()
             .unwrap_or_else(|error| {
                 panic!(
-                    "Encountered error:\n{:?}\ncargo_clippy_path: {}\ncrate path:{}\n",
-                    error,
+                    "Encountered error:\n{error:?}\ncargo_clippy_path: {}\ncrate path:{}\n",
                     &cargo_clippy_path.display(),
                     &self.path.display()
                 );
diff --git a/src/tools/clippy/rust-toolchain b/src/tools/clippy/rust-toolchain
index a806c156479..19fee38db46 100644
--- a/src/tools/clippy/rust-toolchain
+++ b/src/tools/clippy/rust-toolchain
@@ -1,3 +1,3 @@
 [toolchain]
-channel = "nightly-2022-11-21"
+channel = "nightly-2022-12-01"
 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 ad6132a49ba..9ec4df8e651 100644
--- a/src/tools/clippy/src/driver.rs
+++ b/src/tools/clippy/src/driver.rs
@@ -1,6 +1,7 @@
 #![feature(rustc_private)]
 #![feature(let_chains)]
 #![feature(once_cell)]
+#![feature(lint_reasons)]
 #![cfg_attr(feature = "deny-warnings", deny(warnings))]
 // warn on lints, that are included in `rust-lang/rust`s bootstrap
 #![warn(rust_2018_idioms, unused_lifetimes)]
@@ -90,6 +91,10 @@ fn track_files(parse_sess: &mut ParseSess, conf_path_string: Option<String>) {
 
     // During development track the `clippy-driver` executable so that cargo will re-run clippy whenever
     // it is rebuilt
+    #[expect(
+        clippy::collapsible_if,
+        reason = "Due to a bug in let_chains this if statement can't be collapsed"
+    )]
     if cfg!(debug_assertions) {
         if let Ok(current_exe) = env::current_exe()
             && let Some(current_exe) = current_exe.to_str()
diff --git a/src/tools/clippy/tests/ui-internal/invalid_msrv_attr_impl.fixed b/src/tools/clippy/tests/ui-internal/invalid_msrv_attr_impl.fixed
index 900a8fffd40..08634063a57 100644
--- a/src/tools/clippy/tests/ui-internal/invalid_msrv_attr_impl.fixed
+++ b/src/tools/clippy/tests/ui-internal/invalid_msrv_attr_impl.fixed
@@ -11,9 +11,9 @@ extern crate rustc_middle;
 #[macro_use]
 extern crate rustc_session;
 use clippy_utils::extract_msrv_attr;
+use clippy_utils::msrvs::Msrv;
 use rustc_hir::Expr;
 use rustc_lint::{EarlyContext, EarlyLintPass, LateContext, LateLintPass};
-use rustc_semver::RustcVersion;
 
 declare_lint! {
     pub TEST_LINT,
@@ -22,7 +22,7 @@ declare_lint! {
 }
 
 struct Pass {
-    msrv: Option<RustcVersion>,
+    msrv: Msrv,
 }
 
 impl_lint_pass!(Pass => [TEST_LINT]);
diff --git a/src/tools/clippy/tests/ui-internal/invalid_msrv_attr_impl.rs b/src/tools/clippy/tests/ui-internal/invalid_msrv_attr_impl.rs
index 4bc8164db67..f8af77e6d39 100644
--- a/src/tools/clippy/tests/ui-internal/invalid_msrv_attr_impl.rs
+++ b/src/tools/clippy/tests/ui-internal/invalid_msrv_attr_impl.rs
@@ -11,9 +11,9 @@ extern crate rustc_middle;
 #[macro_use]
 extern crate rustc_session;
 use clippy_utils::extract_msrv_attr;
+use clippy_utils::msrvs::Msrv;
 use rustc_hir::Expr;
 use rustc_lint::{EarlyContext, EarlyLintPass, LateContext, LateLintPass};
-use rustc_semver::RustcVersion;
 
 declare_lint! {
     pub TEST_LINT,
@@ -22,7 +22,7 @@ declare_lint! {
 }
 
 struct Pass {
-    msrv: Option<RustcVersion>,
+    msrv: Msrv,
 }
 
 impl_lint_pass!(Pass => [TEST_LINT]);
diff --git a/src/tools/clippy/tests/ui-internal/unnecessary_def_path_hardcoded_path.stderr b/src/tools/clippy/tests/ui-internal/unnecessary_def_path_hardcoded_path.stderr
index 8bfc060e991..2a240cc249b 100644
--- a/src/tools/clippy/tests/ui-internal/unnecessary_def_path_hardcoded_path.stderr
+++ b/src/tools/clippy/tests/ui-internal/unnecessary_def_path_hardcoded_path.stderr
@@ -1,12 +1,3 @@
-error: hardcoded path to a language item
-  --> $DIR/unnecessary_def_path_hardcoded_path.rs:11:40
-   |
-LL |     const DEREF_MUT_TRAIT: [&str; 4] = ["core", "ops", "deref", "DerefMut"];
-   |                                        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-   |
-   = help: convert all references to use `LangItem::DerefMut`
-   = note: `-D clippy::unnecessary-def-path` implied by `-D warnings`
-
 error: hardcoded path to a diagnostic item
   --> $DIR/unnecessary_def_path_hardcoded_path.rs:10:36
    |
@@ -14,6 +5,7 @@ LL |     const DEREF_TRAIT: [&str; 4] = ["core", "ops", "deref", "Deref"];
    |                                    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |
    = help: convert all references to use `sym::Deref`
+   = note: `-D clippy::unnecessary-def-path` implied by `-D warnings`
 
 error: hardcoded path to a diagnostic item
   --> $DIR/unnecessary_def_path_hardcoded_path.rs:12:43
@@ -23,5 +15,13 @@ LL |     const DEREF_TRAIT_METHOD: [&str; 5] = ["core", "ops", "deref", "Deref",
    |
    = help: convert all references to use `sym::deref_method`
 
+error: hardcoded path to a language item
+  --> $DIR/unnecessary_def_path_hardcoded_path.rs:11:40
+   |
+LL |     const DEREF_MUT_TRAIT: [&str; 4] = ["core", "ops", "deref", "DerefMut"];
+   |                                        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = help: convert all references to use `LangItem::DerefMut`
+
 error: aborting due to 3 previous errors
 
diff --git a/src/tools/clippy/tests/ui-toml/allow_mixed_uninlined_format_args/clippy.toml b/src/tools/clippy/tests/ui-toml/allow_mixed_uninlined_format_args/clippy.toml
new file mode 100644
index 00000000000..b95e806aae2
--- /dev/null
+++ b/src/tools/clippy/tests/ui-toml/allow_mixed_uninlined_format_args/clippy.toml
@@ -0,0 +1 @@
+allow-mixed-uninlined-format-args = false
diff --git a/src/tools/clippy/tests/ui-toml/allow_mixed_uninlined_format_args/uninlined_format_args.fixed b/src/tools/clippy/tests/ui-toml/allow_mixed_uninlined_format_args/uninlined_format_args.fixed
new file mode 100644
index 00000000000..aa8b45b5fe7
--- /dev/null
+++ b/src/tools/clippy/tests/ui-toml/allow_mixed_uninlined_format_args/uninlined_format_args.fixed
@@ -0,0 +1,14 @@
+// run-rustfix
+#![warn(clippy::uninlined_format_args)]
+
+fn main() {
+    let local_i32 = 1;
+    let local_f64 = 2.0;
+    let local_opt: Option<i32> = Some(3);
+
+    println!("val='{local_i32}'");
+    println!("Hello x is {local_f64:.local_i32$}");
+    println!("Hello {local_i32} is {local_f64:.*}", 5);
+    println!("Hello {local_i32} is {local_f64:.*}", 5);
+    println!("{local_i32}, {}", local_opt.unwrap());
+}
diff --git a/src/tools/clippy/tests/ui-toml/allow_mixed_uninlined_format_args/uninlined_format_args.rs b/src/tools/clippy/tests/ui-toml/allow_mixed_uninlined_format_args/uninlined_format_args.rs
new file mode 100644
index 00000000000..ad2e4863ee8
--- /dev/null
+++ b/src/tools/clippy/tests/ui-toml/allow_mixed_uninlined_format_args/uninlined_format_args.rs
@@ -0,0 +1,14 @@
+// run-rustfix
+#![warn(clippy::uninlined_format_args)]
+
+fn main() {
+    let local_i32 = 1;
+    let local_f64 = 2.0;
+    let local_opt: Option<i32> = Some(3);
+
+    println!("val='{}'", local_i32);
+    println!("Hello {} is {:.*}", "x", local_i32, local_f64);
+    println!("Hello {} is {:.*}", local_i32, 5, local_f64);
+    println!("Hello {} is {2:.*}", local_i32, 5, local_f64);
+    println!("{}, {}", local_i32, local_opt.unwrap());
+}
diff --git a/src/tools/clippy/tests/ui-toml/allow_mixed_uninlined_format_args/uninlined_format_args.stderr b/src/tools/clippy/tests/ui-toml/allow_mixed_uninlined_format_args/uninlined_format_args.stderr
new file mode 100644
index 00000000000..ee941762196
--- /dev/null
+++ b/src/tools/clippy/tests/ui-toml/allow_mixed_uninlined_format_args/uninlined_format_args.stderr
@@ -0,0 +1,76 @@
+error: variables can be used directly in the `format!` string
+  --> $DIR/uninlined_format_args.rs:9:5
+   |
+LL |     println!("val='{}'", local_i32);
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: `-D clippy::uninlined-format-args` implied by `-D warnings`
+help: change this to
+   |
+LL -     println!("val='{}'", local_i32);
+LL +     println!("val='{local_i32}'");
+   |
+
+error: literal with an empty format string
+  --> $DIR/uninlined_format_args.rs:10:35
+   |
+LL |     println!("Hello {} is {:.*}", "x", local_i32, local_f64);
+   |                                   ^^^
+   |
+   = note: `-D clippy::print-literal` implied by `-D warnings`
+help: try this
+   |
+LL -     println!("Hello {} is {:.*}", "x", local_i32, local_f64);
+LL +     println!("Hello x is {:.*}", local_i32, local_f64);
+   |
+
+error: variables can be used directly in the `format!` string
+  --> $DIR/uninlined_format_args.rs:10:5
+   |
+LL |     println!("Hello {} is {:.*}", "x", local_i32, local_f64);
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: change this to
+   |
+LL -     println!("Hello {} is {:.*}", "x", local_i32, local_f64);
+LL +     println!("Hello {} is {local_f64:.local_i32$}", "x");
+   |
+
+error: variables can be used directly in the `format!` string
+  --> $DIR/uninlined_format_args.rs:11:5
+   |
+LL |     println!("Hello {} is {:.*}", local_i32, 5, local_f64);
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: change this to
+   |
+LL -     println!("Hello {} is {:.*}", local_i32, 5, local_f64);
+LL +     println!("Hello {local_i32} is {local_f64:.*}", 5);
+   |
+
+error: variables can be used directly in the `format!` string
+  --> $DIR/uninlined_format_args.rs:12:5
+   |
+LL |     println!("Hello {} is {2:.*}", local_i32, 5, local_f64);
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: change this to
+   |
+LL -     println!("Hello {} is {2:.*}", local_i32, 5, local_f64);
+LL +     println!("Hello {local_i32} is {local_f64:.*}", 5);
+   |
+
+error: variables can be used directly in the `format!` string
+  --> $DIR/uninlined_format_args.rs:13:5
+   |
+LL |     println!("{}, {}", local_i32, local_opt.unwrap());
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: change this to
+   |
+LL -     println!("{}, {}", local_i32, local_opt.unwrap());
+LL +     println!("{local_i32}, {}", local_opt.unwrap());
+   |
+
+error: aborting due to 6 previous errors
+
diff --git a/src/tools/clippy/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr b/src/tools/clippy/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr
index f91d285c2e0..01a5e962c94 100644
--- a/src/tools/clippy/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr
+++ b/src/tools/clippy/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr
@@ -1,6 +1,7 @@
 error: error reading Clippy's configuration file `$DIR/clippy.toml`: unknown field `foobar`, expected one of
            allow-dbg-in-tests
            allow-expect-in-tests
+           allow-mixed-uninlined-format-args
            allow-print-in-tests
            allow-unwrap-in-tests
            allowed-scripts
diff --git a/src/tools/clippy/tests/ui/almost_complete_letter_range.fixed b/src/tools/clippy/tests/ui/almost_complete_letter_range.fixed
index 079b7c000dc..adcbd4d5134 100644
--- a/src/tools/clippy/tests/ui/almost_complete_letter_range.fixed
+++ b/src/tools/clippy/tests/ui/almost_complete_letter_range.fixed
@@ -2,7 +2,6 @@
 // edition:2018
 // aux-build:macro_rules.rs
 
-#![feature(custom_inner_attributes)]
 #![feature(exclusive_range_pattern)]
 #![feature(stmt_expr_attributes)]
 #![warn(clippy::almost_complete_letter_range)]
@@ -62,16 +61,16 @@ fn main() {
     b!();
 }
 
+#[clippy::msrv = "1.25"]
 fn _under_msrv() {
-    #![clippy::msrv = "1.25"]
     let _ = match 'a' {
         'a'...'z' => 1,
         _ => 2,
     };
 }
 
+#[clippy::msrv = "1.26"]
 fn _meets_msrv() {
-    #![clippy::msrv = "1.26"]
     let _ = 'a'..='z';
     let _ = match 'a' {
         'a'..='z' => 1,
diff --git a/src/tools/clippy/tests/ui/almost_complete_letter_range.rs b/src/tools/clippy/tests/ui/almost_complete_letter_range.rs
index a66900a976e..9979316eca4 100644
--- a/src/tools/clippy/tests/ui/almost_complete_letter_range.rs
+++ b/src/tools/clippy/tests/ui/almost_complete_letter_range.rs
@@ -2,7 +2,6 @@
 // edition:2018
 // aux-build:macro_rules.rs
 
-#![feature(custom_inner_attributes)]
 #![feature(exclusive_range_pattern)]
 #![feature(stmt_expr_attributes)]
 #![warn(clippy::almost_complete_letter_range)]
@@ -62,16 +61,16 @@ fn main() {
     b!();
 }
 
+#[clippy::msrv = "1.25"]
 fn _under_msrv() {
-    #![clippy::msrv = "1.25"]
     let _ = match 'a' {
         'a'..'z' => 1,
         _ => 2,
     };
 }
 
+#[clippy::msrv = "1.26"]
 fn _meets_msrv() {
-    #![clippy::msrv = "1.26"]
     let _ = 'a'..'z';
     let _ = match 'a' {
         'a'..'z' => 1,
diff --git a/src/tools/clippy/tests/ui/almost_complete_letter_range.stderr b/src/tools/clippy/tests/ui/almost_complete_letter_range.stderr
index 3de44c72c1b..9abf6d6c5e7 100644
--- a/src/tools/clippy/tests/ui/almost_complete_letter_range.stderr
+++ b/src/tools/clippy/tests/ui/almost_complete_letter_range.stderr
@@ -1,5 +1,5 @@
 error: almost complete ascii letter range
-  --> $DIR/almost_complete_letter_range.rs:30:17
+  --> $DIR/almost_complete_letter_range.rs:29:17
    |
 LL |         let _ = ('a') ..'z';
    |                 ^^^^^^--^^^
@@ -9,7 +9,7 @@ LL |         let _ = ('a') ..'z';
    = note: `-D clippy::almost-complete-letter-range` implied by `-D warnings`
 
 error: almost complete ascii letter range
-  --> $DIR/almost_complete_letter_range.rs:31:17
+  --> $DIR/almost_complete_letter_range.rs:30:17
    |
 LL |         let _ = 'A' .. ('Z');
    |                 ^^^^--^^^^^^
@@ -17,7 +17,7 @@ LL |         let _ = 'A' .. ('Z');
    |                     help: use an inclusive range: `..=`
 
 error: almost complete ascii letter range
-  --> $DIR/almost_complete_letter_range.rs:37:13
+  --> $DIR/almost_complete_letter_range.rs:36:13
    |
 LL |     let _ = (b'a')..(b'z');
    |             ^^^^^^--^^^^^^
@@ -25,7 +25,7 @@ LL |     let _ = (b'a')..(b'z');
    |                   help: use an inclusive range: `..=`
 
 error: almost complete ascii letter range
-  --> $DIR/almost_complete_letter_range.rs:38:13
+  --> $DIR/almost_complete_letter_range.rs:37:13
    |
 LL |     let _ = b'A'..b'Z';
    |             ^^^^--^^^^
@@ -33,7 +33,7 @@ LL |     let _ = b'A'..b'Z';
    |                 help: use an inclusive range: `..=`
 
 error: almost complete ascii letter range
-  --> $DIR/almost_complete_letter_range.rs:43:13
+  --> $DIR/almost_complete_letter_range.rs:42:13
    |
 LL |     let _ = a!()..'z';
    |             ^^^^--^^^
@@ -41,7 +41,7 @@ LL |     let _ = a!()..'z';
    |                 help: use an inclusive range: `..=`
 
 error: almost complete ascii letter range
-  --> $DIR/almost_complete_letter_range.rs:46:9
+  --> $DIR/almost_complete_letter_range.rs:45:9
    |
 LL |         b'a'..b'z' if true => 1,
    |         ^^^^--^^^^
@@ -49,7 +49,7 @@ LL |         b'a'..b'z' if true => 1,
    |             help: use an inclusive range: `..=`
 
 error: almost complete ascii letter range
-  --> $DIR/almost_complete_letter_range.rs:47:9
+  --> $DIR/almost_complete_letter_range.rs:46:9
    |
 LL |         b'A'..b'Z' if true => 2,
    |         ^^^^--^^^^
@@ -57,7 +57,7 @@ LL |         b'A'..b'Z' if true => 2,
    |             help: use an inclusive range: `..=`
 
 error: almost complete ascii letter range
-  --> $DIR/almost_complete_letter_range.rs:54:9
+  --> $DIR/almost_complete_letter_range.rs:53:9
    |
 LL |         'a'..'z' if true => 1,
    |         ^^^--^^^
@@ -65,7 +65,7 @@ LL |         'a'..'z' if true => 1,
    |            help: use an inclusive range: `..=`
 
 error: almost complete ascii letter range
-  --> $DIR/almost_complete_letter_range.rs:55:9
+  --> $DIR/almost_complete_letter_range.rs:54:9
    |
 LL |         'A'..'Z' if true => 2,
    |         ^^^--^^^
@@ -73,7 +73,7 @@ LL |         'A'..'Z' if true => 2,
    |            help: use an inclusive range: `..=`
 
 error: almost complete ascii letter range
-  --> $DIR/almost_complete_letter_range.rs:23:17
+  --> $DIR/almost_complete_letter_range.rs:22:17
    |
 LL |         let _ = 'a'..'z';
    |                 ^^^--^^^
@@ -86,7 +86,7 @@ LL |     b!();
    = note: this error originates in the macro `b` (in Nightly builds, run with -Z macro-backtrace for more info)
 
 error: almost complete ascii letter range
-  --> $DIR/almost_complete_letter_range.rs:68:9
+  --> $DIR/almost_complete_letter_range.rs:67:9
    |
 LL |         'a'..'z' => 1,
    |         ^^^--^^^
@@ -94,7 +94,7 @@ LL |         'a'..'z' => 1,
    |            help: use an inclusive range: `...`
 
 error: almost complete ascii letter range
-  --> $DIR/almost_complete_letter_range.rs:75:13
+  --> $DIR/almost_complete_letter_range.rs:74:13
    |
 LL |     let _ = 'a'..'z';
    |             ^^^--^^^
@@ -102,7 +102,7 @@ LL |     let _ = 'a'..'z';
    |                help: use an inclusive range: `..=`
 
 error: almost complete ascii letter range
-  --> $DIR/almost_complete_letter_range.rs:77:9
+  --> $DIR/almost_complete_letter_range.rs:76:9
    |
 LL |         'a'..'z' => 1,
    |         ^^^--^^^
diff --git a/src/tools/clippy/tests/ui/async_yields_async.stderr b/src/tools/clippy/tests/ui/async_yields_async.stderr
index b0c4215e7dd..92ba3592967 100644
--- a/src/tools/clippy/tests/ui/async_yields_async.stderr
+++ b/src/tools/clippy/tests/ui/async_yields_async.stderr
@@ -2,14 +2,14 @@ error: an async construct yields a type which is itself awaitable
   --> $DIR/async_yields_async.rs:39:9
    |
 LL |        let _h = async {
-   |   ____________________-
-LL |  |         async {
-   |  |_________^
+   |  _____________________-
+LL | |          async {
+   | | _________^
 LL | ||             3
 LL | ||         }
    | ||_________^ awaitable value not awaited
-LL |  |     };
-   |  |_____- outer async construct
+LL | |      };
+   | |______- outer async construct
    |
    = note: `-D clippy::async-yields-async` implied by `-D warnings`
 help: consider awaiting this value
@@ -36,14 +36,14 @@ error: an async construct yields a type which is itself awaitable
   --> $DIR/async_yields_async.rs:50:9
    |
 LL |        let _j = async || {
-   |   _______________________-
-LL |  |         async {
-   |  |_________^
+   |  ________________________-
+LL | |          async {
+   | | _________^
 LL | ||             3
 LL | ||         }
    | ||_________^ awaitable value not awaited
-LL |  |     };
-   |  |_____- outer async construct
+LL | |      };
+   | |______- outer async construct
    |
 help: consider awaiting this value
    |
diff --git a/src/tools/clippy/tests/ui/cast_abs_to_unsigned.fixed b/src/tools/clippy/tests/ui/cast_abs_to_unsigned.fixed
index e6bf944c7a5..8676b562b4f 100644
--- a/src/tools/clippy/tests/ui/cast_abs_to_unsigned.fixed
+++ b/src/tools/clippy/tests/ui/cast_abs_to_unsigned.fixed
@@ -1,6 +1,5 @@
 // run-rustfix
 
-#![feature(custom_inner_attributes)]
 #![warn(clippy::cast_abs_to_unsigned)]
 #![allow(clippy::uninlined_format_args, unused)]
 
@@ -33,16 +32,14 @@ fn main() {
     let _ = (x as i64 - y as i64).unsigned_abs() as u32;
 }
 
+#[clippy::msrv = "1.50"]
 fn msrv_1_50() {
-    #![clippy::msrv = "1.50"]
-
     let x: i32 = 10;
     assert_eq!(10u32, x.abs() as u32);
 }
 
+#[clippy::msrv = "1.51"]
 fn msrv_1_51() {
-    #![clippy::msrv = "1.51"]
-
     let x: i32 = 10;
     assert_eq!(10u32, x.unsigned_abs());
 }
diff --git a/src/tools/clippy/tests/ui/cast_abs_to_unsigned.rs b/src/tools/clippy/tests/ui/cast_abs_to_unsigned.rs
index c87320b5209..5775af874f8 100644
--- a/src/tools/clippy/tests/ui/cast_abs_to_unsigned.rs
+++ b/src/tools/clippy/tests/ui/cast_abs_to_unsigned.rs
@@ -1,6 +1,5 @@
 // run-rustfix
 
-#![feature(custom_inner_attributes)]
 #![warn(clippy::cast_abs_to_unsigned)]
 #![allow(clippy::uninlined_format_args, unused)]
 
@@ -33,16 +32,14 @@ fn main() {
     let _ = (x as i64 - y as i64).abs() as u32;
 }
 
+#[clippy::msrv = "1.50"]
 fn msrv_1_50() {
-    #![clippy::msrv = "1.50"]
-
     let x: i32 = 10;
     assert_eq!(10u32, x.abs() as u32);
 }
 
+#[clippy::msrv = "1.51"]
 fn msrv_1_51() {
-    #![clippy::msrv = "1.51"]
-
     let x: i32 = 10;
     assert_eq!(10u32, x.abs() as u32);
 }
diff --git a/src/tools/clippy/tests/ui/cast_abs_to_unsigned.stderr b/src/tools/clippy/tests/ui/cast_abs_to_unsigned.stderr
index 1b39c554b03..4668554f451 100644
--- a/src/tools/clippy/tests/ui/cast_abs_to_unsigned.stderr
+++ b/src/tools/clippy/tests/ui/cast_abs_to_unsigned.stderr
@@ -1,5 +1,5 @@
 error: casting the result of `i32::abs()` to u32
-  --> $DIR/cast_abs_to_unsigned.rs:9:18
+  --> $DIR/cast_abs_to_unsigned.rs:8:18
    |
 LL |     let y: u32 = x.abs() as u32;
    |                  ^^^^^^^^^^^^^^ help: replace with: `x.unsigned_abs()`
@@ -7,103 +7,103 @@ LL |     let y: u32 = x.abs() as u32;
    = note: `-D clippy::cast-abs-to-unsigned` implied by `-D warnings`
 
 error: casting the result of `i32::abs()` to usize
-  --> $DIR/cast_abs_to_unsigned.rs:13:20
+  --> $DIR/cast_abs_to_unsigned.rs:12:20
    |
 LL |     let _: usize = a.abs() as usize;
    |                    ^^^^^^^ help: replace with: `a.unsigned_abs()`
 
 error: casting the result of `i32::abs()` to usize
-  --> $DIR/cast_abs_to_unsigned.rs:14:20
+  --> $DIR/cast_abs_to_unsigned.rs:13:20
    |
 LL |     let _: usize = a.abs() as _;
    |                    ^^^^^^^ help: replace with: `a.unsigned_abs()`
 
 error: casting the result of `i32::abs()` to usize
-  --> $DIR/cast_abs_to_unsigned.rs:15:13
+  --> $DIR/cast_abs_to_unsigned.rs:14:13
    |
 LL |     let _ = a.abs() as usize;
    |             ^^^^^^^ help: replace with: `a.unsigned_abs()`
 
 error: casting the result of `i64::abs()` to usize
-  --> $DIR/cast_abs_to_unsigned.rs:18:13
+  --> $DIR/cast_abs_to_unsigned.rs:17:13
    |
 LL |     let _ = a.abs() as usize;
    |             ^^^^^^^ help: replace with: `a.unsigned_abs()`
 
 error: casting the result of `i64::abs()` to u8
-  --> $DIR/cast_abs_to_unsigned.rs:19:13
+  --> $DIR/cast_abs_to_unsigned.rs:18:13
    |
 LL |     let _ = a.abs() as u8;
    |             ^^^^^^^ help: replace with: `a.unsigned_abs()`
 
 error: casting the result of `i64::abs()` to u16
-  --> $DIR/cast_abs_to_unsigned.rs:20:13
+  --> $DIR/cast_abs_to_unsigned.rs:19:13
    |
 LL |     let _ = a.abs() as u16;
    |             ^^^^^^^ help: replace with: `a.unsigned_abs()`
 
 error: casting the result of `i64::abs()` to u32
-  --> $DIR/cast_abs_to_unsigned.rs:21:13
+  --> $DIR/cast_abs_to_unsigned.rs:20:13
    |
 LL |     let _ = a.abs() as u32;
    |             ^^^^^^^ help: replace with: `a.unsigned_abs()`
 
 error: casting the result of `i64::abs()` to u64
-  --> $DIR/cast_abs_to_unsigned.rs:22:13
+  --> $DIR/cast_abs_to_unsigned.rs:21:13
    |
 LL |     let _ = a.abs() as u64;
    |             ^^^^^^^^^^^^^^ help: replace with: `a.unsigned_abs()`
 
 error: casting the result of `i64::abs()` to u128
-  --> $DIR/cast_abs_to_unsigned.rs:23:13
+  --> $DIR/cast_abs_to_unsigned.rs:22:13
    |
 LL |     let _ = a.abs() as u128;
    |             ^^^^^^^ help: replace with: `a.unsigned_abs()`
 
 error: casting the result of `isize::abs()` to usize
-  --> $DIR/cast_abs_to_unsigned.rs:26:13
+  --> $DIR/cast_abs_to_unsigned.rs:25:13
    |
 LL |     let _ = a.abs() as usize;
    |             ^^^^^^^^^^^^^^^^ help: replace with: `a.unsigned_abs()`
 
 error: casting the result of `isize::abs()` to u8
-  --> $DIR/cast_abs_to_unsigned.rs:27:13
+  --> $DIR/cast_abs_to_unsigned.rs:26:13
    |
 LL |     let _ = a.abs() as u8;
    |             ^^^^^^^ help: replace with: `a.unsigned_abs()`
 
 error: casting the result of `isize::abs()` to u16
-  --> $DIR/cast_abs_to_unsigned.rs:28:13
+  --> $DIR/cast_abs_to_unsigned.rs:27:13
    |
 LL |     let _ = a.abs() as u16;
    |             ^^^^^^^ help: replace with: `a.unsigned_abs()`
 
 error: casting the result of `isize::abs()` to u32
-  --> $DIR/cast_abs_to_unsigned.rs:29:13
+  --> $DIR/cast_abs_to_unsigned.rs:28:13
    |
 LL |     let _ = a.abs() as u32;
    |             ^^^^^^^ help: replace with: `a.unsigned_abs()`
 
 error: casting the result of `isize::abs()` to u64
-  --> $DIR/cast_abs_to_unsigned.rs:30:13
+  --> $DIR/cast_abs_to_unsigned.rs:29:13
    |
 LL |     let _ = a.abs() as u64;
    |             ^^^^^^^ help: replace with: `a.unsigned_abs()`
 
 error: casting the result of `isize::abs()` to u128
-  --> $DIR/cast_abs_to_unsigned.rs:31:13
+  --> $DIR/cast_abs_to_unsigned.rs:30:13
    |
 LL |     let _ = a.abs() as u128;
    |             ^^^^^^^ help: replace with: `a.unsigned_abs()`
 
 error: casting the result of `i64::abs()` to u32
-  --> $DIR/cast_abs_to_unsigned.rs:33:13
+  --> $DIR/cast_abs_to_unsigned.rs:32:13
    |
 LL |     let _ = (x as i64 - y as i64).abs() as u32;
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `(x as i64 - y as i64).unsigned_abs()`
 
 error: casting the result of `i32::abs()` to u32
-  --> $DIR/cast_abs_to_unsigned.rs:47:23
+  --> $DIR/cast_abs_to_unsigned.rs:44:23
    |
 LL |     assert_eq!(10u32, x.abs() as u32);
    |                       ^^^^^^^^^^^^^^ help: replace with: `x.unsigned_abs()`
diff --git a/src/tools/clippy/tests/ui/cast_lossless_bool.fixed b/src/tools/clippy/tests/ui/cast_lossless_bool.fixed
index af13b755e31..13b3cf838c9 100644
--- a/src/tools/clippy/tests/ui/cast_lossless_bool.fixed
+++ b/src/tools/clippy/tests/ui/cast_lossless_bool.fixed
@@ -1,6 +1,5 @@
 // run-rustfix
 
-#![feature(custom_inner_attributes)]
 #![allow(dead_code)]
 #![warn(clippy::cast_lossless)]
 
@@ -42,14 +41,12 @@ mod cast_lossless_in_impl {
     }
 }
 
+#[clippy::msrv = "1.27"]
 fn msrv_1_27() {
-    #![clippy::msrv = "1.27"]
-
     let _ = true as u8;
 }
 
+#[clippy::msrv = "1.28"]
 fn msrv_1_28() {
-    #![clippy::msrv = "1.28"]
-
     let _ = u8::from(true);
 }
diff --git a/src/tools/clippy/tests/ui/cast_lossless_bool.rs b/src/tools/clippy/tests/ui/cast_lossless_bool.rs
index 3b06af899c6..3eed2135562 100644
--- a/src/tools/clippy/tests/ui/cast_lossless_bool.rs
+++ b/src/tools/clippy/tests/ui/cast_lossless_bool.rs
@@ -1,6 +1,5 @@
 // run-rustfix
 
-#![feature(custom_inner_attributes)]
 #![allow(dead_code)]
 #![warn(clippy::cast_lossless)]
 
@@ -42,14 +41,12 @@ mod cast_lossless_in_impl {
     }
 }
 
+#[clippy::msrv = "1.27"]
 fn msrv_1_27() {
-    #![clippy::msrv = "1.27"]
-
     let _ = true as u8;
 }
 
+#[clippy::msrv = "1.28"]
 fn msrv_1_28() {
-    #![clippy::msrv = "1.28"]
-
     let _ = true as u8;
 }
diff --git a/src/tools/clippy/tests/ui/cast_lossless_bool.stderr b/src/tools/clippy/tests/ui/cast_lossless_bool.stderr
index 768b033d10a..ce240b70f89 100644
--- a/src/tools/clippy/tests/ui/cast_lossless_bool.stderr
+++ b/src/tools/clippy/tests/ui/cast_lossless_bool.stderr
@@ -1,5 +1,5 @@
 error: casting `bool` to `u8` is more cleanly stated with `u8::from(_)`
-  --> $DIR/cast_lossless_bool.rs:9:13
+  --> $DIR/cast_lossless_bool.rs:8:13
    |
 LL |     let _ = true as u8;
    |             ^^^^^^^^^^ help: try: `u8::from(true)`
@@ -7,79 +7,79 @@ LL |     let _ = true as u8;
    = note: `-D clippy::cast-lossless` implied by `-D warnings`
 
 error: casting `bool` to `u16` is more cleanly stated with `u16::from(_)`
-  --> $DIR/cast_lossless_bool.rs:10:13
+  --> $DIR/cast_lossless_bool.rs:9:13
    |
 LL |     let _ = true as u16;
    |             ^^^^^^^^^^^ help: try: `u16::from(true)`
 
 error: casting `bool` to `u32` is more cleanly stated with `u32::from(_)`
-  --> $DIR/cast_lossless_bool.rs:11:13
+  --> $DIR/cast_lossless_bool.rs:10:13
    |
 LL |     let _ = true as u32;
    |             ^^^^^^^^^^^ help: try: `u32::from(true)`
 
 error: casting `bool` to `u64` is more cleanly stated with `u64::from(_)`
-  --> $DIR/cast_lossless_bool.rs:12:13
+  --> $DIR/cast_lossless_bool.rs:11:13
    |
 LL |     let _ = true as u64;
    |             ^^^^^^^^^^^ help: try: `u64::from(true)`
 
 error: casting `bool` to `u128` is more cleanly stated with `u128::from(_)`
-  --> $DIR/cast_lossless_bool.rs:13:13
+  --> $DIR/cast_lossless_bool.rs:12:13
    |
 LL |     let _ = true as u128;
    |             ^^^^^^^^^^^^ help: try: `u128::from(true)`
 
 error: casting `bool` to `usize` is more cleanly stated with `usize::from(_)`
-  --> $DIR/cast_lossless_bool.rs:14:13
+  --> $DIR/cast_lossless_bool.rs:13:13
    |
 LL |     let _ = true as usize;
    |             ^^^^^^^^^^^^^ help: try: `usize::from(true)`
 
 error: casting `bool` to `i8` is more cleanly stated with `i8::from(_)`
-  --> $DIR/cast_lossless_bool.rs:16:13
+  --> $DIR/cast_lossless_bool.rs:15:13
    |
 LL |     let _ = true as i8;
    |             ^^^^^^^^^^ help: try: `i8::from(true)`
 
 error: casting `bool` to `i16` is more cleanly stated with `i16::from(_)`
-  --> $DIR/cast_lossless_bool.rs:17:13
+  --> $DIR/cast_lossless_bool.rs:16:13
    |
 LL |     let _ = true as i16;
    |             ^^^^^^^^^^^ help: try: `i16::from(true)`
 
 error: casting `bool` to `i32` is more cleanly stated with `i32::from(_)`
-  --> $DIR/cast_lossless_bool.rs:18:13
+  --> $DIR/cast_lossless_bool.rs:17:13
    |
 LL |     let _ = true as i32;
    |             ^^^^^^^^^^^ help: try: `i32::from(true)`
 
 error: casting `bool` to `i64` is more cleanly stated with `i64::from(_)`
-  --> $DIR/cast_lossless_bool.rs:19:13
+  --> $DIR/cast_lossless_bool.rs:18:13
    |
 LL |     let _ = true as i64;
    |             ^^^^^^^^^^^ help: try: `i64::from(true)`
 
 error: casting `bool` to `i128` is more cleanly stated with `i128::from(_)`
-  --> $DIR/cast_lossless_bool.rs:20:13
+  --> $DIR/cast_lossless_bool.rs:19:13
    |
 LL |     let _ = true as i128;
    |             ^^^^^^^^^^^^ help: try: `i128::from(true)`
 
 error: casting `bool` to `isize` is more cleanly stated with `isize::from(_)`
-  --> $DIR/cast_lossless_bool.rs:21:13
+  --> $DIR/cast_lossless_bool.rs:20:13
    |
 LL |     let _ = true as isize;
    |             ^^^^^^^^^^^^^ help: try: `isize::from(true)`
 
 error: casting `bool` to `u16` is more cleanly stated with `u16::from(_)`
-  --> $DIR/cast_lossless_bool.rs:24:13
+  --> $DIR/cast_lossless_bool.rs:23:13
    |
 LL |     let _ = (true | false) as u16;
    |             ^^^^^^^^^^^^^^^^^^^^^ help: try: `u16::from(true | false)`
 
 error: casting `bool` to `u8` is more cleanly stated with `u8::from(_)`
-  --> $DIR/cast_lossless_bool.rs:54:13
+  --> $DIR/cast_lossless_bool.rs:51:13
    |
 LL |     let _ = true as u8;
    |             ^^^^^^^^^^ help: try: `u8::from(true)`
diff --git a/src/tools/clippy/tests/ui/cfg_attr_rustfmt.fixed b/src/tools/clippy/tests/ui/cfg_attr_rustfmt.fixed
index 8a5645b22ed..b970b1209b6 100644
--- a/src/tools/clippy/tests/ui/cfg_attr_rustfmt.fixed
+++ b/src/tools/clippy/tests/ui/cfg_attr_rustfmt.fixed
@@ -1,5 +1,5 @@
 // run-rustfix
-#![feature(stmt_expr_attributes, custom_inner_attributes)]
+#![feature(stmt_expr_attributes)]
 
 #![allow(unused, clippy::no_effect, clippy::unnecessary_operation)]
 #![warn(clippy::deprecated_cfg_attr)]
@@ -30,16 +30,14 @@ mod foo {
     pub fn f() {}
 }
 
+#[clippy::msrv = "1.29"]
 fn msrv_1_29() {
-    #![clippy::msrv = "1.29"]
-
     #[cfg_attr(rustfmt, rustfmt::skip)]
     1+29;
 }
 
+#[clippy::msrv = "1.30"]
 fn msrv_1_30() {
-    #![clippy::msrv = "1.30"]
-
     #[rustfmt::skip]
     1+30;
 }
diff --git a/src/tools/clippy/tests/ui/cfg_attr_rustfmt.rs b/src/tools/clippy/tests/ui/cfg_attr_rustfmt.rs
index 2fb140efae7..0a8e6a89d8a 100644
--- a/src/tools/clippy/tests/ui/cfg_attr_rustfmt.rs
+++ b/src/tools/clippy/tests/ui/cfg_attr_rustfmt.rs
@@ -1,5 +1,5 @@
 // run-rustfix
-#![feature(stmt_expr_attributes, custom_inner_attributes)]
+#![feature(stmt_expr_attributes)]
 
 #![allow(unused, clippy::no_effect, clippy::unnecessary_operation)]
 #![warn(clippy::deprecated_cfg_attr)]
@@ -30,16 +30,14 @@ mod foo {
     pub fn f() {}
 }
 
+#[clippy::msrv = "1.29"]
 fn msrv_1_29() {
-    #![clippy::msrv = "1.29"]
-
     #[cfg_attr(rustfmt, rustfmt::skip)]
     1+29;
 }
 
+#[clippy::msrv = "1.30"]
 fn msrv_1_30() {
-    #![clippy::msrv = "1.30"]
-
     #[cfg_attr(rustfmt, rustfmt::skip)]
     1+30;
 }
diff --git a/src/tools/clippy/tests/ui/cfg_attr_rustfmt.stderr b/src/tools/clippy/tests/ui/cfg_attr_rustfmt.stderr
index 08df7b2b39a..524a2bf7248 100644
--- a/src/tools/clippy/tests/ui/cfg_attr_rustfmt.stderr
+++ b/src/tools/clippy/tests/ui/cfg_attr_rustfmt.stderr
@@ -13,7 +13,7 @@ LL | #[cfg_attr(rustfmt, rustfmt_skip)]
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `#[rustfmt::skip]`
 
 error: `cfg_attr` is deprecated for rustfmt and got replaced by tool attributes
-  --> $DIR/cfg_attr_rustfmt.rs:43:5
+  --> $DIR/cfg_attr_rustfmt.rs:41:5
    |
 LL |     #[cfg_attr(rustfmt, rustfmt::skip)]
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `#[rustfmt::skip]`
diff --git a/src/tools/clippy/tests/ui/checked_conversions.fixed b/src/tools/clippy/tests/ui/checked_conversions.fixed
index f936957cb40..e279ba3147b 100644
--- a/src/tools/clippy/tests/ui/checked_conversions.fixed
+++ b/src/tools/clippy/tests/ui/checked_conversions.fixed
@@ -1,6 +1,5 @@
 // run-rustfix
 
-#![feature(custom_inner_attributes)]
 #![allow(
     clippy::cast_lossless,
     unused,
@@ -78,16 +77,14 @@ pub const fn issue_8898(i: u32) -> bool {
     i <= i32::MAX as u32
 }
 
+#[clippy::msrv = "1.33"]
 fn msrv_1_33() {
-    #![clippy::msrv = "1.33"]
-
     let value: i64 = 33;
     let _ = value <= (u32::MAX as i64) && value >= 0;
 }
 
+#[clippy::msrv = "1.34"]
 fn msrv_1_34() {
-    #![clippy::msrv = "1.34"]
-
     let value: i64 = 34;
     let _ = u32::try_from(value).is_ok();
 }
diff --git a/src/tools/clippy/tests/ui/checked_conversions.rs b/src/tools/clippy/tests/ui/checked_conversions.rs
index 77aec713ff3..9d7a40995c3 100644
--- a/src/tools/clippy/tests/ui/checked_conversions.rs
+++ b/src/tools/clippy/tests/ui/checked_conversions.rs
@@ -1,6 +1,5 @@
 // run-rustfix
 
-#![feature(custom_inner_attributes)]
 #![allow(
     clippy::cast_lossless,
     unused,
@@ -78,16 +77,14 @@ pub const fn issue_8898(i: u32) -> bool {
     i <= i32::MAX as u32
 }
 
+#[clippy::msrv = "1.33"]
 fn msrv_1_33() {
-    #![clippy::msrv = "1.33"]
-
     let value: i64 = 33;
     let _ = value <= (u32::MAX as i64) && value >= 0;
 }
 
+#[clippy::msrv = "1.34"]
 fn msrv_1_34() {
-    #![clippy::msrv = "1.34"]
-
     let value: i64 = 34;
     let _ = value <= (u32::MAX as i64) && value >= 0;
 }
diff --git a/src/tools/clippy/tests/ui/checked_conversions.stderr b/src/tools/clippy/tests/ui/checked_conversions.stderr
index b2bf7af8daf..273ead73bda 100644
--- a/src/tools/clippy/tests/ui/checked_conversions.stderr
+++ b/src/tools/clippy/tests/ui/checked_conversions.stderr
@@ -1,5 +1,5 @@
 error: checked cast can be simplified
-  --> $DIR/checked_conversions.rs:17:13
+  --> $DIR/checked_conversions.rs:16:13
    |
 LL |     let _ = value <= (u32::max_value() as i64) && value >= 0;
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `u32::try_from(value).is_ok()`
@@ -7,97 +7,97 @@ LL |     let _ = value <= (u32::max_value() as i64) && value >= 0;
    = note: `-D clippy::checked-conversions` implied by `-D warnings`
 
 error: checked cast can be simplified
-  --> $DIR/checked_conversions.rs:18:13
+  --> $DIR/checked_conversions.rs:17:13
    |
 LL |     let _ = value <= (u32::MAX as i64) && value >= 0;
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `u32::try_from(value).is_ok()`
 
 error: checked cast can be simplified
-  --> $DIR/checked_conversions.rs:22:13
+  --> $DIR/checked_conversions.rs:21:13
    |
 LL |     let _ = value <= i64::from(u16::max_value()) && value >= 0;
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `u16::try_from(value).is_ok()`
 
 error: checked cast can be simplified
-  --> $DIR/checked_conversions.rs:23:13
+  --> $DIR/checked_conversions.rs:22:13
    |
 LL |     let _ = value <= i64::from(u16::MAX) && value >= 0;
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `u16::try_from(value).is_ok()`
 
 error: checked cast can be simplified
-  --> $DIR/checked_conversions.rs:27:13
+  --> $DIR/checked_conversions.rs:26:13
    |
 LL |     let _ = value <= (u8::max_value() as isize) && value >= 0;
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `u8::try_from(value).is_ok()`
 
 error: checked cast can be simplified
-  --> $DIR/checked_conversions.rs:28:13
+  --> $DIR/checked_conversions.rs:27:13
    |
 LL |     let _ = value <= (u8::MAX as isize) && value >= 0;
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `u8::try_from(value).is_ok()`
 
 error: checked cast can be simplified
-  --> $DIR/checked_conversions.rs:34:13
+  --> $DIR/checked_conversions.rs:33:13
    |
 LL |     let _ = value <= (i32::max_value() as i64) && value >= (i32::min_value() as i64);
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `i32::try_from(value).is_ok()`
 
 error: checked cast can be simplified
-  --> $DIR/checked_conversions.rs:35:13
+  --> $DIR/checked_conversions.rs:34:13
    |
 LL |     let _ = value <= (i32::MAX as i64) && value >= (i32::MIN as i64);
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `i32::try_from(value).is_ok()`
 
 error: checked cast can be simplified
-  --> $DIR/checked_conversions.rs:39:13
+  --> $DIR/checked_conversions.rs:38:13
    |
 LL |     let _ = value <= i64::from(i16::max_value()) && value >= i64::from(i16::min_value());
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `i16::try_from(value).is_ok()`
 
 error: checked cast can be simplified
-  --> $DIR/checked_conversions.rs:40:13
+  --> $DIR/checked_conversions.rs:39:13
    |
 LL |     let _ = value <= i64::from(i16::MAX) && value >= i64::from(i16::MIN);
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `i16::try_from(value).is_ok()`
 
 error: checked cast can be simplified
-  --> $DIR/checked_conversions.rs:46:13
+  --> $DIR/checked_conversions.rs:45:13
    |
 LL |     let _ = value <= i32::max_value() as u32;
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `i32::try_from(value).is_ok()`
 
 error: checked cast can be simplified
-  --> $DIR/checked_conversions.rs:47:13
+  --> $DIR/checked_conversions.rs:46:13
    |
 LL |     let _ = value <= i32::MAX as u32;
    |             ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `i32::try_from(value).is_ok()`
 
 error: checked cast can be simplified
-  --> $DIR/checked_conversions.rs:51:13
+  --> $DIR/checked_conversions.rs:50:13
    |
 LL |     let _ = value <= isize::max_value() as usize && value as i32 == 5;
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `isize::try_from(value).is_ok()`
 
 error: checked cast can be simplified
-  --> $DIR/checked_conversions.rs:52:13
+  --> $DIR/checked_conversions.rs:51:13
    |
 LL |     let _ = value <= isize::MAX as usize && value as i32 == 5;
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `isize::try_from(value).is_ok()`
 
 error: checked cast can be simplified
-  --> $DIR/checked_conversions.rs:56:13
+  --> $DIR/checked_conversions.rs:55:13
    |
 LL |     let _ = value <= u16::max_value() as u32 && value as i32 == 5;
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `u16::try_from(value).is_ok()`
 
 error: checked cast can be simplified
-  --> $DIR/checked_conversions.rs:57:13
+  --> $DIR/checked_conversions.rs:56:13
    |
 LL |     let _ = value <= u16::MAX as u32 && value as i32 == 5;
    |             ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `u16::try_from(value).is_ok()`
 
 error: checked cast can be simplified
-  --> $DIR/checked_conversions.rs:92:13
+  --> $DIR/checked_conversions.rs:89:13
    |
 LL |     let _ = value <= (u32::MAX as i64) && value >= 0;
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `u32::try_from(value).is_ok()`
diff --git a/src/tools/clippy/tests/ui/cloned_instead_of_copied.fixed b/src/tools/clippy/tests/ui/cloned_instead_of_copied.fixed
index 42ed232d100..ecbfc1feedf 100644
--- a/src/tools/clippy/tests/ui/cloned_instead_of_copied.fixed
+++ b/src/tools/clippy/tests/ui/cloned_instead_of_copied.fixed
@@ -1,6 +1,5 @@
 // run-rustfix
 
-#![feature(custom_inner_attributes)]
 #![warn(clippy::cloned_instead_of_copied)]
 #![allow(unused)]
 
@@ -17,23 +16,20 @@ fn main() {
     let _ = Some(&String::new()).cloned();
 }
 
+#[clippy::msrv = "1.34"]
 fn msrv_1_34() {
-    #![clippy::msrv = "1.34"]
-
     let _ = [1].iter().cloned();
     let _ = Some(&1).cloned();
 }
 
+#[clippy::msrv = "1.35"]
 fn msrv_1_35() {
-    #![clippy::msrv = "1.35"]
-
     let _ = [1].iter().cloned();
     let _ = Some(&1).copied(); // Option::copied needs 1.35
 }
 
+#[clippy::msrv = "1.36"]
 fn msrv_1_36() {
-    #![clippy::msrv = "1.36"]
-
     let _ = [1].iter().copied(); // Iterator::copied needs 1.36
     let _ = Some(&1).copied();
 }
diff --git a/src/tools/clippy/tests/ui/cloned_instead_of_copied.rs b/src/tools/clippy/tests/ui/cloned_instead_of_copied.rs
index 471bd9654cc..163dc3dddce 100644
--- a/src/tools/clippy/tests/ui/cloned_instead_of_copied.rs
+++ b/src/tools/clippy/tests/ui/cloned_instead_of_copied.rs
@@ -1,6 +1,5 @@
 // run-rustfix
 
-#![feature(custom_inner_attributes)]
 #![warn(clippy::cloned_instead_of_copied)]
 #![allow(unused)]
 
@@ -17,23 +16,20 @@ fn main() {
     let _ = Some(&String::new()).cloned();
 }
 
+#[clippy::msrv = "1.34"]
 fn msrv_1_34() {
-    #![clippy::msrv = "1.34"]
-
     let _ = [1].iter().cloned();
     let _ = Some(&1).cloned();
 }
 
+#[clippy::msrv = "1.35"]
 fn msrv_1_35() {
-    #![clippy::msrv = "1.35"]
-
     let _ = [1].iter().cloned();
     let _ = Some(&1).cloned(); // Option::copied needs 1.35
 }
 
+#[clippy::msrv = "1.36"]
 fn msrv_1_36() {
-    #![clippy::msrv = "1.36"]
-
     let _ = [1].iter().cloned(); // Iterator::copied needs 1.36
     let _ = Some(&1).cloned();
 }
diff --git a/src/tools/clippy/tests/ui/cloned_instead_of_copied.stderr b/src/tools/clippy/tests/ui/cloned_instead_of_copied.stderr
index 914c9a91e83..e0361acd956 100644
--- a/src/tools/clippy/tests/ui/cloned_instead_of_copied.stderr
+++ b/src/tools/clippy/tests/ui/cloned_instead_of_copied.stderr
@@ -1,5 +1,5 @@
 error: used `cloned` where `copied` could be used instead
-  --> $DIR/cloned_instead_of_copied.rs:9:24
+  --> $DIR/cloned_instead_of_copied.rs:8:24
    |
 LL |     let _ = [1].iter().cloned();
    |                        ^^^^^^ help: try: `copied`
@@ -7,43 +7,43 @@ LL |     let _ = [1].iter().cloned();
    = note: `-D clippy::cloned-instead-of-copied` implied by `-D warnings`
 
 error: used `cloned` where `copied` could be used instead
-  --> $DIR/cloned_instead_of_copied.rs:10:31
+  --> $DIR/cloned_instead_of_copied.rs:9:31
    |
 LL |     let _ = vec!["hi"].iter().cloned();
    |                               ^^^^^^ help: try: `copied`
 
 error: used `cloned` where `copied` could be used instead
-  --> $DIR/cloned_instead_of_copied.rs:11:22
+  --> $DIR/cloned_instead_of_copied.rs:10:22
    |
 LL |     let _ = Some(&1).cloned();
    |                      ^^^^^^ help: try: `copied`
 
 error: used `cloned` where `copied` could be used instead
-  --> $DIR/cloned_instead_of_copied.rs:12:34
+  --> $DIR/cloned_instead_of_copied.rs:11:34
    |
 LL |     let _ = Box::new([1].iter()).cloned();
    |                                  ^^^^^^ help: try: `copied`
 
 error: used `cloned` where `copied` could be used instead
-  --> $DIR/cloned_instead_of_copied.rs:13:32
+  --> $DIR/cloned_instead_of_copied.rs:12:32
    |
 LL |     let _ = Box::new(Some(&1)).cloned();
    |                                ^^^^^^ help: try: `copied`
 
 error: used `cloned` where `copied` could be used instead
-  --> $DIR/cloned_instead_of_copied.rs:31:22
+  --> $DIR/cloned_instead_of_copied.rs:28:22
    |
 LL |     let _ = Some(&1).cloned(); // Option::copied needs 1.35
    |                      ^^^^^^ help: try: `copied`
 
 error: used `cloned` where `copied` could be used instead
-  --> $DIR/cloned_instead_of_copied.rs:37:24
+  --> $DIR/cloned_instead_of_copied.rs:33:24
    |
 LL |     let _ = [1].iter().cloned(); // Iterator::copied needs 1.36
    |                        ^^^^^^ help: try: `copied`
 
 error: used `cloned` where `copied` could be used instead
-  --> $DIR/cloned_instead_of_copied.rs:38:22
+  --> $DIR/cloned_instead_of_copied.rs:34:22
    |
 LL |     let _ = Some(&1).cloned();
    |                      ^^^^^^ help: try: `copied`
diff --git a/src/tools/clippy/tests/ui/err_expect.fixed b/src/tools/clippy/tests/ui/err_expect.fixed
index 3bac738acd6..b63cbd8a8e6 100644
--- a/src/tools/clippy/tests/ui/err_expect.fixed
+++ b/src/tools/clippy/tests/ui/err_expect.fixed
@@ -1,6 +1,5 @@
 // run-rustfix
 
-#![feature(custom_inner_attributes)]
 #![allow(unused)]
 
 struct MyTypeNonDebug;
@@ -16,16 +15,14 @@ fn main() {
     test_non_debug.err().expect("Testing non debug type");
 }
 
+#[clippy::msrv = "1.16"]
 fn msrv_1_16() {
-    #![clippy::msrv = "1.16"]
-
     let x: Result<u32, &str> = Ok(16);
     x.err().expect("16");
 }
 
+#[clippy::msrv = "1.17"]
 fn msrv_1_17() {
-    #![clippy::msrv = "1.17"]
-
     let x: Result<u32, &str> = Ok(17);
     x.expect_err("17");
 }
diff --git a/src/tools/clippy/tests/ui/err_expect.rs b/src/tools/clippy/tests/ui/err_expect.rs
index 6e7c47d9ad3..c081a745fb4 100644
--- a/src/tools/clippy/tests/ui/err_expect.rs
+++ b/src/tools/clippy/tests/ui/err_expect.rs
@@ -1,6 +1,5 @@
 // run-rustfix
 
-#![feature(custom_inner_attributes)]
 #![allow(unused)]
 
 struct MyTypeNonDebug;
@@ -16,16 +15,14 @@ fn main() {
     test_non_debug.err().expect("Testing non debug type");
 }
 
+#[clippy::msrv = "1.16"]
 fn msrv_1_16() {
-    #![clippy::msrv = "1.16"]
-
     let x: Result<u32, &str> = Ok(16);
     x.err().expect("16");
 }
 
+#[clippy::msrv = "1.17"]
 fn msrv_1_17() {
-    #![clippy::msrv = "1.17"]
-
     let x: Result<u32, &str> = Ok(17);
     x.err().expect("17");
 }
diff --git a/src/tools/clippy/tests/ui/err_expect.stderr b/src/tools/clippy/tests/ui/err_expect.stderr
index 91a6cf8de65..82c0754cfb0 100644
--- a/src/tools/clippy/tests/ui/err_expect.stderr
+++ b/src/tools/clippy/tests/ui/err_expect.stderr
@@ -1,5 +1,5 @@
 error: called `.err().expect()` on a `Result` value
-  --> $DIR/err_expect.rs:13:16
+  --> $DIR/err_expect.rs:12:16
    |
 LL |     test_debug.err().expect("Testing debug type");
    |                ^^^^^^^^^^^^ help: try: `expect_err`
@@ -7,7 +7,7 @@ LL |     test_debug.err().expect("Testing debug type");
    = note: `-D clippy::err-expect` implied by `-D warnings`
 
 error: called `.err().expect()` on a `Result` value
-  --> $DIR/err_expect.rs:30:7
+  --> $DIR/err_expect.rs:27:7
    |
 LL |     x.err().expect("17");
    |       ^^^^^^^^^^^^ help: try: `expect_err`
diff --git a/src/tools/clippy/tests/ui/eta.fixed b/src/tools/clippy/tests/ui/eta.fixed
index a9cc80aaaf6..dc129591eac 100644
--- a/src/tools/clippy/tests/ui/eta.fixed
+++ b/src/tools/clippy/tests/ui/eta.fixed
@@ -316,3 +316,25 @@ pub fn mutable_impl_fn_mut(mut f: impl FnMut(), mut f_used_once: impl FnMut()) -
 
     move || takes_fn_mut(&mut f_used_once)
 }
+
+impl dyn TestTrait + '_ {
+    fn method_on_dyn(&self) -> bool {
+        false
+    }
+}
+
+// https://github.com/rust-lang/rust-clippy/issues/7746
+fn angle_brackets_and_substs() {
+    let array_opt: Option<&[u8; 3]> = Some(&[4, 8, 7]);
+    array_opt.map(<[u8; 3]>::as_slice);
+
+    let slice_opt: Option<&[u8]> = Some(b"slice");
+    slice_opt.map(<[u8]>::len);
+
+    let ptr_opt: Option<*const usize> = Some(&487);
+    ptr_opt.map(<*const usize>::is_null);
+
+    let test_struct = TestStruct { some_ref: &487 };
+    let dyn_opt: Option<&dyn TestTrait> = Some(&test_struct);
+    dyn_opt.map(<dyn TestTrait>::method_on_dyn);
+}
diff --git a/src/tools/clippy/tests/ui/eta.rs b/src/tools/clippy/tests/ui/eta.rs
index cc99906ccd6..025fd6a0b7a 100644
--- a/src/tools/clippy/tests/ui/eta.rs
+++ b/src/tools/clippy/tests/ui/eta.rs
@@ -316,3 +316,25 @@ pub fn mutable_impl_fn_mut(mut f: impl FnMut(), mut f_used_once: impl FnMut()) -
 
     move || takes_fn_mut(|| f_used_once())
 }
+
+impl dyn TestTrait + '_ {
+    fn method_on_dyn(&self) -> bool {
+        false
+    }
+}
+
+// https://github.com/rust-lang/rust-clippy/issues/7746
+fn angle_brackets_and_substs() {
+    let array_opt: Option<&[u8; 3]> = Some(&[4, 8, 7]);
+    array_opt.map(|a| a.as_slice());
+
+    let slice_opt: Option<&[u8]> = Some(b"slice");
+    slice_opt.map(|s| s.len());
+
+    let ptr_opt: Option<*const usize> = Some(&487);
+    ptr_opt.map(|p| p.is_null());
+
+    let test_struct = TestStruct { some_ref: &487 };
+    let dyn_opt: Option<&dyn TestTrait> = Some(&test_struct);
+    dyn_opt.map(|d| d.method_on_dyn());
+}
diff --git a/src/tools/clippy/tests/ui/eta.stderr b/src/tools/clippy/tests/ui/eta.stderr
index 434706b7e25..a521fb86860 100644
--- a/src/tools/clippy/tests/ui/eta.stderr
+++ b/src/tools/clippy/tests/ui/eta.stderr
@@ -134,5 +134,29 @@ error: redundant closure
 LL |     move || takes_fn_mut(|| f_used_once())
    |                          ^^^^^^^^^^^^^^^^ help: replace the closure with the function itself: `&mut f_used_once`
 
-error: aborting due to 22 previous errors
+error: redundant closure
+  --> $DIR/eta.rs:329:19
+   |
+LL |     array_opt.map(|a| a.as_slice());
+   |                   ^^^^^^^^^^^^^^^^ help: replace the closure with the method itself: `<[u8; 3]>::as_slice`
+
+error: redundant closure
+  --> $DIR/eta.rs:332:19
+   |
+LL |     slice_opt.map(|s| s.len());
+   |                   ^^^^^^^^^^^ help: replace the closure with the method itself: `<[u8]>::len`
+
+error: redundant closure
+  --> $DIR/eta.rs:335:17
+   |
+LL |     ptr_opt.map(|p| p.is_null());
+   |                 ^^^^^^^^^^^^^^^ help: replace the closure with the method itself: `<*const usize>::is_null`
+
+error: redundant closure
+  --> $DIR/eta.rs:339:17
+   |
+LL |     dyn_opt.map(|d| d.method_on_dyn());
+   |                 ^^^^^^^^^^^^^^^^^^^^^ help: replace the closure with the method itself: `<dyn TestTrait>::method_on_dyn`
+
+error: aborting due to 26 previous errors
 
diff --git a/src/tools/clippy/tests/ui/explicit_auto_deref.fixed b/src/tools/clippy/tests/ui/explicit_auto_deref.fixed
index 59ff5e4040a..475fae5e823 100644
--- a/src/tools/clippy/tests/ui/explicit_auto_deref.fixed
+++ b/src/tools/clippy/tests/ui/explicit_auto_deref.fixed
@@ -277,4 +277,8 @@ fn main() {
         unimplemented!()
     }
     let _: String = takes_assoc(&*String::new());
+
+    // Issue #9901
+    fn takes_ref(_: &i32) {}
+    takes_ref(*Box::new(&0i32));
 }
diff --git a/src/tools/clippy/tests/ui/explicit_auto_deref.rs b/src/tools/clippy/tests/ui/explicit_auto_deref.rs
index bcfb60c3278..c1894258f4d 100644
--- a/src/tools/clippy/tests/ui/explicit_auto_deref.rs
+++ b/src/tools/clippy/tests/ui/explicit_auto_deref.rs
@@ -277,4 +277,8 @@ fn main() {
         unimplemented!()
     }
     let _: String = takes_assoc(&*String::new());
+
+    // Issue #9901
+    fn takes_ref(_: &i32) {}
+    takes_ref(*Box::new(&0i32));
 }
diff --git a/src/tools/clippy/tests/ui/filter_map_next_fixable.fixed b/src/tools/clippy/tests/ui/filter_map_next_fixable.fixed
index 41828ddd7ac..462d46169fc 100644
--- a/src/tools/clippy/tests/ui/filter_map_next_fixable.fixed
+++ b/src/tools/clippy/tests/ui/filter_map_next_fixable.fixed
@@ -1,6 +1,5 @@
 // run-rustfix
 
-#![feature(custom_inner_attributes)]
 #![warn(clippy::all, clippy::pedantic)]
 #![allow(unused)]
 
@@ -11,16 +10,14 @@ fn main() {
     assert_eq!(element, Some(1));
 }
 
+#[clippy::msrv = "1.29"]
 fn msrv_1_29() {
-    #![clippy::msrv = "1.29"]
-
     let a = ["1", "lol", "3", "NaN", "5"];
     let _: Option<i32> = a.iter().filter_map(|s| s.parse().ok()).next();
 }
 
+#[clippy::msrv = "1.30"]
 fn msrv_1_30() {
-    #![clippy::msrv = "1.30"]
-
     let a = ["1", "lol", "3", "NaN", "5"];
     let _: Option<i32> = a.iter().find_map(|s| s.parse().ok());
 }
diff --git a/src/tools/clippy/tests/ui/filter_map_next_fixable.rs b/src/tools/clippy/tests/ui/filter_map_next_fixable.rs
index be492a81b45..2ea00cf7307 100644
--- a/src/tools/clippy/tests/ui/filter_map_next_fixable.rs
+++ b/src/tools/clippy/tests/ui/filter_map_next_fixable.rs
@@ -1,6 +1,5 @@
 // run-rustfix
 
-#![feature(custom_inner_attributes)]
 #![warn(clippy::all, clippy::pedantic)]
 #![allow(unused)]
 
@@ -11,16 +10,14 @@ fn main() {
     assert_eq!(element, Some(1));
 }
 
+#[clippy::msrv = "1.29"]
 fn msrv_1_29() {
-    #![clippy::msrv = "1.29"]
-
     let a = ["1", "lol", "3", "NaN", "5"];
     let _: Option<i32> = a.iter().filter_map(|s| s.parse().ok()).next();
 }
 
+#[clippy::msrv = "1.30"]
 fn msrv_1_30() {
-    #![clippy::msrv = "1.30"]
-
     let a = ["1", "lol", "3", "NaN", "5"];
     let _: Option<i32> = a.iter().filter_map(|s| s.parse().ok()).next();
 }
diff --git a/src/tools/clippy/tests/ui/filter_map_next_fixable.stderr b/src/tools/clippy/tests/ui/filter_map_next_fixable.stderr
index e789efeabd5..a9fc6abe88f 100644
--- a/src/tools/clippy/tests/ui/filter_map_next_fixable.stderr
+++ b/src/tools/clippy/tests/ui/filter_map_next_fixable.stderr
@@ -1,5 +1,5 @@
 error: called `filter_map(..).next()` on an `Iterator`. This is more succinctly expressed by calling `.find_map(..)` instead
-  --> $DIR/filter_map_next_fixable.rs:10:32
+  --> $DIR/filter_map_next_fixable.rs:9:32
    |
 LL |     let element: Option<i32> = a.iter().filter_map(|s| s.parse().ok()).next();
    |                                ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `a.iter().find_map(|s| s.parse().ok())`
@@ -7,7 +7,7 @@ LL |     let element: Option<i32> = a.iter().filter_map(|s| s.parse().ok()).next
    = note: `-D clippy::filter-map-next` implied by `-D warnings`
 
 error: called `filter_map(..).next()` on an `Iterator`. This is more succinctly expressed by calling `.find_map(..)` instead
-  --> $DIR/filter_map_next_fixable.rs:25:26
+  --> $DIR/filter_map_next_fixable.rs:22:26
    |
 LL |     let _: Option<i32> = a.iter().filter_map(|s| s.parse().ok()).next();
    |                          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `a.iter().find_map(|s| s.parse().ok())`
diff --git a/src/tools/clippy/tests/ui/from_over_into.fixed b/src/tools/clippy/tests/ui/from_over_into.fixed
index 1cf49ca45f4..125c9a69cd3 100644
--- a/src/tools/clippy/tests/ui/from_over_into.fixed
+++ b/src/tools/clippy/tests/ui/from_over_into.fixed
@@ -1,6 +1,5 @@
 // run-rustfix
 
-#![feature(custom_inner_attributes)]
 #![warn(clippy::from_over_into)]
 #![allow(unused)]
 
@@ -60,9 +59,8 @@ impl From<String> for A {
     }
 }
 
+#[clippy::msrv = "1.40"]
 fn msrv_1_40() {
-    #![clippy::msrv = "1.40"]
-
     struct FromOverInto<T>(Vec<T>);
 
     impl<T> Into<FromOverInto<T>> for Vec<T> {
@@ -72,9 +70,8 @@ fn msrv_1_40() {
     }
 }
 
+#[clippy::msrv = "1.41"]
 fn msrv_1_41() {
-    #![clippy::msrv = "1.41"]
-
     struct FromOverInto<T>(Vec<T>);
 
     impl<T> From<Vec<T>> for FromOverInto<T> {
diff --git a/src/tools/clippy/tests/ui/from_over_into.rs b/src/tools/clippy/tests/ui/from_over_into.rs
index d30f3c3fc92..5aa127bfabe 100644
--- a/src/tools/clippy/tests/ui/from_over_into.rs
+++ b/src/tools/clippy/tests/ui/from_over_into.rs
@@ -1,6 +1,5 @@
 // run-rustfix
 
-#![feature(custom_inner_attributes)]
 #![warn(clippy::from_over_into)]
 #![allow(unused)]
 
@@ -60,9 +59,8 @@ impl From<String> for A {
     }
 }
 
+#[clippy::msrv = "1.40"]
 fn msrv_1_40() {
-    #![clippy::msrv = "1.40"]
-
     struct FromOverInto<T>(Vec<T>);
 
     impl<T> Into<FromOverInto<T>> for Vec<T> {
@@ -72,9 +70,8 @@ fn msrv_1_40() {
     }
 }
 
+#[clippy::msrv = "1.41"]
 fn msrv_1_41() {
-    #![clippy::msrv = "1.41"]
-
     struct FromOverInto<T>(Vec<T>);
 
     impl<T> Into<FromOverInto<T>> for Vec<T> {
diff --git a/src/tools/clippy/tests/ui/from_over_into.stderr b/src/tools/clippy/tests/ui/from_over_into.stderr
index 9c2a7c04c36..a1764a5ea12 100644
--- a/src/tools/clippy/tests/ui/from_over_into.stderr
+++ b/src/tools/clippy/tests/ui/from_over_into.stderr
@@ -1,5 +1,5 @@
 error: an implementation of `From` is preferred since it gives you `Into<_>` for free where the reverse isn't true
-  --> $DIR/from_over_into.rs:10:1
+  --> $DIR/from_over_into.rs:9:1
    |
 LL | impl Into<StringWrapper> for String {
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -13,7 +13,7 @@ LL ~         StringWrapper(val)
    |
 
 error: an implementation of `From` is preferred since it gives you `Into<_>` for free where the reverse isn't true
-  --> $DIR/from_over_into.rs:18:1
+  --> $DIR/from_over_into.rs:17:1
    |
 LL | impl Into<SelfType> for String {
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -26,7 +26,7 @@ LL ~         SelfType(String::new())
    |
 
 error: an implementation of `From` is preferred since it gives you `Into<_>` for free where the reverse isn't true
-  --> $DIR/from_over_into.rs:33:1
+  --> $DIR/from_over_into.rs:32:1
    |
 LL | impl Into<SelfKeywords> for X {
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -41,7 +41,7 @@ LL ~         let _: X = val;
    |
 
 error: an implementation of `From` is preferred since it gives you `Into<_>` for free where the reverse isn't true
-  --> $DIR/from_over_into.rs:45:1
+  --> $DIR/from_over_into.rs:44:1
    |
 LL | impl core::convert::Into<bool> for crate::ExplicitPaths {
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -59,7 +59,7 @@ LL ~         val.0
    |
 
 error: an implementation of `From` is preferred since it gives you `Into<_>` for free where the reverse isn't true
-  --> $DIR/from_over_into.rs:80:5
+  --> $DIR/from_over_into.rs:77:5
    |
 LL |     impl<T> Into<FromOverInto<T>> for Vec<T> {
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
diff --git a/src/tools/clippy/tests/ui/if_then_some_else_none.rs b/src/tools/clippy/tests/ui/if_then_some_else_none.rs
index 3bc3a039524..0e89fdb0dfa 100644
--- a/src/tools/clippy/tests/ui/if_then_some_else_none.rs
+++ b/src/tools/clippy/tests/ui/if_then_some_else_none.rs
@@ -1,5 +1,4 @@
 #![warn(clippy::if_then_some_else_none)]
-#![feature(custom_inner_attributes)]
 
 fn main() {
     // Should issue an error.
@@ -66,8 +65,8 @@ fn main() {
     let _ = if foo() { into_some("foo") } else { None };
 }
 
+#[clippy::msrv = "1.49"]
 fn _msrv_1_49() {
-    #![clippy::msrv = "1.49"]
     // `bool::then` was stabilized in 1.50. Do not lint this
     let _ = if foo() {
         println!("true!");
@@ -77,8 +76,8 @@ fn _msrv_1_49() {
     };
 }
 
+#[clippy::msrv = "1.50"]
 fn _msrv_1_50() {
-    #![clippy::msrv = "1.50"]
     let _ = if foo() {
         println!("true!");
         Some(150)
diff --git a/src/tools/clippy/tests/ui/if_then_some_else_none.stderr b/src/tools/clippy/tests/ui/if_then_some_else_none.stderr
index 24e0b5947f1..d728a3c31a3 100644
--- a/src/tools/clippy/tests/ui/if_then_some_else_none.stderr
+++ b/src/tools/clippy/tests/ui/if_then_some_else_none.stderr
@@ -1,5 +1,5 @@
 error: this could be simplified with `bool::then`
-  --> $DIR/if_then_some_else_none.rs:6:13
+  --> $DIR/if_then_some_else_none.rs:5:13
    |
 LL |       let _ = if foo() {
    |  _____________^
@@ -14,7 +14,7 @@ LL | |     };
    = note: `-D clippy::if-then-some-else-none` implied by `-D warnings`
 
 error: this could be simplified with `bool::then`
-  --> $DIR/if_then_some_else_none.rs:14:13
+  --> $DIR/if_then_some_else_none.rs:13:13
    |
 LL |       let _ = if matches!(true, true) {
    |  _____________^
@@ -28,7 +28,7 @@ LL | |     };
    = help: consider using `bool::then` like: `matches!(true, true).then(|| { /* snippet */ matches!(true, false) })`
 
 error: this could be simplified with `bool::then_some`
-  --> $DIR/if_then_some_else_none.rs:23:28
+  --> $DIR/if_then_some_else_none.rs:22:28
    |
 LL |     let _ = x.and_then(|o| if o < 32 { Some(o) } else { None });
    |                            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -36,7 +36,7 @@ LL |     let _ = x.and_then(|o| if o < 32 { Some(o) } else { None });
    = help: consider using `bool::then_some` like: `(o < 32).then_some(o)`
 
 error: this could be simplified with `bool::then_some`
-  --> $DIR/if_then_some_else_none.rs:27:13
+  --> $DIR/if_then_some_else_none.rs:26:13
    |
 LL |     let _ = if !x { Some(0) } else { None };
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -44,7 +44,7 @@ LL |     let _ = if !x { Some(0) } else { None };
    = help: consider using `bool::then_some` like: `(!x).then_some(0)`
 
 error: this could be simplified with `bool::then`
-  --> $DIR/if_then_some_else_none.rs:82:13
+  --> $DIR/if_then_some_else_none.rs:81:13
    |
 LL |       let _ = if foo() {
    |  _____________^
diff --git a/src/tools/clippy/tests/ui/macro_use_imports.stderr b/src/tools/clippy/tests/ui/macro_use_imports.stderr
index bf7b6edd0e3..61843124ccd 100644
--- a/src/tools/clippy/tests/ui/macro_use_imports.stderr
+++ b/src/tools/clippy/tests/ui/macro_use_imports.stderr
@@ -1,8 +1,8 @@
 error: `macro_use` attributes are no longer needed in the Rust 2018 edition
-  --> $DIR/macro_use_imports.rs:23:5
+  --> $DIR/macro_use_imports.rs:25:5
    |
 LL |     #[macro_use]
-   |     ^^^^^^^^^^^^ help: remove the attribute and import the macro directly, try: `use mac::{inner::foofoo, inner::try_err};`
+   |     ^^^^^^^^^^^^ help: remove the attribute and import the macro directly, try: `use mac::inner::nested::string_add;`
    |
    = note: `-D clippy::macro-use-imports` implied by `-D warnings`
 
@@ -13,10 +13,10 @@ LL |     #[macro_use]
    |     ^^^^^^^^^^^^ help: remove the attribute and import the macro directly, try: `use mini_mac::ClippyMiniMacroTest;`
 
 error: `macro_use` attributes are no longer needed in the Rust 2018 edition
-  --> $DIR/macro_use_imports.rs:25:5
+  --> $DIR/macro_use_imports.rs:23:5
    |
 LL |     #[macro_use]
-   |     ^^^^^^^^^^^^ help: remove the attribute and import the macro directly, try: `use mac::inner::nested::string_add;`
+   |     ^^^^^^^^^^^^ help: remove the attribute and import the macro directly, try: `use mac::{inner::foofoo, inner::try_err};`
 
 error: `macro_use` attributes are no longer needed in the Rust 2018 edition
   --> $DIR/macro_use_imports.rs:19:5
diff --git a/src/tools/clippy/tests/ui/manual_clamp.rs b/src/tools/clippy/tests/ui/manual_clamp.rs
index 331fd29b74e..f7902e6fd53 100644
--- a/src/tools/clippy/tests/ui/manual_clamp.rs
+++ b/src/tools/clippy/tests/ui/manual_clamp.rs
@@ -1,4 +1,3 @@
-#![feature(custom_inner_attributes)]
 #![warn(clippy::manual_clamp)]
 #![allow(
     unused,
@@ -304,9 +303,8 @@ fn cmp_min_max(input: i32) -> i32 {
     input * 3
 }
 
+#[clippy::msrv = "1.49"]
 fn msrv_1_49() {
-    #![clippy::msrv = "1.49"]
-
     let (input, min, max) = (0, -1, 2);
     let _ = if input < min {
         min
@@ -317,9 +315,8 @@ fn msrv_1_49() {
     };
 }
 
+#[clippy::msrv = "1.50"]
 fn msrv_1_50() {
-    #![clippy::msrv = "1.50"]
-
     let (input, min, max) = (0, -1, 2);
     let _ = if input < min {
         min
diff --git a/src/tools/clippy/tests/ui/manual_clamp.stderr b/src/tools/clippy/tests/ui/manual_clamp.stderr
index 70abe28091c..988ad1527e8 100644
--- a/src/tools/clippy/tests/ui/manual_clamp.stderr
+++ b/src/tools/clippy/tests/ui/manual_clamp.stderr
@@ -1,5 +1,5 @@
 error: clamp-like pattern without using clamp function
-  --> $DIR/manual_clamp.rs:77:5
+  --> $DIR/manual_clamp.rs:76:5
    |
 LL | /     if x9 < min {
 LL | |         x9 = min;
@@ -13,7 +13,7 @@ LL | |     }
    = note: `-D clippy::manual-clamp` implied by `-D warnings`
 
 error: clamp-like pattern without using clamp function
-  --> $DIR/manual_clamp.rs:92:5
+  --> $DIR/manual_clamp.rs:91:5
    |
 LL | /     if x11 > max {
 LL | |         x11 = max;
@@ -26,7 +26,7 @@ LL | |     }
    = note: clamp will panic if max < min
 
 error: clamp-like pattern without using clamp function
-  --> $DIR/manual_clamp.rs:100:5
+  --> $DIR/manual_clamp.rs:99:5
    |
 LL | /     if min > x12 {
 LL | |         x12 = min;
@@ -39,7 +39,7 @@ LL | |     }
    = note: clamp will panic if max < min
 
 error: clamp-like pattern without using clamp function
-  --> $DIR/manual_clamp.rs:108:5
+  --> $DIR/manual_clamp.rs:107:5
    |
 LL | /     if max < x13 {
 LL | |         x13 = max;
@@ -52,7 +52,7 @@ LL | |     }
    = note: clamp will panic if max < min
 
 error: clamp-like pattern without using clamp function
-  --> $DIR/manual_clamp.rs:162:5
+  --> $DIR/manual_clamp.rs:161:5
    |
 LL | /     if max < x33 {
 LL | |         x33 = max;
@@ -65,7 +65,7 @@ LL | |     }
    = note: clamp will panic if max < min
 
 error: clamp-like pattern without using clamp function
-  --> $DIR/manual_clamp.rs:22:14
+  --> $DIR/manual_clamp.rs:21:14
    |
 LL |       let x0 = if max < input {
    |  ______________^
@@ -80,7 +80,7 @@ LL | |     };
    = note: clamp will panic if max < min
 
 error: clamp-like pattern without using clamp function
-  --> $DIR/manual_clamp.rs:30:14
+  --> $DIR/manual_clamp.rs:29:14
    |
 LL |       let x1 = if input > max {
    |  ______________^
@@ -95,7 +95,7 @@ LL | |     };
    = note: clamp will panic if max < min
 
 error: clamp-like pattern without using clamp function
-  --> $DIR/manual_clamp.rs:38:14
+  --> $DIR/manual_clamp.rs:37:14
    |
 LL |       let x2 = if input < min {
    |  ______________^
@@ -110,7 +110,7 @@ LL | |     };
    = note: clamp will panic if max < min
 
 error: clamp-like pattern without using clamp function
-  --> $DIR/manual_clamp.rs:46:14
+  --> $DIR/manual_clamp.rs:45:14
    |
 LL |       let x3 = if min > input {
    |  ______________^
@@ -125,7 +125,7 @@ LL | |     };
    = note: clamp will panic if max < min
 
 error: clamp-like pattern without using clamp function
-  --> $DIR/manual_clamp.rs:54:14
+  --> $DIR/manual_clamp.rs:53:14
    |
 LL |     let x4 = input.max(min).min(max);
    |              ^^^^^^^^^^^^^^^^^^^^^^^ help: replace with clamp: `input.clamp(min, max)`
@@ -133,7 +133,7 @@ LL |     let x4 = input.max(min).min(max);
    = note: clamp will panic if max < min
 
 error: clamp-like pattern without using clamp function
-  --> $DIR/manual_clamp.rs:56:14
+  --> $DIR/manual_clamp.rs:55:14
    |
 LL |     let x5 = input.min(max).max(min);
    |              ^^^^^^^^^^^^^^^^^^^^^^^ help: replace with clamp: `input.clamp(min, max)`
@@ -141,7 +141,7 @@ LL |     let x5 = input.min(max).max(min);
    = note: clamp will panic if max < min
 
 error: clamp-like pattern without using clamp function
-  --> $DIR/manual_clamp.rs:58:14
+  --> $DIR/manual_clamp.rs:57:14
    |
 LL |       let x6 = match input {
    |  ______________^
@@ -154,7 +154,7 @@ LL | |     };
    = note: clamp will panic if max < min
 
 error: clamp-like pattern without using clamp function
-  --> $DIR/manual_clamp.rs:64:14
+  --> $DIR/manual_clamp.rs:63:14
    |
 LL |       let x7 = match input {
    |  ______________^
@@ -167,7 +167,7 @@ LL | |     };
    = note: clamp will panic if max < min
 
 error: clamp-like pattern without using clamp function
-  --> $DIR/manual_clamp.rs:70:14
+  --> $DIR/manual_clamp.rs:69:14
    |
 LL |       let x8 = match input {
    |  ______________^
@@ -180,7 +180,7 @@ LL | |     };
    = note: clamp will panic if max < min
 
 error: clamp-like pattern without using clamp function
-  --> $DIR/manual_clamp.rs:84:15
+  --> $DIR/manual_clamp.rs:83:15
    |
 LL |       let x10 = match input {
    |  _______________^
@@ -193,7 +193,7 @@ LL | |     };
    = note: clamp will panic if max < min
 
 error: clamp-like pattern without using clamp function
-  --> $DIR/manual_clamp.rs:115:15
+  --> $DIR/manual_clamp.rs:114:15
    |
 LL |       let x14 = if input > CONST_MAX {
    |  _______________^
@@ -208,7 +208,7 @@ LL | |     };
    = note: clamp will panic if max < min
 
 error: clamp-like pattern without using clamp function
-  --> $DIR/manual_clamp.rs:124:19
+  --> $DIR/manual_clamp.rs:123:19
    |
 LL |           let x15 = if input > max {
    |  ___________________^
@@ -224,7 +224,7 @@ LL | |         };
    = note: clamp returns NaN if the input is NaN
 
 error: clamp-like pattern without using clamp function
-  --> $DIR/manual_clamp.rs:135:19
+  --> $DIR/manual_clamp.rs:134:19
    |
 LL |         let x16 = cmp_max(cmp_min(input, CONST_MAX), CONST_MIN);
    |                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with clamp: `input.clamp(CONST_MIN, CONST_MAX)`
@@ -232,7 +232,7 @@ LL |         let x16 = cmp_max(cmp_min(input, CONST_MAX), CONST_MIN);
    = note: clamp will panic if max < min
 
 error: clamp-like pattern without using clamp function
-  --> $DIR/manual_clamp.rs:136:19
+  --> $DIR/manual_clamp.rs:135:19
    |
 LL |         let x17 = cmp_min(cmp_max(input, CONST_MIN), CONST_MAX);
    |                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with clamp: `input.clamp(CONST_MIN, CONST_MAX)`
@@ -240,7 +240,7 @@ LL |         let x17 = cmp_min(cmp_max(input, CONST_MIN), CONST_MAX);
    = note: clamp will panic if max < min
 
 error: clamp-like pattern without using clamp function
-  --> $DIR/manual_clamp.rs:137:19
+  --> $DIR/manual_clamp.rs:136:19
    |
 LL |         let x18 = cmp_max(CONST_MIN, cmp_min(input, CONST_MAX));
    |                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with clamp: `input.clamp(CONST_MIN, CONST_MAX)`
@@ -248,7 +248,7 @@ LL |         let x18 = cmp_max(CONST_MIN, cmp_min(input, CONST_MAX));
    = note: clamp will panic if max < min
 
 error: clamp-like pattern without using clamp function
-  --> $DIR/manual_clamp.rs:138:19
+  --> $DIR/manual_clamp.rs:137:19
    |
 LL |         let x19 = cmp_min(CONST_MAX, cmp_max(input, CONST_MIN));
    |                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with clamp: `input.clamp(CONST_MIN, CONST_MAX)`
@@ -256,7 +256,7 @@ LL |         let x19 = cmp_min(CONST_MAX, cmp_max(input, CONST_MIN));
    = note: clamp will panic if max < min
 
 error: clamp-like pattern without using clamp function
-  --> $DIR/manual_clamp.rs:139:19
+  --> $DIR/manual_clamp.rs:138:19
    |
 LL |         let x20 = cmp_max(cmp_min(CONST_MAX, input), CONST_MIN);
    |                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with clamp: `input.clamp(CONST_MIN, CONST_MAX)`
@@ -264,7 +264,7 @@ LL |         let x20 = cmp_max(cmp_min(CONST_MAX, input), CONST_MIN);
    = note: clamp will panic if max < min
 
 error: clamp-like pattern without using clamp function
-  --> $DIR/manual_clamp.rs:140:19
+  --> $DIR/manual_clamp.rs:139:19
    |
 LL |         let x21 = cmp_min(cmp_max(CONST_MIN, input), CONST_MAX);
    |                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with clamp: `input.clamp(CONST_MIN, CONST_MAX)`
@@ -272,7 +272,7 @@ LL |         let x21 = cmp_min(cmp_max(CONST_MIN, input), CONST_MAX);
    = note: clamp will panic if max < min
 
 error: clamp-like pattern without using clamp function
-  --> $DIR/manual_clamp.rs:141:19
+  --> $DIR/manual_clamp.rs:140:19
    |
 LL |         let x22 = cmp_max(CONST_MIN, cmp_min(CONST_MAX, input));
    |                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with clamp: `input.clamp(CONST_MIN, CONST_MAX)`
@@ -280,7 +280,7 @@ LL |         let x22 = cmp_max(CONST_MIN, cmp_min(CONST_MAX, input));
    = note: clamp will panic if max < min
 
 error: clamp-like pattern without using clamp function
-  --> $DIR/manual_clamp.rs:142:19
+  --> $DIR/manual_clamp.rs:141:19
    |
 LL |         let x23 = cmp_min(CONST_MAX, cmp_max(CONST_MIN, input));
    |                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with clamp: `input.clamp(CONST_MIN, CONST_MAX)`
@@ -288,7 +288,7 @@ LL |         let x23 = cmp_min(CONST_MAX, cmp_max(CONST_MIN, input));
    = note: clamp will panic if max < min
 
 error: clamp-like pattern without using clamp function
-  --> $DIR/manual_clamp.rs:144:19
+  --> $DIR/manual_clamp.rs:143:19
    |
 LL |         let x24 = f64::max(f64::min(input, CONST_F64_MAX), CONST_F64_MIN);
    |                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with clamp: `input.clamp(CONST_F64_MIN, CONST_F64_MAX)`
@@ -297,7 +297,7 @@ LL |         let x24 = f64::max(f64::min(input, CONST_F64_MAX), CONST_F64_MIN);
    = note: clamp returns NaN if the input is NaN
 
 error: clamp-like pattern without using clamp function
-  --> $DIR/manual_clamp.rs:145:19
+  --> $DIR/manual_clamp.rs:144:19
    |
 LL |         let x25 = f64::min(f64::max(input, CONST_F64_MIN), CONST_F64_MAX);
    |                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with clamp: `input.clamp(CONST_F64_MIN, CONST_F64_MAX)`
@@ -306,7 +306,7 @@ LL |         let x25 = f64::min(f64::max(input, CONST_F64_MIN), CONST_F64_MAX);
    = note: clamp returns NaN if the input is NaN
 
 error: clamp-like pattern without using clamp function
-  --> $DIR/manual_clamp.rs:146:19
+  --> $DIR/manual_clamp.rs:145:19
    |
 LL |         let x26 = f64::max(CONST_F64_MIN, f64::min(input, CONST_F64_MAX));
    |                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with clamp: `input.clamp(CONST_F64_MIN, CONST_F64_MAX)`
@@ -315,7 +315,7 @@ LL |         let x26 = f64::max(CONST_F64_MIN, f64::min(input, CONST_F64_MAX));
    = note: clamp returns NaN if the input is NaN
 
 error: clamp-like pattern without using clamp function
-  --> $DIR/manual_clamp.rs:147:19
+  --> $DIR/manual_clamp.rs:146:19
    |
 LL |         let x27 = f64::min(CONST_F64_MAX, f64::max(input, CONST_F64_MIN));
    |                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with clamp: `input.clamp(CONST_F64_MIN, CONST_F64_MAX)`
@@ -324,7 +324,7 @@ LL |         let x27 = f64::min(CONST_F64_MAX, f64::max(input, CONST_F64_MIN));
    = note: clamp returns NaN if the input is NaN
 
 error: clamp-like pattern without using clamp function
-  --> $DIR/manual_clamp.rs:148:19
+  --> $DIR/manual_clamp.rs:147:19
    |
 LL |         let x28 = f64::max(f64::min(CONST_F64_MAX, input), CONST_F64_MIN);
    |                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with clamp: `input.clamp(CONST_F64_MIN, CONST_F64_MAX)`
@@ -333,7 +333,7 @@ LL |         let x28 = f64::max(f64::min(CONST_F64_MAX, input), CONST_F64_MIN);
    = note: clamp returns NaN if the input is NaN
 
 error: clamp-like pattern without using clamp function
-  --> $DIR/manual_clamp.rs:149:19
+  --> $DIR/manual_clamp.rs:148:19
    |
 LL |         let x29 = f64::min(f64::max(CONST_F64_MIN, input), CONST_F64_MAX);
    |                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with clamp: `input.clamp(CONST_F64_MIN, CONST_F64_MAX)`
@@ -342,7 +342,7 @@ LL |         let x29 = f64::min(f64::max(CONST_F64_MIN, input), CONST_F64_MAX);
    = note: clamp returns NaN if the input is NaN
 
 error: clamp-like pattern without using clamp function
-  --> $DIR/manual_clamp.rs:150:19
+  --> $DIR/manual_clamp.rs:149:19
    |
 LL |         let x30 = f64::max(CONST_F64_MIN, f64::min(CONST_F64_MAX, input));
    |                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with clamp: `input.clamp(CONST_F64_MIN, CONST_F64_MAX)`
@@ -351,7 +351,7 @@ LL |         let x30 = f64::max(CONST_F64_MIN, f64::min(CONST_F64_MAX, input));
    = note: clamp returns NaN if the input is NaN
 
 error: clamp-like pattern without using clamp function
-  --> $DIR/manual_clamp.rs:151:19
+  --> $DIR/manual_clamp.rs:150:19
    |
 LL |         let x31 = f64::min(CONST_F64_MAX, f64::max(CONST_F64_MIN, input));
    |                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with clamp: `input.clamp(CONST_F64_MIN, CONST_F64_MAX)`
@@ -360,7 +360,7 @@ LL |         let x31 = f64::min(CONST_F64_MAX, f64::max(CONST_F64_MIN, input));
    = note: clamp returns NaN if the input is NaN
 
 error: clamp-like pattern without using clamp function
-  --> $DIR/manual_clamp.rs:154:5
+  --> $DIR/manual_clamp.rs:153:5
    |
 LL | /     if x32 < min {
 LL | |         x32 = min;
@@ -372,7 +372,7 @@ LL | |     }
    = note: clamp will panic if max < min
 
 error: clamp-like pattern without using clamp function
-  --> $DIR/manual_clamp.rs:324:13
+  --> $DIR/manual_clamp.rs:321:13
    |
 LL |       let _ = if input < min {
    |  _____________^
diff --git a/src/tools/clippy/tests/ui/manual_is_ascii_check.fixed b/src/tools/clippy/tests/ui/manual_is_ascii_check.fixed
index 765bb785994..231ba83b142 100644
--- a/src/tools/clippy/tests/ui/manual_is_ascii_check.fixed
+++ b/src/tools/clippy/tests/ui/manual_is_ascii_check.fixed
@@ -1,6 +1,5 @@
 // run-rustfix
 
-#![feature(custom_inner_attributes)]
 #![allow(unused, dead_code)]
 #![warn(clippy::manual_is_ascii_check)]
 
@@ -18,28 +17,26 @@ fn main() {
     assert!(matches!('x', 'A'..='Z' | 'a'..='z' | '_'));
 }
 
+#[clippy::msrv = "1.23"]
 fn msrv_1_23() {
-    #![clippy::msrv = "1.23"]
-
     assert!(matches!(b'1', b'0'..=b'9'));
     assert!(matches!('X', 'A'..='Z'));
     assert!(matches!('x', 'A'..='Z' | 'a'..='z'));
 }
 
+#[clippy::msrv = "1.24"]
 fn msrv_1_24() {
-    #![clippy::msrv = "1.24"]
-
     assert!(b'1'.is_ascii_digit());
     assert!('X'.is_ascii_uppercase());
     assert!('x'.is_ascii_alphabetic());
 }
 
+#[clippy::msrv = "1.46"]
 fn msrv_1_46() {
-    #![clippy::msrv = "1.46"]
     const FOO: bool = matches!('x', '0'..='9');
 }
 
+#[clippy::msrv = "1.47"]
 fn msrv_1_47() {
-    #![clippy::msrv = "1.47"]
     const FOO: bool = 'x'.is_ascii_digit();
 }
diff --git a/src/tools/clippy/tests/ui/manual_is_ascii_check.rs b/src/tools/clippy/tests/ui/manual_is_ascii_check.rs
index be133161041..39ee6151c56 100644
--- a/src/tools/clippy/tests/ui/manual_is_ascii_check.rs
+++ b/src/tools/clippy/tests/ui/manual_is_ascii_check.rs
@@ -1,6 +1,5 @@
 // run-rustfix
 
-#![feature(custom_inner_attributes)]
 #![allow(unused, dead_code)]
 #![warn(clippy::manual_is_ascii_check)]
 
@@ -18,28 +17,26 @@ fn main() {
     assert!(matches!('x', 'A'..='Z' | 'a'..='z' | '_'));
 }
 
+#[clippy::msrv = "1.23"]
 fn msrv_1_23() {
-    #![clippy::msrv = "1.23"]
-
     assert!(matches!(b'1', b'0'..=b'9'));
     assert!(matches!('X', 'A'..='Z'));
     assert!(matches!('x', 'A'..='Z' | 'a'..='z'));
 }
 
+#[clippy::msrv = "1.24"]
 fn msrv_1_24() {
-    #![clippy::msrv = "1.24"]
-
     assert!(matches!(b'1', b'0'..=b'9'));
     assert!(matches!('X', 'A'..='Z'));
     assert!(matches!('x', 'A'..='Z' | 'a'..='z'));
 }
 
+#[clippy::msrv = "1.46"]
 fn msrv_1_46() {
-    #![clippy::msrv = "1.46"]
     const FOO: bool = matches!('x', '0'..='9');
 }
 
+#[clippy::msrv = "1.47"]
 fn msrv_1_47() {
-    #![clippy::msrv = "1.47"]
     const FOO: bool = matches!('x', '0'..='9');
 }
diff --git a/src/tools/clippy/tests/ui/manual_is_ascii_check.stderr b/src/tools/clippy/tests/ui/manual_is_ascii_check.stderr
index c0a9d4db1a1..397cbe05c82 100644
--- a/src/tools/clippy/tests/ui/manual_is_ascii_check.stderr
+++ b/src/tools/clippy/tests/ui/manual_is_ascii_check.stderr
@@ -1,5 +1,5 @@
 error: manual check for common ascii range
-  --> $DIR/manual_is_ascii_check.rs:8:13
+  --> $DIR/manual_is_ascii_check.rs:7:13
    |
 LL |     assert!(matches!('x', 'a'..='z'));
    |             ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `'x'.is_ascii_lowercase()`
@@ -7,61 +7,61 @@ LL |     assert!(matches!('x', 'a'..='z'));
    = note: `-D clippy::manual-is-ascii-check` implied by `-D warnings`
 
 error: manual check for common ascii range
-  --> $DIR/manual_is_ascii_check.rs:9:13
+  --> $DIR/manual_is_ascii_check.rs:8:13
    |
 LL |     assert!(matches!('X', 'A'..='Z'));
    |             ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `'X'.is_ascii_uppercase()`
 
 error: manual check for common ascii range
-  --> $DIR/manual_is_ascii_check.rs:10:13
+  --> $DIR/manual_is_ascii_check.rs:9:13
    |
 LL |     assert!(matches!(b'x', b'a'..=b'z'));
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `b'x'.is_ascii_lowercase()`
 
 error: manual check for common ascii range
-  --> $DIR/manual_is_ascii_check.rs:11:13
+  --> $DIR/manual_is_ascii_check.rs:10:13
    |
 LL |     assert!(matches!(b'X', b'A'..=b'Z'));
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `b'X'.is_ascii_uppercase()`
 
 error: manual check for common ascii range
-  --> $DIR/manual_is_ascii_check.rs:14:13
+  --> $DIR/manual_is_ascii_check.rs:13:13
    |
 LL |     assert!(matches!(num, '0'..='9'));
    |             ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `num.is_ascii_digit()`
 
 error: manual check for common ascii range
-  --> $DIR/manual_is_ascii_check.rs:15:13
+  --> $DIR/manual_is_ascii_check.rs:14:13
    |
 LL |     assert!(matches!(b'1', b'0'..=b'9'));
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `b'1'.is_ascii_digit()`
 
 error: manual check for common ascii range
-  --> $DIR/manual_is_ascii_check.rs:16:13
+  --> $DIR/manual_is_ascii_check.rs:15:13
    |
 LL |     assert!(matches!('x', 'A'..='Z' | 'a'..='z'));
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `'x'.is_ascii_alphabetic()`
 
 error: manual check for common ascii range
-  --> $DIR/manual_is_ascii_check.rs:32:13
+  --> $DIR/manual_is_ascii_check.rs:29:13
    |
 LL |     assert!(matches!(b'1', b'0'..=b'9'));
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `b'1'.is_ascii_digit()`
 
 error: manual check for common ascii range
-  --> $DIR/manual_is_ascii_check.rs:33:13
+  --> $DIR/manual_is_ascii_check.rs:30:13
    |
 LL |     assert!(matches!('X', 'A'..='Z'));
    |             ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `'X'.is_ascii_uppercase()`
 
 error: manual check for common ascii range
-  --> $DIR/manual_is_ascii_check.rs:34:13
+  --> $DIR/manual_is_ascii_check.rs:31:13
    |
 LL |     assert!(matches!('x', 'A'..='Z' | 'a'..='z'));
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `'x'.is_ascii_alphabetic()`
 
 error: manual check for common ascii range
-  --> $DIR/manual_is_ascii_check.rs:44:23
+  --> $DIR/manual_is_ascii_check.rs:41:23
    |
 LL |     const FOO: bool = matches!('x', '0'..='9');
    |                       ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `'x'.is_ascii_digit()`
diff --git a/src/tools/clippy/tests/ui/manual_let_else.rs b/src/tools/clippy/tests/ui/manual_let_else.rs
index 2ef40e5911a..48a162c1360 100644
--- a/src/tools/clippy/tests/ui/manual_let_else.rs
+++ b/src/tools/clippy/tests/ui/manual_let_else.rs
@@ -234,4 +234,18 @@ fn not_fire() {
     // If a type annotation is present, don't lint as
     // expressing the type might be too hard
     let v: () = if let Some(v_some) = g() { v_some } else { panic!() };
+
+    // Issue 9940
+    // Suggestion should not expand macros
+    macro_rules! macro_call {
+        () => {
+            return ()
+        };
+    }
+
+    let ff = Some(1);
+    let _ = match ff {
+        Some(value) => value,
+        _ => macro_call!(),
+    };
 }
diff --git a/src/tools/clippy/tests/ui/manual_let_else.stderr b/src/tools/clippy/tests/ui/manual_let_else.stderr
index 453b68b8bd0..52aac6bc673 100644
--- a/src/tools/clippy/tests/ui/manual_let_else.stderr
+++ b/src/tools/clippy/tests/ui/manual_let_else.stderr
@@ -259,5 +259,14 @@ LL |     create_binding_if_some!(w, g());
    |
    = note: this error originates in the macro `create_binding_if_some` (in Nightly builds, run with -Z macro-backtrace for more info)
 
-error: aborting due to 17 previous errors
+error: this could be rewritten as `let...else`
+  --> $DIR/manual_let_else.rs:247:5
+   |
+LL | /     let _ = match ff {
+LL | |         Some(value) => value,
+LL | |         _ => macro_call!(),
+LL | |     };
+   | |______^ help: consider writing: `let Some(value) = ff else { macro_call!() };`
+
+error: aborting due to 18 previous errors
 
diff --git a/src/tools/clippy/tests/ui/manual_rem_euclid.fixed b/src/tools/clippy/tests/ui/manual_rem_euclid.fixed
index b942fbfe930..4cdc0546a74 100644
--- a/src/tools/clippy/tests/ui/manual_rem_euclid.fixed
+++ b/src/tools/clippy/tests/ui/manual_rem_euclid.fixed
@@ -1,7 +1,6 @@
 // run-rustfix
 // aux-build:macro_rules.rs
 
-#![feature(custom_inner_attributes)]
 #![warn(clippy::manual_rem_euclid)]
 
 #[macro_use]
@@ -55,31 +54,27 @@ pub const fn const_rem_euclid_4(num: i32) -> i32 {
     num.rem_euclid(4)
 }
 
+#[clippy::msrv = "1.37"]
 pub fn msrv_1_37() {
-    #![clippy::msrv = "1.37"]
-
     let x: i32 = 10;
     let _: i32 = ((x % 4) + 4) % 4;
 }
 
+#[clippy::msrv = "1.38"]
 pub fn msrv_1_38() {
-    #![clippy::msrv = "1.38"]
-
     let x: i32 = 10;
     let _: i32 = x.rem_euclid(4);
 }
 
 // For const fns:
+#[clippy::msrv = "1.51"]
 pub const fn msrv_1_51() {
-    #![clippy::msrv = "1.51"]
-
     let x: i32 = 10;
     let _: i32 = ((x % 4) + 4) % 4;
 }
 
+#[clippy::msrv = "1.52"]
 pub const fn msrv_1_52() {
-    #![clippy::msrv = "1.52"]
-
     let x: i32 = 10;
     let _: i32 = x.rem_euclid(4);
 }
diff --git a/src/tools/clippy/tests/ui/manual_rem_euclid.rs b/src/tools/clippy/tests/ui/manual_rem_euclid.rs
index 7462d532169..58a9e20f38b 100644
--- a/src/tools/clippy/tests/ui/manual_rem_euclid.rs
+++ b/src/tools/clippy/tests/ui/manual_rem_euclid.rs
@@ -1,7 +1,6 @@
 // run-rustfix
 // aux-build:macro_rules.rs
 
-#![feature(custom_inner_attributes)]
 #![warn(clippy::manual_rem_euclid)]
 
 #[macro_use]
@@ -55,31 +54,27 @@ pub const fn const_rem_euclid_4(num: i32) -> i32 {
     ((num % 4) + 4) % 4
 }
 
+#[clippy::msrv = "1.37"]
 pub fn msrv_1_37() {
-    #![clippy::msrv = "1.37"]
-
     let x: i32 = 10;
     let _: i32 = ((x % 4) + 4) % 4;
 }
 
+#[clippy::msrv = "1.38"]
 pub fn msrv_1_38() {
-    #![clippy::msrv = "1.38"]
-
     let x: i32 = 10;
     let _: i32 = ((x % 4) + 4) % 4;
 }
 
 // For const fns:
+#[clippy::msrv = "1.51"]
 pub const fn msrv_1_51() {
-    #![clippy::msrv = "1.51"]
-
     let x: i32 = 10;
     let _: i32 = ((x % 4) + 4) % 4;
 }
 
+#[clippy::msrv = "1.52"]
 pub const fn msrv_1_52() {
-    #![clippy::msrv = "1.52"]
-
     let x: i32 = 10;
     let _: i32 = ((x % 4) + 4) % 4;
 }
diff --git a/src/tools/clippy/tests/ui/manual_rem_euclid.stderr b/src/tools/clippy/tests/ui/manual_rem_euclid.stderr
index d51bac03b56..e3122a588b6 100644
--- a/src/tools/clippy/tests/ui/manual_rem_euclid.stderr
+++ b/src/tools/clippy/tests/ui/manual_rem_euclid.stderr
@@ -1,5 +1,5 @@
 error: manual `rem_euclid` implementation
-  --> $DIR/manual_rem_euclid.rs:20:18
+  --> $DIR/manual_rem_euclid.rs:19:18
    |
 LL |     let _: i32 = ((value % 4) + 4) % 4;
    |                  ^^^^^^^^^^^^^^^^^^^^^ help: consider using: `value.rem_euclid(4)`
@@ -7,31 +7,31 @@ LL |     let _: i32 = ((value % 4) + 4) % 4;
    = note: `-D clippy::manual-rem-euclid` implied by `-D warnings`
 
 error: manual `rem_euclid` implementation
-  --> $DIR/manual_rem_euclid.rs:21:18
+  --> $DIR/manual_rem_euclid.rs:20:18
    |
 LL |     let _: i32 = (4 + (value % 4)) % 4;
    |                  ^^^^^^^^^^^^^^^^^^^^^ help: consider using: `value.rem_euclid(4)`
 
 error: manual `rem_euclid` implementation
-  --> $DIR/manual_rem_euclid.rs:22:18
+  --> $DIR/manual_rem_euclid.rs:21:18
    |
 LL |     let _: i32 = (value % 4 + 4) % 4;
    |                  ^^^^^^^^^^^^^^^^^^^ help: consider using: `value.rem_euclid(4)`
 
 error: manual `rem_euclid` implementation
-  --> $DIR/manual_rem_euclid.rs:23:18
+  --> $DIR/manual_rem_euclid.rs:22:18
    |
 LL |     let _: i32 = (4 + value % 4) % 4;
    |                  ^^^^^^^^^^^^^^^^^^^ help: consider using: `value.rem_euclid(4)`
 
 error: manual `rem_euclid` implementation
-  --> $DIR/manual_rem_euclid.rs:24:22
+  --> $DIR/manual_rem_euclid.rs:23:22
    |
 LL |     let _: i32 = 1 + (4 + value % 4) % 4;
    |                      ^^^^^^^^^^^^^^^^^^^ help: consider using: `value.rem_euclid(4)`
 
 error: manual `rem_euclid` implementation
-  --> $DIR/manual_rem_euclid.rs:13:22
+  --> $DIR/manual_rem_euclid.rs:12:22
    |
 LL |         let _: i32 = ((value % 4) + 4) % 4;
    |                      ^^^^^^^^^^^^^^^^^^^^^ help: consider using: `value.rem_euclid(4)`
@@ -42,25 +42,25 @@ LL |     internal_rem_euclid!();
    = note: this error originates in the macro `internal_rem_euclid` (in Nightly builds, run with -Z macro-backtrace for more info)
 
 error: manual `rem_euclid` implementation
-  --> $DIR/manual_rem_euclid.rs:50:5
+  --> $DIR/manual_rem_euclid.rs:49:5
    |
 LL |     ((num % 4) + 4) % 4
    |     ^^^^^^^^^^^^^^^^^^^ help: consider using: `num.rem_euclid(4)`
 
 error: manual `rem_euclid` implementation
-  --> $DIR/manual_rem_euclid.rs:55:5
+  --> $DIR/manual_rem_euclid.rs:54:5
    |
 LL |     ((num % 4) + 4) % 4
    |     ^^^^^^^^^^^^^^^^^^^ help: consider using: `num.rem_euclid(4)`
 
 error: manual `rem_euclid` implementation
-  --> $DIR/manual_rem_euclid.rs:69:18
+  --> $DIR/manual_rem_euclid.rs:66:18
    |
 LL |     let _: i32 = ((x % 4) + 4) % 4;
    |                  ^^^^^^^^^^^^^^^^^ help: consider using: `x.rem_euclid(4)`
 
 error: manual `rem_euclid` implementation
-  --> $DIR/manual_rem_euclid.rs:84:18
+  --> $DIR/manual_rem_euclid.rs:79:18
    |
 LL |     let _: i32 = ((x % 4) + 4) % 4;
    |                  ^^^^^^^^^^^^^^^^^ help: consider using: `x.rem_euclid(4)`
diff --git a/src/tools/clippy/tests/ui/manual_retain.fixed b/src/tools/clippy/tests/ui/manual_retain.fixed
index fba503a2066..e5ae3cf3e50 100644
--- a/src/tools/clippy/tests/ui/manual_retain.fixed
+++ b/src/tools/clippy/tests/ui/manual_retain.fixed
@@ -1,5 +1,4 @@
 // run-rustfix
-#![feature(custom_inner_attributes)]
 #![warn(clippy::manual_retain)]
 #![allow(unused)]
 use std::collections::BTreeMap;
@@ -216,8 +215,8 @@ fn vec_deque_retain() {
     bar = foobar.into_iter().filter(|x| x % 2 == 0).collect();
 }
 
+#[clippy::msrv = "1.52"]
 fn _msrv_153() {
-    #![clippy::msrv = "1.52"]
     let mut btree_map: BTreeMap<i8, i8> = (0..8).map(|x| (x, x * 10)).collect();
     btree_map = btree_map.into_iter().filter(|(k, _)| k % 2 == 0).collect();
 
@@ -225,14 +224,14 @@ fn _msrv_153() {
     btree_set = btree_set.iter().filter(|&x| x % 2 == 0).copied().collect();
 }
 
+#[clippy::msrv = "1.25"]
 fn _msrv_126() {
-    #![clippy::msrv = "1.25"]
     let mut s = String::from("foobar");
     s = s.chars().filter(|&c| c != 'o').to_owned().collect();
 }
 
+#[clippy::msrv = "1.17"]
 fn _msrv_118() {
-    #![clippy::msrv = "1.17"]
     let mut hash_set = HashSet::from([1, 2, 3, 4, 5, 6]);
     hash_set = hash_set.into_iter().filter(|x| x % 2 == 0).collect();
     let mut hash_map: HashMap<i8, i8> = (0..8).map(|x| (x, x * 10)).collect();
diff --git a/src/tools/clippy/tests/ui/manual_retain.rs b/src/tools/clippy/tests/ui/manual_retain.rs
index 81a849fe768..1021f15edd1 100644
--- a/src/tools/clippy/tests/ui/manual_retain.rs
+++ b/src/tools/clippy/tests/ui/manual_retain.rs
@@ -1,5 +1,4 @@
 // run-rustfix
-#![feature(custom_inner_attributes)]
 #![warn(clippy::manual_retain)]
 #![allow(unused)]
 use std::collections::BTreeMap;
@@ -222,8 +221,8 @@ fn vec_deque_retain() {
     bar = foobar.into_iter().filter(|x| x % 2 == 0).collect();
 }
 
+#[clippy::msrv = "1.52"]
 fn _msrv_153() {
-    #![clippy::msrv = "1.52"]
     let mut btree_map: BTreeMap<i8, i8> = (0..8).map(|x| (x, x * 10)).collect();
     btree_map = btree_map.into_iter().filter(|(k, _)| k % 2 == 0).collect();
 
@@ -231,14 +230,14 @@ fn _msrv_153() {
     btree_set = btree_set.iter().filter(|&x| x % 2 == 0).copied().collect();
 }
 
+#[clippy::msrv = "1.25"]
 fn _msrv_126() {
-    #![clippy::msrv = "1.25"]
     let mut s = String::from("foobar");
     s = s.chars().filter(|&c| c != 'o').to_owned().collect();
 }
 
+#[clippy::msrv = "1.17"]
 fn _msrv_118() {
-    #![clippy::msrv = "1.17"]
     let mut hash_set = HashSet::from([1, 2, 3, 4, 5, 6]);
     hash_set = hash_set.into_iter().filter(|x| x % 2 == 0).collect();
     let mut hash_map: HashMap<i8, i8> = (0..8).map(|x| (x, x * 10)).collect();
diff --git a/src/tools/clippy/tests/ui/manual_retain.stderr b/src/tools/clippy/tests/ui/manual_retain.stderr
index ec635919b48..89316ce1d99 100644
--- a/src/tools/clippy/tests/ui/manual_retain.stderr
+++ b/src/tools/clippy/tests/ui/manual_retain.stderr
@@ -1,5 +1,5 @@
 error: this expression can be written more simply using `.retain()`
-  --> $DIR/manual_retain.rs:52:5
+  --> $DIR/manual_retain.rs:51:5
    |
 LL |     btree_map = btree_map.into_iter().filter(|(k, _)| k % 2 == 0).collect();
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `btree_map.retain(|k, _| k % 2 == 0)`
@@ -7,13 +7,13 @@ LL |     btree_map = btree_map.into_iter().filter(|(k, _)| k % 2 == 0).collect()
    = note: `-D clippy::manual-retain` implied by `-D warnings`
 
 error: this expression can be written more simply using `.retain()`
-  --> $DIR/manual_retain.rs:53:5
+  --> $DIR/manual_retain.rs:52:5
    |
 LL |     btree_map = btree_map.into_iter().filter(|(_, v)| v % 2 == 0).collect();
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `btree_map.retain(|_, &mut v| v % 2 == 0)`
 
 error: this expression can be written more simply using `.retain()`
-  --> $DIR/manual_retain.rs:54:5
+  --> $DIR/manual_retain.rs:53:5
    |
 LL | /     btree_map = btree_map
 LL | |         .into_iter()
@@ -22,37 +22,37 @@ LL | |         .collect();
    | |__________________^ help: consider calling `.retain()` instead: `btree_map.retain(|k, &mut v| (k % 2 == 0) && (v % 2 == 0))`
 
 error: this expression can be written more simply using `.retain()`
-  --> $DIR/manual_retain.rs:76:5
+  --> $DIR/manual_retain.rs:75:5
    |
 LL |     btree_set = btree_set.iter().filter(|&x| x % 2 == 0).copied().collect();
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `btree_set.retain(|x| x % 2 == 0)`
 
 error: this expression can be written more simply using `.retain()`
-  --> $DIR/manual_retain.rs:77:5
+  --> $DIR/manual_retain.rs:76:5
    |
 LL |     btree_set = btree_set.iter().filter(|&x| x % 2 == 0).cloned().collect();
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `btree_set.retain(|x| x % 2 == 0)`
 
 error: this expression can be written more simply using `.retain()`
-  --> $DIR/manual_retain.rs:78:5
+  --> $DIR/manual_retain.rs:77:5
    |
 LL |     btree_set = btree_set.into_iter().filter(|x| x % 2 == 0).collect();
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `btree_set.retain(|x| x % 2 == 0)`
 
 error: this expression can be written more simply using `.retain()`
-  --> $DIR/manual_retain.rs:108:5
+  --> $DIR/manual_retain.rs:107:5
    |
 LL |     hash_map = hash_map.into_iter().filter(|(k, _)| k % 2 == 0).collect();
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `hash_map.retain(|k, _| k % 2 == 0)`
 
 error: this expression can be written more simply using `.retain()`
-  --> $DIR/manual_retain.rs:109:5
+  --> $DIR/manual_retain.rs:108:5
    |
 LL |     hash_map = hash_map.into_iter().filter(|(_, v)| v % 2 == 0).collect();
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `hash_map.retain(|_, &mut v| v % 2 == 0)`
 
 error: this expression can be written more simply using `.retain()`
-  --> $DIR/manual_retain.rs:110:5
+  --> $DIR/manual_retain.rs:109:5
    |
 LL | /     hash_map = hash_map
 LL | |         .into_iter()
@@ -61,61 +61,61 @@ LL | |         .collect();
    | |__________________^ help: consider calling `.retain()` instead: `hash_map.retain(|k, &mut v| (k % 2 == 0) && (v % 2 == 0))`
 
 error: this expression can be written more simply using `.retain()`
-  --> $DIR/manual_retain.rs:131:5
+  --> $DIR/manual_retain.rs:130:5
    |
 LL |     hash_set = hash_set.into_iter().filter(|x| x % 2 == 0).collect();
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `hash_set.retain(|x| x % 2 == 0)`
 
 error: this expression can be written more simply using `.retain()`
-  --> $DIR/manual_retain.rs:132:5
+  --> $DIR/manual_retain.rs:131:5
    |
 LL |     hash_set = hash_set.iter().filter(|&x| x % 2 == 0).copied().collect();
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `hash_set.retain(|x| x % 2 == 0)`
 
 error: this expression can be written more simply using `.retain()`
-  --> $DIR/manual_retain.rs:133:5
+  --> $DIR/manual_retain.rs:132:5
    |
 LL |     hash_set = hash_set.iter().filter(|&x| x % 2 == 0).cloned().collect();
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `hash_set.retain(|x| x % 2 == 0)`
 
 error: this expression can be written more simply using `.retain()`
-  --> $DIR/manual_retain.rs:162:5
+  --> $DIR/manual_retain.rs:161:5
    |
 LL |     s = s.chars().filter(|&c| c != 'o').to_owned().collect();
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `s.retain(|c| c != 'o')`
 
 error: this expression can be written more simply using `.retain()`
-  --> $DIR/manual_retain.rs:174:5
+  --> $DIR/manual_retain.rs:173:5
    |
 LL |     vec = vec.iter().filter(|&x| x % 2 == 0).copied().collect();
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `vec.retain(|x| x % 2 == 0)`
 
 error: this expression can be written more simply using `.retain()`
-  --> $DIR/manual_retain.rs:175:5
+  --> $DIR/manual_retain.rs:174:5
    |
 LL |     vec = vec.iter().filter(|&x| x % 2 == 0).cloned().collect();
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `vec.retain(|x| x % 2 == 0)`
 
 error: this expression can be written more simply using `.retain()`
-  --> $DIR/manual_retain.rs:176:5
+  --> $DIR/manual_retain.rs:175:5
    |
 LL |     vec = vec.into_iter().filter(|x| x % 2 == 0).collect();
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `vec.retain(|x| x % 2 == 0)`
 
 error: this expression can be written more simply using `.retain()`
-  --> $DIR/manual_retain.rs:198:5
+  --> $DIR/manual_retain.rs:197:5
    |
 LL |     vec_deque = vec_deque.iter().filter(|&x| x % 2 == 0).copied().collect();
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `vec_deque.retain(|x| x % 2 == 0)`
 
 error: this expression can be written more simply using `.retain()`
-  --> $DIR/manual_retain.rs:199:5
+  --> $DIR/manual_retain.rs:198:5
    |
 LL |     vec_deque = vec_deque.iter().filter(|&x| x % 2 == 0).cloned().collect();
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `vec_deque.retain(|x| x % 2 == 0)`
 
 error: this expression can be written more simply using `.retain()`
-  --> $DIR/manual_retain.rs:200:5
+  --> $DIR/manual_retain.rs:199:5
    |
 LL |     vec_deque = vec_deque.into_iter().filter(|x| x % 2 == 0).collect();
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `vec_deque.retain(|x| x % 2 == 0)`
diff --git a/src/tools/clippy/tests/ui/manual_split_once.fixed b/src/tools/clippy/tests/ui/manual_split_once.fixed
index c7ca770434a..50b02019cc2 100644
--- a/src/tools/clippy/tests/ui/manual_split_once.fixed
+++ b/src/tools/clippy/tests/ui/manual_split_once.fixed
@@ -1,6 +1,5 @@
 // run-rustfix
 
-#![feature(custom_inner_attributes)]
 #![warn(clippy::manual_split_once)]
 #![allow(unused, clippy::iter_skip_next, clippy::iter_nth_zero)]
 
@@ -127,8 +126,8 @@ fn indirect() -> Option<()> {
     None
 }
 
+#[clippy::msrv = "1.51"]
 fn _msrv_1_51() {
-    #![clippy::msrv = "1.51"]
     // `str::split_once` was stabilized in 1.52. Do not lint this
     let _ = "key=value".splitn(2, '=').nth(1).unwrap();
 
@@ -137,8 +136,8 @@ fn _msrv_1_51() {
     let b = iter.next().unwrap();
 }
 
+#[clippy::msrv = "1.52"]
 fn _msrv_1_52() {
-    #![clippy::msrv = "1.52"]
     let _ = "key=value".split_once('=').unwrap().1;
 
     let (a, b) = "a.b.c".split_once('.').unwrap();
diff --git a/src/tools/clippy/tests/ui/manual_split_once.rs b/src/tools/clippy/tests/ui/manual_split_once.rs
index ee2848a251e..e1e8b71a9de 100644
--- a/src/tools/clippy/tests/ui/manual_split_once.rs
+++ b/src/tools/clippy/tests/ui/manual_split_once.rs
@@ -1,6 +1,5 @@
 // run-rustfix
 
-#![feature(custom_inner_attributes)]
 #![warn(clippy::manual_split_once)]
 #![allow(unused, clippy::iter_skip_next, clippy::iter_nth_zero)]
 
@@ -127,8 +126,8 @@ fn indirect() -> Option<()> {
     None
 }
 
+#[clippy::msrv = "1.51"]
 fn _msrv_1_51() {
-    #![clippy::msrv = "1.51"]
     // `str::split_once` was stabilized in 1.52. Do not lint this
     let _ = "key=value".splitn(2, '=').nth(1).unwrap();
 
@@ -137,8 +136,8 @@ fn _msrv_1_51() {
     let b = iter.next().unwrap();
 }
 
+#[clippy::msrv = "1.52"]
 fn _msrv_1_52() {
-    #![clippy::msrv = "1.52"]
     let _ = "key=value".splitn(2, '=').nth(1).unwrap();
 
     let mut iter = "a.b.c".splitn(2, '.');
diff --git a/src/tools/clippy/tests/ui/manual_split_once.stderr b/src/tools/clippy/tests/ui/manual_split_once.stderr
index 2696694680a..78da5a16cc5 100644
--- a/src/tools/clippy/tests/ui/manual_split_once.stderr
+++ b/src/tools/clippy/tests/ui/manual_split_once.stderr
@@ -1,5 +1,5 @@
 error: manual implementation of `split_once`
-  --> $DIR/manual_split_once.rs:14:13
+  --> $DIR/manual_split_once.rs:13:13
    |
 LL |     let _ = "key=value".splitn(2, '=').nth(1).unwrap();
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `"key=value".split_once('=').unwrap().1`
@@ -7,79 +7,79 @@ LL |     let _ = "key=value".splitn(2, '=').nth(1).unwrap();
    = note: `-D clippy::manual-split-once` implied by `-D warnings`
 
 error: manual implementation of `split_once`
-  --> $DIR/manual_split_once.rs:15:13
+  --> $DIR/manual_split_once.rs:14:13
    |
 LL |     let _ = "key=value".splitn(2, '=').skip(1).next().unwrap();
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `"key=value".split_once('=').unwrap().1`
 
 error: manual implementation of `split_once`
-  --> $DIR/manual_split_once.rs:16:18
+  --> $DIR/manual_split_once.rs:15:18
    |
 LL |     let (_, _) = "key=value".splitn(2, '=').next_tuple().unwrap();
    |                  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `"key=value".split_once('=')`
 
 error: manual implementation of `split_once`
-  --> $DIR/manual_split_once.rs:19:13
+  --> $DIR/manual_split_once.rs:18:13
    |
 LL |     let _ = s.splitn(2, '=').nth(1).unwrap();
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `s.split_once('=').unwrap().1`
 
 error: manual implementation of `split_once`
-  --> $DIR/manual_split_once.rs:22:13
+  --> $DIR/manual_split_once.rs:21:13
    |
 LL |     let _ = s.splitn(2, '=').nth(1).unwrap();
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `s.split_once('=').unwrap().1`
 
 error: manual implementation of `split_once`
-  --> $DIR/manual_split_once.rs:25:13
+  --> $DIR/manual_split_once.rs:24:13
    |
 LL |     let _ = s.splitn(2, '=').skip(1).next().unwrap();
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `s.split_once('=').unwrap().1`
 
 error: manual implementation of `split_once`
-  --> $DIR/manual_split_once.rs:28:17
+  --> $DIR/manual_split_once.rs:27:17
    |
 LL |         let _ = s.splitn(2, '=').nth(1)?;
    |                 ^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `s.split_once('=')?.1`
 
 error: manual implementation of `split_once`
-  --> $DIR/manual_split_once.rs:29:17
+  --> $DIR/manual_split_once.rs:28:17
    |
 LL |         let _ = s.splitn(2, '=').skip(1).next()?;
    |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `s.split_once('=')?.1`
 
 error: manual implementation of `rsplit_once`
-  --> $DIR/manual_split_once.rs:30:17
+  --> $DIR/manual_split_once.rs:29:17
    |
 LL |         let _ = s.rsplitn(2, '=').nth(1)?;
    |                 ^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `s.rsplit_once('=')?.0`
 
 error: manual implementation of `rsplit_once`
-  --> $DIR/manual_split_once.rs:31:17
+  --> $DIR/manual_split_once.rs:30:17
    |
 LL |         let _ = s.rsplitn(2, '=').skip(1).next()?;
    |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `s.rsplit_once('=')?.0`
 
 error: manual implementation of `rsplit_once`
-  --> $DIR/manual_split_once.rs:39:13
+  --> $DIR/manual_split_once.rs:38:13
    |
 LL |     let _ = "key=value".rsplitn(2, '=').nth(1).unwrap();
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `"key=value".rsplit_once('=').unwrap().0`
 
 error: manual implementation of `rsplit_once`
-  --> $DIR/manual_split_once.rs:40:18
+  --> $DIR/manual_split_once.rs:39:18
    |
 LL |     let (_, _) = "key=value".rsplitn(2, '=').next_tuple().unwrap();
    |                  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `"key=value".rsplit_once('=').map(|(x, y)| (y, x))`
 
 error: manual implementation of `rsplit_once`
-  --> $DIR/manual_split_once.rs:41:13
+  --> $DIR/manual_split_once.rs:40:13
    |
 LL |     let _ = s.rsplitn(2, '=').nth(1);
    |             ^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `s.rsplit_once('=').map(|x| x.0)`
 
 error: manual implementation of `split_once`
-  --> $DIR/manual_split_once.rs:45:5
+  --> $DIR/manual_split_once.rs:44:5
    |
 LL |     let mut iter = "a.b.c".splitn(2, '.');
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -104,7 +104,7 @@ LL +
    |
 
 error: manual implementation of `split_once`
-  --> $DIR/manual_split_once.rs:49:5
+  --> $DIR/manual_split_once.rs:48:5
    |
 LL |     let mut iter = "a.b.c".splitn(2, '.');
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -129,7 +129,7 @@ LL +
    |
 
 error: manual implementation of `rsplit_once`
-  --> $DIR/manual_split_once.rs:53:5
+  --> $DIR/manual_split_once.rs:52:5
    |
 LL |     let mut iter = "a.b.c".rsplitn(2, '.');
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -154,7 +154,7 @@ LL +
    |
 
 error: manual implementation of `rsplit_once`
-  --> $DIR/manual_split_once.rs:57:5
+  --> $DIR/manual_split_once.rs:56:5
    |
 LL |     let mut iter = "a.b.c".rsplitn(2, '.');
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -179,13 +179,13 @@ LL +
    |
 
 error: manual implementation of `split_once`
-  --> $DIR/manual_split_once.rs:142:13
+  --> $DIR/manual_split_once.rs:141:13
    |
 LL |     let _ = "key=value".splitn(2, '=').nth(1).unwrap();
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `"key=value".split_once('=').unwrap().1`
 
 error: manual implementation of `split_once`
-  --> $DIR/manual_split_once.rs:144:5
+  --> $DIR/manual_split_once.rs:143:5
    |
 LL |     let mut iter = "a.b.c".splitn(2, '.');
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
diff --git a/src/tools/clippy/tests/ui/manual_str_repeat.fixed b/src/tools/clippy/tests/ui/manual_str_repeat.fixed
index 0704ba2f933..3d56f2a0ded 100644
--- a/src/tools/clippy/tests/ui/manual_str_repeat.fixed
+++ b/src/tools/clippy/tests/ui/manual_str_repeat.fixed
@@ -1,6 +1,5 @@
 // run-rustfix
 
-#![feature(custom_inner_attributes)]
 #![warn(clippy::manual_str_repeat)]
 
 use std::borrow::Cow;
@@ -54,13 +53,13 @@ fn main() {
     let _: String = repeat(x).take(count).collect();
 }
 
+#[clippy::msrv = "1.15"]
 fn _msrv_1_15() {
-    #![clippy::msrv = "1.15"]
     // `str::repeat` was stabilized in 1.16. Do not lint this
     let _: String = std::iter::repeat("test").take(10).collect();
 }
 
+#[clippy::msrv = "1.16"]
 fn _msrv_1_16() {
-    #![clippy::msrv = "1.16"]
     let _: String = "test".repeat(10);
 }
diff --git a/src/tools/clippy/tests/ui/manual_str_repeat.rs b/src/tools/clippy/tests/ui/manual_str_repeat.rs
index f522be439aa..e8240a949db 100644
--- a/src/tools/clippy/tests/ui/manual_str_repeat.rs
+++ b/src/tools/clippy/tests/ui/manual_str_repeat.rs
@@ -1,6 +1,5 @@
 // run-rustfix
 
-#![feature(custom_inner_attributes)]
 #![warn(clippy::manual_str_repeat)]
 
 use std::borrow::Cow;
@@ -54,13 +53,13 @@ fn main() {
     let _: String = repeat(x).take(count).collect();
 }
 
+#[clippy::msrv = "1.15"]
 fn _msrv_1_15() {
-    #![clippy::msrv = "1.15"]
     // `str::repeat` was stabilized in 1.16. Do not lint this
     let _: String = std::iter::repeat("test").take(10).collect();
 }
 
+#[clippy::msrv = "1.16"]
 fn _msrv_1_16() {
-    #![clippy::msrv = "1.16"]
     let _: String = std::iter::repeat("test").take(10).collect();
 }
diff --git a/src/tools/clippy/tests/ui/manual_str_repeat.stderr b/src/tools/clippy/tests/ui/manual_str_repeat.stderr
index c6511689716..bdfee7cab26 100644
--- a/src/tools/clippy/tests/ui/manual_str_repeat.stderr
+++ b/src/tools/clippy/tests/ui/manual_str_repeat.stderr
@@ -1,5 +1,5 @@
 error: manual implementation of `str::repeat` using iterators
-  --> $DIR/manual_str_repeat.rs:10:21
+  --> $DIR/manual_str_repeat.rs:9:21
    |
 LL |     let _: String = std::iter::repeat("test").take(10).collect();
    |                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `"test".repeat(10)`
@@ -7,55 +7,55 @@ LL |     let _: String = std::iter::repeat("test").take(10).collect();
    = note: `-D clippy::manual-str-repeat` implied by `-D warnings`
 
 error: manual implementation of `str::repeat` using iterators
-  --> $DIR/manual_str_repeat.rs:11:21
+  --> $DIR/manual_str_repeat.rs:10:21
    |
 LL |     let _: String = std::iter::repeat('x').take(10).collect();
    |                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `"x".repeat(10)`
 
 error: manual implementation of `str::repeat` using iterators
-  --> $DIR/manual_str_repeat.rs:12:21
+  --> $DIR/manual_str_repeat.rs:11:21
    |
 LL |     let _: String = std::iter::repeat('/'').take(10).collect();
    |                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `"'".repeat(10)`
 
 error: manual implementation of `str::repeat` using iterators
-  --> $DIR/manual_str_repeat.rs:13:21
+  --> $DIR/manual_str_repeat.rs:12:21
    |
 LL |     let _: String = std::iter::repeat('"').take(10).collect();
    |                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `"/"".repeat(10)`
 
 error: manual implementation of `str::repeat` using iterators
-  --> $DIR/manual_str_repeat.rs:17:13
+  --> $DIR/manual_str_repeat.rs:16:13
    |
 LL |     let _ = repeat(x).take(count + 2).collect::<String>();
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `x.repeat(count + 2)`
 
 error: manual implementation of `str::repeat` using iterators
-  --> $DIR/manual_str_repeat.rs:26:21
+  --> $DIR/manual_str_repeat.rs:25:21
    |
 LL |     let _: String = repeat(*x).take(count).collect();
    |                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `(*x).repeat(count)`
 
 error: manual implementation of `str::repeat` using iterators
-  --> $DIR/manual_str_repeat.rs:35:21
+  --> $DIR/manual_str_repeat.rs:34:21
    |
 LL |     let _: String = repeat(x).take(count).collect();
    |                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `x.repeat(count)`
 
 error: manual implementation of `str::repeat` using iterators
-  --> $DIR/manual_str_repeat.rs:47:21
+  --> $DIR/manual_str_repeat.rs:46:21
    |
 LL |     let _: String = repeat(Cow::Borrowed("test")).take(count).collect();
    |                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `Cow::Borrowed("test").repeat(count)`
 
 error: manual implementation of `str::repeat` using iterators
-  --> $DIR/manual_str_repeat.rs:50:21
+  --> $DIR/manual_str_repeat.rs:49:21
    |
 LL |     let _: String = repeat(x).take(count).collect();
    |                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `x.repeat(count)`
 
 error: manual implementation of `str::repeat` using iterators
-  --> $DIR/manual_str_repeat.rs:65:21
+  --> $DIR/manual_str_repeat.rs:64:21
    |
 LL |     let _: String = std::iter::repeat("test").take(10).collect();
    |                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `"test".repeat(10)`
diff --git a/src/tools/clippy/tests/ui/manual_strip.rs b/src/tools/clippy/tests/ui/manual_strip.rs
index 85009d78558..b0b1c262aee 100644
--- a/src/tools/clippy/tests/ui/manual_strip.rs
+++ b/src/tools/clippy/tests/ui/manual_strip.rs
@@ -1,4 +1,3 @@
-#![feature(custom_inner_attributes)]
 #![warn(clippy::manual_strip)]
 
 fn main() {
@@ -66,18 +65,16 @@ fn main() {
     }
 }
 
+#[clippy::msrv = "1.44"]
 fn msrv_1_44() {
-    #![clippy::msrv = "1.44"]
-
     let s = "abc";
     if s.starts_with('a') {
         s[1..].to_string();
     }
 }
 
+#[clippy::msrv = "1.45"]
 fn msrv_1_45() {
-    #![clippy::msrv = "1.45"]
-
     let s = "abc";
     if s.starts_with('a') {
         s[1..].to_string();
diff --git a/src/tools/clippy/tests/ui/manual_strip.stderr b/src/tools/clippy/tests/ui/manual_strip.stderr
index ad2a362f3e7..f592e898fc9 100644
--- a/src/tools/clippy/tests/ui/manual_strip.stderr
+++ b/src/tools/clippy/tests/ui/manual_strip.stderr
@@ -1,11 +1,11 @@
 error: stripping a prefix manually
-  --> $DIR/manual_strip.rs:8:24
+  --> $DIR/manual_strip.rs:7:24
    |
 LL |         str::to_string(&s["ab".len()..]);
    |                        ^^^^^^^^^^^^^^^^
    |
 note: the prefix was tested here
-  --> $DIR/manual_strip.rs:7:5
+  --> $DIR/manual_strip.rs:6:5
    |
 LL |     if s.starts_with("ab") {
    |     ^^^^^^^^^^^^^^^^^^^^^^^
@@ -21,13 +21,13 @@ LL ~         <stripped>.to_string();
    |
 
 error: stripping a suffix manually
-  --> $DIR/manual_strip.rs:16:24
+  --> $DIR/manual_strip.rs:15:24
    |
 LL |         str::to_string(&s[..s.len() - "bc".len()]);
    |                        ^^^^^^^^^^^^^^^^^^^^^^^^^^
    |
 note: the suffix was tested here
-  --> $DIR/manual_strip.rs:15:5
+  --> $DIR/manual_strip.rs:14:5
    |
 LL |     if s.ends_with("bc") {
    |     ^^^^^^^^^^^^^^^^^^^^^
@@ -42,13 +42,13 @@ LL ~         <stripped>.to_string();
    |
 
 error: stripping a prefix manually
-  --> $DIR/manual_strip.rs:25:24
+  --> $DIR/manual_strip.rs:24:24
    |
 LL |         str::to_string(&s[1..]);
    |                        ^^^^^^^
    |
 note: the prefix was tested here
-  --> $DIR/manual_strip.rs:24:5
+  --> $DIR/manual_strip.rs:23:5
    |
 LL |     if s.starts_with('a') {
    |     ^^^^^^^^^^^^^^^^^^^^^^
@@ -60,13 +60,13 @@ LL ~         <stripped>.to_string();
    |
 
 error: stripping a prefix manually
-  --> $DIR/manual_strip.rs:32:24
+  --> $DIR/manual_strip.rs:31:24
    |
 LL |         str::to_string(&s[prefix.len()..]);
    |                        ^^^^^^^^^^^^^^^^^^
    |
 note: the prefix was tested here
-  --> $DIR/manual_strip.rs:31:5
+  --> $DIR/manual_strip.rs:30:5
    |
 LL |     if s.starts_with(prefix) {
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -77,13 +77,13 @@ LL ~         str::to_string(<stripped>);
    |
 
 error: stripping a prefix manually
-  --> $DIR/manual_strip.rs:38:24
+  --> $DIR/manual_strip.rs:37:24
    |
 LL |         str::to_string(&s[PREFIX.len()..]);
    |                        ^^^^^^^^^^^^^^^^^^
    |
 note: the prefix was tested here
-  --> $DIR/manual_strip.rs:37:5
+  --> $DIR/manual_strip.rs:36:5
    |
 LL |     if s.starts_with(PREFIX) {
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -95,13 +95,13 @@ LL ~         str::to_string(<stripped>);
    |
 
 error: stripping a prefix manually
-  --> $DIR/manual_strip.rs:45:24
+  --> $DIR/manual_strip.rs:44:24
    |
 LL |         str::to_string(&TARGET[prefix.len()..]);
    |                        ^^^^^^^^^^^^^^^^^^^^^^^
    |
 note: the prefix was tested here
-  --> $DIR/manual_strip.rs:44:5
+  --> $DIR/manual_strip.rs:43:5
    |
 LL |     if TARGET.starts_with(prefix) {
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -112,13 +112,13 @@ LL ~         str::to_string(<stripped>);
    |
 
 error: stripping a prefix manually
-  --> $DIR/manual_strip.rs:51:9
+  --> $DIR/manual_strip.rs:50:9
    |
 LL |         s1[2..].to_uppercase();
    |         ^^^^^^^
    |
 note: the prefix was tested here
-  --> $DIR/manual_strip.rs:50:5
+  --> $DIR/manual_strip.rs:49:5
    |
 LL |     if s1.starts_with("ab") {
    |     ^^^^^^^^^^^^^^^^^^^^^^^^
@@ -129,13 +129,13 @@ LL ~         <stripped>.to_uppercase();
    |
 
 error: stripping a prefix manually
-  --> $DIR/manual_strip.rs:83:9
+  --> $DIR/manual_strip.rs:80:9
    |
 LL |         s[1..].to_string();
    |         ^^^^^^
    |
 note: the prefix was tested here
-  --> $DIR/manual_strip.rs:82:5
+  --> $DIR/manual_strip.rs:79:5
    |
 LL |     if s.starts_with('a') {
    |     ^^^^^^^^^^^^^^^^^^^^^^
diff --git a/src/tools/clippy/tests/ui/map_unwrap_or.rs b/src/tools/clippy/tests/ui/map_unwrap_or.rs
index 396b22a9abb..32631024ca5 100644
--- a/src/tools/clippy/tests/ui/map_unwrap_or.rs
+++ b/src/tools/clippy/tests/ui/map_unwrap_or.rs
@@ -1,6 +1,5 @@
 // aux-build:option_helpers.rs
 
-#![feature(custom_inner_attributes)]
 #![warn(clippy::map_unwrap_or)]
 #![allow(clippy::uninlined_format_args, clippy::unnecessary_lazy_evaluations)]
 
@@ -82,17 +81,15 @@ fn main() {
     result_methods();
 }
 
+#[clippy::msrv = "1.40"]
 fn msrv_1_40() {
-    #![clippy::msrv = "1.40"]
-
     let res: Result<i32, ()> = Ok(1);
 
     let _ = res.map(|x| x + 1).unwrap_or_else(|_e| 0);
 }
 
+#[clippy::msrv = "1.41"]
 fn msrv_1_41() {
-    #![clippy::msrv = "1.41"]
-
     let res: Result<i32, ()> = Ok(1);
 
     let _ = res.map(|x| x + 1).unwrap_or_else(|_e| 0);
diff --git a/src/tools/clippy/tests/ui/map_unwrap_or.stderr b/src/tools/clippy/tests/ui/map_unwrap_or.stderr
index d17d24a403e..41781b050fa 100644
--- a/src/tools/clippy/tests/ui/map_unwrap_or.stderr
+++ b/src/tools/clippy/tests/ui/map_unwrap_or.stderr
@@ -1,5 +1,5 @@
 error: called `map(<f>).unwrap_or(<a>)` on an `Option` value. This can be done more directly by calling `map_or(<a>, <f>)` instead
-  --> $DIR/map_unwrap_or.rs:18:13
+  --> $DIR/map_unwrap_or.rs:17:13
    |
 LL |       let _ = opt.map(|x| x + 1)
    |  _____________^
@@ -15,7 +15,7 @@ LL +     let _ = opt.map_or(0, |x| x + 1);
    |
 
 error: called `map(<f>).unwrap_or(<a>)` on an `Option` value. This can be done more directly by calling `map_or(<a>, <f>)` instead
-  --> $DIR/map_unwrap_or.rs:22:13
+  --> $DIR/map_unwrap_or.rs:21:13
    |
 LL |       let _ = opt.map(|x| {
    |  _____________^
@@ -33,7 +33,7 @@ LL ~     );
    |
 
 error: called `map(<f>).unwrap_or(<a>)` on an `Option` value. This can be done more directly by calling `map_or(<a>, <f>)` instead
-  --> $DIR/map_unwrap_or.rs:26:13
+  --> $DIR/map_unwrap_or.rs:25:13
    |
 LL |       let _ = opt.map(|x| x + 1)
    |  _____________^
@@ -50,7 +50,7 @@ LL ~         }, |x| x + 1);
    |
 
 error: called `map(<f>).unwrap_or(None)` on an `Option` value. This can be done more directly by calling `and_then(<f>)` instead
-  --> $DIR/map_unwrap_or.rs:31:13
+  --> $DIR/map_unwrap_or.rs:30:13
    |
 LL |     let _ = opt.map(|x| Some(x + 1)).unwrap_or(None);
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -62,7 +62,7 @@ LL +     let _ = opt.and_then(|x| Some(x + 1));
    |
 
 error: called `map(<f>).unwrap_or(None)` on an `Option` value. This can be done more directly by calling `and_then(<f>)` instead
-  --> $DIR/map_unwrap_or.rs:33:13
+  --> $DIR/map_unwrap_or.rs:32:13
    |
 LL |       let _ = opt.map(|x| {
    |  _____________^
@@ -80,7 +80,7 @@ LL ~     );
    |
 
 error: called `map(<f>).unwrap_or(None)` on an `Option` value. This can be done more directly by calling `and_then(<f>)` instead
-  --> $DIR/map_unwrap_or.rs:37:13
+  --> $DIR/map_unwrap_or.rs:36:13
    |
 LL |       let _ = opt
    |  _____________^
@@ -95,7 +95,7 @@ LL +         .and_then(|x| Some(x + 1));
    |
 
 error: called `map(<f>).unwrap_or(<a>)` on an `Option` value. This can be done more directly by calling `map_or(<a>, <f>)` instead
-  --> $DIR/map_unwrap_or.rs:48:13
+  --> $DIR/map_unwrap_or.rs:47:13
    |
 LL |     let _ = Some("prefix").map(|p| format!("{}.", p)).unwrap_or(id);
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -107,7 +107,7 @@ LL +     let _ = Some("prefix").map_or(id, |p| format!("{}.", p));
    |
 
 error: called `map(<f>).unwrap_or_else(<g>)` on an `Option` value. This can be done more directly by calling `map_or_else(<g>, <f>)` instead
-  --> $DIR/map_unwrap_or.rs:52:13
+  --> $DIR/map_unwrap_or.rs:51:13
    |
 LL |       let _ = opt.map(|x| {
    |  _____________^
@@ -117,7 +117,7 @@ LL | |     ).unwrap_or_else(|| 0);
    | |__________________________^
 
 error: called `map(<f>).unwrap_or_else(<g>)` on an `Option` value. This can be done more directly by calling `map_or_else(<g>, <f>)` instead
-  --> $DIR/map_unwrap_or.rs:56:13
+  --> $DIR/map_unwrap_or.rs:55:13
    |
 LL |       let _ = opt.map(|x| x + 1)
    |  _____________^
@@ -127,7 +127,7 @@ LL | |         );
    | |_________^
 
 error: called `map(<f>).unwrap_or_else(<g>)` on a `Result` value. This can be done more directly by calling `.map_or_else(<g>, <f>)` instead
-  --> $DIR/map_unwrap_or.rs:68:13
+  --> $DIR/map_unwrap_or.rs:67:13
    |
 LL |       let _ = res.map(|x| {
    |  _____________^
@@ -137,7 +137,7 @@ LL | |     ).unwrap_or_else(|_e| 0);
    | |____________________________^
 
 error: called `map(<f>).unwrap_or_else(<g>)` on a `Result` value. This can be done more directly by calling `.map_or_else(<g>, <f>)` instead
-  --> $DIR/map_unwrap_or.rs:72:13
+  --> $DIR/map_unwrap_or.rs:71:13
    |
 LL |       let _ = res.map(|x| x + 1)
    |  _____________^
@@ -147,7 +147,7 @@ LL | |         });
    | |__________^
 
 error: called `map(<f>).unwrap_or_else(<g>)` on a `Result` value. This can be done more directly by calling `.map_or_else(<g>, <f>)` instead
-  --> $DIR/map_unwrap_or.rs:98:13
+  --> $DIR/map_unwrap_or.rs:95:13
    |
 LL |     let _ = res.map(|x| x + 1).unwrap_or_else(|_e| 0);
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `res.map_or_else(|_e| 0, |x| x + 1)`
diff --git a/src/tools/clippy/tests/ui/match_expr_like_matches_macro.fixed b/src/tools/clippy/tests/ui/match_expr_like_matches_macro.fixed
index 968f462f8a0..55cd15bd5c3 100644
--- a/src/tools/clippy/tests/ui/match_expr_like_matches_macro.fixed
+++ b/src/tools/clippy/tests/ui/match_expr_like_matches_macro.fixed
@@ -1,6 +1,5 @@
 // run-rustfix
 
-#![feature(custom_inner_attributes)]
 #![warn(clippy::match_like_matches_macro)]
 #![allow(
     unreachable_patterns,
@@ -200,17 +199,15 @@ fn main() {
     };
 }
 
+#[clippy::msrv = "1.41"]
 fn msrv_1_41() {
-    #![clippy::msrv = "1.41"]
-
     let _y = match Some(5) {
         Some(0) => true,
         _ => false,
     };
 }
 
+#[clippy::msrv = "1.42"]
 fn msrv_1_42() {
-    #![clippy::msrv = "1.42"]
-
     let _y = matches!(Some(5), Some(0));
 }
diff --git a/src/tools/clippy/tests/ui/match_expr_like_matches_macro.rs b/src/tools/clippy/tests/ui/match_expr_like_matches_macro.rs
index c6b479e27c5..5d645e108e5 100644
--- a/src/tools/clippy/tests/ui/match_expr_like_matches_macro.rs
+++ b/src/tools/clippy/tests/ui/match_expr_like_matches_macro.rs
@@ -1,6 +1,5 @@
 // run-rustfix
 
-#![feature(custom_inner_attributes)]
 #![warn(clippy::match_like_matches_macro)]
 #![allow(
     unreachable_patterns,
@@ -241,18 +240,16 @@ fn main() {
     };
 }
 
+#[clippy::msrv = "1.41"]
 fn msrv_1_41() {
-    #![clippy::msrv = "1.41"]
-
     let _y = match Some(5) {
         Some(0) => true,
         _ => false,
     };
 }
 
+#[clippy::msrv = "1.42"]
 fn msrv_1_42() {
-    #![clippy::msrv = "1.42"]
-
     let _y = match Some(5) {
         Some(0) => true,
         _ => false,
diff --git a/src/tools/clippy/tests/ui/match_expr_like_matches_macro.stderr b/src/tools/clippy/tests/ui/match_expr_like_matches_macro.stderr
index a4df8008ac2..46f67ef4900 100644
--- a/src/tools/clippy/tests/ui/match_expr_like_matches_macro.stderr
+++ b/src/tools/clippy/tests/ui/match_expr_like_matches_macro.stderr
@@ -1,5 +1,5 @@
 error: match expression looks like `matches!` macro
-  --> $DIR/match_expr_like_matches_macro.rs:16:14
+  --> $DIR/match_expr_like_matches_macro.rs:15:14
    |
 LL |       let _y = match x {
    |  ______________^
@@ -11,7 +11,7 @@ LL | |     };
    = note: `-D clippy::match-like-matches-macro` implied by `-D warnings`
 
 error: match expression looks like `matches!` macro
-  --> $DIR/match_expr_like_matches_macro.rs:22:14
+  --> $DIR/match_expr_like_matches_macro.rs:21:14
    |
 LL |       let _w = match x {
    |  ______________^
@@ -21,7 +21,7 @@ LL | |     };
    | |_____^ help: try this: `matches!(x, Some(_))`
 
 error: redundant pattern matching, consider using `is_none()`
-  --> $DIR/match_expr_like_matches_macro.rs:28:14
+  --> $DIR/match_expr_like_matches_macro.rs:27:14
    |
 LL |       let _z = match x {
    |  ______________^
@@ -33,7 +33,7 @@ LL | |     };
    = note: `-D clippy::redundant-pattern-matching` implied by `-D warnings`
 
 error: match expression looks like `matches!` macro
-  --> $DIR/match_expr_like_matches_macro.rs:34:15
+  --> $DIR/match_expr_like_matches_macro.rs:33:15
    |
 LL |       let _zz = match x {
    |  _______________^
@@ -43,13 +43,13 @@ LL | |     };
    | |_____^ help: try this: `!matches!(x, Some(r) if r == 0)`
 
 error: if let .. else expression looks like `matches!` macro
-  --> $DIR/match_expr_like_matches_macro.rs:40:16
+  --> $DIR/match_expr_like_matches_macro.rs:39:16
    |
 LL |     let _zzz = if let Some(5) = x { true } else { false };
    |                ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `matches!(x, Some(5))`
 
 error: match expression looks like `matches!` macro
-  --> $DIR/match_expr_like_matches_macro.rs:64:20
+  --> $DIR/match_expr_like_matches_macro.rs:63:20
    |
 LL |           let _ans = match x {
    |  ____________________^
@@ -60,7 +60,7 @@ LL | |         };
    | |_________^ help: try this: `matches!(x, E::A(_) | E::B(_))`
 
 error: match expression looks like `matches!` macro
-  --> $DIR/match_expr_like_matches_macro.rs:74:20
+  --> $DIR/match_expr_like_matches_macro.rs:73:20
    |
 LL |           let _ans = match x {
    |  ____________________^
@@ -73,7 +73,7 @@ LL | |         };
    | |_________^ help: try this: `matches!(x, E::A(_) | E::B(_))`
 
 error: match expression looks like `matches!` macro
-  --> $DIR/match_expr_like_matches_macro.rs:84:20
+  --> $DIR/match_expr_like_matches_macro.rs:83:20
    |
 LL |           let _ans = match x {
    |  ____________________^
@@ -84,7 +84,7 @@ LL | |         };
    | |_________^ help: try this: `!matches!(x, E::B(_) | E::C)`
 
 error: match expression looks like `matches!` macro
-  --> $DIR/match_expr_like_matches_macro.rs:144:18
+  --> $DIR/match_expr_like_matches_macro.rs:143:18
    |
 LL |           let _z = match &z {
    |  __________________^
@@ -94,7 +94,7 @@ LL | |         };
    | |_________^ help: try this: `matches!(z, Some(3))`
 
 error: match expression looks like `matches!` macro
-  --> $DIR/match_expr_like_matches_macro.rs:153:18
+  --> $DIR/match_expr_like_matches_macro.rs:152:18
    |
 LL |           let _z = match &z {
    |  __________________^
@@ -104,7 +104,7 @@ LL | |         };
    | |_________^ help: try this: `matches!(&z, Some(3))`
 
 error: match expression looks like `matches!` macro
-  --> $DIR/match_expr_like_matches_macro.rs:170:21
+  --> $DIR/match_expr_like_matches_macro.rs:169:21
    |
 LL |               let _ = match &z {
    |  _____________________^
@@ -114,7 +114,7 @@ LL | |             };
    | |_____________^ help: try this: `matches!(&z, AnEnum::X)`
 
 error: match expression looks like `matches!` macro
-  --> $DIR/match_expr_like_matches_macro.rs:184:20
+  --> $DIR/match_expr_like_matches_macro.rs:183:20
    |
 LL |           let _res = match &val {
    |  ____________________^
@@ -124,7 +124,7 @@ LL | |         };
    | |_________^ help: try this: `matches!(&val, &Some(ref _a))`
 
 error: match expression looks like `matches!` macro
-  --> $DIR/match_expr_like_matches_macro.rs:196:20
+  --> $DIR/match_expr_like_matches_macro.rs:195:20
    |
 LL |           let _res = match &val {
    |  ____________________^
@@ -134,7 +134,7 @@ LL | |         };
    | |_________^ help: try this: `matches!(&val, &Some(ref _a))`
 
 error: match expression looks like `matches!` macro
-  --> $DIR/match_expr_like_matches_macro.rs:256:14
+  --> $DIR/match_expr_like_matches_macro.rs:253:14
    |
 LL |       let _y = match Some(5) {
    |  ______________^
diff --git a/src/tools/clippy/tests/ui/mem_replace.fixed b/src/tools/clippy/tests/ui/mem_replace.fixed
index ae237395b95..874d5584330 100644
--- a/src/tools/clippy/tests/ui/mem_replace.fixed
+++ b/src/tools/clippy/tests/ui/mem_replace.fixed
@@ -1,6 +1,5 @@
 // run-rustfix
 
-#![feature(custom_inner_attributes)]
 #![allow(unused)]
 #![warn(
     clippy::all,
@@ -80,16 +79,14 @@ fn main() {
     dont_lint_primitive();
 }
 
+#[clippy::msrv = "1.39"]
 fn msrv_1_39() {
-    #![clippy::msrv = "1.39"]
-
     let mut s = String::from("foo");
     let _ = std::mem::replace(&mut s, String::default());
 }
 
+#[clippy::msrv = "1.40"]
 fn msrv_1_40() {
-    #![clippy::msrv = "1.40"]
-
     let mut s = String::from("foo");
     let _ = std::mem::take(&mut s);
 }
diff --git a/src/tools/clippy/tests/ui/mem_replace.rs b/src/tools/clippy/tests/ui/mem_replace.rs
index 3202e99e0be..f4f3bff5144 100644
--- a/src/tools/clippy/tests/ui/mem_replace.rs
+++ b/src/tools/clippy/tests/ui/mem_replace.rs
@@ -1,6 +1,5 @@
 // run-rustfix
 
-#![feature(custom_inner_attributes)]
 #![allow(unused)]
 #![warn(
     clippy::all,
@@ -80,16 +79,14 @@ fn main() {
     dont_lint_primitive();
 }
 
+#[clippy::msrv = "1.39"]
 fn msrv_1_39() {
-    #![clippy::msrv = "1.39"]
-
     let mut s = String::from("foo");
     let _ = std::mem::replace(&mut s, String::default());
 }
 
+#[clippy::msrv = "1.40"]
 fn msrv_1_40() {
-    #![clippy::msrv = "1.40"]
-
     let mut s = String::from("foo");
     let _ = std::mem::replace(&mut s, String::default());
 }
diff --git a/src/tools/clippy/tests/ui/mem_replace.stderr b/src/tools/clippy/tests/ui/mem_replace.stderr
index dd8a50dab90..caa127f76ee 100644
--- a/src/tools/clippy/tests/ui/mem_replace.stderr
+++ b/src/tools/clippy/tests/ui/mem_replace.stderr
@@ -1,5 +1,5 @@
 error: replacing an `Option` with `None`
-  --> $DIR/mem_replace.rs:17:13
+  --> $DIR/mem_replace.rs:16:13
    |
 LL |     let _ = mem::replace(&mut an_option, None);
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider `Option::take()` instead: `an_option.take()`
@@ -7,13 +7,13 @@ LL |     let _ = mem::replace(&mut an_option, None);
    = note: `-D clippy::mem-replace-option-with-none` implied by `-D warnings`
 
 error: replacing an `Option` with `None`
-  --> $DIR/mem_replace.rs:19:13
+  --> $DIR/mem_replace.rs:18:13
    |
 LL |     let _ = mem::replace(an_option, None);
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider `Option::take()` instead: `an_option.take()`
 
 error: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take`
-  --> $DIR/mem_replace.rs:24:13
+  --> $DIR/mem_replace.rs:23:13
    |
 LL |     let _ = std::mem::replace(&mut s, String::default());
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(&mut s)`
@@ -21,103 +21,103 @@ LL |     let _ = std::mem::replace(&mut s, String::default());
    = note: `-D clippy::mem-replace-with-default` implied by `-D warnings`
 
 error: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take`
-  --> $DIR/mem_replace.rs:27:13
+  --> $DIR/mem_replace.rs:26:13
    |
 LL |     let _ = std::mem::replace(s, String::default());
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(s)`
 
 error: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take`
-  --> $DIR/mem_replace.rs:28:13
+  --> $DIR/mem_replace.rs:27:13
    |
 LL |     let _ = std::mem::replace(s, Default::default());
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(s)`
 
 error: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take`
-  --> $DIR/mem_replace.rs:31:13
+  --> $DIR/mem_replace.rs:30:13
    |
 LL |     let _ = std::mem::replace(&mut v, Vec::default());
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(&mut v)`
 
 error: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take`
-  --> $DIR/mem_replace.rs:32:13
+  --> $DIR/mem_replace.rs:31:13
    |
 LL |     let _ = std::mem::replace(&mut v, Default::default());
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(&mut v)`
 
 error: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take`
-  --> $DIR/mem_replace.rs:33:13
+  --> $DIR/mem_replace.rs:32:13
    |
 LL |     let _ = std::mem::replace(&mut v, Vec::new());
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(&mut v)`
 
 error: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take`
-  --> $DIR/mem_replace.rs:34:13
+  --> $DIR/mem_replace.rs:33:13
    |
 LL |     let _ = std::mem::replace(&mut v, vec![]);
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(&mut v)`
 
 error: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take`
-  --> $DIR/mem_replace.rs:37:13
+  --> $DIR/mem_replace.rs:36:13
    |
 LL |     let _ = std::mem::replace(&mut hash_map, HashMap::new());
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(&mut hash_map)`
 
 error: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take`
-  --> $DIR/mem_replace.rs:40:13
+  --> $DIR/mem_replace.rs:39:13
    |
 LL |     let _ = std::mem::replace(&mut btree_map, BTreeMap::new());
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(&mut btree_map)`
 
 error: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take`
-  --> $DIR/mem_replace.rs:43:13
+  --> $DIR/mem_replace.rs:42:13
    |
 LL |     let _ = std::mem::replace(&mut vd, VecDeque::new());
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(&mut vd)`
 
 error: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take`
-  --> $DIR/mem_replace.rs:46:13
+  --> $DIR/mem_replace.rs:45:13
    |
 LL |     let _ = std::mem::replace(&mut hash_set, HashSet::new());
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(&mut hash_set)`
 
 error: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take`
-  --> $DIR/mem_replace.rs:49:13
+  --> $DIR/mem_replace.rs:48:13
    |
 LL |     let _ = std::mem::replace(&mut btree_set, BTreeSet::new());
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(&mut btree_set)`
 
 error: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take`
-  --> $DIR/mem_replace.rs:52:13
+  --> $DIR/mem_replace.rs:51:13
    |
 LL |     let _ = std::mem::replace(&mut list, LinkedList::new());
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(&mut list)`
 
 error: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take`
-  --> $DIR/mem_replace.rs:55:13
+  --> $DIR/mem_replace.rs:54:13
    |
 LL |     let _ = std::mem::replace(&mut binary_heap, BinaryHeap::new());
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(&mut binary_heap)`
 
 error: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take`
-  --> $DIR/mem_replace.rs:58:13
+  --> $DIR/mem_replace.rs:57:13
    |
 LL |     let _ = std::mem::replace(&mut tuple, (vec![], BinaryHeap::new()));
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(&mut tuple)`
 
 error: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take`
-  --> $DIR/mem_replace.rs:61:13
+  --> $DIR/mem_replace.rs:60:13
    |
 LL |     let _ = std::mem::replace(&mut refstr, "");
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(&mut refstr)`
 
 error: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take`
-  --> $DIR/mem_replace.rs:64:13
+  --> $DIR/mem_replace.rs:63:13
    |
 LL |     let _ = std::mem::replace(&mut slice, &[]);
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(&mut slice)`
 
 error: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take`
-  --> $DIR/mem_replace.rs:94:13
+  --> $DIR/mem_replace.rs:91:13
    |
 LL |     let _ = std::mem::replace(&mut s, String::default());
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(&mut s)`
diff --git a/src/tools/clippy/tests/ui/min_rust_version_attr.rs b/src/tools/clippy/tests/ui/min_rust_version_attr.rs
index cd148063bf0..955e7eb7276 100644
--- a/src/tools/clippy/tests/ui/min_rust_version_attr.rs
+++ b/src/tools/clippy/tests/ui/min_rust_version_attr.rs
@@ -3,27 +3,60 @@
 
 fn main() {}
 
+#[clippy::msrv = "1.42.0"]
 fn just_under_msrv() {
-    #![clippy::msrv = "1.42.0"]
     let log2_10 = 3.321928094887362;
 }
 
+#[clippy::msrv = "1.43.0"]
 fn meets_msrv() {
-    #![clippy::msrv = "1.43.0"]
     let log2_10 = 3.321928094887362;
 }
 
+#[clippy::msrv = "1.44.0"]
 fn just_above_msrv() {
-    #![clippy::msrv = "1.44.0"]
     let log2_10 = 3.321928094887362;
 }
 
+#[clippy::msrv = "1.42"]
 fn no_patch_under() {
-    #![clippy::msrv = "1.42"]
     let log2_10 = 3.321928094887362;
 }
 
+#[clippy::msrv = "1.43"]
 fn no_patch_meets() {
+    let log2_10 = 3.321928094887362;
+}
+
+fn inner_attr_under() {
+    #![clippy::msrv = "1.42"]
+    let log2_10 = 3.321928094887362;
+}
+
+fn inner_attr_meets() {
     #![clippy::msrv = "1.43"]
     let log2_10 = 3.321928094887362;
 }
+
+// https://github.com/rust-lang/rust-clippy/issues/6920
+fn scoping() {
+    mod m {
+        #![clippy::msrv = "1.42.0"]
+    }
+
+    // Should warn
+    let log2_10 = 3.321928094887362;
+
+    mod a {
+        #![clippy::msrv = "1.42.0"]
+
+        fn should_warn() {
+            #![clippy::msrv = "1.43.0"]
+            let log2_10 = 3.321928094887362;
+        }
+
+        fn should_not_warn() {
+            let log2_10 = 3.321928094887362;
+        }
+    }
+}
diff --git a/src/tools/clippy/tests/ui/min_rust_version_attr.stderr b/src/tools/clippy/tests/ui/min_rust_version_attr.stderr
index 68aa5874819..7e2135584ef 100644
--- a/src/tools/clippy/tests/ui/min_rust_version_attr.stderr
+++ b/src/tools/clippy/tests/ui/min_rust_version_attr.stderr
@@ -23,5 +23,29 @@ LL |     let log2_10 = 3.321928094887362;
    |
    = help: consider using the constant directly
 
-error: aborting due to 3 previous errors
+error: approximate value of `f{32, 64}::consts::LOG2_10` found
+  --> $DIR/min_rust_version_attr.rs:38:19
+   |
+LL |     let log2_10 = 3.321928094887362;
+   |                   ^^^^^^^^^^^^^^^^^
+   |
+   = help: consider using the constant directly
+
+error: approximate value of `f{32, 64}::consts::LOG2_10` found
+  --> $DIR/min_rust_version_attr.rs:48:19
+   |
+LL |     let log2_10 = 3.321928094887362;
+   |                   ^^^^^^^^^^^^^^^^^
+   |
+   = help: consider using the constant directly
+
+error: approximate value of `f{32, 64}::consts::LOG2_10` found
+  --> $DIR/min_rust_version_attr.rs:55:27
+   |
+LL |             let log2_10 = 3.321928094887362;
+   |                           ^^^^^^^^^^^^^^^^^
+   |
+   = help: consider using the constant directly
+
+error: aborting due to 6 previous errors
 
diff --git a/src/tools/clippy/tests/ui/min_rust_version_invalid_attr.stderr b/src/tools/clippy/tests/ui/min_rust_version_invalid_attr.stderr
index 93370a0fa9c..675b7803152 100644
--- a/src/tools/clippy/tests/ui/min_rust_version_invalid_attr.stderr
+++ b/src/tools/clippy/tests/ui/min_rust_version_invalid_attr.stderr
@@ -4,7 +4,7 @@ error: `invalid.version` is not a valid Rust version
 LL | #![clippy::msrv = "invalid.version"]
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
-error: `msrv` cannot be an outer attribute
+error: `invalid.version` is not a valid Rust version
   --> $DIR/min_rust_version_invalid_attr.rs:6:1
    |
 LL | #[clippy::msrv = "invalid.version"]
diff --git a/src/tools/clippy/tests/ui/misnamed_getters.rs b/src/tools/clippy/tests/ui/misnamed_getters.rs
new file mode 100644
index 00000000000..03e7dac7df9
--- /dev/null
+++ b/src/tools/clippy/tests/ui/misnamed_getters.rs
@@ -0,0 +1,124 @@
+#![allow(unused)]
+#![warn(clippy::misnamed_getters)]
+
+struct A {
+    a: u8,
+    b: u8,
+    c: u8,
+}
+
+impl A {
+    fn a(&self) -> &u8 {
+        &self.b
+    }
+    fn a_mut(&mut self) -> &mut u8 {
+        &mut self.b
+    }
+
+    fn b(self) -> u8 {
+        self.a
+    }
+
+    fn b_mut(&mut self) -> &mut u8 {
+        &mut self.a
+    }
+
+    fn c(&self) -> &u8 {
+        &self.b
+    }
+
+    fn c_mut(&mut self) -> &mut u8 {
+        &mut self.a
+    }
+}
+
+union B {
+    a: u8,
+    b: u8,
+}
+
+impl B {
+    unsafe fn a(&self) -> &u8 {
+        &self.b
+    }
+    unsafe fn a_mut(&mut self) -> &mut u8 {
+        &mut self.b
+    }
+
+    unsafe fn b(self) -> u8 {
+        self.a
+    }
+
+    unsafe fn b_mut(&mut self) -> &mut u8 {
+        &mut self.a
+    }
+
+    unsafe fn c(&self) -> &u8 {
+        &self.b
+    }
+
+    unsafe fn c_mut(&mut self) -> &mut u8 {
+        &mut self.a
+    }
+
+    unsafe fn a_unchecked(&self) -> &u8 {
+        &self.b
+    }
+    unsafe fn a_unchecked_mut(&mut self) -> &mut u8 {
+        &mut self.b
+    }
+
+    unsafe fn b_unchecked(self) -> u8 {
+        self.a
+    }
+
+    unsafe fn b_unchecked_mut(&mut self) -> &mut u8 {
+        &mut self.a
+    }
+
+    unsafe fn c_unchecked(&self) -> &u8 {
+        &self.b
+    }
+
+    unsafe fn c_unchecked_mut(&mut self) -> &mut u8 {
+        &mut self.a
+    }
+}
+
+struct D {
+    d: u8,
+    inner: A,
+}
+
+impl core::ops::Deref for D {
+    type Target = A;
+    fn deref(&self) -> &A {
+        &self.inner
+    }
+}
+
+impl core::ops::DerefMut for D {
+    fn deref_mut(&mut self) -> &mut A {
+        &mut self.inner
+    }
+}
+
+impl D {
+    fn a(&self) -> &u8 {
+        &self.b
+    }
+    fn a_mut(&mut self) -> &mut u8 {
+        &mut self.b
+    }
+
+    fn d(&self) -> &u8 {
+        &self.b
+    }
+    fn d_mut(&mut self) -> &mut u8 {
+        &mut self.b
+    }
+}
+
+fn main() {
+    // test code goes here
+}
diff --git a/src/tools/clippy/tests/ui/misnamed_getters.stderr b/src/tools/clippy/tests/ui/misnamed_getters.stderr
new file mode 100644
index 00000000000..1e38a83d019
--- /dev/null
+++ b/src/tools/clippy/tests/ui/misnamed_getters.stderr
@@ -0,0 +1,166 @@
+error: getter function appears to return the wrong field
+  --> $DIR/misnamed_getters.rs:11:5
+   |
+LL | /     fn a(&self) -> &u8 {
+LL | |         &self.b
+   | |         ------- help: consider using: `&self.a`
+LL | |     }
+   | |_____^
+   |
+   = note: `-D clippy::misnamed-getters` implied by `-D warnings`
+
+error: getter function appears to return the wrong field
+  --> $DIR/misnamed_getters.rs:14:5
+   |
+LL | /     fn a_mut(&mut self) -> &mut u8 {
+LL | |         &mut self.b
+   | |         ----------- help: consider using: `&mut self.a`
+LL | |     }
+   | |_____^
+
+error: getter function appears to return the wrong field
+  --> $DIR/misnamed_getters.rs:18:5
+   |
+LL | /     fn b(self) -> u8 {
+LL | |         self.a
+   | |         ------ help: consider using: `self.b`
+LL | |     }
+   | |_____^
+
+error: getter function appears to return the wrong field
+  --> $DIR/misnamed_getters.rs:22:5
+   |
+LL | /     fn b_mut(&mut self) -> &mut u8 {
+LL | |         &mut self.a
+   | |         ----------- help: consider using: `&mut self.b`
+LL | |     }
+   | |_____^
+
+error: getter function appears to return the wrong field
+  --> $DIR/misnamed_getters.rs:26:5
+   |
+LL | /     fn c(&self) -> &u8 {
+LL | |         &self.b
+   | |         ------- help: consider using: `&self.c`
+LL | |     }
+   | |_____^
+
+error: getter function appears to return the wrong field
+  --> $DIR/misnamed_getters.rs:30:5
+   |
+LL | /     fn c_mut(&mut self) -> &mut u8 {
+LL | |         &mut self.a
+   | |         ----------- help: consider using: `&mut self.c`
+LL | |     }
+   | |_____^
+
+error: getter function appears to return the wrong field
+  --> $DIR/misnamed_getters.rs:41:5
+   |
+LL | /     unsafe fn a(&self) -> &u8 {
+LL | |         &self.b
+   | |         ------- help: consider using: `&self.a`
+LL | |     }
+   | |_____^
+
+error: getter function appears to return the wrong field
+  --> $DIR/misnamed_getters.rs:44:5
+   |
+LL | /     unsafe fn a_mut(&mut self) -> &mut u8 {
+LL | |         &mut self.b
+   | |         ----------- help: consider using: `&mut self.a`
+LL | |     }
+   | |_____^
+
+error: getter function appears to return the wrong field
+  --> $DIR/misnamed_getters.rs:48:5
+   |
+LL | /     unsafe fn b(self) -> u8 {
+LL | |         self.a
+   | |         ------ help: consider using: `self.b`
+LL | |     }
+   | |_____^
+
+error: getter function appears to return the wrong field
+  --> $DIR/misnamed_getters.rs:52:5
+   |
+LL | /     unsafe fn b_mut(&mut self) -> &mut u8 {
+LL | |         &mut self.a
+   | |         ----------- help: consider using: `&mut self.b`
+LL | |     }
+   | |_____^
+
+error: getter function appears to return the wrong field
+  --> $DIR/misnamed_getters.rs:64:5
+   |
+LL | /     unsafe fn a_unchecked(&self) -> &u8 {
+LL | |         &self.b
+   | |         ------- help: consider using: `&self.a`
+LL | |     }
+   | |_____^
+
+error: getter function appears to return the wrong field
+  --> $DIR/misnamed_getters.rs:67:5
+   |
+LL | /     unsafe fn a_unchecked_mut(&mut self) -> &mut u8 {
+LL | |         &mut self.b
+   | |         ----------- help: consider using: `&mut self.a`
+LL | |     }
+   | |_____^
+
+error: getter function appears to return the wrong field
+  --> $DIR/misnamed_getters.rs:71:5
+   |
+LL | /     unsafe fn b_unchecked(self) -> u8 {
+LL | |         self.a
+   | |         ------ help: consider using: `self.b`
+LL | |     }
+   | |_____^
+
+error: getter function appears to return the wrong field
+  --> $DIR/misnamed_getters.rs:75:5
+   |
+LL | /     unsafe fn b_unchecked_mut(&mut self) -> &mut u8 {
+LL | |         &mut self.a
+   | |         ----------- help: consider using: `&mut self.b`
+LL | |     }
+   | |_____^
+
+error: getter function appears to return the wrong field
+  --> $DIR/misnamed_getters.rs:107:5
+   |
+LL | /     fn a(&self) -> &u8 {
+LL | |         &self.b
+   | |         ------- help: consider using: `&self.a`
+LL | |     }
+   | |_____^
+
+error: getter function appears to return the wrong field
+  --> $DIR/misnamed_getters.rs:110:5
+   |
+LL | /     fn a_mut(&mut self) -> &mut u8 {
+LL | |         &mut self.b
+   | |         ----------- help: consider using: `&mut self.a`
+LL | |     }
+   | |_____^
+
+error: getter function appears to return the wrong field
+  --> $DIR/misnamed_getters.rs:114:5
+   |
+LL | /     fn d(&self) -> &u8 {
+LL | |         &self.b
+   | |         ------- help: consider using: `&self.d`
+LL | |     }
+   | |_____^
+
+error: getter function appears to return the wrong field
+  --> $DIR/misnamed_getters.rs:117:5
+   |
+LL | /     fn d_mut(&mut self) -> &mut u8 {
+LL | |         &mut self.b
+   | |         ----------- help: consider using: `&mut self.d`
+LL | |     }
+   | |_____^
+
+error: aborting due to 18 previous errors
+
diff --git a/src/tools/clippy/tests/ui/missing_const_for_fn/cant_be_const.rs b/src/tools/clippy/tests/ui/missing_const_for_fn/cant_be_const.rs
index b950248ef94..75cace18167 100644
--- a/src/tools/clippy/tests/ui/missing_const_for_fn/cant_be_const.rs
+++ b/src/tools/clippy/tests/ui/missing_const_for_fn/cant_be_const.rs
@@ -7,7 +7,6 @@
 
 #![warn(clippy::missing_const_for_fn)]
 #![feature(start)]
-#![feature(custom_inner_attributes)]
 
 extern crate helper;
 extern crate proc_macro_with_span;
@@ -115,9 +114,8 @@ fn unstably_const_fn() {
     helper::unstably_const_fn()
 }
 
+#[clippy::msrv = "1.46.0"]
 mod const_fn_stabilized_after_msrv {
-    #![clippy::msrv = "1.46.0"]
-
     // Do not lint this because `u8::is_ascii_digit` is stabilized as a const function in 1.47.0.
     fn const_fn_stabilized_after_msrv(byte: u8) {
         byte.is_ascii_digit();
diff --git a/src/tools/clippy/tests/ui/missing_const_for_fn/could_be_const.rs b/src/tools/clippy/tests/ui/missing_const_for_fn/could_be_const.rs
index b85e8878491..0246c8622ed 100644
--- a/src/tools/clippy/tests/ui/missing_const_for_fn/could_be_const.rs
+++ b/src/tools/clippy/tests/ui/missing_const_for_fn/could_be_const.rs
@@ -1,6 +1,5 @@
 #![warn(clippy::missing_const_for_fn)]
 #![allow(incomplete_features, clippy::let_and_return)]
-#![feature(custom_inner_attributes)]
 
 use std::mem::transmute;
 
@@ -68,24 +67,21 @@ mod with_drop {
     }
 }
 
+#[clippy::msrv = "1.47.0"]
 mod const_fn_stabilized_before_msrv {
-    #![clippy::msrv = "1.47.0"]
-
     // This could be const because `u8::is_ascii_digit` is a stable const function in 1.47.
     fn const_fn_stabilized_before_msrv(byte: u8) {
         byte.is_ascii_digit();
     }
 }
 
+#[clippy::msrv = "1.45"]
 fn msrv_1_45() -> i32 {
-    #![clippy::msrv = "1.45"]
-
     45
 }
 
+#[clippy::msrv = "1.46"]
 fn msrv_1_46() -> i32 {
-    #![clippy::msrv = "1.46"]
-
     46
 }
 
diff --git a/src/tools/clippy/tests/ui/missing_const_for_fn/could_be_const.stderr b/src/tools/clippy/tests/ui/missing_const_for_fn/could_be_const.stderr
index f8e221c82f1..955e1ed2634 100644
--- a/src/tools/clippy/tests/ui/missing_const_for_fn/could_be_const.stderr
+++ b/src/tools/clippy/tests/ui/missing_const_for_fn/could_be_const.stderr
@@ -1,5 +1,5 @@
 error: this could be a `const fn`
-  --> $DIR/could_be_const.rs:13:5
+  --> $DIR/could_be_const.rs:12:5
    |
 LL | /     pub fn new() -> Self {
 LL | |         Self { guess: 42 }
@@ -9,7 +9,7 @@ LL | |     }
    = note: `-D clippy::missing-const-for-fn` implied by `-D warnings`
 
 error: this could be a `const fn`
-  --> $DIR/could_be_const.rs:17:5
+  --> $DIR/could_be_const.rs:16:5
    |
 LL | /     fn const_generic_params<'a, T, const N: usize>(&self, b: &'a [T; N]) -> &'a [T; N] {
 LL | |         b
@@ -17,7 +17,7 @@ LL | |     }
    | |_____^
 
 error: this could be a `const fn`
-  --> $DIR/could_be_const.rs:23:1
+  --> $DIR/could_be_const.rs:22:1
    |
 LL | / fn one() -> i32 {
 LL | |     1
@@ -25,7 +25,7 @@ LL | | }
    | |_^
 
 error: this could be a `const fn`
-  --> $DIR/could_be_const.rs:28:1
+  --> $DIR/could_be_const.rs:27:1
    |
 LL | / fn two() -> i32 {
 LL | |     let abc = 2;
@@ -34,7 +34,7 @@ LL | | }
    | |_^
 
 error: this could be a `const fn`
-  --> $DIR/could_be_const.rs:34:1
+  --> $DIR/could_be_const.rs:33:1
    |
 LL | / fn string() -> String {
 LL | |     String::new()
@@ -42,7 +42,7 @@ LL | | }
    | |_^
 
 error: this could be a `const fn`
-  --> $DIR/could_be_const.rs:39:1
+  --> $DIR/could_be_const.rs:38:1
    |
 LL | / unsafe fn four() -> i32 {
 LL | |     4
@@ -50,7 +50,7 @@ LL | | }
    | |_^
 
 error: this could be a `const fn`
-  --> $DIR/could_be_const.rs:44:1
+  --> $DIR/could_be_const.rs:43:1
    |
 LL | / fn generic<T>(t: T) -> T {
 LL | |     t
@@ -58,7 +58,7 @@ LL | | }
    | |_^
 
 error: this could be a `const fn`
-  --> $DIR/could_be_const.rs:52:1
+  --> $DIR/could_be_const.rs:51:1
    |
 LL | / fn generic_arr<T: Copy>(t: [T; 1]) -> T {
 LL | |     t[0]
@@ -66,7 +66,7 @@ LL | | }
    | |_^
 
 error: this could be a `const fn`
-  --> $DIR/could_be_const.rs:65:9
+  --> $DIR/could_be_const.rs:64:9
    |
 LL | /         pub fn b(self, a: &A) -> B {
 LL | |             B
@@ -74,7 +74,7 @@ LL | |         }
    | |_________^
 
 error: this could be a `const fn`
-  --> $DIR/could_be_const.rs:75:5
+  --> $DIR/could_be_const.rs:73:5
    |
 LL | /     fn const_fn_stabilized_before_msrv(byte: u8) {
 LL | |         byte.is_ascii_digit();
@@ -82,11 +82,9 @@ LL | |     }
    | |_____^
 
 error: this could be a `const fn`
-  --> $DIR/could_be_const.rs:86:1
+  --> $DIR/could_be_const.rs:84:1
    |
 LL | / fn msrv_1_46() -> i32 {
-LL | |     #![clippy::msrv = "1.46"]
-LL | |
 LL | |     46
 LL | | }
    | |_^
diff --git a/src/tools/clippy/tests/ui/needless_borrow.fixed b/src/tools/clippy/tests/ui/needless_borrow.fixed
index 85b6b639d55..4cb7f6b687f 100644
--- a/src/tools/clippy/tests/ui/needless_borrow.fixed
+++ b/src/tools/clippy/tests/ui/needless_borrow.fixed
@@ -1,13 +1,13 @@
 // run-rustfix
-#![feature(custom_inner_attributes, lint_reasons)]
-
-#[warn(clippy::all, clippy::needless_borrow)]
-#[allow(unused_variables)]
-#[allow(
+#![feature(lint_reasons)]
+#![allow(
+    unused,
     clippy::uninlined_format_args,
     clippy::unnecessary_mut_passed,
     clippy::unnecessary_to_owned
 )]
+#![warn(clippy::needless_borrow)]
+
 fn main() {
     let a = 5;
     let ref_a = &a;
@@ -171,14 +171,12 @@ impl<'a> Trait for &'a str {}
 
 fn h(_: &dyn Trait) {}
 
-#[allow(dead_code)]
 fn check_expect_suppression() {
     let a = 5;
     #[expect(clippy::needless_borrow)]
     let _ = x(&&a);
 }
 
-#[allow(dead_code)]
 mod issue9160 {
     pub struct S<F> {
         f: F,
@@ -267,7 +265,6 @@ where
 }
 
 // https://github.com/rust-lang/rust-clippy/pull/9136#pullrequestreview-1037379321
-#[allow(dead_code)]
 mod copyable_iterator {
     #[derive(Clone, Copy)]
     struct Iter;
@@ -287,25 +284,20 @@ mod copyable_iterator {
     }
 }
 
+#[clippy::msrv = "1.52.0"]
 mod under_msrv {
-    #![allow(dead_code)]
-    #![clippy::msrv = "1.52.0"]
-
     fn foo() {
         let _ = std::process::Command::new("ls").args(&["-a", "-l"]).status().unwrap();
     }
 }
 
+#[clippy::msrv = "1.53.0"]
 mod meets_msrv {
-    #![allow(dead_code)]
-    #![clippy::msrv = "1.53.0"]
-
     fn foo() {
         let _ = std::process::Command::new("ls").args(["-a", "-l"]).status().unwrap();
     }
 }
 
-#[allow(unused)]
 fn issue9383() {
     // Should not lint because unions need explicit deref when accessing field
     use std::mem::ManuallyDrop;
@@ -334,7 +326,6 @@ fn issue9383() {
     }
 }
 
-#[allow(dead_code)]
 fn closure_test() {
     let env = "env".to_owned();
     let arg = "arg".to_owned();
@@ -348,7 +339,6 @@ fn closure_test() {
     f(arg);
 }
 
-#[allow(dead_code)]
 mod significant_drop {
     #[derive(Debug)]
     struct X;
@@ -368,7 +358,6 @@ mod significant_drop {
     fn debug(_: impl std::fmt::Debug) {}
 }
 
-#[allow(dead_code)]
 mod used_exactly_once {
     fn foo(x: String) {
         use_x(x);
@@ -376,7 +365,6 @@ mod used_exactly_once {
     fn use_x(_: impl AsRef<str>) {}
 }
 
-#[allow(dead_code)]
 mod used_more_than_once {
     fn foo(x: String) {
         use_x(&x);
@@ -387,7 +375,6 @@ mod used_more_than_once {
 }
 
 // https://github.com/rust-lang/rust-clippy/issues/9111#issuecomment-1277114280
-#[allow(dead_code)]
 mod issue_9111 {
     struct A;
 
@@ -409,7 +396,6 @@ mod issue_9111 {
     }
 }
 
-#[allow(dead_code)]
 mod issue_9710 {
     fn main() {
         let string = String::new();
@@ -421,7 +407,6 @@ mod issue_9710 {
     fn f<T: AsRef<str>>(_: T) {}
 }
 
-#[allow(dead_code)]
 mod issue_9739 {
     fn foo<D: std::fmt::Display>(_it: impl IntoIterator<Item = D>) {}
 
@@ -434,7 +419,6 @@ mod issue_9739 {
     }
 }
 
-#[allow(dead_code)]
 mod issue_9739_method_variant {
     struct S;
 
@@ -451,7 +435,6 @@ mod issue_9739_method_variant {
     }
 }
 
-#[allow(dead_code)]
 mod issue_9782 {
     fn foo<T: AsRef<[u8]>>(t: T) {
         println!("{}", std::mem::size_of::<T>());
@@ -475,7 +458,6 @@ mod issue_9782 {
     }
 }
 
-#[allow(dead_code)]
 mod issue_9782_type_relative_variant {
     struct S;
 
@@ -493,7 +475,6 @@ mod issue_9782_type_relative_variant {
     }
 }
 
-#[allow(dead_code)]
 mod issue_9782_method_variant {
     struct S;
 
diff --git a/src/tools/clippy/tests/ui/needless_borrow.rs b/src/tools/clippy/tests/ui/needless_borrow.rs
index 7b97bcf3817..9a01190ed8d 100644
--- a/src/tools/clippy/tests/ui/needless_borrow.rs
+++ b/src/tools/clippy/tests/ui/needless_borrow.rs
@@ -1,13 +1,13 @@
 // run-rustfix
-#![feature(custom_inner_attributes, lint_reasons)]
-
-#[warn(clippy::all, clippy::needless_borrow)]
-#[allow(unused_variables)]
-#[allow(
+#![feature(lint_reasons)]
+#![allow(
+    unused,
     clippy::uninlined_format_args,
     clippy::unnecessary_mut_passed,
     clippy::unnecessary_to_owned
 )]
+#![warn(clippy::needless_borrow)]
+
 fn main() {
     let a = 5;
     let ref_a = &a;
@@ -171,14 +171,12 @@ impl<'a> Trait for &'a str {}
 
 fn h(_: &dyn Trait) {}
 
-#[allow(dead_code)]
 fn check_expect_suppression() {
     let a = 5;
     #[expect(clippy::needless_borrow)]
     let _ = x(&&a);
 }
 
-#[allow(dead_code)]
 mod issue9160 {
     pub struct S<F> {
         f: F,
@@ -267,7 +265,6 @@ where
 }
 
 // https://github.com/rust-lang/rust-clippy/pull/9136#pullrequestreview-1037379321
-#[allow(dead_code)]
 mod copyable_iterator {
     #[derive(Clone, Copy)]
     struct Iter;
@@ -287,25 +284,20 @@ mod copyable_iterator {
     }
 }
 
+#[clippy::msrv = "1.52.0"]
 mod under_msrv {
-    #![allow(dead_code)]
-    #![clippy::msrv = "1.52.0"]
-
     fn foo() {
         let _ = std::process::Command::new("ls").args(&["-a", "-l"]).status().unwrap();
     }
 }
 
+#[clippy::msrv = "1.53.0"]
 mod meets_msrv {
-    #![allow(dead_code)]
-    #![clippy::msrv = "1.53.0"]
-
     fn foo() {
         let _ = std::process::Command::new("ls").args(&["-a", "-l"]).status().unwrap();
     }
 }
 
-#[allow(unused)]
 fn issue9383() {
     // Should not lint because unions need explicit deref when accessing field
     use std::mem::ManuallyDrop;
@@ -334,7 +326,6 @@ fn issue9383() {
     }
 }
 
-#[allow(dead_code)]
 fn closure_test() {
     let env = "env".to_owned();
     let arg = "arg".to_owned();
@@ -348,7 +339,6 @@ fn closure_test() {
     f(arg);
 }
 
-#[allow(dead_code)]
 mod significant_drop {
     #[derive(Debug)]
     struct X;
@@ -368,7 +358,6 @@ mod significant_drop {
     fn debug(_: impl std::fmt::Debug) {}
 }
 
-#[allow(dead_code)]
 mod used_exactly_once {
     fn foo(x: String) {
         use_x(&x);
@@ -376,7 +365,6 @@ mod used_exactly_once {
     fn use_x(_: impl AsRef<str>) {}
 }
 
-#[allow(dead_code)]
 mod used_more_than_once {
     fn foo(x: String) {
         use_x(&x);
@@ -387,7 +375,6 @@ mod used_more_than_once {
 }
 
 // https://github.com/rust-lang/rust-clippy/issues/9111#issuecomment-1277114280
-#[allow(dead_code)]
 mod issue_9111 {
     struct A;
 
@@ -409,7 +396,6 @@ mod issue_9111 {
     }
 }
 
-#[allow(dead_code)]
 mod issue_9710 {
     fn main() {
         let string = String::new();
@@ -421,7 +407,6 @@ mod issue_9710 {
     fn f<T: AsRef<str>>(_: T) {}
 }
 
-#[allow(dead_code)]
 mod issue_9739 {
     fn foo<D: std::fmt::Display>(_it: impl IntoIterator<Item = D>) {}
 
@@ -434,7 +419,6 @@ mod issue_9739 {
     }
 }
 
-#[allow(dead_code)]
 mod issue_9739_method_variant {
     struct S;
 
@@ -451,7 +435,6 @@ mod issue_9739_method_variant {
     }
 }
 
-#[allow(dead_code)]
 mod issue_9782 {
     fn foo<T: AsRef<[u8]>>(t: T) {
         println!("{}", std::mem::size_of::<T>());
@@ -475,7 +458,6 @@ mod issue_9782 {
     }
 }
 
-#[allow(dead_code)]
 mod issue_9782_type_relative_variant {
     struct S;
 
@@ -493,7 +475,6 @@ mod issue_9782_type_relative_variant {
     }
 }
 
-#[allow(dead_code)]
 mod issue_9782_method_variant {
     struct S;
 
diff --git a/src/tools/clippy/tests/ui/needless_borrow.stderr b/src/tools/clippy/tests/ui/needless_borrow.stderr
index 485e6b84c86..d26c317124b 100644
--- a/src/tools/clippy/tests/ui/needless_borrow.stderr
+++ b/src/tools/clippy/tests/ui/needless_borrow.stderr
@@ -163,55 +163,55 @@ LL |     let _ = std::fs::write("x", &"".to_string());
    |                                 ^^^^^^^^^^^^^^^ help: change this to: `"".to_string()`
 
 error: this expression borrows a value the compiler would automatically borrow
-  --> $DIR/needless_borrow.rs:192:13
+  --> $DIR/needless_borrow.rs:190:13
    |
 LL |             (&self.f)()
    |             ^^^^^^^^^ help: change this to: `(self.f)`
 
 error: this expression borrows a value the compiler would automatically borrow
-  --> $DIR/needless_borrow.rs:201:13
+  --> $DIR/needless_borrow.rs:199:13
    |
 LL |             (&mut self.f)()
    |             ^^^^^^^^^^^^^ help: change this to: `(self.f)`
 
 error: the borrowed expression implements the required traits
-  --> $DIR/needless_borrow.rs:286:20
+  --> $DIR/needless_borrow.rs:283:20
    |
 LL |         takes_iter(&mut x)
    |                    ^^^^^^ help: change this to: `x`
 
 error: the borrowed expression implements the required traits
-  --> $DIR/needless_borrow.rs:304:55
+  --> $DIR/needless_borrow.rs:297:55
    |
 LL |         let _ = std::process::Command::new("ls").args(&["-a", "-l"]).status().unwrap();
    |                                                       ^^^^^^^^^^^^^ help: change this to: `["-a", "-l"]`
 
 error: the borrowed expression implements the required traits
-  --> $DIR/needless_borrow.rs:344:37
+  --> $DIR/needless_borrow.rs:335:37
    |
 LL |         let _ = std::fs::write("x", &arg);
    |                                     ^^^^ help: change this to: `arg`
 
 error: the borrowed expression implements the required traits
-  --> $DIR/needless_borrow.rs:345:37
+  --> $DIR/needless_borrow.rs:336:37
    |
 LL |         let _ = std::fs::write("x", &loc);
    |                                     ^^^^ help: change this to: `loc`
 
 error: the borrowed expression implements the required traits
-  --> $DIR/needless_borrow.rs:364:15
+  --> $DIR/needless_borrow.rs:354:15
    |
 LL |         debug(&x);
    |               ^^ help: change this to: `x`
 
 error: the borrowed expression implements the required traits
-  --> $DIR/needless_borrow.rs:374:15
+  --> $DIR/needless_borrow.rs:363:15
    |
 LL |         use_x(&x);
    |               ^^ help: change this to: `x`
 
 error: the borrowed expression implements the required traits
-  --> $DIR/needless_borrow.rs:474:13
+  --> $DIR/needless_borrow.rs:457:13
    |
 LL |         foo(&a);
    |             ^^ help: change this to: `a`
diff --git a/src/tools/clippy/tests/ui/needless_question_mark.fixed b/src/tools/clippy/tests/ui/needless_question_mark.fixed
index ba9d15e59d0..7eaca571992 100644
--- a/src/tools/clippy/tests/ui/needless_question_mark.fixed
+++ b/src/tools/clippy/tests/ui/needless_question_mark.fixed
@@ -8,7 +8,6 @@
     dead_code,
     unused_must_use
 )]
-#![feature(custom_inner_attributes)]
 
 struct TO {
     magic: Option<usize>,
diff --git a/src/tools/clippy/tests/ui/needless_question_mark.rs b/src/tools/clippy/tests/ui/needless_question_mark.rs
index 3a6523e8fe8..960bc7b7898 100644
--- a/src/tools/clippy/tests/ui/needless_question_mark.rs
+++ b/src/tools/clippy/tests/ui/needless_question_mark.rs
@@ -8,7 +8,6 @@
     dead_code,
     unused_must_use
 )]
-#![feature(custom_inner_attributes)]
 
 struct TO {
     magic: Option<usize>,
diff --git a/src/tools/clippy/tests/ui/needless_question_mark.stderr b/src/tools/clippy/tests/ui/needless_question_mark.stderr
index f8308e24e77..d1f89e326c6 100644
--- a/src/tools/clippy/tests/ui/needless_question_mark.stderr
+++ b/src/tools/clippy/tests/ui/needless_question_mark.stderr
@@ -1,5 +1,5 @@
 error: question mark operator is useless here
-  --> $DIR/needless_question_mark.rs:23:12
+  --> $DIR/needless_question_mark.rs:22:12
    |
 LL |     return Some(to.magic?);
    |            ^^^^^^^^^^^^^^^ help: try removing question mark and `Some()`: `to.magic`
@@ -7,67 +7,67 @@ LL |     return Some(to.magic?);
    = note: `-D clippy::needless-question-mark` implied by `-D warnings`
 
 error: question mark operator is useless here
-  --> $DIR/needless_question_mark.rs:31:12
+  --> $DIR/needless_question_mark.rs:30:12
    |
 LL |     return Some(to.magic?)
    |            ^^^^^^^^^^^^^^^ help: try removing question mark and `Some()`: `to.magic`
 
 error: question mark operator is useless here
-  --> $DIR/needless_question_mark.rs:36:5
+  --> $DIR/needless_question_mark.rs:35:5
    |
 LL |     Some(to.magic?)
    |     ^^^^^^^^^^^^^^^ help: try removing question mark and `Some()`: `to.magic`
 
 error: question mark operator is useless here
-  --> $DIR/needless_question_mark.rs:41:21
+  --> $DIR/needless_question_mark.rs:40:21
    |
 LL |     to.and_then(|t| Some(t.magic?))
    |                     ^^^^^^^^^^^^^^ help: try removing question mark and `Some()`: `t.magic`
 
 error: question mark operator is useless here
-  --> $DIR/needless_question_mark.rs:50:9
+  --> $DIR/needless_question_mark.rs:49:9
    |
 LL |         Some(t.magic?)
    |         ^^^^^^^^^^^^^^ help: try removing question mark and `Some()`: `t.magic`
 
 error: question mark operator is useless here
-  --> $DIR/needless_question_mark.rs:55:12
+  --> $DIR/needless_question_mark.rs:54:12
    |
 LL |     return Ok(tr.magic?);
    |            ^^^^^^^^^^^^^ help: try removing question mark and `Ok()`: `tr.magic`
 
 error: question mark operator is useless here
-  --> $DIR/needless_question_mark.rs:62:12
+  --> $DIR/needless_question_mark.rs:61:12
    |
 LL |     return Ok(tr.magic?)
    |            ^^^^^^^^^^^^^ help: try removing question mark and `Ok()`: `tr.magic`
 
 error: question mark operator is useless here
-  --> $DIR/needless_question_mark.rs:66:5
+  --> $DIR/needless_question_mark.rs:65:5
    |
 LL |     Ok(tr.magic?)
    |     ^^^^^^^^^^^^^ help: try removing question mark and `Ok()`: `tr.magic`
 
 error: question mark operator is useless here
-  --> $DIR/needless_question_mark.rs:70:21
+  --> $DIR/needless_question_mark.rs:69:21
    |
 LL |     tr.and_then(|t| Ok(t.magic?))
    |                     ^^^^^^^^^^^^ help: try removing question mark and `Ok()`: `t.magic`
 
 error: question mark operator is useless here
-  --> $DIR/needless_question_mark.rs:78:9
+  --> $DIR/needless_question_mark.rs:77:9
    |
 LL |         Ok(t.magic?)
    |         ^^^^^^^^^^^^ help: try removing question mark and `Ok()`: `t.magic`
 
 error: question mark operator is useless here
-  --> $DIR/needless_question_mark.rs:85:16
+  --> $DIR/needless_question_mark.rs:84:16
    |
 LL |         return Ok(t.magic?);
    |                ^^^^^^^^^^^^ help: try removing question mark and `Ok()`: `t.magic`
 
 error: question mark operator is useless here
-  --> $DIR/needless_question_mark.rs:120:27
+  --> $DIR/needless_question_mark.rs:119:27
    |
 LL |         || -> Option<_> { Some(Some($expr)?) }()
    |                           ^^^^^^^^^^^^^^^^^^ help: try removing question mark and `Some()`: `Some($expr)`
@@ -78,13 +78,13 @@ LL |     let _x = some_and_qmark_in_macro!(x?);
    = note: this error originates in the macro `some_and_qmark_in_macro` (in Nightly builds, run with -Z macro-backtrace for more info)
 
 error: question mark operator is useless here
-  --> $DIR/needless_question_mark.rs:131:5
+  --> $DIR/needless_question_mark.rs:130:5
    |
 LL |     Some(to.magic?)
    |     ^^^^^^^^^^^^^^^ help: try removing question mark and `Some()`: `to.magic`
 
 error: question mark operator is useless here
-  --> $DIR/needless_question_mark.rs:139:5
+  --> $DIR/needless_question_mark.rs:138:5
    |
 LL |     Ok(s.magic?)
    |     ^^^^^^^^^^^^ help: try removing question mark and `Ok()`: `s.magic`
diff --git a/src/tools/clippy/tests/ui/needless_return.fixed b/src/tools/clippy/tests/ui/needless_return.fixed
index d2163b14fca..4386aaec49e 100644
--- a/src/tools/clippy/tests/ui/needless_return.fixed
+++ b/src/tools/clippy/tests/ui/needless_return.fixed
@@ -59,14 +59,11 @@ fn test_macro_call() -> i32 {
 }
 
 fn test_void_fun() {
-    
 }
 
 fn test_void_if_fun(b: bool) {
     if b {
-        
     } else {
-        
     }
 }
 
@@ -82,7 +79,6 @@ fn test_nested_match(x: u32) {
         0 => (),
         1 => {
             let _ = 42;
-            
         },
         _ => (),
     }
@@ -126,7 +122,6 @@ mod issue6501 {
 
     fn test_closure() {
         let _ = || {
-            
         };
         let _ = || {};
     }
@@ -179,14 +174,11 @@ async fn async_test_macro_call() -> i32 {
 }
 
 async fn async_test_void_fun() {
-    
 }
 
 async fn async_test_void_if_fun(b: bool) {
     if b {
-        
     } else {
-        
     }
 }
 
@@ -269,4 +261,15 @@ fn issue9503(x: usize) -> isize {
     }
 }
 
+mod issue9416 {
+    pub fn with_newline() {
+        let _ = 42;
+    }
+
+    #[rustfmt::skip]
+    pub fn oneline() {
+        let _ = 42;
+    }
+}
+
 fn main() {}
diff --git a/src/tools/clippy/tests/ui/needless_return.rs b/src/tools/clippy/tests/ui/needless_return.rs
index 114414b5fac..666dc54b76b 100644
--- a/src/tools/clippy/tests/ui/needless_return.rs
+++ b/src/tools/clippy/tests/ui/needless_return.rs
@@ -269,4 +269,17 @@ fn issue9503(x: usize) -> isize {
     };
 }
 
+mod issue9416 {
+    pub fn with_newline() {
+        let _ = 42;
+
+        return;
+    }
+
+    #[rustfmt::skip]
+    pub fn oneline() {
+        let _ = 42; return;
+    }
+}
+
 fn main() {}
diff --git a/src/tools/clippy/tests/ui/needless_return.stderr b/src/tools/clippy/tests/ui/needless_return.stderr
index 047fb6c2311..a8b5d86cd55 100644
--- a/src/tools/clippy/tests/ui/needless_return.stderr
+++ b/src/tools/clippy/tests/ui/needless_return.stderr
@@ -72,26 +72,32 @@ LL |     return the_answer!();
    = help: remove `return`
 
 error: unneeded `return` statement
-  --> $DIR/needless_return.rs:62:5
+  --> $DIR/needless_return.rs:61:21
    |
-LL |     return;
-   |     ^^^^^^
+LL |   fn test_void_fun() {
+   |  _____________________^
+LL | |     return;
+   | |__________^
    |
    = help: remove `return`
 
 error: unneeded `return` statement
-  --> $DIR/needless_return.rs:67:9
+  --> $DIR/needless_return.rs:66:11
    |
-LL |         return;
-   |         ^^^^^^
+LL |       if b {
+   |  ___________^
+LL | |         return;
+   | |______________^
    |
    = help: remove `return`
 
 error: unneeded `return` statement
-  --> $DIR/needless_return.rs:69:9
+  --> $DIR/needless_return.rs:68:13
    |
-LL |         return;
-   |         ^^^^^^
+LL |       } else {
+   |  _____________^
+LL | |         return;
+   | |______________^
    |
    = help: remove `return`
 
@@ -104,10 +110,12 @@ LL |         _ => return,
    = help: replace `return` with a unit value
 
 error: unneeded `return` statement
-  --> $DIR/needless_return.rs:85:13
+  --> $DIR/needless_return.rs:84:24
    |
-LL |             return;
-   |             ^^^^^^
+LL |               let _ = 42;
+   |  ________________________^
+LL | |             return;
+   | |__________________^
    |
    = help: remove `return`
 
@@ -144,10 +152,12 @@ LL |         bar.unwrap_or_else(|_| return)
    = help: replace `return` with an empty block
 
 error: unneeded `return` statement
-  --> $DIR/needless_return.rs:129:13
+  --> $DIR/needless_return.rs:128:21
    |
-LL |             return;
-   |             ^^^^^^
+LL |           let _ = || {
+   |  _____________________^
+LL | |             return;
+   | |__________________^
    |
    = help: remove `return`
 
@@ -240,26 +250,32 @@ LL |     return the_answer!();
    = help: remove `return`
 
 error: unneeded `return` statement
-  --> $DIR/needless_return.rs:182:5
+  --> $DIR/needless_return.rs:181:33
    |
-LL |     return;
-   |     ^^^^^^
+LL |   async fn async_test_void_fun() {
+   |  _________________________________^
+LL | |     return;
+   | |__________^
    |
    = help: remove `return`
 
 error: unneeded `return` statement
-  --> $DIR/needless_return.rs:187:9
+  --> $DIR/needless_return.rs:186:11
    |
-LL |         return;
-   |         ^^^^^^
+LL |       if b {
+   |  ___________^
+LL | |         return;
+   | |______________^
    |
    = help: remove `return`
 
 error: unneeded `return` statement
-  --> $DIR/needless_return.rs:189:9
+  --> $DIR/needless_return.rs:188:13
    |
-LL |         return;
-   |         ^^^^^^
+LL |       } else {
+   |  _____________^
+LL | |         return;
+   | |______________^
    |
    = help: remove `return`
 
@@ -351,5 +367,24 @@ LL |             return !*(x as *const isize);
    |
    = help: remove `return`
 
-error: aborting due to 44 previous errors
+error: unneeded `return` statement
+  --> $DIR/needless_return.rs:274:20
+   |
+LL |           let _ = 42;
+   |  ____________________^
+LL | |
+LL | |         return;
+   | |______________^
+   |
+   = help: remove `return`
+
+error: unneeded `return` statement
+  --> $DIR/needless_return.rs:281:20
+   |
+LL |         let _ = 42; return;
+   |                    ^^^^^^^
+   |
+   = help: remove `return`
+
+error: aborting due to 46 previous errors
 
diff --git a/src/tools/clippy/tests/ui/needless_splitn.fixed b/src/tools/clippy/tests/ui/needless_splitn.fixed
index 61f5fc4e679..5496031fefa 100644
--- a/src/tools/clippy/tests/ui/needless_splitn.fixed
+++ b/src/tools/clippy/tests/ui/needless_splitn.fixed
@@ -1,7 +1,6 @@
 // run-rustfix
 // edition:2018
 
-#![feature(custom_inner_attributes)]
 #![warn(clippy::needless_splitn)]
 #![allow(clippy::iter_skip_next, clippy::iter_nth_zero, clippy::manual_split_once)]
 
@@ -40,8 +39,8 @@ fn _question_mark(s: &str) -> Option<()> {
     Some(())
 }
 
+#[clippy::msrv = "1.51"]
 fn _test_msrv() {
-    #![clippy::msrv = "1.51"]
     // `manual_split_once` MSRV shouldn't apply to `needless_splitn`
     let _ = "key=value".split('=').nth(0).unwrap();
 }
diff --git a/src/tools/clippy/tests/ui/needless_splitn.rs b/src/tools/clippy/tests/ui/needless_splitn.rs
index 71d9a7077fa..35c2465bae1 100644
--- a/src/tools/clippy/tests/ui/needless_splitn.rs
+++ b/src/tools/clippy/tests/ui/needless_splitn.rs
@@ -1,7 +1,6 @@
 // run-rustfix
 // edition:2018
 
-#![feature(custom_inner_attributes)]
 #![warn(clippy::needless_splitn)]
 #![allow(clippy::iter_skip_next, clippy::iter_nth_zero, clippy::manual_split_once)]
 
@@ -40,8 +39,8 @@ fn _question_mark(s: &str) -> Option<()> {
     Some(())
 }
 
+#[clippy::msrv = "1.51"]
 fn _test_msrv() {
-    #![clippy::msrv = "1.51"]
     // `manual_split_once` MSRV shouldn't apply to `needless_splitn`
     let _ = "key=value".splitn(2, '=').nth(0).unwrap();
 }
diff --git a/src/tools/clippy/tests/ui/needless_splitn.stderr b/src/tools/clippy/tests/ui/needless_splitn.stderr
index f112b29e7f2..f607d8e1ab5 100644
--- a/src/tools/clippy/tests/ui/needless_splitn.stderr
+++ b/src/tools/clippy/tests/ui/needless_splitn.stderr
@@ -1,5 +1,5 @@
 error: unnecessary use of `splitn`
-  --> $DIR/needless_splitn.rs:15:13
+  --> $DIR/needless_splitn.rs:14:13
    |
 LL |     let _ = str.splitn(2, '=').next();
    |             ^^^^^^^^^^^^^^^^^^ help: try this: `str.split('=')`
@@ -7,73 +7,73 @@ LL |     let _ = str.splitn(2, '=').next();
    = note: `-D clippy::needless-splitn` implied by `-D warnings`
 
 error: unnecessary use of `splitn`
-  --> $DIR/needless_splitn.rs:16:13
+  --> $DIR/needless_splitn.rs:15:13
    |
 LL |     let _ = str.splitn(2, '=').nth(0);
    |             ^^^^^^^^^^^^^^^^^^ help: try this: `str.split('=')`
 
 error: unnecessary use of `splitn`
-  --> $DIR/needless_splitn.rs:19:18
+  --> $DIR/needless_splitn.rs:18:18
    |
 LL |     let (_, _) = str.splitn(3, '=').next_tuple().unwrap();
    |                  ^^^^^^^^^^^^^^^^^^ help: try this: `str.split('=')`
 
 error: unnecessary use of `rsplitn`
-  --> $DIR/needless_splitn.rs:22:13
+  --> $DIR/needless_splitn.rs:21:13
    |
 LL |     let _ = str.rsplitn(2, '=').next();
    |             ^^^^^^^^^^^^^^^^^^^ help: try this: `str.rsplit('=')`
 
 error: unnecessary use of `rsplitn`
-  --> $DIR/needless_splitn.rs:23:13
+  --> $DIR/needless_splitn.rs:22:13
    |
 LL |     let _ = str.rsplitn(2, '=').nth(0);
    |             ^^^^^^^^^^^^^^^^^^^ help: try this: `str.rsplit('=')`
 
 error: unnecessary use of `rsplitn`
-  --> $DIR/needless_splitn.rs:26:18
+  --> $DIR/needless_splitn.rs:25:18
    |
 LL |     let (_, _) = str.rsplitn(3, '=').next_tuple().unwrap();
    |                  ^^^^^^^^^^^^^^^^^^^ help: try this: `str.rsplit('=')`
 
 error: unnecessary use of `splitn`
-  --> $DIR/needless_splitn.rs:28:13
+  --> $DIR/needless_splitn.rs:27:13
    |
 LL |     let _ = str.splitn(5, '=').next();
    |             ^^^^^^^^^^^^^^^^^^ help: try this: `str.split('=')`
 
 error: unnecessary use of `splitn`
-  --> $DIR/needless_splitn.rs:29:13
+  --> $DIR/needless_splitn.rs:28:13
    |
 LL |     let _ = str.splitn(5, '=').nth(3);
    |             ^^^^^^^^^^^^^^^^^^ help: try this: `str.split('=')`
 
 error: unnecessary use of `splitn`
-  --> $DIR/needless_splitn.rs:35:13
+  --> $DIR/needless_splitn.rs:34:13
    |
 LL |     let _ = s.splitn(2, '=').next()?;
    |             ^^^^^^^^^^^^^^^^ help: try this: `s.split('=')`
 
 error: unnecessary use of `splitn`
-  --> $DIR/needless_splitn.rs:36:13
+  --> $DIR/needless_splitn.rs:35:13
    |
 LL |     let _ = s.splitn(2, '=').nth(0)?;
    |             ^^^^^^^^^^^^^^^^ help: try this: `s.split('=')`
 
 error: unnecessary use of `rsplitn`
-  --> $DIR/needless_splitn.rs:37:13
+  --> $DIR/needless_splitn.rs:36:13
    |
 LL |     let _ = s.rsplitn(2, '=').next()?;
    |             ^^^^^^^^^^^^^^^^^ help: try this: `s.rsplit('=')`
 
 error: unnecessary use of `rsplitn`
-  --> $DIR/needless_splitn.rs:38:13
+  --> $DIR/needless_splitn.rs:37:13
    |
 LL |     let _ = s.rsplitn(2, '=').nth(0)?;
    |             ^^^^^^^^^^^^^^^^^ help: try this: `s.rsplit('=')`
 
 error: unnecessary use of `splitn`
-  --> $DIR/needless_splitn.rs:46:13
+  --> $DIR/needless_splitn.rs:45:13
    |
 LL |     let _ = "key=value".splitn(2, '=').nth(0).unwrap();
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `"key=value".split('=')`
diff --git a/src/tools/clippy/tests/ui/option_as_ref_deref.fixed b/src/tools/clippy/tests/ui/option_as_ref_deref.fixed
index bc376d0d7fb..d124d133faa 100644
--- a/src/tools/clippy/tests/ui/option_as_ref_deref.fixed
+++ b/src/tools/clippy/tests/ui/option_as_ref_deref.fixed
@@ -1,6 +1,5 @@
 // run-rustfix
 
-#![feature(custom_inner_attributes)]
 #![allow(unused, clippy::redundant_clone)]
 #![warn(clippy::option_as_ref_deref)]
 
@@ -44,16 +43,14 @@ fn main() {
     let _ = opt.as_deref();
 }
 
+#[clippy::msrv = "1.39"]
 fn msrv_1_39() {
-    #![clippy::msrv = "1.39"]
-
     let opt = Some(String::from("123"));
     let _ = opt.as_ref().map(String::as_str);
 }
 
+#[clippy::msrv = "1.40"]
 fn msrv_1_40() {
-    #![clippy::msrv = "1.40"]
-
     let opt = Some(String::from("123"));
     let _ = opt.as_deref();
 }
diff --git a/src/tools/clippy/tests/ui/option_as_ref_deref.rs b/src/tools/clippy/tests/ui/option_as_ref_deref.rs
index ba3a2eedc22..86e354c6716 100644
--- a/src/tools/clippy/tests/ui/option_as_ref_deref.rs
+++ b/src/tools/clippy/tests/ui/option_as_ref_deref.rs
@@ -1,6 +1,5 @@
 // run-rustfix
 
-#![feature(custom_inner_attributes)]
 #![allow(unused, clippy::redundant_clone)]
 #![warn(clippy::option_as_ref_deref)]
 
@@ -47,16 +46,14 @@ fn main() {
     let _ = opt.as_ref().map(std::ops::Deref::deref);
 }
 
+#[clippy::msrv = "1.39"]
 fn msrv_1_39() {
-    #![clippy::msrv = "1.39"]
-
     let opt = Some(String::from("123"));
     let _ = opt.as_ref().map(String::as_str);
 }
 
+#[clippy::msrv = "1.40"]
 fn msrv_1_40() {
-    #![clippy::msrv = "1.40"]
-
     let opt = Some(String::from("123"));
     let _ = opt.as_ref().map(String::as_str);
 }
diff --git a/src/tools/clippy/tests/ui/option_as_ref_deref.stderr b/src/tools/clippy/tests/ui/option_as_ref_deref.stderr
index 7de8b3b6ba4..e471b56eea8 100644
--- a/src/tools/clippy/tests/ui/option_as_ref_deref.stderr
+++ b/src/tools/clippy/tests/ui/option_as_ref_deref.stderr
@@ -1,5 +1,5 @@
 error: called `.as_ref().map(Deref::deref)` on an Option value. This can be done more directly by calling `opt.clone().as_deref()` instead
-  --> $DIR/option_as_ref_deref.rs:14:13
+  --> $DIR/option_as_ref_deref.rs:13:13
    |
 LL |     let _ = opt.clone().as_ref().map(Deref::deref).map(str::len);
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using as_deref instead: `opt.clone().as_deref()`
@@ -7,7 +7,7 @@ LL |     let _ = opt.clone().as_ref().map(Deref::deref).map(str::len);
    = note: `-D clippy::option-as-ref-deref` implied by `-D warnings`
 
 error: called `.as_ref().map(Deref::deref)` on an Option value. This can be done more directly by calling `opt.clone().as_deref()` instead
-  --> $DIR/option_as_ref_deref.rs:17:13
+  --> $DIR/option_as_ref_deref.rs:16:13
    |
 LL |       let _ = opt.clone()
    |  _____________^
@@ -17,97 +17,97 @@ LL | |         )
    | |_________^ help: try using as_deref instead: `opt.clone().as_deref()`
 
 error: called `.as_mut().map(DerefMut::deref_mut)` on an Option value. This can be done more directly by calling `opt.as_deref_mut()` instead
-  --> $DIR/option_as_ref_deref.rs:23:13
+  --> $DIR/option_as_ref_deref.rs:22:13
    |
 LL |     let _ = opt.as_mut().map(DerefMut::deref_mut);
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using as_deref_mut instead: `opt.as_deref_mut()`
 
 error: called `.as_ref().map(String::as_str)` on an Option value. This can be done more directly by calling `opt.as_deref()` instead
-  --> $DIR/option_as_ref_deref.rs:25:13
+  --> $DIR/option_as_ref_deref.rs:24:13
    |
 LL |     let _ = opt.as_ref().map(String::as_str);
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using as_deref instead: `opt.as_deref()`
 
 error: called `.as_ref().map(|x| x.as_str())` on an Option value. This can be done more directly by calling `opt.as_deref()` instead
-  --> $DIR/option_as_ref_deref.rs:26:13
+  --> $DIR/option_as_ref_deref.rs:25:13
    |
 LL |     let _ = opt.as_ref().map(|x| x.as_str());
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using as_deref instead: `opt.as_deref()`
 
 error: called `.as_mut().map(String::as_mut_str)` on an Option value. This can be done more directly by calling `opt.as_deref_mut()` instead
-  --> $DIR/option_as_ref_deref.rs:27:13
+  --> $DIR/option_as_ref_deref.rs:26:13
    |
 LL |     let _ = opt.as_mut().map(String::as_mut_str);
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using as_deref_mut instead: `opt.as_deref_mut()`
 
 error: called `.as_mut().map(|x| x.as_mut_str())` on an Option value. This can be done more directly by calling `opt.as_deref_mut()` instead
-  --> $DIR/option_as_ref_deref.rs:28:13
+  --> $DIR/option_as_ref_deref.rs:27:13
    |
 LL |     let _ = opt.as_mut().map(|x| x.as_mut_str());
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using as_deref_mut instead: `opt.as_deref_mut()`
 
 error: called `.as_ref().map(CString::as_c_str)` on an Option value. This can be done more directly by calling `Some(CString::new(vec![]).unwrap()).as_deref()` instead
-  --> $DIR/option_as_ref_deref.rs:29:13
+  --> $DIR/option_as_ref_deref.rs:28:13
    |
 LL |     let _ = Some(CString::new(vec![]).unwrap()).as_ref().map(CString::as_c_str);
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using as_deref instead: `Some(CString::new(vec![]).unwrap()).as_deref()`
 
 error: called `.as_ref().map(OsString::as_os_str)` on an Option value. This can be done more directly by calling `Some(OsString::new()).as_deref()` instead
-  --> $DIR/option_as_ref_deref.rs:30:13
+  --> $DIR/option_as_ref_deref.rs:29:13
    |
 LL |     let _ = Some(OsString::new()).as_ref().map(OsString::as_os_str);
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using as_deref instead: `Some(OsString::new()).as_deref()`
 
 error: called `.as_ref().map(PathBuf::as_path)` on an Option value. This can be done more directly by calling `Some(PathBuf::new()).as_deref()` instead
-  --> $DIR/option_as_ref_deref.rs:31:13
+  --> $DIR/option_as_ref_deref.rs:30:13
    |
 LL |     let _ = Some(PathBuf::new()).as_ref().map(PathBuf::as_path);
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using as_deref instead: `Some(PathBuf::new()).as_deref()`
 
 error: called `.as_ref().map(Vec::as_slice)` on an Option value. This can be done more directly by calling `Some(Vec::<()>::new()).as_deref()` instead
-  --> $DIR/option_as_ref_deref.rs:32:13
+  --> $DIR/option_as_ref_deref.rs:31:13
    |
 LL |     let _ = Some(Vec::<()>::new()).as_ref().map(Vec::as_slice);
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using as_deref instead: `Some(Vec::<()>::new()).as_deref()`
 
 error: called `.as_mut().map(Vec::as_mut_slice)` on an Option value. This can be done more directly by calling `Some(Vec::<()>::new()).as_deref_mut()` instead
-  --> $DIR/option_as_ref_deref.rs:33:13
+  --> $DIR/option_as_ref_deref.rs:32:13
    |
 LL |     let _ = Some(Vec::<()>::new()).as_mut().map(Vec::as_mut_slice);
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using as_deref_mut instead: `Some(Vec::<()>::new()).as_deref_mut()`
 
 error: called `.as_ref().map(|x| x.deref())` on an Option value. This can be done more directly by calling `opt.as_deref()` instead
-  --> $DIR/option_as_ref_deref.rs:35:13
+  --> $DIR/option_as_ref_deref.rs:34:13
    |
 LL |     let _ = opt.as_ref().map(|x| x.deref());
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using as_deref instead: `opt.as_deref()`
 
 error: called `.as_mut().map(|x| x.deref_mut())` on an Option value. This can be done more directly by calling `opt.clone().as_deref_mut()` instead
-  --> $DIR/option_as_ref_deref.rs:36:13
+  --> $DIR/option_as_ref_deref.rs:35:13
    |
 LL |     let _ = opt.clone().as_mut().map(|x| x.deref_mut()).map(|x| x.len());
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using as_deref_mut instead: `opt.clone().as_deref_mut()`
 
 error: called `.as_ref().map(|x| &**x)` on an Option value. This can be done more directly by calling `opt.as_deref()` instead
-  --> $DIR/option_as_ref_deref.rs:43:13
+  --> $DIR/option_as_ref_deref.rs:42:13
    |
 LL |     let _ = opt.as_ref().map(|x| &**x);
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using as_deref instead: `opt.as_deref()`
 
 error: called `.as_mut().map(|x| &mut **x)` on an Option value. This can be done more directly by calling `opt.as_deref_mut()` instead
-  --> $DIR/option_as_ref_deref.rs:44:13
+  --> $DIR/option_as_ref_deref.rs:43:13
    |
 LL |     let _ = opt.as_mut().map(|x| &mut **x);
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using as_deref_mut instead: `opt.as_deref_mut()`
 
 error: called `.as_ref().map(std::ops::Deref::deref)` on an Option value. This can be done more directly by calling `opt.as_deref()` instead
-  --> $DIR/option_as_ref_deref.rs:47:13
+  --> $DIR/option_as_ref_deref.rs:46:13
    |
 LL |     let _ = opt.as_ref().map(std::ops::Deref::deref);
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using as_deref instead: `opt.as_deref()`
 
 error: called `.as_ref().map(String::as_str)` on an Option value. This can be done more directly by calling `opt.as_deref()` instead
-  --> $DIR/option_as_ref_deref.rs:61:13
+  --> $DIR/option_as_ref_deref.rs:58:13
    |
 LL |     let _ = opt.as_ref().map(String::as_str);
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using as_deref instead: `opt.as_deref()`
diff --git a/src/tools/clippy/tests/ui/ptr_as_ptr.fixed b/src/tools/clippy/tests/ui/ptr_as_ptr.fixed
index bea6be66a8e..df36a9b842b 100644
--- a/src/tools/clippy/tests/ui/ptr_as_ptr.fixed
+++ b/src/tools/clippy/tests/ui/ptr_as_ptr.fixed
@@ -2,7 +2,6 @@
 // aux-build:macro_rules.rs
 
 #![warn(clippy::ptr_as_ptr)]
-#![feature(custom_inner_attributes)]
 
 extern crate macro_rules;
 
@@ -45,8 +44,8 @@ fn main() {
     let _ = macro_rules::ptr_as_ptr_cast!(ptr);
 }
 
+#[clippy::msrv = "1.37"]
 fn _msrv_1_37() {
-    #![clippy::msrv = "1.37"]
     let ptr: *const u32 = &42_u32;
     let mut_ptr: *mut u32 = &mut 42_u32;
 
@@ -55,8 +54,8 @@ fn _msrv_1_37() {
     let _ = mut_ptr as *mut i32;
 }
 
+#[clippy::msrv = "1.38"]
 fn _msrv_1_38() {
-    #![clippy::msrv = "1.38"]
     let ptr: *const u32 = &42_u32;
     let mut_ptr: *mut u32 = &mut 42_u32;
 
diff --git a/src/tools/clippy/tests/ui/ptr_as_ptr.rs b/src/tools/clippy/tests/ui/ptr_as_ptr.rs
index ca2616b0069..302c66462d9 100644
--- a/src/tools/clippy/tests/ui/ptr_as_ptr.rs
+++ b/src/tools/clippy/tests/ui/ptr_as_ptr.rs
@@ -2,7 +2,6 @@
 // aux-build:macro_rules.rs
 
 #![warn(clippy::ptr_as_ptr)]
-#![feature(custom_inner_attributes)]
 
 extern crate macro_rules;
 
@@ -45,8 +44,8 @@ fn main() {
     let _ = macro_rules::ptr_as_ptr_cast!(ptr);
 }
 
+#[clippy::msrv = "1.37"]
 fn _msrv_1_37() {
-    #![clippy::msrv = "1.37"]
     let ptr: *const u32 = &42_u32;
     let mut_ptr: *mut u32 = &mut 42_u32;
 
@@ -55,8 +54,8 @@ fn _msrv_1_37() {
     let _ = mut_ptr as *mut i32;
 }
 
+#[clippy::msrv = "1.38"]
 fn _msrv_1_38() {
-    #![clippy::msrv = "1.38"]
     let ptr: *const u32 = &42_u32;
     let mut_ptr: *mut u32 = &mut 42_u32;
 
diff --git a/src/tools/clippy/tests/ui/ptr_as_ptr.stderr b/src/tools/clippy/tests/ui/ptr_as_ptr.stderr
index c58c55cfd83..a68e1cab6d3 100644
--- a/src/tools/clippy/tests/ui/ptr_as_ptr.stderr
+++ b/src/tools/clippy/tests/ui/ptr_as_ptr.stderr
@@ -1,5 +1,5 @@
 error: `as` casting between raw pointers without changing its mutability
-  --> $DIR/ptr_as_ptr.rs:19:13
+  --> $DIR/ptr_as_ptr.rs:18:13
    |
 LL |     let _ = ptr as *const i32;
    |             ^^^^^^^^^^^^^^^^^ help: try `pointer::cast`, a safer alternative: `ptr.cast::<i32>()`
@@ -7,31 +7,31 @@ LL |     let _ = ptr as *const i32;
    = note: `-D clippy::ptr-as-ptr` implied by `-D warnings`
 
 error: `as` casting between raw pointers without changing its mutability
-  --> $DIR/ptr_as_ptr.rs:20:13
+  --> $DIR/ptr_as_ptr.rs:19:13
    |
 LL |     let _ = mut_ptr as *mut i32;
    |             ^^^^^^^^^^^^^^^^^^^ help: try `pointer::cast`, a safer alternative: `mut_ptr.cast::<i32>()`
 
 error: `as` casting between raw pointers without changing its mutability
-  --> $DIR/ptr_as_ptr.rs:25:17
+  --> $DIR/ptr_as_ptr.rs:24:17
    |
 LL |         let _ = *ptr_ptr as *const i32;
    |                 ^^^^^^^^^^^^^^^^^^^^^^ help: try `pointer::cast`, a safer alternative: `(*ptr_ptr).cast::<i32>()`
 
 error: `as` casting between raw pointers without changing its mutability
-  --> $DIR/ptr_as_ptr.rs:38:25
+  --> $DIR/ptr_as_ptr.rs:37:25
    |
 LL |     let _: *const i32 = ptr as *const _;
    |                         ^^^^^^^^^^^^^^^ help: try `pointer::cast`, a safer alternative: `ptr.cast()`
 
 error: `as` casting between raw pointers without changing its mutability
-  --> $DIR/ptr_as_ptr.rs:39:23
+  --> $DIR/ptr_as_ptr.rs:38:23
    |
 LL |     let _: *mut i32 = mut_ptr as _;
    |                       ^^^^^^^^^^^^ help: try `pointer::cast`, a safer alternative: `mut_ptr.cast()`
 
 error: `as` casting between raw pointers without changing its mutability
-  --> $DIR/ptr_as_ptr.rs:11:9
+  --> $DIR/ptr_as_ptr.rs:10:9
    |
 LL |         $ptr as *const i32
    |         ^^^^^^^^^^^^^^^^^^ help: try `pointer::cast`, a safer alternative: `$ptr.cast::<i32>()`
@@ -42,13 +42,13 @@ LL |     let _ = cast_it!(ptr);
    = note: this error originates in the macro `cast_it` (in Nightly builds, run with -Z macro-backtrace for more info)
 
 error: `as` casting between raw pointers without changing its mutability
-  --> $DIR/ptr_as_ptr.rs:63:13
+  --> $DIR/ptr_as_ptr.rs:62:13
    |
 LL |     let _ = ptr as *const i32;
    |             ^^^^^^^^^^^^^^^^^ help: try `pointer::cast`, a safer alternative: `ptr.cast::<i32>()`
 
 error: `as` casting between raw pointers without changing its mutability
-  --> $DIR/ptr_as_ptr.rs:64:13
+  --> $DIR/ptr_as_ptr.rs:63:13
    |
 LL |     let _ = mut_ptr as *mut i32;
    |             ^^^^^^^^^^^^^^^^^^^ help: try `pointer::cast`, a safer alternative: `mut_ptr.cast::<i32>()`
diff --git a/src/tools/clippy/tests/ui/range_contains.fixed b/src/tools/clippy/tests/ui/range_contains.fixed
index 824f00cb99e..4923731fe45 100644
--- a/src/tools/clippy/tests/ui/range_contains.fixed
+++ b/src/tools/clippy/tests/ui/range_contains.fixed
@@ -1,6 +1,5 @@
 // run-rustfix
 
-#![feature(custom_inner_attributes)]
 #![warn(clippy::manual_range_contains)]
 #![allow(unused)]
 #![allow(clippy::no_effect)]
@@ -65,16 +64,14 @@ pub const fn in_range(a: i32) -> bool {
     3 <= a && a <= 20
 }
 
+#[clippy::msrv = "1.34"]
 fn msrv_1_34() {
-    #![clippy::msrv = "1.34"]
-
     let x = 5;
     x >= 8 && x < 34;
 }
 
+#[clippy::msrv = "1.35"]
 fn msrv_1_35() {
-    #![clippy::msrv = "1.35"]
-
     let x = 5;
     (8..35).contains(&x);
 }
diff --git a/src/tools/clippy/tests/ui/range_contains.rs b/src/tools/clippy/tests/ui/range_contains.rs
index df925eeadfe..d623ccb5da6 100644
--- a/src/tools/clippy/tests/ui/range_contains.rs
+++ b/src/tools/clippy/tests/ui/range_contains.rs
@@ -1,6 +1,5 @@
 // run-rustfix
 
-#![feature(custom_inner_attributes)]
 #![warn(clippy::manual_range_contains)]
 #![allow(unused)]
 #![allow(clippy::no_effect)]
@@ -65,16 +64,14 @@ pub const fn in_range(a: i32) -> bool {
     3 <= a && a <= 20
 }
 
+#[clippy::msrv = "1.34"]
 fn msrv_1_34() {
-    #![clippy::msrv = "1.34"]
-
     let x = 5;
     x >= 8 && x < 34;
 }
 
+#[clippy::msrv = "1.35"]
 fn msrv_1_35() {
-    #![clippy::msrv = "1.35"]
-
     let x = 5;
     x >= 8 && x < 35;
 }
diff --git a/src/tools/clippy/tests/ui/range_contains.stderr b/src/tools/clippy/tests/ui/range_contains.stderr
index 9689e665b05..ea34023a466 100644
--- a/src/tools/clippy/tests/ui/range_contains.stderr
+++ b/src/tools/clippy/tests/ui/range_contains.stderr
@@ -1,5 +1,5 @@
 error: manual `Range::contains` implementation
-  --> $DIR/range_contains.rs:14:5
+  --> $DIR/range_contains.rs:13:5
    |
 LL |     x >= 8 && x < 12;
    |     ^^^^^^^^^^^^^^^^ help: use: `(8..12).contains(&x)`
@@ -7,121 +7,121 @@ LL |     x >= 8 && x < 12;
    = note: `-D clippy::manual-range-contains` implied by `-D warnings`
 
 error: manual `Range::contains` implementation
-  --> $DIR/range_contains.rs:15:5
+  --> $DIR/range_contains.rs:14:5
    |
 LL |     x < 42 && x >= 21;
    |     ^^^^^^^^^^^^^^^^^ help: use: `(21..42).contains(&x)`
 
 error: manual `Range::contains` implementation
-  --> $DIR/range_contains.rs:16:5
+  --> $DIR/range_contains.rs:15:5
    |
 LL |     100 > x && 1 <= x;
    |     ^^^^^^^^^^^^^^^^^ help: use: `(1..100).contains(&x)`
 
 error: manual `RangeInclusive::contains` implementation
-  --> $DIR/range_contains.rs:19:5
+  --> $DIR/range_contains.rs:18:5
    |
 LL |     x >= 9 && x <= 99;
    |     ^^^^^^^^^^^^^^^^^ help: use: `(9..=99).contains(&x)`
 
 error: manual `RangeInclusive::contains` implementation
-  --> $DIR/range_contains.rs:20:5
+  --> $DIR/range_contains.rs:19:5
    |
 LL |     x <= 33 && x >= 1;
    |     ^^^^^^^^^^^^^^^^^ help: use: `(1..=33).contains(&x)`
 
 error: manual `RangeInclusive::contains` implementation
-  --> $DIR/range_contains.rs:21:5
+  --> $DIR/range_contains.rs:20:5
    |
 LL |     999 >= x && 1 <= x;
    |     ^^^^^^^^^^^^^^^^^^ help: use: `(1..=999).contains(&x)`
 
 error: manual `!Range::contains` implementation
-  --> $DIR/range_contains.rs:24:5
+  --> $DIR/range_contains.rs:23:5
    |
 LL |     x < 8 || x >= 12;
    |     ^^^^^^^^^^^^^^^^ help: use: `!(8..12).contains(&x)`
 
 error: manual `!Range::contains` implementation
-  --> $DIR/range_contains.rs:25:5
+  --> $DIR/range_contains.rs:24:5
    |
 LL |     x >= 42 || x < 21;
    |     ^^^^^^^^^^^^^^^^^ help: use: `!(21..42).contains(&x)`
 
 error: manual `!Range::contains` implementation
-  --> $DIR/range_contains.rs:26:5
+  --> $DIR/range_contains.rs:25:5
    |
 LL |     100 <= x || 1 > x;
    |     ^^^^^^^^^^^^^^^^^ help: use: `!(1..100).contains(&x)`
 
 error: manual `!RangeInclusive::contains` implementation
-  --> $DIR/range_contains.rs:29:5
+  --> $DIR/range_contains.rs:28:5
    |
 LL |     x < 9 || x > 99;
    |     ^^^^^^^^^^^^^^^ help: use: `!(9..=99).contains(&x)`
 
 error: manual `!RangeInclusive::contains` implementation
-  --> $DIR/range_contains.rs:30:5
+  --> $DIR/range_contains.rs:29:5
    |
 LL |     x > 33 || x < 1;
    |     ^^^^^^^^^^^^^^^ help: use: `!(1..=33).contains(&x)`
 
 error: manual `!RangeInclusive::contains` implementation
-  --> $DIR/range_contains.rs:31:5
+  --> $DIR/range_contains.rs:30:5
    |
 LL |     999 < x || 1 > x;
    |     ^^^^^^^^^^^^^^^^ help: use: `!(1..=999).contains(&x)`
 
 error: manual `Range::contains` implementation
-  --> $DIR/range_contains.rs:46:5
+  --> $DIR/range_contains.rs:45:5
    |
 LL |     y >= 0. && y < 1.;
    |     ^^^^^^^^^^^^^^^^^ help: use: `(0. ..1.).contains(&y)`
 
 error: manual `!RangeInclusive::contains` implementation
-  --> $DIR/range_contains.rs:47:5
+  --> $DIR/range_contains.rs:46:5
    |
 LL |     y < 0. || y > 1.;
    |     ^^^^^^^^^^^^^^^^ help: use: `!(0. ..=1.).contains(&y)`
 
 error: manual `RangeInclusive::contains` implementation
-  --> $DIR/range_contains.rs:50:5
+  --> $DIR/range_contains.rs:49:5
    |
 LL |     x >= -10 && x <= 10;
    |     ^^^^^^^^^^^^^^^^^^^ help: use: `(-10..=10).contains(&x)`
 
 error: manual `RangeInclusive::contains` implementation
-  --> $DIR/range_contains.rs:52:5
+  --> $DIR/range_contains.rs:51:5
    |
 LL |     y >= -3. && y <= 3.;
    |     ^^^^^^^^^^^^^^^^^^^ help: use: `(-3. ..=3.).contains(&y)`
 
 error: manual `RangeInclusive::contains` implementation
-  --> $DIR/range_contains.rs:57:30
+  --> $DIR/range_contains.rs:56:30
    |
 LL |     (x >= 0) && (x <= 10) && (z >= 0) && (z <= 10);
    |                              ^^^^^^^^^^^^^^^^^^^^^ help: use: `(0..=10).contains(&z)`
 
 error: manual `RangeInclusive::contains` implementation
-  --> $DIR/range_contains.rs:57:5
+  --> $DIR/range_contains.rs:56:5
    |
 LL |     (x >= 0) && (x <= 10) && (z >= 0) && (z <= 10);
    |     ^^^^^^^^^^^^^^^^^^^^^ help: use: `(0..=10).contains(&x)`
 
 error: manual `!Range::contains` implementation
-  --> $DIR/range_contains.rs:58:29
+  --> $DIR/range_contains.rs:57:29
    |
 LL |     (x < 0) || (x >= 10) || (z < 0) || (z >= 10);
    |                             ^^^^^^^^^^^^^^^^^^^^ help: use: `!(0..10).contains(&z)`
 
 error: manual `!Range::contains` implementation
-  --> $DIR/range_contains.rs:58:5
+  --> $DIR/range_contains.rs:57:5
    |
 LL |     (x < 0) || (x >= 10) || (z < 0) || (z >= 10);
    |     ^^^^^^^^^^^^^^^^^^^^ help: use: `!(0..10).contains(&x)`
 
 error: manual `Range::contains` implementation
-  --> $DIR/range_contains.rs:79:5
+  --> $DIR/range_contains.rs:76:5
    |
 LL |     x >= 8 && x < 35;
    |     ^^^^^^^^^^^^^^^^ help: use: `(8..35).contains(&x)`
diff --git a/src/tools/clippy/tests/ui/redundant_closure_call_fixable.fixed b/src/tools/clippy/tests/ui/redundant_closure_call_fixable.fixed
index 7cd687c95a0..c0e49ff4caa 100644
--- a/src/tools/clippy/tests/ui/redundant_closure_call_fixable.fixed
+++ b/src/tools/clippy/tests/ui/redundant_closure_call_fixable.fixed
@@ -25,4 +25,16 @@ fn main() {
         x * y
     };
     let d = async { something().await };
+
+    macro_rules! m {
+        () => {
+            0
+        };
+    }
+    macro_rules! m2 {
+        () => {
+            m!()
+        };
+    }
+    m2!();
 }
diff --git a/src/tools/clippy/tests/ui/redundant_closure_call_fixable.rs b/src/tools/clippy/tests/ui/redundant_closure_call_fixable.rs
index 37e4d223864..9e6e54348a8 100644
--- a/src/tools/clippy/tests/ui/redundant_closure_call_fixable.rs
+++ b/src/tools/clippy/tests/ui/redundant_closure_call_fixable.rs
@@ -25,4 +25,16 @@ fn main() {
         x * y
     })();
     let d = (async || something().await)();
+
+    macro_rules! m {
+        () => {
+            (|| 0)()
+        };
+    }
+    macro_rules! m2 {
+        () => {
+            (|| m!())()
+        };
+    }
+    m2!();
 }
diff --git a/src/tools/clippy/tests/ui/redundant_closure_call_fixable.stderr b/src/tools/clippy/tests/ui/redundant_closure_call_fixable.stderr
index 56a8e57c0c3..d71bcba2a82 100644
--- a/src/tools/clippy/tests/ui/redundant_closure_call_fixable.stderr
+++ b/src/tools/clippy/tests/ui/redundant_closure_call_fixable.stderr
@@ -52,5 +52,27 @@ error: try not to call a closure in the expression where it is declared
 LL |     let d = (async || something().await)();
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try doing something like: `async { something().await }`
 
-error: aborting due to 4 previous errors
+error: try not to call a closure in the expression where it is declared
+  --> $DIR/redundant_closure_call_fixable.rs:36:13
+   |
+LL |             (|| m!())()
+   |             ^^^^^^^^^^^ help: try doing something like: `m!()`
+...
+LL |     m2!();
+   |     ----- in this macro invocation
+   |
+   = note: this error originates in the macro `m2` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: try not to call a closure in the expression where it is declared
+  --> $DIR/redundant_closure_call_fixable.rs:31:13
+   |
+LL |             (|| 0)()
+   |             ^^^^^^^^ help: try doing something like: `0`
+...
+LL |     m2!();
+   |     ----- in this macro invocation
+   |
+   = note: this error originates in the macro `m` which comes from the expansion of the macro `m2` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: aborting due to 6 previous errors
 
diff --git a/src/tools/clippy/tests/ui/redundant_field_names.fixed b/src/tools/clippy/tests/ui/redundant_field_names.fixed
index 34ab552cb1d..ec7f8ae923a 100644
--- a/src/tools/clippy/tests/ui/redundant_field_names.fixed
+++ b/src/tools/clippy/tests/ui/redundant_field_names.fixed
@@ -1,6 +1,5 @@
 // run-rustfix
 
-#![feature(custom_inner_attributes)]
 #![warn(clippy::redundant_field_names)]
 #![allow(clippy::no_effect, dead_code, unused_variables)]
 
@@ -72,16 +71,14 @@ fn issue_3476() {
     S { foo: foo::<i32> };
 }
 
+#[clippy::msrv = "1.16"]
 fn msrv_1_16() {
-    #![clippy::msrv = "1.16"]
-
     let start = 0;
     let _ = RangeFrom { start: start };
 }
 
+#[clippy::msrv = "1.17"]
 fn msrv_1_17() {
-    #![clippy::msrv = "1.17"]
-
     let start = 0;
     let _ = RangeFrom { start };
 }
diff --git a/src/tools/clippy/tests/ui/redundant_field_names.rs b/src/tools/clippy/tests/ui/redundant_field_names.rs
index a051b1f96f0..73122016cf6 100644
--- a/src/tools/clippy/tests/ui/redundant_field_names.rs
+++ b/src/tools/clippy/tests/ui/redundant_field_names.rs
@@ -1,6 +1,5 @@
 // run-rustfix
 
-#![feature(custom_inner_attributes)]
 #![warn(clippy::redundant_field_names)]
 #![allow(clippy::no_effect, dead_code, unused_variables)]
 
@@ -72,16 +71,14 @@ fn issue_3476() {
     S { foo: foo::<i32> };
 }
 
+#[clippy::msrv = "1.16"]
 fn msrv_1_16() {
-    #![clippy::msrv = "1.16"]
-
     let start = 0;
     let _ = RangeFrom { start: start };
 }
 
+#[clippy::msrv = "1.17"]
 fn msrv_1_17() {
-    #![clippy::msrv = "1.17"]
-
     let start = 0;
     let _ = RangeFrom { start: start };
 }
diff --git a/src/tools/clippy/tests/ui/redundant_field_names.stderr b/src/tools/clippy/tests/ui/redundant_field_names.stderr
index 8b82e062b93..00a72c50cf7 100644
--- a/src/tools/clippy/tests/ui/redundant_field_names.stderr
+++ b/src/tools/clippy/tests/ui/redundant_field_names.stderr
@@ -1,5 +1,5 @@
 error: redundant field names in struct initialization
-  --> $DIR/redundant_field_names.rs:36:9
+  --> $DIR/redundant_field_names.rs:35:9
    |
 LL |         gender: gender,
    |         ^^^^^^^^^^^^^^ help: replace it with: `gender`
@@ -7,43 +7,43 @@ LL |         gender: gender,
    = note: `-D clippy::redundant-field-names` implied by `-D warnings`
 
 error: redundant field names in struct initialization
-  --> $DIR/redundant_field_names.rs:37:9
+  --> $DIR/redundant_field_names.rs:36:9
    |
 LL |         age: age,
    |         ^^^^^^^^ help: replace it with: `age`
 
 error: redundant field names in struct initialization
-  --> $DIR/redundant_field_names.rs:58:25
+  --> $DIR/redundant_field_names.rs:57:25
    |
 LL |     let _ = RangeFrom { start: start };
    |                         ^^^^^^^^^^^^ help: replace it with: `start`
 
 error: redundant field names in struct initialization
-  --> $DIR/redundant_field_names.rs:59:23
+  --> $DIR/redundant_field_names.rs:58:23
    |
 LL |     let _ = RangeTo { end: end };
    |                       ^^^^^^^^ help: replace it with: `end`
 
 error: redundant field names in struct initialization
-  --> $DIR/redundant_field_names.rs:60:21
+  --> $DIR/redundant_field_names.rs:59:21
    |
 LL |     let _ = Range { start: start, end: end };
    |                     ^^^^^^^^^^^^ help: replace it with: `start`
 
 error: redundant field names in struct initialization
-  --> $DIR/redundant_field_names.rs:60:35
+  --> $DIR/redundant_field_names.rs:59:35
    |
 LL |     let _ = Range { start: start, end: end };
    |                                   ^^^^^^^^ help: replace it with: `end`
 
 error: redundant field names in struct initialization
-  --> $DIR/redundant_field_names.rs:62:32
+  --> $DIR/redundant_field_names.rs:61:32
    |
 LL |     let _ = RangeToInclusive { end: end };
    |                                ^^^^^^^^ help: replace it with: `end`
 
 error: redundant field names in struct initialization
-  --> $DIR/redundant_field_names.rs:86:25
+  --> $DIR/redundant_field_names.rs:83:25
    |
 LL |     let _ = RangeFrom { start: start };
    |                         ^^^^^^^^^^^^ help: replace it with: `start`
diff --git a/src/tools/clippy/tests/ui/redundant_static_lifetimes.fixed b/src/tools/clippy/tests/ui/redundant_static_lifetimes.fixed
index 42110dbe81e..4c5846fe837 100644
--- a/src/tools/clippy/tests/ui/redundant_static_lifetimes.fixed
+++ b/src/tools/clippy/tests/ui/redundant_static_lifetimes.fixed
@@ -1,6 +1,5 @@
 // run-rustfix
 
-#![feature(custom_inner_attributes)]
 #![allow(unused)]
 
 #[derive(Debug)]
@@ -56,14 +55,12 @@ impl Bar for Foo {
     const TRAIT_VAR: &'static str = "foo";
 }
 
+#[clippy::msrv = "1.16"]
 fn msrv_1_16() {
-    #![clippy::msrv = "1.16"]
-
     static V: &'static u8 = &16;
 }
 
+#[clippy::msrv = "1.17"]
 fn msrv_1_17() {
-    #![clippy::msrv = "1.17"]
-
     static V: &u8 = &17;
 }
diff --git a/src/tools/clippy/tests/ui/redundant_static_lifetimes.rs b/src/tools/clippy/tests/ui/redundant_static_lifetimes.rs
index bc5200bc862..64a66be1a83 100644
--- a/src/tools/clippy/tests/ui/redundant_static_lifetimes.rs
+++ b/src/tools/clippy/tests/ui/redundant_static_lifetimes.rs
@@ -1,6 +1,5 @@
 // run-rustfix
 
-#![feature(custom_inner_attributes)]
 #![allow(unused)]
 
 #[derive(Debug)]
@@ -56,14 +55,12 @@ impl Bar for Foo {
     const TRAIT_VAR: &'static str = "foo";
 }
 
+#[clippy::msrv = "1.16"]
 fn msrv_1_16() {
-    #![clippy::msrv = "1.16"]
-
     static V: &'static u8 = &16;
 }
 
+#[clippy::msrv = "1.17"]
 fn msrv_1_17() {
-    #![clippy::msrv = "1.17"]
-
     static V: &'static u8 = &17;
 }
diff --git a/src/tools/clippy/tests/ui/redundant_static_lifetimes.stderr b/src/tools/clippy/tests/ui/redundant_static_lifetimes.stderr
index 735113460d2..0938ebf783f 100644
--- a/src/tools/clippy/tests/ui/redundant_static_lifetimes.stderr
+++ b/src/tools/clippy/tests/ui/redundant_static_lifetimes.stderr
@@ -1,5 +1,5 @@
 error: constants have by default a `'static` lifetime
-  --> $DIR/redundant_static_lifetimes.rs:9:17
+  --> $DIR/redundant_static_lifetimes.rs:8:17
    |
 LL | const VAR_ONE: &'static str = "Test constant #1"; // ERROR Consider removing 'static.
    |                -^^^^^^^---- help: consider removing `'static`: `&str`
@@ -7,97 +7,97 @@ LL | const VAR_ONE: &'static str = "Test constant #1"; // ERROR Consider removin
    = note: `-D clippy::redundant-static-lifetimes` implied by `-D warnings`
 
 error: constants have by default a `'static` lifetime
-  --> $DIR/redundant_static_lifetimes.rs:13:21
+  --> $DIR/redundant_static_lifetimes.rs:12:21
    |
 LL | const VAR_THREE: &[&'static str] = &["one", "two"]; // ERROR Consider removing 'static
    |                    -^^^^^^^---- help: consider removing `'static`: `&str`
 
 error: constants have by default a `'static` lifetime
-  --> $DIR/redundant_static_lifetimes.rs:15:32
+  --> $DIR/redundant_static_lifetimes.rs:14:32
    |
 LL | const VAR_FOUR: (&str, (&str, &'static str), &'static str) = ("on", ("th", "th"), "on"); // ERROR Consider removing 'static
    |                               -^^^^^^^---- help: consider removing `'static`: `&str`
 
 error: constants have by default a `'static` lifetime
-  --> $DIR/redundant_static_lifetimes.rs:15:47
+  --> $DIR/redundant_static_lifetimes.rs:14:47
    |
 LL | const VAR_FOUR: (&str, (&str, &'static str), &'static str) = ("on", ("th", "th"), "on"); // ERROR Consider removing 'static
    |                                              -^^^^^^^---- help: consider removing `'static`: `&str`
 
 error: constants have by default a `'static` lifetime
-  --> $DIR/redundant_static_lifetimes.rs:17:17
+  --> $DIR/redundant_static_lifetimes.rs:16:17
    |
 LL | const VAR_SIX: &'static u8 = &5;
    |                -^^^^^^^--- help: consider removing `'static`: `&u8`
 
 error: constants have by default a `'static` lifetime
-  --> $DIR/redundant_static_lifetimes.rs:19:20
+  --> $DIR/redundant_static_lifetimes.rs:18:20
    |
 LL | const VAR_HEIGHT: &'static Foo = &Foo {};
    |                   -^^^^^^^---- help: consider removing `'static`: `&Foo`
 
 error: constants have by default a `'static` lifetime
-  --> $DIR/redundant_static_lifetimes.rs:21:19
+  --> $DIR/redundant_static_lifetimes.rs:20:19
    |
 LL | const VAR_SLICE: &'static [u8] = b"Test constant #1"; // ERROR Consider removing 'static.
    |                  -^^^^^^^----- help: consider removing `'static`: `&[u8]`
 
 error: constants have by default a `'static` lifetime
-  --> $DIR/redundant_static_lifetimes.rs:23:19
+  --> $DIR/redundant_static_lifetimes.rs:22:19
    |
 LL | const VAR_TUPLE: &'static (u8, u8) = &(1, 2); // ERROR Consider removing 'static.
    |                  -^^^^^^^--------- help: consider removing `'static`: `&(u8, u8)`
 
 error: constants have by default a `'static` lifetime
-  --> $DIR/redundant_static_lifetimes.rs:25:19
+  --> $DIR/redundant_static_lifetimes.rs:24:19
    |
 LL | const VAR_ARRAY: &'static [u8; 1] = b"T"; // ERROR Consider removing 'static.
    |                  -^^^^^^^-------- help: consider removing `'static`: `&[u8; 1]`
 
 error: statics have by default a `'static` lifetime
-  --> $DIR/redundant_static_lifetimes.rs:27:25
+  --> $DIR/redundant_static_lifetimes.rs:26:25
    |
 LL | static STATIC_VAR_ONE: &'static str = "Test static #1"; // ERROR Consider removing 'static.
    |                        -^^^^^^^---- help: consider removing `'static`: `&str`
 
 error: statics have by default a `'static` lifetime
-  --> $DIR/redundant_static_lifetimes.rs:31:29
+  --> $DIR/redundant_static_lifetimes.rs:30:29
    |
 LL | static STATIC_VAR_THREE: &[&'static str] = &["one", "two"]; // ERROR Consider removing 'static
    |                            -^^^^^^^---- help: consider removing `'static`: `&str`
 
 error: statics have by default a `'static` lifetime
-  --> $DIR/redundant_static_lifetimes.rs:33:25
+  --> $DIR/redundant_static_lifetimes.rs:32:25
    |
 LL | static STATIC_VAR_SIX: &'static u8 = &5;
    |                        -^^^^^^^--- help: consider removing `'static`: `&u8`
 
 error: statics have by default a `'static` lifetime
-  --> $DIR/redundant_static_lifetimes.rs:35:28
+  --> $DIR/redundant_static_lifetimes.rs:34:28
    |
 LL | static STATIC_VAR_HEIGHT: &'static Foo = &Foo {};
    |                           -^^^^^^^---- help: consider removing `'static`: `&Foo`
 
 error: statics have by default a `'static` lifetime
-  --> $DIR/redundant_static_lifetimes.rs:37:27
+  --> $DIR/redundant_static_lifetimes.rs:36:27
    |
 LL | static STATIC_VAR_SLICE: &'static [u8] = b"Test static #3"; // ERROR Consider removing 'static.
    |                          -^^^^^^^----- help: consider removing `'static`: `&[u8]`
 
 error: statics have by default a `'static` lifetime
-  --> $DIR/redundant_static_lifetimes.rs:39:27
+  --> $DIR/redundant_static_lifetimes.rs:38:27
    |
 LL | static STATIC_VAR_TUPLE: &'static (u8, u8) = &(1, 2); // ERROR Consider removing 'static.
    |                          -^^^^^^^--------- help: consider removing `'static`: `&(u8, u8)`
 
 error: statics have by default a `'static` lifetime
-  --> $DIR/redundant_static_lifetimes.rs:41:27
+  --> $DIR/redundant_static_lifetimes.rs:40:27
    |
 LL | static STATIC_VAR_ARRAY: &'static [u8; 1] = b"T"; // ERROR Consider removing 'static.
    |                          -^^^^^^^-------- help: consider removing `'static`: `&[u8; 1]`
 
 error: statics have by default a `'static` lifetime
-  --> $DIR/redundant_static_lifetimes.rs:68:16
+  --> $DIR/redundant_static_lifetimes.rs:65:16
    |
 LL |     static V: &'static u8 = &17;
    |               -^^^^^^^--- help: consider removing `'static`: `&u8`
diff --git a/src/tools/clippy/tests/ui/result_large_err.rs b/src/tools/clippy/tests/ui/result_large_err.rs
index 9dd27d6dc01..1c12cebfd97 100644
--- a/src/tools/clippy/tests/ui/result_large_err.rs
+++ b/src/tools/clippy/tests/ui/result_large_err.rs
@@ -108,4 +108,10 @@ pub fn array_error<T, U>() -> Result<(), ArrayError<(i32, T), U>> {
     Ok(())
 }
 
+// Issue #10005
+enum Empty {}
+fn _empty_error() -> Result<(), Empty> {
+    Ok(())
+}
+
 fn main() {}
diff --git a/src/tools/clippy/tests/ui/result_map_unit_fn_unfixable.stderr b/src/tools/clippy/tests/ui/result_map_unit_fn_unfixable.stderr
index 88e4efdb0f0..2e1eb8eb180 100644
--- a/src/tools/clippy/tests/ui/result_map_unit_fn_unfixable.stderr
+++ b/src/tools/clippy/tests/ui/result_map_unit_fn_unfixable.stderr
@@ -20,14 +20,14 @@ error: called `map(f)` on an `Result` value where `f` is a closure that returns
   --> $DIR/result_map_unit_fn_unfixable.rs:29:5
    |
 LL |        x.field.map(|value| {
-   |   _____^
-   |  |_____|
+   |  ______^
+   | | _____|
    | ||
 LL | ||         do_nothing(value);
 LL | ||         do_nothing(value)
 LL | ||     });
    | ||______^- help: try this: `if let Ok(value) = x.field { ... }`
-   | |_______|
+   |  |______|
    | 
 
 error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type `()`
diff --git a/src/tools/clippy/tests/ui/seek_from_current.fixed b/src/tools/clippy/tests/ui/seek_from_current.fixed
index 4b5303324bc..1309c91b81c 100644
--- a/src/tools/clippy/tests/ui/seek_from_current.fixed
+++ b/src/tools/clippy/tests/ui/seek_from_current.fixed
@@ -1,12 +1,11 @@
 // run-rustfix
 #![warn(clippy::seek_from_current)]
-#![feature(custom_inner_attributes)]
 
 use std::fs::File;
 use std::io::{self, Seek, SeekFrom, Write};
 
+#[clippy::msrv = "1.50"]
 fn _msrv_1_50() -> io::Result<()> {
-    #![clippy::msrv = "1.50"]
     let mut f = File::create("foo.txt")?;
     f.write_all(b"Hi!")?;
     f.seek(SeekFrom::Current(0))?;
@@ -14,8 +13,8 @@ fn _msrv_1_50() -> io::Result<()> {
     Ok(())
 }
 
+#[clippy::msrv = "1.51"]
 fn _msrv_1_51() -> io::Result<()> {
-    #![clippy::msrv = "1.51"]
     let mut f = File::create("foo.txt")?;
     f.write_all(b"Hi!")?;
     f.stream_position()?;
diff --git a/src/tools/clippy/tests/ui/seek_from_current.rs b/src/tools/clippy/tests/ui/seek_from_current.rs
index f93639261a1..5d9b1424cf6 100644
--- a/src/tools/clippy/tests/ui/seek_from_current.rs
+++ b/src/tools/clippy/tests/ui/seek_from_current.rs
@@ -1,12 +1,11 @@
 // run-rustfix
 #![warn(clippy::seek_from_current)]
-#![feature(custom_inner_attributes)]
 
 use std::fs::File;
 use std::io::{self, Seek, SeekFrom, Write};
 
+#[clippy::msrv = "1.50"]
 fn _msrv_1_50() -> io::Result<()> {
-    #![clippy::msrv = "1.50"]
     let mut f = File::create("foo.txt")?;
     f.write_all(b"Hi!")?;
     f.seek(SeekFrom::Current(0))?;
@@ -14,8 +13,8 @@ fn _msrv_1_50() -> io::Result<()> {
     Ok(())
 }
 
+#[clippy::msrv = "1.51"]
 fn _msrv_1_51() -> io::Result<()> {
-    #![clippy::msrv = "1.51"]
     let mut f = File::create("foo.txt")?;
     f.write_all(b"Hi!")?;
     f.seek(SeekFrom::Current(0))?;
diff --git a/src/tools/clippy/tests/ui/seek_from_current.stderr b/src/tools/clippy/tests/ui/seek_from_current.stderr
index db1125b53cd..c079f361192 100644
--- a/src/tools/clippy/tests/ui/seek_from_current.stderr
+++ b/src/tools/clippy/tests/ui/seek_from_current.stderr
@@ -1,5 +1,5 @@
 error: using `SeekFrom::Current` to start from current position
-  --> $DIR/seek_from_current.rs:21:5
+  --> $DIR/seek_from_current.rs:20:5
    |
 LL |     f.seek(SeekFrom::Current(0))?;
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `f.stream_position()`
diff --git a/src/tools/clippy/tests/ui/seek_to_start_instead_of_rewind.fixed b/src/tools/clippy/tests/ui/seek_to_start_instead_of_rewind.fixed
index 464b6cdef63..9d0d1124c46 100644
--- a/src/tools/clippy/tests/ui/seek_to_start_instead_of_rewind.fixed
+++ b/src/tools/clippy/tests/ui/seek_to_start_instead_of_rewind.fixed
@@ -1,6 +1,5 @@
 // run-rustfix
 #![allow(unused)]
-#![feature(custom_inner_attributes)]
 #![warn(clippy::seek_to_start_instead_of_rewind)]
 
 use std::fs::OpenOptions;
@@ -94,9 +93,8 @@ fn main() {
     assert_eq!(&buf, hello);
 }
 
+#[clippy::msrv = "1.54"]
 fn msrv_1_54() {
-    #![clippy::msrv = "1.54"]
-
     let mut f = OpenOptions::new()
         .write(true)
         .read(true)
@@ -115,9 +113,8 @@ fn msrv_1_54() {
     assert_eq!(&buf, hello);
 }
 
+#[clippy::msrv = "1.55"]
 fn msrv_1_55() {
-    #![clippy::msrv = "1.55"]
-
     let mut f = OpenOptions::new()
         .write(true)
         .read(true)
diff --git a/src/tools/clippy/tests/ui/seek_to_start_instead_of_rewind.rs b/src/tools/clippy/tests/ui/seek_to_start_instead_of_rewind.rs
index 68e09bd7c1f..c5bc57cc3a7 100644
--- a/src/tools/clippy/tests/ui/seek_to_start_instead_of_rewind.rs
+++ b/src/tools/clippy/tests/ui/seek_to_start_instead_of_rewind.rs
@@ -1,6 +1,5 @@
 // run-rustfix
 #![allow(unused)]
-#![feature(custom_inner_attributes)]
 #![warn(clippy::seek_to_start_instead_of_rewind)]
 
 use std::fs::OpenOptions;
@@ -94,9 +93,8 @@ fn main() {
     assert_eq!(&buf, hello);
 }
 
+#[clippy::msrv = "1.54"]
 fn msrv_1_54() {
-    #![clippy::msrv = "1.54"]
-
     let mut f = OpenOptions::new()
         .write(true)
         .read(true)
@@ -115,9 +113,8 @@ fn msrv_1_54() {
     assert_eq!(&buf, hello);
 }
 
+#[clippy::msrv = "1.55"]
 fn msrv_1_55() {
-    #![clippy::msrv = "1.55"]
-
     let mut f = OpenOptions::new()
         .write(true)
         .read(true)
diff --git a/src/tools/clippy/tests/ui/seek_to_start_instead_of_rewind.stderr b/src/tools/clippy/tests/ui/seek_to_start_instead_of_rewind.stderr
index de0eec5d909..6cce025359f 100644
--- a/src/tools/clippy/tests/ui/seek_to_start_instead_of_rewind.stderr
+++ b/src/tools/clippy/tests/ui/seek_to_start_instead_of_rewind.stderr
@@ -1,5 +1,5 @@
 error: used `seek` to go to the start of the stream
-  --> $DIR/seek_to_start_instead_of_rewind.rs:54:7
+  --> $DIR/seek_to_start_instead_of_rewind.rs:53:7
    |
 LL |     t.seek(SeekFrom::Start(0));
    |       ^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `rewind()`
@@ -7,13 +7,13 @@ LL |     t.seek(SeekFrom::Start(0));
    = note: `-D clippy::seek-to-start-instead-of-rewind` implied by `-D warnings`
 
 error: used `seek` to go to the start of the stream
-  --> $DIR/seek_to_start_instead_of_rewind.rs:59:7
+  --> $DIR/seek_to_start_instead_of_rewind.rs:58:7
    |
 LL |     t.seek(SeekFrom::Start(0));
    |       ^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `rewind()`
 
 error: used `seek` to go to the start of the stream
-  --> $DIR/seek_to_start_instead_of_rewind.rs:131:7
+  --> $DIR/seek_to_start_instead_of_rewind.rs:128:7
    |
 LL |     f.seek(SeekFrom::Start(0));
    |       ^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `rewind()`
diff --git a/src/tools/clippy/tests/ui/transmute_ptr_to_ref.fixed b/src/tools/clippy/tests/ui/transmute_ptr_to_ref.fixed
index e5fe9133f97..074dae5fb28 100644
--- a/src/tools/clippy/tests/ui/transmute_ptr_to_ref.fixed
+++ b/src/tools/clippy/tests/ui/transmute_ptr_to_ref.fixed
@@ -1,6 +1,5 @@
 // run-rustfix
 
-#![feature(custom_inner_attributes)]
 #![warn(clippy::transmute_ptr_to_ref)]
 #![allow(clippy::match_single_binding)]
 
@@ -51,8 +50,8 @@ unsafe fn _issue8924<'a, 'b, 'c>(x: *const &'a u32, y: *const &'b u32) -> &'c &'
     }
 }
 
+#[clippy::msrv = "1.38"]
 unsafe fn _meets_msrv<'a, 'b, 'c>(x: *const &'a u32) -> &'c &'b u32 {
-    #![clippy::msrv = "1.38"]
     let a = 0u32;
     let a = &a as *const u32;
     let _: &u32 = &*a;
@@ -63,8 +62,8 @@ unsafe fn _meets_msrv<'a, 'b, 'c>(x: *const &'a u32) -> &'c &'b u32 {
     }
 }
 
+#[clippy::msrv = "1.37"]
 unsafe fn _under_msrv<'a, 'b, 'c>(x: *const &'a u32) -> &'c &'b u32 {
-    #![clippy::msrv = "1.37"]
     let a = 0u32;
     let a = &a as *const u32;
     let _: &u32 = &*a;
diff --git a/src/tools/clippy/tests/ui/transmute_ptr_to_ref.rs b/src/tools/clippy/tests/ui/transmute_ptr_to_ref.rs
index fe49cdc324f..2edc122cf47 100644
--- a/src/tools/clippy/tests/ui/transmute_ptr_to_ref.rs
+++ b/src/tools/clippy/tests/ui/transmute_ptr_to_ref.rs
@@ -1,6 +1,5 @@
 // run-rustfix
 
-#![feature(custom_inner_attributes)]
 #![warn(clippy::transmute_ptr_to_ref)]
 #![allow(clippy::match_single_binding)]
 
@@ -51,8 +50,8 @@ unsafe fn _issue8924<'a, 'b, 'c>(x: *const &'a u32, y: *const &'b u32) -> &'c &'
     }
 }
 
+#[clippy::msrv = "1.38"]
 unsafe fn _meets_msrv<'a, 'b, 'c>(x: *const &'a u32) -> &'c &'b u32 {
-    #![clippy::msrv = "1.38"]
     let a = 0u32;
     let a = &a as *const u32;
     let _: &u32 = std::mem::transmute(a);
@@ -63,8 +62,8 @@ unsafe fn _meets_msrv<'a, 'b, 'c>(x: *const &'a u32) -> &'c &'b u32 {
     }
 }
 
+#[clippy::msrv = "1.37"]
 unsafe fn _under_msrv<'a, 'b, 'c>(x: *const &'a u32) -> &'c &'b u32 {
-    #![clippy::msrv = "1.37"]
     let a = 0u32;
     let a = &a as *const u32;
     let _: &u32 = std::mem::transmute(a);
diff --git a/src/tools/clippy/tests/ui/transmute_ptr_to_ref.stderr b/src/tools/clippy/tests/ui/transmute_ptr_to_ref.stderr
index 10117ee9182..b3e6c09d2d7 100644
--- a/src/tools/clippy/tests/ui/transmute_ptr_to_ref.stderr
+++ b/src/tools/clippy/tests/ui/transmute_ptr_to_ref.stderr
@@ -1,5 +1,5 @@
 error: transmute from a pointer type (`*const T`) to a reference type (`&T`)
-  --> $DIR/transmute_ptr_to_ref.rs:8:17
+  --> $DIR/transmute_ptr_to_ref.rs:7:17
    |
 LL |     let _: &T = std::mem::transmute(p);
    |                 ^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*p`
@@ -7,127 +7,127 @@ LL |     let _: &T = std::mem::transmute(p);
    = note: `-D clippy::transmute-ptr-to-ref` implied by `-D warnings`
 
 error: transmute from a pointer type (`*mut T`) to a reference type (`&mut T`)
-  --> $DIR/transmute_ptr_to_ref.rs:11:21
+  --> $DIR/transmute_ptr_to_ref.rs:10:21
    |
 LL |     let _: &mut T = std::mem::transmute(m);
    |                     ^^^^^^^^^^^^^^^^^^^^^^ help: try: `&mut *m`
 
 error: transmute from a pointer type (`*mut T`) to a reference type (`&T`)
-  --> $DIR/transmute_ptr_to_ref.rs:14:17
+  --> $DIR/transmute_ptr_to_ref.rs:13:17
    |
 LL |     let _: &T = std::mem::transmute(m);
    |                 ^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*m`
 
 error: transmute from a pointer type (`*mut T`) to a reference type (`&mut T`)
-  --> $DIR/transmute_ptr_to_ref.rs:17:21
+  --> $DIR/transmute_ptr_to_ref.rs:16:21
    |
 LL |     let _: &mut T = std::mem::transmute(p as *mut T);
    |                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&mut *(p as *mut T)`
 
 error: transmute from a pointer type (`*const U`) to a reference type (`&T`)
-  --> $DIR/transmute_ptr_to_ref.rs:20:17
+  --> $DIR/transmute_ptr_to_ref.rs:19:17
    |
 LL |     let _: &T = std::mem::transmute(o);
    |                 ^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*(o as *const T)`
 
 error: transmute from a pointer type (`*mut U`) to a reference type (`&mut T`)
-  --> $DIR/transmute_ptr_to_ref.rs:23:21
+  --> $DIR/transmute_ptr_to_ref.rs:22:21
    |
 LL |     let _: &mut T = std::mem::transmute(om);
    |                     ^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&mut *(om as *mut T)`
 
 error: transmute from a pointer type (`*mut U`) to a reference type (`&T`)
-  --> $DIR/transmute_ptr_to_ref.rs:26:17
+  --> $DIR/transmute_ptr_to_ref.rs:25:17
    |
 LL |     let _: &T = std::mem::transmute(om);
    |                 ^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*(om as *const T)`
 
 error: transmute from a pointer type (`*const i32`) to a reference type (`&_issue1231::Foo<'_, u8>`)
-  --> $DIR/transmute_ptr_to_ref.rs:36:32
+  --> $DIR/transmute_ptr_to_ref.rs:35:32
    |
 LL |     let _: &Foo<u8> = unsafe { std::mem::transmute::<_, &Foo<_>>(raw) };
    |                                ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*raw.cast::<Foo<_>>()`
 
 error: transmute from a pointer type (`*const i32`) to a reference type (`&_issue1231::Foo<'_, &u8>`)
-  --> $DIR/transmute_ptr_to_ref.rs:38:33
+  --> $DIR/transmute_ptr_to_ref.rs:37:33
    |
 LL |     let _: &Foo<&u8> = unsafe { std::mem::transmute::<_, &Foo<&_>>(raw) };
    |                                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*raw.cast::<Foo<&_>>()`
 
 error: transmute from a pointer type (`*const i32`) to a reference type (`&u8`)
-  --> $DIR/transmute_ptr_to_ref.rs:42:14
+  --> $DIR/transmute_ptr_to_ref.rs:41:14
    |
 LL |     unsafe { std::mem::transmute::<_, Bar>(raw) };
    |              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*(raw as *const u8)`
 
 error: transmute from a pointer type (`*const &u32`) to a reference type (`&&u32`)
-  --> $DIR/transmute_ptr_to_ref.rs:47:14
+  --> $DIR/transmute_ptr_to_ref.rs:46:14
    |
 LL |         0 => std::mem::transmute(x),
    |              ^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*x.cast::<&u32>()`
 
 error: transmute from a pointer type (`*const &u32`) to a reference type (`&&u32`)
-  --> $DIR/transmute_ptr_to_ref.rs:48:14
+  --> $DIR/transmute_ptr_to_ref.rs:47:14
    |
 LL |         1 => std::mem::transmute(y),
    |              ^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*y.cast::<&u32>()`
 
 error: transmute from a pointer type (`*const &u32`) to a reference type (`&&u32`)
-  --> $DIR/transmute_ptr_to_ref.rs:49:14
+  --> $DIR/transmute_ptr_to_ref.rs:48:14
    |
 LL |         2 => std::mem::transmute::<_, &&'b u32>(x),
    |              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*x.cast::<&'b u32>()`
 
 error: transmute from a pointer type (`*const &u32`) to a reference type (`&&u32`)
-  --> $DIR/transmute_ptr_to_ref.rs:50:14
+  --> $DIR/transmute_ptr_to_ref.rs:49:14
    |
 LL |         _ => std::mem::transmute::<_, &&'b u32>(y),
    |              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*y.cast::<&'b u32>()`
 
 error: transmute from a pointer type (`*const u32`) to a reference type (`&u32`)
-  --> $DIR/transmute_ptr_to_ref.rs:58:19
+  --> $DIR/transmute_ptr_to_ref.rs:57:19
    |
 LL |     let _: &u32 = std::mem::transmute(a);
    |                   ^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*a`
 
 error: transmute from a pointer type (`*const u32`) to a reference type (`&u32`)
-  --> $DIR/transmute_ptr_to_ref.rs:59:19
+  --> $DIR/transmute_ptr_to_ref.rs:58:19
    |
 LL |     let _: &u32 = std::mem::transmute::<_, &u32>(a);
    |                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*a.cast::<u32>()`
 
 error: transmute from a pointer type (`*const &u32`) to a reference type (`&&u32`)
-  --> $DIR/transmute_ptr_to_ref.rs:61:14
+  --> $DIR/transmute_ptr_to_ref.rs:60:14
    |
 LL |         0 => std::mem::transmute(x),
    |              ^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*x.cast::<&u32>()`
 
 error: transmute from a pointer type (`*const &u32`) to a reference type (`&&u32`)
-  --> $DIR/transmute_ptr_to_ref.rs:62:14
+  --> $DIR/transmute_ptr_to_ref.rs:61:14
    |
 LL |         _ => std::mem::transmute::<_, &&'b u32>(x),
    |              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*x.cast::<&'b u32>()`
 
 error: transmute from a pointer type (`*const u32`) to a reference type (`&u32`)
-  --> $DIR/transmute_ptr_to_ref.rs:70:19
+  --> $DIR/transmute_ptr_to_ref.rs:69:19
    |
 LL |     let _: &u32 = std::mem::transmute(a);
    |                   ^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*a`
 
 error: transmute from a pointer type (`*const u32`) to a reference type (`&u32`)
-  --> $DIR/transmute_ptr_to_ref.rs:71:19
+  --> $DIR/transmute_ptr_to_ref.rs:70:19
    |
 LL |     let _: &u32 = std::mem::transmute::<_, &u32>(a);
    |                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*(a as *const u32)`
 
 error: transmute from a pointer type (`*const &u32`) to a reference type (`&&u32`)
-  --> $DIR/transmute_ptr_to_ref.rs:73:14
+  --> $DIR/transmute_ptr_to_ref.rs:72:14
    |
 LL |         0 => std::mem::transmute(x),
    |              ^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*(x as *const () as *const &u32)`
 
 error: transmute from a pointer type (`*const &u32`) to a reference type (`&&u32`)
-  --> $DIR/transmute_ptr_to_ref.rs:74:14
+  --> $DIR/transmute_ptr_to_ref.rs:73:14
    |
 LL |         _ => std::mem::transmute::<_, &&'b u32>(x),
    |              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*(x as *const () as *const &'b u32)`
diff --git a/src/tools/clippy/tests/ui/undocumented_unsafe_blocks.rs b/src/tools/clippy/tests/ui/undocumented_unsafe_blocks.rs
index cbc6768033e..c05eb447b2e 100644
--- a/src/tools/clippy/tests/ui/undocumented_unsafe_blocks.rs
+++ b/src/tools/clippy/tests/ui/undocumented_unsafe_blocks.rs
@@ -1,6 +1,6 @@
 // aux-build:proc_macro_unsafe.rs
 
-#![warn(clippy::undocumented_unsafe_blocks)]
+#![warn(clippy::undocumented_unsafe_blocks, clippy::unnecessary_safety_comment)]
 #![allow(clippy::let_unit_value, clippy::missing_safety_doc)]
 
 extern crate proc_macro_unsafe;
diff --git a/src/tools/clippy/tests/ui/undocumented_unsafe_blocks.stderr b/src/tools/clippy/tests/ui/undocumented_unsafe_blocks.stderr
index ba4de9806d1..d1c1bb5ffea 100644
--- a/src/tools/clippy/tests/ui/undocumented_unsafe_blocks.stderr
+++ b/src/tools/clippy/tests/ui/undocumented_unsafe_blocks.stderr
@@ -239,6 +239,19 @@ LL |     unsafe impl TrailingComment for () {} // SAFETY:
    |
    = help: consider adding a safety comment on the preceding line
 
+error: constant item has unnecessary safety comment
+  --> $DIR/undocumented_unsafe_blocks.rs:471:5
+   |
+LL |     const BIG_NUMBER: i32 = 1000000;
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: consider removing the safety comment
+  --> $DIR/undocumented_unsafe_blocks.rs:470:5
+   |
+LL |     // SAFETY:
+   |     ^^^^^^^^^^
+   = note: `-D clippy::unnecessary-safety-comment` implied by `-D warnings`
+
 error: unsafe impl missing a safety comment
   --> $DIR/undocumented_unsafe_blocks.rs:472:5
    |
@@ -271,6 +284,24 @@ LL |         unsafe {};
    |
    = help: consider adding a safety comment on the preceding line
 
+error: statement has unnecessary safety comment
+  --> $DIR/undocumented_unsafe_blocks.rs:501:5
+   |
+LL | /     let _ = {
+LL | |         if unsafe { true } {
+LL | |             todo!();
+LL | |         } else {
+...  |
+LL | |         }
+LL | |     };
+   | |______^
+   |
+help: consider removing the safety comment
+  --> $DIR/undocumented_unsafe_blocks.rs:500:5
+   |
+LL |     // SAFETY: this is more than one level away, so it should warn
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
 error: unsafe block missing a safety comment
   --> $DIR/undocumented_unsafe_blocks.rs:502:12
    |
@@ -287,5 +318,5 @@ LL |             let bar = unsafe {};
    |
    = help: consider adding a safety comment on the preceding line
 
-error: aborting due to 34 previous errors
+error: aborting due to 36 previous errors
 
diff --git a/src/tools/clippy/tests/ui/uninlined_format_args.fixed b/src/tools/clippy/tests/ui/uninlined_format_args.fixed
index 10627447975..9d08e80cf9a 100644
--- a/src/tools/clippy/tests/ui/uninlined_format_args.fixed
+++ b/src/tools/clippy/tests/ui/uninlined_format_args.fixed
@@ -1,6 +1,5 @@
 // aux-build:proc_macro_with_span.rs
 // run-rustfix
-#![feature(custom_inner_attributes)]
 #![warn(clippy::uninlined_format_args)]
 #![allow(named_arguments_used_positionally, unused_imports, unused_macros, unused_variables)]
 #![allow(clippy::eq_op, clippy::format_in_format_args, clippy::print_literal)]
@@ -44,9 +43,7 @@ fn tester(fn_arg: i32) {
     println!("val='{local_i32}'"); // space+tab
     println!("val='{local_i32}'"); // tab+space
     println!(
-        "val='{
-    }'",
-        local_i32
+        "val='{local_i32}'"
     );
     println!("{local_i32}");
     println!("{fn_arg}");
@@ -57,11 +54,11 @@ fn tester(fn_arg: i32) {
     println!("{local_i32:<3}");
     println!("{local_i32:#010x}");
     println!("{local_f64:.1}");
-    println!("Hello {} is {local_f64:.local_i32$}", "x");
-    println!("Hello {local_i32} is {local_f64:.*}", 5);
-    println!("Hello {local_i32} is {local_f64:.*}", 5);
+    println!("Hello {} is {:.*}", "x", local_i32, local_f64);
+    println!("Hello {} is {:.*}", local_i32, 5, local_f64);
+    println!("Hello {} is {2:.*}", local_i32, 5, local_f64);
     println!("{local_i32} {local_f64}");
-    println!("{local_i32}, {}", local_opt.unwrap());
+    println!("{}, {}", local_i32, local_opt.unwrap());
     println!("{val}");
     println!("{val}");
     println!("{} {1}", local_i32, 42);
@@ -110,8 +107,7 @@ fn tester(fn_arg: i32) {
     println!("{local_f64:width$.prec$}");
     println!("{local_f64:width$.prec$} {local_f64} {width} {prec}");
     println!(
-        "{0:1$.2$} {0:2$.1$} {1:0$.2$} {1:2$.0$} {2:0$.1$} {2:1$.0$}",
-        local_i32, width, prec,
+        "{local_i32:width$.prec$} {local_i32:prec$.width$} {width:local_i32$.prec$} {width:prec$.local_i32$} {prec:local_i32$.width$} {prec:width$.local_i32$}",
     );
     println!(
         "{0:1$.2$} {0:2$.1$} {1:0$.2$} {1:2$.0$} {2:0$.1$} {2:1$.0$} {3}",
@@ -142,9 +138,7 @@ fn tester(fn_arg: i32) {
     println!(no_param_str!(), local_i32);
 
     println!(
-        "{}",
-        // comment with a comma , in it
-        val,
+        "{val}",
     );
     println!("{val}");
 
@@ -169,14 +163,14 @@ fn main() {
     tester(42);
 }
 
+#[clippy::msrv = "1.57"]
 fn _under_msrv() {
-    #![clippy::msrv = "1.57"]
     let local_i32 = 1;
     println!("don't expand='{}'", local_i32);
 }
 
+#[clippy::msrv = "1.58"]
 fn _meets_msrv() {
-    #![clippy::msrv = "1.58"]
     let local_i32 = 1;
     println!("expand='{local_i32}'");
 }
diff --git a/src/tools/clippy/tests/ui/uninlined_format_args.rs b/src/tools/clippy/tests/ui/uninlined_format_args.rs
index 8e495ebd083..35b3677a896 100644
--- a/src/tools/clippy/tests/ui/uninlined_format_args.rs
+++ b/src/tools/clippy/tests/ui/uninlined_format_args.rs
@@ -1,6 +1,5 @@
 // aux-build:proc_macro_with_span.rs
 // run-rustfix
-#![feature(custom_inner_attributes)]
 #![warn(clippy::uninlined_format_args)]
 #![allow(named_arguments_used_positionally, unused_imports, unused_macros, unused_variables)]
 #![allow(clippy::eq_op, clippy::format_in_format_args, clippy::print_literal)]
@@ -169,14 +168,14 @@ fn main() {
     tester(42);
 }
 
+#[clippy::msrv = "1.57"]
 fn _under_msrv() {
-    #![clippy::msrv = "1.57"]
     let local_i32 = 1;
     println!("don't expand='{}'", local_i32);
 }
 
+#[clippy::msrv = "1.58"]
 fn _meets_msrv() {
-    #![clippy::msrv = "1.58"]
     let local_i32 = 1;
     println!("expand='{}'", local_i32);
 }
diff --git a/src/tools/clippy/tests/ui/uninlined_format_args.stderr b/src/tools/clippy/tests/ui/uninlined_format_args.stderr
index 2ce3b7fa960..a12abf8bef8 100644
--- a/src/tools/clippy/tests/ui/uninlined_format_args.stderr
+++ b/src/tools/clippy/tests/ui/uninlined_format_args.stderr
@@ -1,5 +1,5 @@
 error: variables can be used directly in the `format!` string
-  --> $DIR/uninlined_format_args.rs:41:5
+  --> $DIR/uninlined_format_args.rs:40:5
    |
 LL |     println!("val='{}'", local_i32);
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -12,7 +12,7 @@ LL +     println!("val='{local_i32}'");
    |
 
 error: variables can be used directly in the `format!` string
-  --> $DIR/uninlined_format_args.rs:42:5
+  --> $DIR/uninlined_format_args.rs:41:5
    |
 LL |     println!("val='{   }'", local_i32); // 3 spaces
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -24,7 +24,7 @@ LL +     println!("val='{local_i32}'"); // 3 spaces
    |
 
 error: variables can be used directly in the `format!` string
-  --> $DIR/uninlined_format_args.rs:43:5
+  --> $DIR/uninlined_format_args.rs:42:5
    |
 LL |     println!("val='{    }'", local_i32); // tab
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -36,7 +36,7 @@ LL +     println!("val='{local_i32}'"); // tab
    |
 
 error: variables can be used directly in the `format!` string
-  --> $DIR/uninlined_format_args.rs:44:5
+  --> $DIR/uninlined_format_args.rs:43:5
    |
 LL |     println!("val='{     }'", local_i32); // space+tab
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -48,7 +48,7 @@ LL +     println!("val='{local_i32}'"); // space+tab
    |
 
 error: variables can be used directly in the `format!` string
-  --> $DIR/uninlined_format_args.rs:45:5
+  --> $DIR/uninlined_format_args.rs:44:5
    |
 LL |     println!("val='{     }'", local_i32); // tab+space
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -60,7 +60,17 @@ LL +     println!("val='{local_i32}'"); // tab+space
    |
 
 error: variables can be used directly in the `format!` string
-  --> $DIR/uninlined_format_args.rs:51:5
+  --> $DIR/uninlined_format_args.rs:45:5
+   |
+LL | /     println!(
+LL | |         "val='{
+LL | |     }'",
+LL | |         local_i32
+LL | |     );
+   | |_____^
+
+error: variables can be used directly in the `format!` string
+  --> $DIR/uninlined_format_args.rs:50:5
    |
 LL |     println!("{}", local_i32);
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -72,7 +82,7 @@ LL +     println!("{local_i32}");
    |
 
 error: variables can be used directly in the `format!` string
-  --> $DIR/uninlined_format_args.rs:52:5
+  --> $DIR/uninlined_format_args.rs:51:5
    |
 LL |     println!("{}", fn_arg);
    |     ^^^^^^^^^^^^^^^^^^^^^^
@@ -84,7 +94,7 @@ LL +     println!("{fn_arg}");
    |
 
 error: variables can be used directly in the `format!` string
-  --> $DIR/uninlined_format_args.rs:53:5
+  --> $DIR/uninlined_format_args.rs:52:5
    |
 LL |     println!("{:?}", local_i32);
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -96,7 +106,7 @@ LL +     println!("{local_i32:?}");
    |
 
 error: variables can be used directly in the `format!` string
-  --> $DIR/uninlined_format_args.rs:54:5
+  --> $DIR/uninlined_format_args.rs:53:5
    |
 LL |     println!("{:#?}", local_i32);
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -108,7 +118,7 @@ LL +     println!("{local_i32:#?}");
    |
 
 error: variables can be used directly in the `format!` string
-  --> $DIR/uninlined_format_args.rs:55:5
+  --> $DIR/uninlined_format_args.rs:54:5
    |
 LL |     println!("{:4}", local_i32);
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -120,7 +130,7 @@ LL +     println!("{local_i32:4}");
    |
 
 error: variables can be used directly in the `format!` string
-  --> $DIR/uninlined_format_args.rs:56:5
+  --> $DIR/uninlined_format_args.rs:55:5
    |
 LL |     println!("{:04}", local_i32);
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -132,7 +142,7 @@ LL +     println!("{local_i32:04}");
    |
 
 error: variables can be used directly in the `format!` string
-  --> $DIR/uninlined_format_args.rs:57:5
+  --> $DIR/uninlined_format_args.rs:56:5
    |
 LL |     println!("{:<3}", local_i32);
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -144,7 +154,7 @@ LL +     println!("{local_i32:<3}");
    |
 
 error: variables can be used directly in the `format!` string
-  --> $DIR/uninlined_format_args.rs:58:5
+  --> $DIR/uninlined_format_args.rs:57:5
    |
 LL |     println!("{:#010x}", local_i32);
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -156,7 +166,7 @@ LL +     println!("{local_i32:#010x}");
    |
 
 error: variables can be used directly in the `format!` string
-  --> $DIR/uninlined_format_args.rs:59:5
+  --> $DIR/uninlined_format_args.rs:58:5
    |
 LL |     println!("{:.1}", local_f64);
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -168,44 +178,8 @@ LL +     println!("{local_f64:.1}");
    |
 
 error: variables can be used directly in the `format!` string
-  --> $DIR/uninlined_format_args.rs:60:5
-   |
-LL |     println!("Hello {} is {:.*}", "x", local_i32, local_f64);
-   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-   |
-help: change this to
-   |
-LL -     println!("Hello {} is {:.*}", "x", local_i32, local_f64);
-LL +     println!("Hello {} is {local_f64:.local_i32$}", "x");
-   |
-
-error: variables can be used directly in the `format!` string
-  --> $DIR/uninlined_format_args.rs:61:5
-   |
-LL |     println!("Hello {} is {:.*}", local_i32, 5, local_f64);
-   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-   |
-help: change this to
-   |
-LL -     println!("Hello {} is {:.*}", local_i32, 5, local_f64);
-LL +     println!("Hello {local_i32} is {local_f64:.*}", 5);
-   |
-
-error: variables can be used directly in the `format!` string
   --> $DIR/uninlined_format_args.rs:62:5
    |
-LL |     println!("Hello {} is {2:.*}", local_i32, 5, local_f64);
-   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-   |
-help: change this to
-   |
-LL -     println!("Hello {} is {2:.*}", local_i32, 5, local_f64);
-LL +     println!("Hello {local_i32} is {local_f64:.*}", 5);
-   |
-
-error: variables can be used directly in the `format!` string
-  --> $DIR/uninlined_format_args.rs:63:5
-   |
 LL |     println!("{} {}", local_i32, local_f64);
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |
@@ -218,18 +192,6 @@ LL +     println!("{local_i32} {local_f64}");
 error: variables can be used directly in the `format!` string
   --> $DIR/uninlined_format_args.rs:64:5
    |
-LL |     println!("{}, {}", local_i32, local_opt.unwrap());
-   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-   |
-help: change this to
-   |
-LL -     println!("{}, {}", local_i32, local_opt.unwrap());
-LL +     println!("{local_i32}, {}", local_opt.unwrap());
-   |
-
-error: variables can be used directly in the `format!` string
-  --> $DIR/uninlined_format_args.rs:65:5
-   |
 LL |     println!("{}", val);
    |     ^^^^^^^^^^^^^^^^^^^
    |
@@ -240,7 +202,7 @@ LL +     println!("{val}");
    |
 
 error: variables can be used directly in the `format!` string
-  --> $DIR/uninlined_format_args.rs:66:5
+  --> $DIR/uninlined_format_args.rs:65:5
    |
 LL |     println!("{}", v = val);
    |     ^^^^^^^^^^^^^^^^^^^^^^^
@@ -252,7 +214,7 @@ LL +     println!("{val}");
    |
 
 error: variables can be used directly in the `format!` string
-  --> $DIR/uninlined_format_args.rs:68:5
+  --> $DIR/uninlined_format_args.rs:67:5
    |
 LL |     println!("val='{/t }'", local_i32);
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -264,7 +226,7 @@ LL +     println!("val='{local_i32}'");
    |
 
 error: variables can be used directly in the `format!` string
-  --> $DIR/uninlined_format_args.rs:69:5
+  --> $DIR/uninlined_format_args.rs:68:5
    |
 LL |     println!("val='{/n }'", local_i32);
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -276,7 +238,7 @@ LL +     println!("val='{local_i32}'");
    |
 
 error: variables can be used directly in the `format!` string
-  --> $DIR/uninlined_format_args.rs:70:5
+  --> $DIR/uninlined_format_args.rs:69:5
    |
 LL |     println!("val='{local_i32}'", local_i32 = local_i32);
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -288,7 +250,7 @@ LL +     println!("val='{local_i32}'");
    |
 
 error: variables can be used directly in the `format!` string
-  --> $DIR/uninlined_format_args.rs:71:5
+  --> $DIR/uninlined_format_args.rs:70:5
    |
 LL |     println!("val='{local_i32}'", local_i32 = fn_arg);
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -300,7 +262,7 @@ LL +     println!("val='{fn_arg}'");
    |
 
 error: variables can be used directly in the `format!` string
-  --> $DIR/uninlined_format_args.rs:72:5
+  --> $DIR/uninlined_format_args.rs:71:5
    |
 LL |     println!("{0}", local_i32);
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -312,7 +274,7 @@ LL +     println!("{local_i32}");
    |
 
 error: variables can be used directly in the `format!` string
-  --> $DIR/uninlined_format_args.rs:73:5
+  --> $DIR/uninlined_format_args.rs:72:5
    |
 LL |     println!("{0:?}", local_i32);
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -324,7 +286,7 @@ LL +     println!("{local_i32:?}");
    |
 
 error: variables can be used directly in the `format!` string
-  --> $DIR/uninlined_format_args.rs:74:5
+  --> $DIR/uninlined_format_args.rs:73:5
    |
 LL |     println!("{0:#?}", local_i32);
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -336,7 +298,7 @@ LL +     println!("{local_i32:#?}");
    |
 
 error: variables can be used directly in the `format!` string
-  --> $DIR/uninlined_format_args.rs:75:5
+  --> $DIR/uninlined_format_args.rs:74:5
    |
 LL |     println!("{0:04}", local_i32);
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -348,7 +310,7 @@ LL +     println!("{local_i32:04}");
    |
 
 error: variables can be used directly in the `format!` string
-  --> $DIR/uninlined_format_args.rs:76:5
+  --> $DIR/uninlined_format_args.rs:75:5
    |
 LL |     println!("{0:<3}", local_i32);
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -360,7 +322,7 @@ LL +     println!("{local_i32:<3}");
    |
 
 error: variables can be used directly in the `format!` string
-  --> $DIR/uninlined_format_args.rs:77:5
+  --> $DIR/uninlined_format_args.rs:76:5
    |
 LL |     println!("{0:#010x}", local_i32);
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -372,7 +334,7 @@ LL +     println!("{local_i32:#010x}");
    |
 
 error: variables can be used directly in the `format!` string
-  --> $DIR/uninlined_format_args.rs:78:5
+  --> $DIR/uninlined_format_args.rs:77:5
    |
 LL |     println!("{0:.1}", local_f64);
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -384,7 +346,7 @@ LL +     println!("{local_f64:.1}");
    |
 
 error: variables can be used directly in the `format!` string
-  --> $DIR/uninlined_format_args.rs:79:5
+  --> $DIR/uninlined_format_args.rs:78:5
    |
 LL |     println!("{0} {0}", local_i32);
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -396,7 +358,7 @@ LL +     println!("{local_i32} {local_i32}");
    |
 
 error: variables can be used directly in the `format!` string
-  --> $DIR/uninlined_format_args.rs:80:5
+  --> $DIR/uninlined_format_args.rs:79:5
    |
 LL |     println!("{1} {} {0} {}", local_i32, local_f64);
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -408,7 +370,7 @@ LL +     println!("{local_f64} {local_i32} {local_i32} {local_f64}");
    |
 
 error: variables can be used directly in the `format!` string
-  --> $DIR/uninlined_format_args.rs:81:5
+  --> $DIR/uninlined_format_args.rs:80:5
    |
 LL |     println!("{0} {1}", local_i32, local_f64);
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -420,7 +382,7 @@ LL +     println!("{local_i32} {local_f64}");
    |
 
 error: variables can be used directly in the `format!` string
-  --> $DIR/uninlined_format_args.rs:82:5
+  --> $DIR/uninlined_format_args.rs:81:5
    |
 LL |     println!("{1} {0}", local_i32, local_f64);
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -432,7 +394,7 @@ LL +     println!("{local_f64} {local_i32}");
    |
 
 error: variables can be used directly in the `format!` string
-  --> $DIR/uninlined_format_args.rs:83:5
+  --> $DIR/uninlined_format_args.rs:82:5
    |
 LL |     println!("{1} {0} {1} {0}", local_i32, local_f64);
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -444,7 +406,7 @@ LL +     println!("{local_f64} {local_i32} {local_f64} {local_i32}");
    |
 
 error: variables can be used directly in the `format!` string
-  --> $DIR/uninlined_format_args.rs:85:5
+  --> $DIR/uninlined_format_args.rs:84:5
    |
 LL |     println!("{v}", v = local_i32);
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -456,7 +418,7 @@ LL +     println!("{local_i32}");
    |
 
 error: variables can be used directly in the `format!` string
-  --> $DIR/uninlined_format_args.rs:86:5
+  --> $DIR/uninlined_format_args.rs:85:5
    |
 LL |     println!("{local_i32:0$}", width);
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -468,7 +430,7 @@ LL +     println!("{local_i32:width$}");
    |
 
 error: variables can be used directly in the `format!` string
-  --> $DIR/uninlined_format_args.rs:87:5
+  --> $DIR/uninlined_format_args.rs:86:5
    |
 LL |     println!("{local_i32:w$}", w = width);
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -480,7 +442,7 @@ LL +     println!("{local_i32:width$}");
    |
 
 error: variables can be used directly in the `format!` string
-  --> $DIR/uninlined_format_args.rs:88:5
+  --> $DIR/uninlined_format_args.rs:87:5
    |
 LL |     println!("{local_i32:.0$}", prec);
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -492,7 +454,7 @@ LL +     println!("{local_i32:.prec$}");
    |
 
 error: variables can be used directly in the `format!` string
-  --> $DIR/uninlined_format_args.rs:89:5
+  --> $DIR/uninlined_format_args.rs:88:5
    |
 LL |     println!("{local_i32:.p$}", p = prec);
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -504,7 +466,7 @@ LL +     println!("{local_i32:.prec$}");
    |
 
 error: variables can be used directly in the `format!` string
-  --> $DIR/uninlined_format_args.rs:90:5
+  --> $DIR/uninlined_format_args.rs:89:5
    |
 LL |     println!("{:0$}", v = val);
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -516,7 +478,7 @@ LL +     println!("{val:val$}");
    |
 
 error: variables can be used directly in the `format!` string
-  --> $DIR/uninlined_format_args.rs:91:5
+  --> $DIR/uninlined_format_args.rs:90:5
    |
 LL |     println!("{0:0$}", v = val);
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -528,7 +490,7 @@ LL +     println!("{val:val$}");
    |
 
 error: variables can be used directly in the `format!` string
-  --> $DIR/uninlined_format_args.rs:92:5
+  --> $DIR/uninlined_format_args.rs:91:5
    |
 LL |     println!("{:0$.0$}", v = val);
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -540,7 +502,7 @@ LL +     println!("{val:val$.val$}");
    |
 
 error: variables can be used directly in the `format!` string
-  --> $DIR/uninlined_format_args.rs:93:5
+  --> $DIR/uninlined_format_args.rs:92:5
    |
 LL |     println!("{0:0$.0$}", v = val);
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -552,7 +514,7 @@ LL +     println!("{val:val$.val$}");
    |
 
 error: variables can be used directly in the `format!` string
-  --> $DIR/uninlined_format_args.rs:94:5
+  --> $DIR/uninlined_format_args.rs:93:5
    |
 LL |     println!("{0:0$.v$}", v = val);
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -564,7 +526,7 @@ LL +     println!("{val:val$.val$}");
    |
 
 error: variables can be used directly in the `format!` string
-  --> $DIR/uninlined_format_args.rs:95:5
+  --> $DIR/uninlined_format_args.rs:94:5
    |
 LL |     println!("{0:v$.0$}", v = val);
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -576,7 +538,7 @@ LL +     println!("{val:val$.val$}");
    |
 
 error: variables can be used directly in the `format!` string
-  --> $DIR/uninlined_format_args.rs:96:5
+  --> $DIR/uninlined_format_args.rs:95:5
    |
 LL |     println!("{v:0$.0$}", v = val);
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -588,7 +550,7 @@ LL +     println!("{val:val$.val$}");
    |
 
 error: variables can be used directly in the `format!` string
-  --> $DIR/uninlined_format_args.rs:97:5
+  --> $DIR/uninlined_format_args.rs:96:5
    |
 LL |     println!("{v:v$.0$}", v = val);
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -600,7 +562,7 @@ LL +     println!("{val:val$.val$}");
    |
 
 error: variables can be used directly in the `format!` string
-  --> $DIR/uninlined_format_args.rs:98:5
+  --> $DIR/uninlined_format_args.rs:97:5
    |
 LL |     println!("{v:0$.v$}", v = val);
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -612,7 +574,7 @@ LL +     println!("{val:val$.val$}");
    |
 
 error: variables can be used directly in the `format!` string
-  --> $DIR/uninlined_format_args.rs:99:5
+  --> $DIR/uninlined_format_args.rs:98:5
    |
 LL |     println!("{v:v$.v$}", v = val);
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -624,7 +586,7 @@ LL +     println!("{val:val$.val$}");
    |
 
 error: variables can be used directly in the `format!` string
-  --> $DIR/uninlined_format_args.rs:100:5
+  --> $DIR/uninlined_format_args.rs:99:5
    |
 LL |     println!("{:0$}", width);
    |     ^^^^^^^^^^^^^^^^^^^^^^^^
@@ -636,7 +598,7 @@ LL +     println!("{width:width$}");
    |
 
 error: variables can be used directly in the `format!` string
-  --> $DIR/uninlined_format_args.rs:101:5
+  --> $DIR/uninlined_format_args.rs:100:5
    |
 LL |     println!("{:1$}", local_i32, width);
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -648,7 +610,7 @@ LL +     println!("{local_i32:width$}");
    |
 
 error: variables can be used directly in the `format!` string
-  --> $DIR/uninlined_format_args.rs:102:5
+  --> $DIR/uninlined_format_args.rs:101:5
    |
 LL |     println!("{:w$}", w = width);
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -660,7 +622,7 @@ LL +     println!("{width:width$}");
    |
 
 error: variables can be used directly in the `format!` string
-  --> $DIR/uninlined_format_args.rs:103:5
+  --> $DIR/uninlined_format_args.rs:102:5
    |
 LL |     println!("{:w$}", local_i32, w = width);
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -672,7 +634,7 @@ LL +     println!("{local_i32:width$}");
    |
 
 error: variables can be used directly in the `format!` string
-  --> $DIR/uninlined_format_args.rs:104:5
+  --> $DIR/uninlined_format_args.rs:103:5
    |
 LL |     println!("{:.0$}", prec);
    |     ^^^^^^^^^^^^^^^^^^^^^^^^
@@ -684,7 +646,7 @@ LL +     println!("{prec:.prec$}");
    |
 
 error: variables can be used directly in the `format!` string
-  --> $DIR/uninlined_format_args.rs:105:5
+  --> $DIR/uninlined_format_args.rs:104:5
    |
 LL |     println!("{:.1$}", local_i32, prec);
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -696,7 +658,7 @@ LL +     println!("{local_i32:.prec$}");
    |
 
 error: variables can be used directly in the `format!` string
-  --> $DIR/uninlined_format_args.rs:106:5
+  --> $DIR/uninlined_format_args.rs:105:5
    |
 LL |     println!("{:.p$}", p = prec);
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -708,7 +670,7 @@ LL +     println!("{prec:.prec$}");
    |
 
 error: variables can be used directly in the `format!` string
-  --> $DIR/uninlined_format_args.rs:107:5
+  --> $DIR/uninlined_format_args.rs:106:5
    |
 LL |     println!("{:.p$}", local_i32, p = prec);
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -720,7 +682,7 @@ LL +     println!("{local_i32:.prec$}");
    |
 
 error: variables can be used directly in the `format!` string
-  --> $DIR/uninlined_format_args.rs:108:5
+  --> $DIR/uninlined_format_args.rs:107:5
    |
 LL |     println!("{:0$.1$}", width, prec);
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -732,7 +694,7 @@ LL +     println!("{width:width$.prec$}");
    |
 
 error: variables can be used directly in the `format!` string
-  --> $DIR/uninlined_format_args.rs:109:5
+  --> $DIR/uninlined_format_args.rs:108:5
    |
 LL |     println!("{:0$.w$}", width, w = prec);
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -744,7 +706,7 @@ LL +     println!("{width:width$.prec$}");
    |
 
 error: variables can be used directly in the `format!` string
-  --> $DIR/uninlined_format_args.rs:110:5
+  --> $DIR/uninlined_format_args.rs:109:5
    |
 LL |     println!("{:1$.2$}", local_f64, width, prec);
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -756,7 +718,7 @@ LL +     println!("{local_f64:width$.prec$}");
    |
 
 error: variables can be used directly in the `format!` string
-  --> $DIR/uninlined_format_args.rs:111:5
+  --> $DIR/uninlined_format_args.rs:110:5
    |
 LL |     println!("{:1$.2$} {0} {1} {2}", local_f64, width, prec);
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -768,7 +730,16 @@ LL +     println!("{local_f64:width$.prec$} {local_f64} {width} {prec}");
    |
 
 error: variables can be used directly in the `format!` string
-  --> $DIR/uninlined_format_args.rs:123:5
+  --> $DIR/uninlined_format_args.rs:111:5
+   |
+LL | /     println!(
+LL | |         "{0:1$.2$} {0:2$.1$} {1:0$.2$} {1:2$.0$} {2:0$.1$} {2:1$.0$}",
+LL | |         local_i32, width, prec,
+LL | |     );
+   | |_____^
+
+error: variables can be used directly in the `format!` string
+  --> $DIR/uninlined_format_args.rs:122:5
    |
 LL |     println!("Width = {}, value with width = {:0$}", local_i32, local_f64);
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -780,7 +751,7 @@ LL +     println!("Width = {local_i32}, value with width = {local_f64:local_i32$
    |
 
 error: variables can be used directly in the `format!` string
-  --> $DIR/uninlined_format_args.rs:124:5
+  --> $DIR/uninlined_format_args.rs:123:5
    |
 LL |     println!("{:w$.p$}", local_i32, w = width, p = prec);
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -792,7 +763,7 @@ LL +     println!("{local_i32:width$.prec$}");
    |
 
 error: variables can be used directly in the `format!` string
-  --> $DIR/uninlined_format_args.rs:125:5
+  --> $DIR/uninlined_format_args.rs:124:5
    |
 LL |     println!("{:w$.p$}", w = width, p = prec);
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -804,7 +775,7 @@ LL +     println!("{width:width$.prec$}");
    |
 
 error: variables can be used directly in the `format!` string
-  --> $DIR/uninlined_format_args.rs:126:20
+  --> $DIR/uninlined_format_args.rs:125:20
    |
 LL |     println!("{}", format!("{}", local_i32));
    |                    ^^^^^^^^^^^^^^^^^^^^^^^^
@@ -816,7 +787,17 @@ LL +     println!("{}", format!("{local_i32}"));
    |
 
 error: variables can be used directly in the `format!` string
-  --> $DIR/uninlined_format_args.rs:149:5
+  --> $DIR/uninlined_format_args.rs:143:5
+   |
+LL | /     println!(
+LL | |         "{}",
+LL | |         // comment with a comma , in it
+LL | |         val,
+LL | |     );
+   | |_____^
+
+error: variables can be used directly in the `format!` string
+  --> $DIR/uninlined_format_args.rs:148:5
    |
 LL |     println!("{}", /* comment with a comma , in it */ val);
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -828,7 +809,7 @@ LL +     println!("{val}");
    |
 
 error: variables can be used directly in the `format!` string
-  --> $DIR/uninlined_format_args.rs:155:9
+  --> $DIR/uninlined_format_args.rs:154:9
    |
 LL |         panic!("p1 {}", local_i32);
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -840,7 +821,7 @@ LL +         panic!("p1 {local_i32}");
    |
 
 error: variables can be used directly in the `format!` string
-  --> $DIR/uninlined_format_args.rs:158:9
+  --> $DIR/uninlined_format_args.rs:157:9
    |
 LL |         panic!("p2 {0}", local_i32);
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -852,7 +833,7 @@ LL +         panic!("p2 {local_i32}");
    |
 
 error: variables can be used directly in the `format!` string
-  --> $DIR/uninlined_format_args.rs:161:9
+  --> $DIR/uninlined_format_args.rs:160:9
    |
 LL |         panic!("p3 {local_i32}", local_i32 = local_i32);
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -864,7 +845,7 @@ LL +         panic!("p3 {local_i32}");
    |
 
 error: variables can be used directly in the `format!` string
-  --> $DIR/uninlined_format_args.rs:181:5
+  --> $DIR/uninlined_format_args.rs:180:5
    |
 LL |     println!("expand='{}'", local_i32);
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -875,5 +856,5 @@ LL -     println!("expand='{}'", local_i32);
 LL +     println!("expand='{local_i32}'");
    |
 
-error: aborting due to 73 previous errors
+error: aborting due to 72 previous errors
 
diff --git a/src/tools/clippy/tests/ui/unnecessary_cast.fixed b/src/tools/clippy/tests/ui/unnecessary_cast.fixed
index ec8c6abfab9..2f7e2997e73 100644
--- a/src/tools/clippy/tests/ui/unnecessary_cast.fixed
+++ b/src/tools/clippy/tests/ui/unnecessary_cast.fixed
@@ -41,6 +41,17 @@ fn main() {
     // do not lint cast to alias type
     1 as I32Alias;
     &1 as &I32Alias;
+
+    // issue #9960
+    macro_rules! bind_var {
+        ($id:ident, $e:expr) => {{
+            let $id = 0usize;
+            let _ = $e != 0usize;
+            let $id = 0isize;
+            let _ = $e != 0usize;
+        }}
+    }
+    bind_var!(x, (x as usize) + 1);
 }
 
 type I32Alias = i32;
@@ -85,6 +96,9 @@ mod fixable {
 
         let _ = 1 as I32Alias;
         let _ = &1 as &I32Alias;
+
+        let x = 1i32;
+        let _ = &{ x };
     }
 
     type I32Alias = i32;
diff --git a/src/tools/clippy/tests/ui/unnecessary_cast.rs b/src/tools/clippy/tests/ui/unnecessary_cast.rs
index 5213cdc269b..54dd46ba59f 100644
--- a/src/tools/clippy/tests/ui/unnecessary_cast.rs
+++ b/src/tools/clippy/tests/ui/unnecessary_cast.rs
@@ -41,6 +41,17 @@ fn main() {
     // do not lint cast to alias type
     1 as I32Alias;
     &1 as &I32Alias;
+
+    // issue #9960
+    macro_rules! bind_var {
+        ($id:ident, $e:expr) => {{
+            let $id = 0usize;
+            let _ = $e != 0usize;
+            let $id = 0isize;
+            let _ = $e != 0usize;
+        }}
+    }
+    bind_var!(x, (x as usize) + 1);
 }
 
 type I32Alias = i32;
@@ -85,6 +96,9 @@ mod fixable {
 
         let _ = 1 as I32Alias;
         let _ = &1 as &I32Alias;
+
+        let x = 1i32;
+        let _ = &(x as i32);
     }
 
     type I32Alias = i32;
diff --git a/src/tools/clippy/tests/ui/unnecessary_cast.stderr b/src/tools/clippy/tests/ui/unnecessary_cast.stderr
index e5c3dd5e53f..fcee4ee2a65 100644
--- a/src/tools/clippy/tests/ui/unnecessary_cast.stderr
+++ b/src/tools/clippy/tests/ui/unnecessary_cast.stderr
@@ -49,136 +49,142 @@ LL |     1_f32 as f32;
    |     ^^^^^^^^^^^^ help: try: `1_f32`
 
 error: casting integer literal to `f32` is unnecessary
-  --> $DIR/unnecessary_cast.rs:53:9
+  --> $DIR/unnecessary_cast.rs:64:9
    |
 LL |         100 as f32;
    |         ^^^^^^^^^^ help: try: `100_f32`
 
 error: casting integer literal to `f64` is unnecessary
-  --> $DIR/unnecessary_cast.rs:54:9
+  --> $DIR/unnecessary_cast.rs:65:9
    |
 LL |         100 as f64;
    |         ^^^^^^^^^^ help: try: `100_f64`
 
 error: casting integer literal to `f64` is unnecessary
-  --> $DIR/unnecessary_cast.rs:55:9
+  --> $DIR/unnecessary_cast.rs:66:9
    |
 LL |         100_i32 as f64;
    |         ^^^^^^^^^^^^^^ help: try: `100_f64`
 
 error: casting integer literal to `f32` is unnecessary
-  --> $DIR/unnecessary_cast.rs:56:17
+  --> $DIR/unnecessary_cast.rs:67:17
    |
 LL |         let _ = -100 as f32;
    |                 ^^^^^^^^^^^ help: try: `-100_f32`
 
 error: casting integer literal to `f64` is unnecessary
-  --> $DIR/unnecessary_cast.rs:57:17
+  --> $DIR/unnecessary_cast.rs:68:17
    |
 LL |         let _ = -100 as f64;
    |                 ^^^^^^^^^^^ help: try: `-100_f64`
 
 error: casting integer literal to `f64` is unnecessary
-  --> $DIR/unnecessary_cast.rs:58:17
+  --> $DIR/unnecessary_cast.rs:69:17
    |
 LL |         let _ = -100_i32 as f64;
    |                 ^^^^^^^^^^^^^^^ help: try: `-100_f64`
 
 error: casting float literal to `f32` is unnecessary
-  --> $DIR/unnecessary_cast.rs:59:9
+  --> $DIR/unnecessary_cast.rs:70:9
    |
 LL |         100. as f32;
    |         ^^^^^^^^^^^ help: try: `100_f32`
 
 error: casting float literal to `f64` is unnecessary
-  --> $DIR/unnecessary_cast.rs:60:9
+  --> $DIR/unnecessary_cast.rs:71:9
    |
 LL |         100. as f64;
    |         ^^^^^^^^^^^ help: try: `100_f64`
 
 error: casting integer literal to `u32` is unnecessary
-  --> $DIR/unnecessary_cast.rs:72:9
+  --> $DIR/unnecessary_cast.rs:83:9
    |
 LL |         1 as u32;
    |         ^^^^^^^^ help: try: `1_u32`
 
 error: casting integer literal to `i32` is unnecessary
-  --> $DIR/unnecessary_cast.rs:73:9
+  --> $DIR/unnecessary_cast.rs:84:9
    |
 LL |         0x10 as i32;
    |         ^^^^^^^^^^^ help: try: `0x10_i32`
 
 error: casting integer literal to `usize` is unnecessary
-  --> $DIR/unnecessary_cast.rs:74:9
+  --> $DIR/unnecessary_cast.rs:85:9
    |
 LL |         0b10 as usize;
    |         ^^^^^^^^^^^^^ help: try: `0b10_usize`
 
 error: casting integer literal to `u16` is unnecessary
-  --> $DIR/unnecessary_cast.rs:75:9
+  --> $DIR/unnecessary_cast.rs:86:9
    |
 LL |         0o73 as u16;
    |         ^^^^^^^^^^^ help: try: `0o73_u16`
 
 error: casting integer literal to `u32` is unnecessary
-  --> $DIR/unnecessary_cast.rs:76:9
+  --> $DIR/unnecessary_cast.rs:87:9
    |
 LL |         1_000_000_000 as u32;
    |         ^^^^^^^^^^^^^^^^^^^^ help: try: `1_000_000_000_u32`
 
 error: casting float literal to `f64` is unnecessary
-  --> $DIR/unnecessary_cast.rs:78:9
+  --> $DIR/unnecessary_cast.rs:89:9
    |
 LL |         1.0 as f64;
    |         ^^^^^^^^^^ help: try: `1.0_f64`
 
 error: casting float literal to `f32` is unnecessary
-  --> $DIR/unnecessary_cast.rs:79:9
+  --> $DIR/unnecessary_cast.rs:90:9
    |
 LL |         0.5 as f32;
    |         ^^^^^^^^^^ help: try: `0.5_f32`
 
 error: casting integer literal to `i32` is unnecessary
-  --> $DIR/unnecessary_cast.rs:83:17
+  --> $DIR/unnecessary_cast.rs:94:17
    |
 LL |         let _ = -1 as i32;
    |                 ^^^^^^^^^ help: try: `-1_i32`
 
 error: casting float literal to `f32` is unnecessary
-  --> $DIR/unnecessary_cast.rs:84:17
+  --> $DIR/unnecessary_cast.rs:95:17
    |
 LL |         let _ = -1.0 as f32;
    |                 ^^^^^^^^^^^ help: try: `-1.0_f32`
 
+error: casting to the same type is unnecessary (`i32` -> `i32`)
+  --> $DIR/unnecessary_cast.rs:101:18
+   |
+LL |         let _ = &(x as i32);
+   |                  ^^^^^^^^^^ help: try: `{ x }`
+
 error: casting integer literal to `i32` is unnecessary
-  --> $DIR/unnecessary_cast.rs:93:22
+  --> $DIR/unnecessary_cast.rs:107:22
    |
 LL |         let _: i32 = -(1) as i32;
    |                      ^^^^^^^^^^^ help: try: `-1_i32`
 
 error: casting integer literal to `i64` is unnecessary
-  --> $DIR/unnecessary_cast.rs:95:22
+  --> $DIR/unnecessary_cast.rs:109:22
    |
 LL |         let _: i64 = -(1) as i64;
    |                      ^^^^^^^^^^^ help: try: `-1_i64`
 
 error: casting float literal to `f64` is unnecessary
-  --> $DIR/unnecessary_cast.rs:102:22
+  --> $DIR/unnecessary_cast.rs:116:22
    |
 LL |         let _: f64 = (-8.0 as f64).exp();
    |                      ^^^^^^^^^^^^^ help: try: `(-8.0_f64)`
 
 error: casting float literal to `f64` is unnecessary
-  --> $DIR/unnecessary_cast.rs:104:23
+  --> $DIR/unnecessary_cast.rs:118:23
    |
 LL |         let _: f64 = -(8.0 as f64).exp(); // should suggest `-8.0_f64.exp()` here not to change code behavior
    |                       ^^^^^^^^^^^^ help: try: `8.0_f64`
 
 error: casting to the same type is unnecessary (`f32` -> `f32`)
-  --> $DIR/unnecessary_cast.rs:112:20
+  --> $DIR/unnecessary_cast.rs:126:20
    |
 LL |         let _num = foo() as f32;
    |                    ^^^^^^^^^^^^ help: try: `foo()`
 
-error: aborting due to 30 previous errors
+error: aborting due to 31 previous errors
 
diff --git a/src/tools/clippy/tests/ui/unnecessary_lazy_eval.fixed b/src/tools/clippy/tests/ui/unnecessary_lazy_eval.fixed
index ce4a82e0217..22e9bd8bdc5 100644
--- a/src/tools/clippy/tests/ui/unnecessary_lazy_eval.fixed
+++ b/src/tools/clippy/tests/ui/unnecessary_lazy_eval.fixed
@@ -33,6 +33,14 @@ impl Drop for Issue9427 {
     }
 }
 
+struct Issue9427FollowUp;
+
+impl Drop for Issue9427FollowUp {
+    fn drop(&mut self) {
+        panic!("side effect drop");
+    }
+}
+
 fn main() {
     let astronomers_pi = 10;
     let ext_arr: [usize; 1] = [2];
@@ -87,6 +95,7 @@ fn main() {
 
     // Should not lint - bool
     let _ = (0 == 1).then(|| Issue9427(0)); // Issue9427 has a significant drop
+    let _ = false.then(|| Issue9427FollowUp); // Issue9427FollowUp has a significant drop
 
     // should not lint, bind_instead_of_map takes priority
     let _ = Some(10).and_then(|idx| Some(ext_arr[idx]));
@@ -133,13 +142,13 @@ fn main() {
     let _: Result<usize, usize> = res.or(Ok(astronomers_pi));
     let _: Result<usize, usize> = res.or(Ok(ext_str.some_field));
     let _: Result<usize, usize> = res.
-        // some lines
-        // some lines
-        // some lines
-        // some lines
-        // some lines
-        // some lines
-        or(Ok(ext_str.some_field));
+    // some lines
+    // some lines
+    // some lines
+    // some lines
+    // some lines
+    // some lines
+    or(Ok(ext_str.some_field));
 
     // neither bind_instead_of_map nor unnecessary_lazy_eval applies here
     let _: Result<usize, usize> = res.and_then(|x| Err(x));
diff --git a/src/tools/clippy/tests/ui/unnecessary_lazy_eval.rs b/src/tools/clippy/tests/ui/unnecessary_lazy_eval.rs
index 59cdf662854..8726d84a23f 100644
--- a/src/tools/clippy/tests/ui/unnecessary_lazy_eval.rs
+++ b/src/tools/clippy/tests/ui/unnecessary_lazy_eval.rs
@@ -33,6 +33,14 @@ impl Drop for Issue9427 {
     }
 }
 
+struct Issue9427FollowUp;
+
+impl Drop for Issue9427FollowUp {
+    fn drop(&mut self) {
+        panic!("side effect drop");
+    }
+}
+
 fn main() {
     let astronomers_pi = 10;
     let ext_arr: [usize; 1] = [2];
@@ -87,6 +95,7 @@ fn main() {
 
     // Should not lint - bool
     let _ = (0 == 1).then(|| Issue9427(0)); // Issue9427 has a significant drop
+    let _ = false.then(|| Issue9427FollowUp); // Issue9427FollowUp has a significant drop
 
     // should not lint, bind_instead_of_map takes priority
     let _ = Some(10).and_then(|idx| Some(ext_arr[idx]));
@@ -133,13 +142,13 @@ fn main() {
     let _: Result<usize, usize> = res.or_else(|_| Ok(astronomers_pi));
     let _: Result<usize, usize> = res.or_else(|_| Ok(ext_str.some_field));
     let _: Result<usize, usize> = res.
-        // some lines
-        // some lines
-        // some lines
-        // some lines
-        // some lines
-        // some lines
-        or_else(|_| Ok(ext_str.some_field));
+    // some lines
+    // some lines
+    // some lines
+    // some lines
+    // some lines
+    // some lines
+    or_else(|_| Ok(ext_str.some_field));
 
     // neither bind_instead_of_map nor unnecessary_lazy_eval applies here
     let _: Result<usize, usize> = res.and_then(|x| Err(x));
diff --git a/src/tools/clippy/tests/ui/unnecessary_lazy_eval.stderr b/src/tools/clippy/tests/ui/unnecessary_lazy_eval.stderr
index 8a9ece4aa7e..0339755442c 100644
--- a/src/tools/clippy/tests/ui/unnecessary_lazy_eval.stderr
+++ b/src/tools/clippy/tests/ui/unnecessary_lazy_eval.stderr
@@ -1,5 +1,5 @@
 error: unnecessary closure used to substitute value for `Option::None`
-  --> $DIR/unnecessary_lazy_eval.rs:48:13
+  --> $DIR/unnecessary_lazy_eval.rs:56:13
    |
 LL |     let _ = opt.unwrap_or_else(|| 2);
    |             ^^^^--------------------
@@ -9,7 +9,7 @@ LL |     let _ = opt.unwrap_or_else(|| 2);
    = note: `-D clippy::unnecessary-lazy-evaluations` implied by `-D warnings`
 
 error: unnecessary closure used to substitute value for `Option::None`
-  --> $DIR/unnecessary_lazy_eval.rs:49:13
+  --> $DIR/unnecessary_lazy_eval.rs:57:13
    |
 LL |     let _ = opt.unwrap_or_else(|| astronomers_pi);
    |             ^^^^---------------------------------
@@ -17,7 +17,7 @@ LL |     let _ = opt.unwrap_or_else(|| astronomers_pi);
    |                 help: use `unwrap_or(..)` instead: `unwrap_or(astronomers_pi)`
 
 error: unnecessary closure used to substitute value for `Option::None`
-  --> $DIR/unnecessary_lazy_eval.rs:50:13
+  --> $DIR/unnecessary_lazy_eval.rs:58:13
    |
 LL |     let _ = opt.unwrap_or_else(|| ext_str.some_field);
    |             ^^^^-------------------------------------
@@ -25,7 +25,7 @@ LL |     let _ = opt.unwrap_or_else(|| ext_str.some_field);
    |                 help: use `unwrap_or(..)` instead: `unwrap_or(ext_str.some_field)`
 
 error: unnecessary closure used to substitute value for `Option::None`
-  --> $DIR/unnecessary_lazy_eval.rs:52:13
+  --> $DIR/unnecessary_lazy_eval.rs:60:13
    |
 LL |     let _ = opt.and_then(|_| ext_opt);
    |             ^^^^---------------------
@@ -33,7 +33,7 @@ LL |     let _ = opt.and_then(|_| ext_opt);
    |                 help: use `and(..)` instead: `and(ext_opt)`
 
 error: unnecessary closure used to substitute value for `Option::None`
-  --> $DIR/unnecessary_lazy_eval.rs:53:13
+  --> $DIR/unnecessary_lazy_eval.rs:61:13
    |
 LL |     let _ = opt.or_else(|| ext_opt);
    |             ^^^^-------------------
@@ -41,7 +41,7 @@ LL |     let _ = opt.or_else(|| ext_opt);
    |                 help: use `or(..)` instead: `or(ext_opt)`
 
 error: unnecessary closure used to substitute value for `Option::None`
-  --> $DIR/unnecessary_lazy_eval.rs:54:13
+  --> $DIR/unnecessary_lazy_eval.rs:62:13
    |
 LL |     let _ = opt.or_else(|| None);
    |             ^^^^----------------
@@ -49,7 +49,7 @@ LL |     let _ = opt.or_else(|| None);
    |                 help: use `or(..)` instead: `or(None)`
 
 error: unnecessary closure used to substitute value for `Option::None`
-  --> $DIR/unnecessary_lazy_eval.rs:55:13
+  --> $DIR/unnecessary_lazy_eval.rs:63:13
    |
 LL |     let _ = opt.get_or_insert_with(|| 2);
    |             ^^^^------------------------
@@ -57,7 +57,7 @@ LL |     let _ = opt.get_or_insert_with(|| 2);
    |                 help: use `get_or_insert(..)` instead: `get_or_insert(2)`
 
 error: unnecessary closure used to substitute value for `Option::None`
-  --> $DIR/unnecessary_lazy_eval.rs:56:13
+  --> $DIR/unnecessary_lazy_eval.rs:64:13
    |
 LL |     let _ = opt.ok_or_else(|| 2);
    |             ^^^^----------------
@@ -65,7 +65,7 @@ LL |     let _ = opt.ok_or_else(|| 2);
    |                 help: use `ok_or(..)` instead: `ok_or(2)`
 
 error: unnecessary closure used to substitute value for `Option::None`
-  --> $DIR/unnecessary_lazy_eval.rs:57:13
+  --> $DIR/unnecessary_lazy_eval.rs:65:13
    |
 LL |     let _ = nested_tuple_opt.unwrap_or_else(|| Some((1, 2)));
    |             ^^^^^^^^^^^^^^^^^-------------------------------
@@ -73,7 +73,7 @@ LL |     let _ = nested_tuple_opt.unwrap_or_else(|| Some((1, 2)));
    |                              help: use `unwrap_or(..)` instead: `unwrap_or(Some((1, 2)))`
 
 error: unnecessary closure used with `bool::then`
-  --> $DIR/unnecessary_lazy_eval.rs:58:13
+  --> $DIR/unnecessary_lazy_eval.rs:66:13
    |
 LL |     let _ = cond.then(|| astronomers_pi);
    |             ^^^^^-----------------------
@@ -81,7 +81,7 @@ LL |     let _ = cond.then(|| astronomers_pi);
    |                  help: use `then_some(..)` instead: `then_some(astronomers_pi)`
 
 error: unnecessary closure used to substitute value for `Option::None`
-  --> $DIR/unnecessary_lazy_eval.rs:61:13
+  --> $DIR/unnecessary_lazy_eval.rs:69:13
    |
 LL |     let _ = Some(10).unwrap_or_else(|| 2);
    |             ^^^^^^^^^--------------------
@@ -89,7 +89,7 @@ LL |     let _ = Some(10).unwrap_or_else(|| 2);
    |                      help: use `unwrap_or(..)` instead: `unwrap_or(2)`
 
 error: unnecessary closure used to substitute value for `Option::None`
-  --> $DIR/unnecessary_lazy_eval.rs:62:13
+  --> $DIR/unnecessary_lazy_eval.rs:70:13
    |
 LL |     let _ = Some(10).and_then(|_| ext_opt);
    |             ^^^^^^^^^---------------------
@@ -97,7 +97,7 @@ LL |     let _ = Some(10).and_then(|_| ext_opt);
    |                      help: use `and(..)` instead: `and(ext_opt)`
 
 error: unnecessary closure used to substitute value for `Option::None`
-  --> $DIR/unnecessary_lazy_eval.rs:63:28
+  --> $DIR/unnecessary_lazy_eval.rs:71:28
    |
 LL |     let _: Option<usize> = None.or_else(|| ext_opt);
    |                            ^^^^^-------------------
@@ -105,7 +105,7 @@ LL |     let _: Option<usize> = None.or_else(|| ext_opt);
    |                                 help: use `or(..)` instead: `or(ext_opt)`
 
 error: unnecessary closure used to substitute value for `Option::None`
-  --> $DIR/unnecessary_lazy_eval.rs:64:13
+  --> $DIR/unnecessary_lazy_eval.rs:72:13
    |
 LL |     let _ = None.get_or_insert_with(|| 2);
    |             ^^^^^------------------------
@@ -113,7 +113,7 @@ LL |     let _ = None.get_or_insert_with(|| 2);
    |                  help: use `get_or_insert(..)` instead: `get_or_insert(2)`
 
 error: unnecessary closure used to substitute value for `Option::None`
-  --> $DIR/unnecessary_lazy_eval.rs:65:35
+  --> $DIR/unnecessary_lazy_eval.rs:73:35
    |
 LL |     let _: Result<usize, usize> = None.ok_or_else(|| 2);
    |                                   ^^^^^----------------
@@ -121,7 +121,7 @@ LL |     let _: Result<usize, usize> = None.ok_or_else(|| 2);
    |                                        help: use `ok_or(..)` instead: `ok_or(2)`
 
 error: unnecessary closure used to substitute value for `Option::None`
-  --> $DIR/unnecessary_lazy_eval.rs:66:28
+  --> $DIR/unnecessary_lazy_eval.rs:74:28
    |
 LL |     let _: Option<usize> = None.or_else(|| None);
    |                            ^^^^^----------------
@@ -129,7 +129,7 @@ LL |     let _: Option<usize> = None.or_else(|| None);
    |                                 help: use `or(..)` instead: `or(None)`
 
 error: unnecessary closure used to substitute value for `Option::None`
-  --> $DIR/unnecessary_lazy_eval.rs:69:13
+  --> $DIR/unnecessary_lazy_eval.rs:77:13
    |
 LL |     let _ = deep.0.unwrap_or_else(|| 2);
    |             ^^^^^^^--------------------
@@ -137,7 +137,7 @@ LL |     let _ = deep.0.unwrap_or_else(|| 2);
    |                    help: use `unwrap_or(..)` instead: `unwrap_or(2)`
 
 error: unnecessary closure used to substitute value for `Option::None`
-  --> $DIR/unnecessary_lazy_eval.rs:70:13
+  --> $DIR/unnecessary_lazy_eval.rs:78:13
    |
 LL |     let _ = deep.0.and_then(|_| ext_opt);
    |             ^^^^^^^---------------------
@@ -145,7 +145,7 @@ LL |     let _ = deep.0.and_then(|_| ext_opt);
    |                    help: use `and(..)` instead: `and(ext_opt)`
 
 error: unnecessary closure used to substitute value for `Option::None`
-  --> $DIR/unnecessary_lazy_eval.rs:71:13
+  --> $DIR/unnecessary_lazy_eval.rs:79:13
    |
 LL |     let _ = deep.0.or_else(|| None);
    |             ^^^^^^^----------------
@@ -153,7 +153,7 @@ LL |     let _ = deep.0.or_else(|| None);
    |                    help: use `or(..)` instead: `or(None)`
 
 error: unnecessary closure used to substitute value for `Option::None`
-  --> $DIR/unnecessary_lazy_eval.rs:72:13
+  --> $DIR/unnecessary_lazy_eval.rs:80:13
    |
 LL |     let _ = deep.0.get_or_insert_with(|| 2);
    |             ^^^^^^^------------------------
@@ -161,7 +161,7 @@ LL |     let _ = deep.0.get_or_insert_with(|| 2);
    |                    help: use `get_or_insert(..)` instead: `get_or_insert(2)`
 
 error: unnecessary closure used to substitute value for `Option::None`
-  --> $DIR/unnecessary_lazy_eval.rs:73:13
+  --> $DIR/unnecessary_lazy_eval.rs:81:13
    |
 LL |     let _ = deep.0.ok_or_else(|| 2);
    |             ^^^^^^^----------------
@@ -169,7 +169,7 @@ LL |     let _ = deep.0.ok_or_else(|| 2);
    |                    help: use `ok_or(..)` instead: `ok_or(2)`
 
 error: unnecessary closure used to substitute value for `Option::None`
-  --> $DIR/unnecessary_lazy_eval.rs:96:28
+  --> $DIR/unnecessary_lazy_eval.rs:105:28
    |
 LL |     let _: Option<usize> = None.or_else(|| Some(3));
    |                            ^^^^^-------------------
@@ -177,7 +177,7 @@ LL |     let _: Option<usize> = None.or_else(|| Some(3));
    |                                 help: use `or(..)` instead: `or(Some(3))`
 
 error: unnecessary closure used to substitute value for `Option::None`
-  --> $DIR/unnecessary_lazy_eval.rs:97:13
+  --> $DIR/unnecessary_lazy_eval.rs:106:13
    |
 LL |     let _ = deep.0.or_else(|| Some(3));
    |             ^^^^^^^-------------------
@@ -185,7 +185,7 @@ LL |     let _ = deep.0.or_else(|| Some(3));
    |                    help: use `or(..)` instead: `or(Some(3))`
 
 error: unnecessary closure used to substitute value for `Option::None`
-  --> $DIR/unnecessary_lazy_eval.rs:98:13
+  --> $DIR/unnecessary_lazy_eval.rs:107:13
    |
 LL |     let _ = opt.or_else(|| Some(3));
    |             ^^^^-------------------
@@ -193,7 +193,7 @@ LL |     let _ = opt.or_else(|| Some(3));
    |                 help: use `or(..)` instead: `or(Some(3))`
 
 error: unnecessary closure used to substitute value for `Result::Err`
-  --> $DIR/unnecessary_lazy_eval.rs:104:13
+  --> $DIR/unnecessary_lazy_eval.rs:113:13
    |
 LL |     let _ = res2.unwrap_or_else(|_| 2);
    |             ^^^^^---------------------
@@ -201,7 +201,7 @@ LL |     let _ = res2.unwrap_or_else(|_| 2);
    |                  help: use `unwrap_or(..)` instead: `unwrap_or(2)`
 
 error: unnecessary closure used to substitute value for `Result::Err`
-  --> $DIR/unnecessary_lazy_eval.rs:105:13
+  --> $DIR/unnecessary_lazy_eval.rs:114:13
    |
 LL |     let _ = res2.unwrap_or_else(|_| astronomers_pi);
    |             ^^^^^----------------------------------
@@ -209,7 +209,7 @@ LL |     let _ = res2.unwrap_or_else(|_| astronomers_pi);
    |                  help: use `unwrap_or(..)` instead: `unwrap_or(astronomers_pi)`
 
 error: unnecessary closure used to substitute value for `Result::Err`
-  --> $DIR/unnecessary_lazy_eval.rs:106:13
+  --> $DIR/unnecessary_lazy_eval.rs:115:13
    |
 LL |     let _ = res2.unwrap_or_else(|_| ext_str.some_field);
    |             ^^^^^--------------------------------------
@@ -217,7 +217,7 @@ LL |     let _ = res2.unwrap_or_else(|_| ext_str.some_field);
    |                  help: use `unwrap_or(..)` instead: `unwrap_or(ext_str.some_field)`
 
 error: unnecessary closure used to substitute value for `Result::Err`
-  --> $DIR/unnecessary_lazy_eval.rs:128:35
+  --> $DIR/unnecessary_lazy_eval.rs:137:35
    |
 LL |     let _: Result<usize, usize> = res.and_then(|_| Err(2));
    |                                   ^^^^--------------------
@@ -225,7 +225,7 @@ LL |     let _: Result<usize, usize> = res.and_then(|_| Err(2));
    |                                       help: use `and(..)` instead: `and(Err(2))`
 
 error: unnecessary closure used to substitute value for `Result::Err`
-  --> $DIR/unnecessary_lazy_eval.rs:129:35
+  --> $DIR/unnecessary_lazy_eval.rs:138:35
    |
 LL |     let _: Result<usize, usize> = res.and_then(|_| Err(astronomers_pi));
    |                                   ^^^^---------------------------------
@@ -233,7 +233,7 @@ LL |     let _: Result<usize, usize> = res.and_then(|_| Err(astronomers_pi));
    |                                       help: use `and(..)` instead: `and(Err(astronomers_pi))`
 
 error: unnecessary closure used to substitute value for `Result::Err`
-  --> $DIR/unnecessary_lazy_eval.rs:130:35
+  --> $DIR/unnecessary_lazy_eval.rs:139:35
    |
 LL |     let _: Result<usize, usize> = res.and_then(|_| Err(ext_str.some_field));
    |                                   ^^^^-------------------------------------
@@ -241,7 +241,7 @@ LL |     let _: Result<usize, usize> = res.and_then(|_| Err(ext_str.some_field))
    |                                       help: use `and(..)` instead: `and(Err(ext_str.some_field))`
 
 error: unnecessary closure used to substitute value for `Result::Err`
-  --> $DIR/unnecessary_lazy_eval.rs:132:35
+  --> $DIR/unnecessary_lazy_eval.rs:141:35
    |
 LL |     let _: Result<usize, usize> = res.or_else(|_| Ok(2));
    |                                   ^^^^------------------
@@ -249,7 +249,7 @@ LL |     let _: Result<usize, usize> = res.or_else(|_| Ok(2));
    |                                       help: use `or(..)` instead: `or(Ok(2))`
 
 error: unnecessary closure used to substitute value for `Result::Err`
-  --> $DIR/unnecessary_lazy_eval.rs:133:35
+  --> $DIR/unnecessary_lazy_eval.rs:142:35
    |
 LL |     let _: Result<usize, usize> = res.or_else(|_| Ok(astronomers_pi));
    |                                   ^^^^-------------------------------
@@ -257,7 +257,7 @@ LL |     let _: Result<usize, usize> = res.or_else(|_| Ok(astronomers_pi));
    |                                       help: use `or(..)` instead: `or(Ok(astronomers_pi))`
 
 error: unnecessary closure used to substitute value for `Result::Err`
-  --> $DIR/unnecessary_lazy_eval.rs:134:35
+  --> $DIR/unnecessary_lazy_eval.rs:143:35
    |
 LL |     let _: Result<usize, usize> = res.or_else(|_| Ok(ext_str.some_field));
    |                                   ^^^^-----------------------------------
@@ -265,19 +265,19 @@ LL |     let _: Result<usize, usize> = res.or_else(|_| Ok(ext_str.some_field));
    |                                       help: use `or(..)` instead: `or(Ok(ext_str.some_field))`
 
 error: unnecessary closure used to substitute value for `Result::Err`
-  --> $DIR/unnecessary_lazy_eval.rs:135:35
+  --> $DIR/unnecessary_lazy_eval.rs:144:35
    |
 LL |       let _: Result<usize, usize> = res.
    |  ___________________________________^
-LL | |         // some lines
-LL | |         // some lines
-LL | |         // some lines
+LL | |     // some lines
+LL | |     // some lines
+LL | |     // some lines
 ...  |
-LL | |         // some lines
-LL | |         or_else(|_| Ok(ext_str.some_field));
-   | |_________----------------------------------^
-   |           |
-   |           help: use `or(..)` instead: `or(Ok(ext_str.some_field))`
+LL | |     // some lines
+LL | |     or_else(|_| Ok(ext_str.some_field));
+   | |_____----------------------------------^
+   |       |
+   |       help: use `or(..)` instead: `or(Ok(ext_str.some_field))`
 
 error: aborting due to 34 previous errors
 
diff --git a/src/tools/clippy/tests/ui/unnecessary_operation.fixed b/src/tools/clippy/tests/ui/unnecessary_operation.fixed
index bf0ec8deb34..d37163570ab 100644
--- a/src/tools/clippy/tests/ui/unnecessary_operation.fixed
+++ b/src/tools/clippy/tests/ui/unnecessary_operation.fixed
@@ -76,4 +76,13 @@ fn main() {
     DropStruct { ..get_drop_struct() };
     DropEnum::Tuple(get_number());
     DropEnum::Struct { field: get_number() };
+
+    // Issue #9954
+    fn one() -> i8 {
+        1
+    }
+    macro_rules! use_expr {
+        ($($e:expr),*) => {{ $($e;)* }}
+    }
+    use_expr!(isize::MIN / -(one() as isize), i8::MIN / -one());
 }
diff --git a/src/tools/clippy/tests/ui/unnecessary_operation.rs b/src/tools/clippy/tests/ui/unnecessary_operation.rs
index 08cb9ab522e..a14fd4bca0e 100644
--- a/src/tools/clippy/tests/ui/unnecessary_operation.rs
+++ b/src/tools/clippy/tests/ui/unnecessary_operation.rs
@@ -80,4 +80,13 @@ fn main() {
     DropStruct { ..get_drop_struct() };
     DropEnum::Tuple(get_number());
     DropEnum::Struct { field: get_number() };
+
+    // Issue #9954
+    fn one() -> i8 {
+        1
+    }
+    macro_rules! use_expr {
+        ($($e:expr),*) => {{ $($e;)* }}
+    }
+    use_expr!(isize::MIN / -(one() as isize), i8::MIN / -one());
 }
diff --git a/src/tools/clippy/tests/ui/unnecessary_safety_comment.rs b/src/tools/clippy/tests/ui/unnecessary_safety_comment.rs
new file mode 100644
index 00000000000..7fefea7051d
--- /dev/null
+++ b/src/tools/clippy/tests/ui/unnecessary_safety_comment.rs
@@ -0,0 +1,51 @@
+#![warn(clippy::undocumented_unsafe_blocks, clippy::unnecessary_safety_comment)]
+#![allow(clippy::let_unit_value, clippy::missing_safety_doc)]
+
+mod unsafe_items_invalid_comment {
+    // SAFETY:
+    const CONST: u32 = 0;
+    // SAFETY:
+    static STATIC: u32 = 0;
+    // SAFETY:
+    struct Struct;
+    // SAFETY:
+    enum Enum {}
+    // SAFETY:
+    mod module {}
+}
+
+mod unnecessary_from_macro {
+    trait T {}
+
+    macro_rules! no_safety_comment {
+        ($t:ty) => {
+            impl T for $t {}
+        };
+    }
+
+    // FIXME: This is not caught
+    // Safety: unnecessary
+    no_safety_comment!(());
+
+    macro_rules! with_safety_comment {
+        ($t:ty) => {
+            // Safety: unnecessary
+            impl T for $t {}
+        };
+    }
+
+    with_safety_comment!(i32);
+}
+
+fn unnecessary_on_stmt_and_expr() -> u32 {
+    // SAFETY: unnecessary
+    let num = 42;
+
+    // SAFETY: unnecessary
+    if num > 24 {}
+
+    // SAFETY: unnecessary
+    24
+}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/unnecessary_safety_comment.stderr b/src/tools/clippy/tests/ui/unnecessary_safety_comment.stderr
new file mode 100644
index 00000000000..7b2af67d64c
--- /dev/null
+++ b/src/tools/clippy/tests/ui/unnecessary_safety_comment.stderr
@@ -0,0 +1,115 @@
+error: constant item has unnecessary safety comment
+  --> $DIR/unnecessary_safety_comment.rs:6:5
+   |
+LL |     const CONST: u32 = 0;
+   |     ^^^^^^^^^^^^^^^^^^^^^
+   |
+help: consider removing the safety comment
+  --> $DIR/unnecessary_safety_comment.rs:5:5
+   |
+LL |     // SAFETY:
+   |     ^^^^^^^^^^
+   = note: `-D clippy::unnecessary-safety-comment` implied by `-D warnings`
+
+error: static item has unnecessary safety comment
+  --> $DIR/unnecessary_safety_comment.rs:8:5
+   |
+LL |     static STATIC: u32 = 0;
+   |     ^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: consider removing the safety comment
+  --> $DIR/unnecessary_safety_comment.rs:7:5
+   |
+LL |     // SAFETY:
+   |     ^^^^^^^^^^
+
+error: struct has unnecessary safety comment
+  --> $DIR/unnecessary_safety_comment.rs:10:5
+   |
+LL |     struct Struct;
+   |     ^^^^^^^^^^^^^^
+   |
+help: consider removing the safety comment
+  --> $DIR/unnecessary_safety_comment.rs:9:5
+   |
+LL |     // SAFETY:
+   |     ^^^^^^^^^^
+
+error: enum has unnecessary safety comment
+  --> $DIR/unnecessary_safety_comment.rs:12:5
+   |
+LL |     enum Enum {}
+   |     ^^^^^^^^^^^^
+   |
+help: consider removing the safety comment
+  --> $DIR/unnecessary_safety_comment.rs:11:5
+   |
+LL |     // SAFETY:
+   |     ^^^^^^^^^^
+
+error: module has unnecessary safety comment
+  --> $DIR/unnecessary_safety_comment.rs:14:5
+   |
+LL |     mod module {}
+   |     ^^^^^^^^^^^^^
+   |
+help: consider removing the safety comment
+  --> $DIR/unnecessary_safety_comment.rs:13:5
+   |
+LL |     // SAFETY:
+   |     ^^^^^^^^^^
+
+error: impl has unnecessary safety comment
+  --> $DIR/unnecessary_safety_comment.rs:33:13
+   |
+LL |             impl T for $t {}
+   |             ^^^^^^^^^^^^^^^^
+...
+LL |     with_safety_comment!(i32);
+   |     ------------------------- in this macro invocation
+   |
+help: consider removing the safety comment
+  --> $DIR/unnecessary_safety_comment.rs:32:13
+   |
+LL |             // Safety: unnecessary
+   |             ^^^^^^^^^^^^^^^^^^^^^^
+   = note: this error originates in the macro `with_safety_comment` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: expression has unnecessary safety comment
+  --> $DIR/unnecessary_safety_comment.rs:48:5
+   |
+LL |     24
+   |     ^^
+   |
+help: consider removing the safety comment
+  --> $DIR/unnecessary_safety_comment.rs:47:5
+   |
+LL |     // SAFETY: unnecessary
+   |     ^^^^^^^^^^^^^^^^^^^^^^
+
+error: statement has unnecessary safety comment
+  --> $DIR/unnecessary_safety_comment.rs:42:5
+   |
+LL |     let num = 42;
+   |     ^^^^^^^^^^^^^
+   |
+help: consider removing the safety comment
+  --> $DIR/unnecessary_safety_comment.rs:41:5
+   |
+LL |     // SAFETY: unnecessary
+   |     ^^^^^^^^^^^^^^^^^^^^^^
+
+error: statement has unnecessary safety comment
+  --> $DIR/unnecessary_safety_comment.rs:45:5
+   |
+LL |     if num > 24 {}
+   |     ^^^^^^^^^^^^^^
+   |
+help: consider removing the safety comment
+  --> $DIR/unnecessary_safety_comment.rs:44:5
+   |
+LL |     // SAFETY: unnecessary
+   |     ^^^^^^^^^^^^^^^^^^^^^^
+
+error: aborting due to 9 previous errors
+
diff --git a/src/tools/clippy/tests/ui/unnecessary_to_owned.fixed b/src/tools/clippy/tests/ui/unnecessary_to_owned.fixed
index fe09aad06bc..ddeda795f81 100644
--- a/src/tools/clippy/tests/ui/unnecessary_to_owned.fixed
+++ b/src/tools/clippy/tests/ui/unnecessary_to_owned.fixed
@@ -2,7 +2,6 @@
 
 #![allow(clippy::needless_borrow, clippy::ptr_arg)]
 #![warn(clippy::unnecessary_to_owned)]
-#![feature(custom_inner_attributes)]
 
 use std::borrow::Cow;
 use std::ffi::{CStr, CString, OsStr, OsString};
@@ -215,14 +214,14 @@ fn get_file_path(_file_type: &FileType) -> Result<std::path::PathBuf, std::io::E
 
 fn require_string(_: &String) {}
 
+#[clippy::msrv = "1.35"]
 fn _msrv_1_35() {
-    #![clippy::msrv = "1.35"]
     // `copied` was stabilized in 1.36, so clippy should use `cloned`.
     let _ = &["x"][..].iter().cloned();
 }
 
+#[clippy::msrv = "1.36"]
 fn _msrv_1_36() {
-    #![clippy::msrv = "1.36"]
     let _ = &["x"][..].iter().copied();
 }
 
@@ -426,3 +425,32 @@ mod issue_9504 {
         foo(std::path::PathBuf::new().to_string_lossy().to_string()).await;
     }
 }
+
+mod issue_9771a {
+    #![allow(dead_code)]
+
+    use std::marker::PhantomData;
+
+    pub struct Key<K: AsRef<[u8]>, V: ?Sized>(K, PhantomData<V>);
+
+    impl<K: AsRef<[u8]>, V: ?Sized> Key<K, V> {
+        pub fn new(key: K) -> Key<K, V> {
+            Key(key, PhantomData)
+        }
+    }
+
+    pub fn pkh(pkh: &[u8]) -> Key<Vec<u8>, String> {
+        Key::new([b"pkh-", pkh].concat().to_vec())
+    }
+}
+
+mod issue_9771b {
+    #![allow(dead_code)]
+
+    pub struct Key<K: AsRef<[u8]>>(K);
+
+    pub fn from(c: &[u8]) -> Key<Vec<u8>> {
+        let v = [c].concat();
+        Key(v.to_vec())
+    }
+}
diff --git a/src/tools/clippy/tests/ui/unnecessary_to_owned.rs b/src/tools/clippy/tests/ui/unnecessary_to_owned.rs
index 3de6d0903c0..95d2576733c 100644
--- a/src/tools/clippy/tests/ui/unnecessary_to_owned.rs
+++ b/src/tools/clippy/tests/ui/unnecessary_to_owned.rs
@@ -2,7 +2,6 @@
 
 #![allow(clippy::needless_borrow, clippy::ptr_arg)]
 #![warn(clippy::unnecessary_to_owned)]
-#![feature(custom_inner_attributes)]
 
 use std::borrow::Cow;
 use std::ffi::{CStr, CString, OsStr, OsString};
@@ -215,14 +214,14 @@ fn get_file_path(_file_type: &FileType) -> Result<std::path::PathBuf, std::io::E
 
 fn require_string(_: &String) {}
 
+#[clippy::msrv = "1.35"]
 fn _msrv_1_35() {
-    #![clippy::msrv = "1.35"]
     // `copied` was stabilized in 1.36, so clippy should use `cloned`.
     let _ = &["x"][..].to_vec().into_iter();
 }
 
+#[clippy::msrv = "1.36"]
 fn _msrv_1_36() {
-    #![clippy::msrv = "1.36"]
     let _ = &["x"][..].to_vec().into_iter();
 }
 
@@ -426,3 +425,32 @@ mod issue_9504 {
         foo(std::path::PathBuf::new().to_string_lossy().to_string()).await;
     }
 }
+
+mod issue_9771a {
+    #![allow(dead_code)]
+
+    use std::marker::PhantomData;
+
+    pub struct Key<K: AsRef<[u8]>, V: ?Sized>(K, PhantomData<V>);
+
+    impl<K: AsRef<[u8]>, V: ?Sized> Key<K, V> {
+        pub fn new(key: K) -> Key<K, V> {
+            Key(key, PhantomData)
+        }
+    }
+
+    pub fn pkh(pkh: &[u8]) -> Key<Vec<u8>, String> {
+        Key::new([b"pkh-", pkh].concat().to_vec())
+    }
+}
+
+mod issue_9771b {
+    #![allow(dead_code)]
+
+    pub struct Key<K: AsRef<[u8]>>(K);
+
+    pub fn from(c: &[u8]) -> Key<Vec<u8>> {
+        let v = [c].concat();
+        Key(v.to_vec())
+    }
+}
diff --git a/src/tools/clippy/tests/ui/unnecessary_to_owned.stderr b/src/tools/clippy/tests/ui/unnecessary_to_owned.stderr
index 02bf45a33fb..4918fe35598 100644
--- a/src/tools/clippy/tests/ui/unnecessary_to_owned.stderr
+++ b/src/tools/clippy/tests/ui/unnecessary_to_owned.stderr
@@ -1,66 +1,66 @@
 error: redundant clone
-  --> $DIR/unnecessary_to_owned.rs:151:64
+  --> $DIR/unnecessary_to_owned.rs:150: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:151:20
+  --> $DIR/unnecessary_to_owned.rs:150:20
    |
 LL |     require_c_str(&CString::from_vec_with_nul(vec![0]).unwrap().to_owned());
    |                    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    = note: `-D clippy::redundant-clone` implied by `-D warnings`
 
 error: redundant clone
-  --> $DIR/unnecessary_to_owned.rs:152:40
+  --> $DIR/unnecessary_to_owned.rs:151: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:152:21
+  --> $DIR/unnecessary_to_owned.rs:151:21
    |
 LL |     require_os_str(&OsString::from("x").to_os_string());
    |                     ^^^^^^^^^^^^^^^^^^^
 
 error: redundant clone
-  --> $DIR/unnecessary_to_owned.rs:153:48
+  --> $DIR/unnecessary_to_owned.rs:152: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:153:19
+  --> $DIR/unnecessary_to_owned.rs:152:19
    |
 LL |     require_path(&std::path::PathBuf::from("x").to_path_buf());
    |                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 error: redundant clone
-  --> $DIR/unnecessary_to_owned.rs:154:35
+  --> $DIR/unnecessary_to_owned.rs:153: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:154:18
+  --> $DIR/unnecessary_to_owned.rs:153:18
    |
 LL |     require_str(&String::from("x").to_string());
    |                  ^^^^^^^^^^^^^^^^^
 
 error: redundant clone
-  --> $DIR/unnecessary_to_owned.rs:155:39
+  --> $DIR/unnecessary_to_owned.rs:154: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:155:20
+  --> $DIR/unnecessary_to_owned.rs:154:20
    |
 LL |     require_slice(&[String::from("x")].to_owned());
    |                    ^^^^^^^^^^^^^^^^^^^
 
 error: unnecessary use of `into_owned`
-  --> $DIR/unnecessary_to_owned.rs:60:36
+  --> $DIR/unnecessary_to_owned.rs:59:36
    |
 LL |     require_c_str(&Cow::from(c_str).into_owned());
    |                                    ^^^^^^^^^^^^^ help: remove this
@@ -68,415 +68,415 @@ LL |     require_c_str(&Cow::from(c_str).into_owned());
    = note: `-D clippy::unnecessary-to-owned` implied by `-D warnings`
 
 error: unnecessary use of `to_owned`
-  --> $DIR/unnecessary_to_owned.rs:61:19
+  --> $DIR/unnecessary_to_owned.rs:60: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:63:20
+  --> $DIR/unnecessary_to_owned.rs:62: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:64:38
+  --> $DIR/unnecessary_to_owned.rs:63: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:65:20
+  --> $DIR/unnecessary_to_owned.rs:64: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:67:18
+  --> $DIR/unnecessary_to_owned.rs:66:18
    |
 LL |     require_path(&path.to_path_buf());
    |                  ^^^^^^^^^^^^^^^^^^^ help: use: `path`
 
 error: unnecessary use of `into_owned`
-  --> $DIR/unnecessary_to_owned.rs:68:34
+  --> $DIR/unnecessary_to_owned.rs:67:34
    |
 LL |     require_path(&Cow::from(path).into_owned());
    |                                  ^^^^^^^^^^^^^ help: remove this
 
 error: unnecessary use of `to_owned`
-  --> $DIR/unnecessary_to_owned.rs:69:18
+  --> $DIR/unnecessary_to_owned.rs:68:18
    |
 LL |     require_path(&path.to_owned());
    |                  ^^^^^^^^^^^^^^^^ help: use: `path`
 
 error: unnecessary use of `to_string`
-  --> $DIR/unnecessary_to_owned.rs:71:17
+  --> $DIR/unnecessary_to_owned.rs:70:17
    |
 LL |     require_str(&s.to_string());
    |                 ^^^^^^^^^^^^^^ help: use: `s`
 
 error: unnecessary use of `into_owned`
-  --> $DIR/unnecessary_to_owned.rs:72:30
+  --> $DIR/unnecessary_to_owned.rs:71:30
    |
 LL |     require_str(&Cow::from(s).into_owned());
    |                              ^^^^^^^^^^^^^ help: remove this
 
 error: unnecessary use of `to_owned`
-  --> $DIR/unnecessary_to_owned.rs:73:17
+  --> $DIR/unnecessary_to_owned.rs:72:17
    |
 LL |     require_str(&s.to_owned());
    |                 ^^^^^^^^^^^^^ help: use: `s`
 
 error: unnecessary use of `to_string`
-  --> $DIR/unnecessary_to_owned.rs:74:17
+  --> $DIR/unnecessary_to_owned.rs:73: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:76:19
+  --> $DIR/unnecessary_to_owned.rs:75:19
    |
 LL |     require_slice(&slice.to_vec());
    |                   ^^^^^^^^^^^^^^^ help: use: `slice`
 
 error: unnecessary use of `into_owned`
-  --> $DIR/unnecessary_to_owned.rs:77:36
+  --> $DIR/unnecessary_to_owned.rs:76:36
    |
 LL |     require_slice(&Cow::from(slice).into_owned());
    |                                    ^^^^^^^^^^^^^ help: remove this
 
 error: unnecessary use of `to_owned`
-  --> $DIR/unnecessary_to_owned.rs:78:19
+  --> $DIR/unnecessary_to_owned.rs:77:19
    |
 LL |     require_slice(&array.to_owned());
    |                   ^^^^^^^^^^^^^^^^^ help: use: `array.as_ref()`
 
 error: unnecessary use of `to_owned`
-  --> $DIR/unnecessary_to_owned.rs:79:19
+  --> $DIR/unnecessary_to_owned.rs:78: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:80:19
+  --> $DIR/unnecessary_to_owned.rs:79:19
    |
 LL |     require_slice(&slice.to_owned());
    |                   ^^^^^^^^^^^^^^^^^ help: use: `slice`
 
 error: unnecessary use of `into_owned`
-  --> $DIR/unnecessary_to_owned.rs:83:42
+  --> $DIR/unnecessary_to_owned.rs:82: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:86:25
+  --> $DIR/unnecessary_to_owned.rs:85: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:87:26
+  --> $DIR/unnecessary_to_owned.rs:86: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:88:24
+  --> $DIR/unnecessary_to_owned.rs:87:24
    |
 LL |     require_deref_path(path.to_owned());
    |                        ^^^^^^^^^^^^^^^ help: use: `path`
 
 error: unnecessary use of `to_owned`
-  --> $DIR/unnecessary_to_owned.rs:89:23
+  --> $DIR/unnecessary_to_owned.rs:88:23
    |
 LL |     require_deref_str(s.to_owned());
    |                       ^^^^^^^^^^^^ help: use: `s`
 
 error: unnecessary use of `to_owned`
-  --> $DIR/unnecessary_to_owned.rs:90:25
+  --> $DIR/unnecessary_to_owned.rs:89:25
    |
 LL |     require_deref_slice(slice.to_owned());
    |                         ^^^^^^^^^^^^^^^^ help: use: `slice`
 
 error: unnecessary use of `to_owned`
-  --> $DIR/unnecessary_to_owned.rs:92:30
+  --> $DIR/unnecessary_to_owned.rs:91: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:93:31
+  --> $DIR/unnecessary_to_owned.rs:92: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:94:29
+  --> $DIR/unnecessary_to_owned.rs:93:29
    |
 LL |     require_impl_deref_path(path.to_owned());
    |                             ^^^^^^^^^^^^^^^ help: use: `path`
 
 error: unnecessary use of `to_owned`
-  --> $DIR/unnecessary_to_owned.rs:95:28
+  --> $DIR/unnecessary_to_owned.rs:94:28
    |
 LL |     require_impl_deref_str(s.to_owned());
    |                            ^^^^^^^^^^^^ help: use: `s`
 
 error: unnecessary use of `to_owned`
-  --> $DIR/unnecessary_to_owned.rs:96:30
+  --> $DIR/unnecessary_to_owned.rs:95:30
    |
 LL |     require_impl_deref_slice(slice.to_owned());
    |                              ^^^^^^^^^^^^^^^^ help: use: `slice`
 
 error: unnecessary use of `to_owned`
-  --> $DIR/unnecessary_to_owned.rs:98:29
+  --> $DIR/unnecessary_to_owned.rs:97: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:98:43
+  --> $DIR/unnecessary_to_owned.rs:97: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:99:29
+  --> $DIR/unnecessary_to_owned.rs:98: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:99:47
+  --> $DIR/unnecessary_to_owned.rs:98: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:101:26
+  --> $DIR/unnecessary_to_owned.rs:100: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:102:27
+  --> $DIR/unnecessary_to_owned.rs:101: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:103:25
+  --> $DIR/unnecessary_to_owned.rs:102:25
    |
 LL |     require_as_ref_path(path.to_owned());
    |                         ^^^^^^^^^^^^^^^ help: use: `path`
 
 error: unnecessary use of `to_owned`
-  --> $DIR/unnecessary_to_owned.rs:104:24
+  --> $DIR/unnecessary_to_owned.rs:103:24
    |
 LL |     require_as_ref_str(s.to_owned());
    |                        ^^^^^^^^^^^^ help: use: `s`
 
 error: unnecessary use of `to_owned`
-  --> $DIR/unnecessary_to_owned.rs:105:24
+  --> $DIR/unnecessary_to_owned.rs:104:24
    |
 LL |     require_as_ref_str(x.to_owned());
    |                        ^^^^^^^^^^^^ help: use: `&x`
 
 error: unnecessary use of `to_owned`
-  --> $DIR/unnecessary_to_owned.rs:106:26
+  --> $DIR/unnecessary_to_owned.rs:105:26
    |
 LL |     require_as_ref_slice(array.to_owned());
    |                          ^^^^^^^^^^^^^^^^ help: use: `array`
 
 error: unnecessary use of `to_owned`
-  --> $DIR/unnecessary_to_owned.rs:107:26
+  --> $DIR/unnecessary_to_owned.rs:106: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:108:26
+  --> $DIR/unnecessary_to_owned.rs:107:26
    |
 LL |     require_as_ref_slice(slice.to_owned());
    |                          ^^^^^^^^^^^^^^^^ help: use: `slice`
 
 error: unnecessary use of `to_owned`
-  --> $DIR/unnecessary_to_owned.rs:110:31
+  --> $DIR/unnecessary_to_owned.rs:109: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:111:32
+  --> $DIR/unnecessary_to_owned.rs:110: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:112:30
+  --> $DIR/unnecessary_to_owned.rs:111:30
    |
 LL |     require_impl_as_ref_path(path.to_owned());
    |                              ^^^^^^^^^^^^^^^ help: use: `path`
 
 error: unnecessary use of `to_owned`
-  --> $DIR/unnecessary_to_owned.rs:113:29
+  --> $DIR/unnecessary_to_owned.rs:112:29
    |
 LL |     require_impl_as_ref_str(s.to_owned());
    |                             ^^^^^^^^^^^^ help: use: `s`
 
 error: unnecessary use of `to_owned`
-  --> $DIR/unnecessary_to_owned.rs:114:29
+  --> $DIR/unnecessary_to_owned.rs:113:29
    |
 LL |     require_impl_as_ref_str(x.to_owned());
    |                             ^^^^^^^^^^^^ help: use: `&x`
 
 error: unnecessary use of `to_owned`
-  --> $DIR/unnecessary_to_owned.rs:115:31
+  --> $DIR/unnecessary_to_owned.rs:114:31
    |
 LL |     require_impl_as_ref_slice(array.to_owned());
    |                               ^^^^^^^^^^^^^^^^ help: use: `array`
 
 error: unnecessary use of `to_owned`
-  --> $DIR/unnecessary_to_owned.rs:116:31
+  --> $DIR/unnecessary_to_owned.rs:115: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:117:31
+  --> $DIR/unnecessary_to_owned.rs:116:31
    |
 LL |     require_impl_as_ref_slice(slice.to_owned());
    |                               ^^^^^^^^^^^^^^^^ help: use: `slice`
 
 error: unnecessary use of `to_owned`
-  --> $DIR/unnecessary_to_owned.rs:119:30
+  --> $DIR/unnecessary_to_owned.rs:118: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:119:44
+  --> $DIR/unnecessary_to_owned.rs:118: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:120:30
+  --> $DIR/unnecessary_to_owned.rs:119: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:120:44
+  --> $DIR/unnecessary_to_owned.rs:119: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:121:30
+  --> $DIR/unnecessary_to_owned.rs:120: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:121:44
+  --> $DIR/unnecessary_to_owned.rs:120: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:122:30
+  --> $DIR/unnecessary_to_owned.rs:121: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:122:48
+  --> $DIR/unnecessary_to_owned.rs:121: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:123:30
+  --> $DIR/unnecessary_to_owned.rs:122: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:123:52
+  --> $DIR/unnecessary_to_owned.rs:122: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:124:30
+  --> $DIR/unnecessary_to_owned.rs:123: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:124:48
+  --> $DIR/unnecessary_to_owned.rs:123: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:126:20
+  --> $DIR/unnecessary_to_owned.rs:125:20
    |
 LL |     let _ = x.join(&x_ref.to_string());
    |                    ^^^^^^^^^^^^^^^^^^ help: use: `x_ref`
 
 error: unnecessary use of `to_vec`
-  --> $DIR/unnecessary_to_owned.rs:128:13
+  --> $DIR/unnecessary_to_owned.rs:127:13
    |
 LL |     let _ = slice.to_vec().into_iter();
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `slice.iter().copied()`
 
 error: unnecessary use of `to_owned`
-  --> $DIR/unnecessary_to_owned.rs:129:13
+  --> $DIR/unnecessary_to_owned.rs:128:13
    |
 LL |     let _ = slice.to_owned().into_iter();
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `slice.iter().copied()`
 
 error: unnecessary use of `to_vec`
-  --> $DIR/unnecessary_to_owned.rs:130:13
+  --> $DIR/unnecessary_to_owned.rs:129: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:131:13
+  --> $DIR/unnecessary_to_owned.rs:130: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:133:13
+  --> $DIR/unnecessary_to_owned.rs:132: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:134:13
+  --> $DIR/unnecessary_to_owned.rs:133: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:135:13
+  --> $DIR/unnecessary_to_owned.rs:134: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:136:13
+  --> $DIR/unnecessary_to_owned.rs:135: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:198:14
+  --> $DIR/unnecessary_to_owned.rs:197:14
    |
 LL |     for t in file_types.to_vec() {
    |              ^^^^^^^^^^^^^^^^^^^
@@ -492,25 +492,25 @@ LL +         let path = match get_file_path(t) {
    |
 
 error: unnecessary use of `to_vec`
-  --> $DIR/unnecessary_to_owned.rs:221:14
+  --> $DIR/unnecessary_to_owned.rs:220:14
    |
 LL |     let _ = &["x"][..].to_vec().into_iter();
    |              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `["x"][..].iter().cloned()`
 
 error: unnecessary use of `to_vec`
-  --> $DIR/unnecessary_to_owned.rs:226:14
+  --> $DIR/unnecessary_to_owned.rs:225:14
    |
 LL |     let _ = &["x"][..].to_vec().into_iter();
    |              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `["x"][..].iter().copied()`
 
 error: unnecessary use of `to_string`
-  --> $DIR/unnecessary_to_owned.rs:273:24
+  --> $DIR/unnecessary_to_owned.rs:272:24
    |
 LL |         Box::new(build(y.to_string()))
    |                        ^^^^^^^^^^^^^ help: use: `y`
 
 error: unnecessary use of `to_string`
-  --> $DIR/unnecessary_to_owned.rs:381:12
+  --> $DIR/unnecessary_to_owned.rs:380:12
    |
 LL |         id("abc".to_string())
    |            ^^^^^^^^^^^^^^^^^ help: use: `"abc"`
diff --git a/src/tools/clippy/tests/ui/doc_unnecessary_unsafe.rs b/src/tools/clippy/tests/ui/unnecessary_unsafety_doc.rs
index d9e9363b0f4..c160e31afd3 100644
--- a/src/tools/clippy/tests/ui/doc_unnecessary_unsafe.rs
+++ b/src/tools/clippy/tests/ui/unnecessary_unsafety_doc.rs
@@ -1,6 +1,7 @@
 // aux-build:doc_unsafe_macros.rs
 
 #![allow(clippy::let_unit_value)]
+#![warn(clippy::unnecessary_safety_doc)]
 
 #[macro_use]
 extern crate doc_unsafe_macros;
diff --git a/src/tools/clippy/tests/ui/doc_unnecessary_unsafe.stderr b/src/tools/clippy/tests/ui/unnecessary_unsafety_doc.stderr
index 83b2efbb346..72898c93fa1 100644
--- a/src/tools/clippy/tests/ui/doc_unnecessary_unsafe.stderr
+++ b/src/tools/clippy/tests/ui/unnecessary_unsafety_doc.stderr
@@ -1,5 +1,5 @@
 error: safe function's docs have unnecessary `# Safety` section
-  --> $DIR/doc_unnecessary_unsafe.rs:18:1
+  --> $DIR/unnecessary_unsafety_doc.rs:19:1
    |
 LL | pub fn apocalypse(universe: &mut ()) {
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -7,31 +7,31 @@ LL | pub fn apocalypse(universe: &mut ()) {
    = note: `-D clippy::unnecessary-safety-doc` implied by `-D warnings`
 
 error: safe function's docs have unnecessary `# Safety` section
-  --> $DIR/doc_unnecessary_unsafe.rs:44:5
+  --> $DIR/unnecessary_unsafety_doc.rs:45:5
    |
 LL |     pub fn republished() {
    |     ^^^^^^^^^^^^^^^^^^^^
 
 error: safe function's docs have unnecessary `# Safety` section
-  --> $DIR/doc_unnecessary_unsafe.rs:57:5
+  --> $DIR/unnecessary_unsafety_doc.rs:58:5
    |
 LL |     fn documented(self);
    |     ^^^^^^^^^^^^^^^^^^^^
 
 error: docs for safe trait have unnecessary `# Safety` section
-  --> $DIR/doc_unnecessary_unsafe.rs:67:1
+  --> $DIR/unnecessary_unsafety_doc.rs:68:1
    |
 LL | pub trait DocumentedSafeTrait {
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 error: safe function's docs have unnecessary `# Safety` section
-  --> $DIR/doc_unnecessary_unsafe.rs:95:5
+  --> $DIR/unnecessary_unsafety_doc.rs:96:5
    |
 LL |     pub fn documented() -> Self {
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 error: safe function's docs have unnecessary `# Safety` section
-  --> $DIR/doc_unnecessary_unsafe.rs:122:9
+  --> $DIR/unnecessary_unsafety_doc.rs:123:9
    |
 LL |         pub fn drive() {
    |         ^^^^^^^^^^^^^^
@@ -42,7 +42,7 @@ LL | very_safe!();
    = note: this error originates in the macro `very_safe` (in Nightly builds, run with -Z macro-backtrace for more info)
 
 error: docs for safe trait have unnecessary `# Safety` section
-  --> $DIR/doc_unnecessary_unsafe.rs:146:1
+  --> $DIR/unnecessary_unsafety_doc.rs:147:1
    |
 LL | pub trait DocumentedSafeTraitWithImplementationHeader {
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
diff --git a/src/tools/clippy/tests/ui/unnested_or_patterns.fixed b/src/tools/clippy/tests/ui/unnested_or_patterns.fixed
index 9786c7b1212..0a8e7b34dfa 100644
--- a/src/tools/clippy/tests/ui/unnested_or_patterns.fixed
+++ b/src/tools/clippy/tests/ui/unnested_or_patterns.fixed
@@ -1,6 +1,6 @@
 // run-rustfix
 
-#![feature(box_patterns, custom_inner_attributes)]
+#![feature(box_patterns)]
 #![warn(clippy::unnested_or_patterns)]
 #![allow(clippy::cognitive_complexity, clippy::match_ref_pats, clippy::upper_case_acronyms)]
 #![allow(unreachable_patterns, irrefutable_let_patterns, unused)]
@@ -34,14 +34,12 @@ fn main() {
     if let S { x: 0, y, .. } | S { y, x: 1 } = (S { x: 0, y: 1 }) {}
 }
 
+#[clippy::msrv = "1.52"]
 fn msrv_1_52() {
-    #![clippy::msrv = "1.52"]
-
     if let [1] | [52] = [0] {}
 }
 
+#[clippy::msrv = "1.53"]
 fn msrv_1_53() {
-    #![clippy::msrv = "1.53"]
-
     if let [1 | 53] = [0] {}
 }
diff --git a/src/tools/clippy/tests/ui/unnested_or_patterns.rs b/src/tools/clippy/tests/ui/unnested_or_patterns.rs
index f57322396d4..2c454adfe89 100644
--- a/src/tools/clippy/tests/ui/unnested_or_patterns.rs
+++ b/src/tools/clippy/tests/ui/unnested_or_patterns.rs
@@ -1,6 +1,6 @@
 // run-rustfix
 
-#![feature(box_patterns, custom_inner_attributes)]
+#![feature(box_patterns)]
 #![warn(clippy::unnested_or_patterns)]
 #![allow(clippy::cognitive_complexity, clippy::match_ref_pats, clippy::upper_case_acronyms)]
 #![allow(unreachable_patterns, irrefutable_let_patterns, unused)]
@@ -34,14 +34,12 @@ fn main() {
     if let S { x: 0, y, .. } | S { y, x: 1 } = (S { x: 0, y: 1 }) {}
 }
 
+#[clippy::msrv = "1.52"]
 fn msrv_1_52() {
-    #![clippy::msrv = "1.52"]
-
     if let [1] | [52] = [0] {}
 }
 
+#[clippy::msrv = "1.53"]
 fn msrv_1_53() {
-    #![clippy::msrv = "1.53"]
-
     if let [1] | [53] = [0] {}
 }
diff --git a/src/tools/clippy/tests/ui/unnested_or_patterns.stderr b/src/tools/clippy/tests/ui/unnested_or_patterns.stderr
index fbc12fff0b0..a1f193db555 100644
--- a/src/tools/clippy/tests/ui/unnested_or_patterns.stderr
+++ b/src/tools/clippy/tests/ui/unnested_or_patterns.stderr
@@ -176,7 +176,7 @@ LL |     if let S { x: 0 | 1, y } = (S { x: 0, y: 1 }) {}
    |            ~~~~~~~~~~~~~~~~~
 
 error: unnested or-patterns
-  --> $DIR/unnested_or_patterns.rs:46:12
+  --> $DIR/unnested_or_patterns.rs:44:12
    |
 LL |     if let [1] | [53] = [0] {}
    |            ^^^^^^^^^^
diff --git a/src/tools/clippy/tests/ui/unused_rounding.fixed b/src/tools/clippy/tests/ui/unused_rounding.fixed
index 38fe6c34cfe..f6f734c05ed 100644
--- a/src/tools/clippy/tests/ui/unused_rounding.fixed
+++ b/src/tools/clippy/tests/ui/unused_rounding.fixed
@@ -11,4 +11,7 @@ fn main() {
     let _ = 3.3_f32.round();
     let _ = 3.3_f64.round();
     let _ = 3.0_f32;
+
+    let _ = 3_3.0_0_f32;
+    let _ = 3_3.0_1_f64.round();
 }
diff --git a/src/tools/clippy/tests/ui/unused_rounding.rs b/src/tools/clippy/tests/ui/unused_rounding.rs
index a5cac64d023..a0267d8144a 100644
--- a/src/tools/clippy/tests/ui/unused_rounding.rs
+++ b/src/tools/clippy/tests/ui/unused_rounding.rs
@@ -11,4 +11,7 @@ fn main() {
     let _ = 3.3_f32.round();
     let _ = 3.3_f64.round();
     let _ = 3.0_f32.round();
+
+    let _ = 3_3.0_0_f32.round();
+    let _ = 3_3.0_1_f64.round();
 }
diff --git a/src/tools/clippy/tests/ui/unused_rounding.stderr b/src/tools/clippy/tests/ui/unused_rounding.stderr
index 1eeb5d1de88..b867996fe57 100644
--- a/src/tools/clippy/tests/ui/unused_rounding.stderr
+++ b/src/tools/clippy/tests/ui/unused_rounding.stderr
@@ -24,5 +24,11 @@ error: used the `round` method with a whole number float
 LL |     let _ = 3.0_f32.round();
    |             ^^^^^^^^^^^^^^^ help: remove the `round` method call: `3.0_f32`
 
-error: aborting due to 4 previous errors
+error: used the `round` method with a whole number float
+  --> $DIR/unused_rounding.rs:15:13
+   |
+LL |     let _ = 3_3.0_0_f32.round();
+   |             ^^^^^^^^^^^^^^^^^^^ help: remove the `round` method call: `3_3.0_0_f32`
+
+error: aborting due to 5 previous errors
 
diff --git a/src/tools/clippy/tests/ui/use_self.fixed b/src/tools/clippy/tests/ui/use_self.fixed
index 3b54fe9d5ff..0a6166571eb 100644
--- a/src/tools/clippy/tests/ui/use_self.fixed
+++ b/src/tools/clippy/tests/ui/use_self.fixed
@@ -1,7 +1,6 @@
 // run-rustfix
 // aux-build:proc_macro_derive.rs
 
-#![feature(custom_inner_attributes)]
 #![warn(clippy::use_self)]
 #![allow(dead_code, unreachable_code)]
 #![allow(
@@ -619,9 +618,8 @@ mod issue6902 {
     }
 }
 
+#[clippy::msrv = "1.36"]
 fn msrv_1_36() {
-    #![clippy::msrv = "1.36"]
-
     enum E {
         A,
     }
@@ -635,9 +633,8 @@ fn msrv_1_36() {
     }
 }
 
+#[clippy::msrv = "1.37"]
 fn msrv_1_37() {
-    #![clippy::msrv = "1.37"]
-
     enum E {
         A,
     }
diff --git a/src/tools/clippy/tests/ui/use_self.rs b/src/tools/clippy/tests/ui/use_self.rs
index bf87633cd2d..39c2b431f7f 100644
--- a/src/tools/clippy/tests/ui/use_self.rs
+++ b/src/tools/clippy/tests/ui/use_self.rs
@@ -1,7 +1,6 @@
 // run-rustfix
 // aux-build:proc_macro_derive.rs
 
-#![feature(custom_inner_attributes)]
 #![warn(clippy::use_self)]
 #![allow(dead_code, unreachable_code)]
 #![allow(
@@ -619,9 +618,8 @@ mod issue6902 {
     }
 }
 
+#[clippy::msrv = "1.36"]
 fn msrv_1_36() {
-    #![clippy::msrv = "1.36"]
-
     enum E {
         A,
     }
@@ -635,9 +633,8 @@ fn msrv_1_36() {
     }
 }
 
+#[clippy::msrv = "1.37"]
 fn msrv_1_37() {
-    #![clippy::msrv = "1.37"]
-
     enum E {
         A,
     }
diff --git a/src/tools/clippy/tests/ui/use_self.stderr b/src/tools/clippy/tests/ui/use_self.stderr
index 16fb0609242..48364c40c3b 100644
--- a/src/tools/clippy/tests/ui/use_self.stderr
+++ b/src/tools/clippy/tests/ui/use_self.stderr
@@ -1,5 +1,5 @@
 error: unnecessary structure name repetition
-  --> $DIR/use_self.rs:23:21
+  --> $DIR/use_self.rs:22:21
    |
 LL |         fn new() -> Foo {
    |                     ^^^ help: use the applicable keyword: `Self`
@@ -7,247 +7,247 @@ LL |         fn new() -> Foo {
    = note: `-D clippy::use-self` implied by `-D warnings`
 
 error: unnecessary structure name repetition
-  --> $DIR/use_self.rs:24:13
+  --> $DIR/use_self.rs:23:13
    |
 LL |             Foo {}
    |             ^^^ help: use the applicable keyword: `Self`
 
 error: unnecessary structure name repetition
-  --> $DIR/use_self.rs:26:22
+  --> $DIR/use_self.rs:25:22
    |
 LL |         fn test() -> Foo {
    |                      ^^^ help: use the applicable keyword: `Self`
 
 error: unnecessary structure name repetition
-  --> $DIR/use_self.rs:27:13
+  --> $DIR/use_self.rs:26:13
    |
 LL |             Foo::new()
    |             ^^^ help: use the applicable keyword: `Self`
 
 error: unnecessary structure name repetition
-  --> $DIR/use_self.rs:32:25
+  --> $DIR/use_self.rs:31:25
    |
 LL |         fn default() -> Foo {
    |                         ^^^ help: use the applicable keyword: `Self`
 
 error: unnecessary structure name repetition
-  --> $DIR/use_self.rs:33:13
+  --> $DIR/use_self.rs:32:13
    |
 LL |             Foo::new()
    |             ^^^ help: use the applicable keyword: `Self`
 
 error: unnecessary structure name repetition
-  --> $DIR/use_self.rs:98:24
+  --> $DIR/use_self.rs:97:24
    |
 LL |         fn bad(foos: &[Foo]) -> impl Iterator<Item = &Foo> {
    |                        ^^^ help: use the applicable keyword: `Self`
 
 error: unnecessary structure name repetition
-  --> $DIR/use_self.rs:98:55
+  --> $DIR/use_self.rs:97:55
    |
 LL |         fn bad(foos: &[Foo]) -> impl Iterator<Item = &Foo> {
    |                                                       ^^^ help: use the applicable keyword: `Self`
 
 error: unnecessary structure name repetition
-  --> $DIR/use_self.rs:113:13
+  --> $DIR/use_self.rs:112:13
    |
 LL |             TS(0)
    |             ^^ help: use the applicable keyword: `Self`
 
 error: unnecessary structure name repetition
-  --> $DIR/use_self.rs:148:29
+  --> $DIR/use_self.rs:147:29
    |
 LL |                 fn bar() -> Bar {
    |                             ^^^ help: use the applicable keyword: `Self`
 
 error: unnecessary structure name repetition
-  --> $DIR/use_self.rs:149:21
+  --> $DIR/use_self.rs:148:21
    |
 LL |                     Bar { foo: Foo {} }
    |                     ^^^ help: use the applicable keyword: `Self`
 
 error: unnecessary structure name repetition
-  --> $DIR/use_self.rs:160:21
+  --> $DIR/use_self.rs:159:21
    |
 LL |         fn baz() -> Foo {
    |                     ^^^ help: use the applicable keyword: `Self`
 
 error: unnecessary structure name repetition
-  --> $DIR/use_self.rs:161:13
+  --> $DIR/use_self.rs:160:13
    |
 LL |             Foo {}
    |             ^^^ help: use the applicable keyword: `Self`
 
 error: unnecessary structure name repetition
-  --> $DIR/use_self.rs:178:21
+  --> $DIR/use_self.rs:177:21
    |
 LL |             let _ = Enum::B(42);
    |                     ^^^^ help: use the applicable keyword: `Self`
 
 error: unnecessary structure name repetition
-  --> $DIR/use_self.rs:179:21
+  --> $DIR/use_self.rs:178:21
    |
 LL |             let _ = Enum::C { field: true };
    |                     ^^^^ help: use the applicable keyword: `Self`
 
 error: unnecessary structure name repetition
-  --> $DIR/use_self.rs:180:21
+  --> $DIR/use_self.rs:179:21
    |
 LL |             let _ = Enum::A;
    |                     ^^^^ help: use the applicable keyword: `Self`
 
 error: unnecessary structure name repetition
-  --> $DIR/use_self.rs:222:13
+  --> $DIR/use_self.rs:221:13
    |
 LL |             nested::A::fun_1();
    |             ^^^^^^^^^ help: use the applicable keyword: `Self`
 
 error: unnecessary structure name repetition
-  --> $DIR/use_self.rs:223:13
+  --> $DIR/use_self.rs:222:13
    |
 LL |             nested::A::A;
    |             ^^^^^^^^^ help: use the applicable keyword: `Self`
 
 error: unnecessary structure name repetition
-  --> $DIR/use_self.rs:225:13
+  --> $DIR/use_self.rs:224:13
    |
 LL |             nested::A {};
    |             ^^^^^^^^^ help: use the applicable keyword: `Self`
 
 error: unnecessary structure name repetition
-  --> $DIR/use_self.rs:244:13
+  --> $DIR/use_self.rs:243:13
    |
 LL |             TestStruct::from_something()
    |             ^^^^^^^^^^ help: use the applicable keyword: `Self`
 
 error: unnecessary structure name repetition
-  --> $DIR/use_self.rs:258:25
+  --> $DIR/use_self.rs:257:25
    |
 LL |         async fn g() -> S {
    |                         ^ help: use the applicable keyword: `Self`
 
 error: unnecessary structure name repetition
-  --> $DIR/use_self.rs:259:13
+  --> $DIR/use_self.rs:258:13
    |
 LL |             S {}
    |             ^ help: use the applicable keyword: `Self`
 
 error: unnecessary structure name repetition
-  --> $DIR/use_self.rs:263:16
+  --> $DIR/use_self.rs:262:16
    |
 LL |             &p[S::A..S::B]
    |                ^ help: use the applicable keyword: `Self`
 
 error: unnecessary structure name repetition
-  --> $DIR/use_self.rs:263:22
+  --> $DIR/use_self.rs:262:22
    |
 LL |             &p[S::A..S::B]
    |                      ^ help: use the applicable keyword: `Self`
 
 error: unnecessary structure name repetition
-  --> $DIR/use_self.rs:286:29
+  --> $DIR/use_self.rs:285:29
    |
 LL |         fn foo(value: T) -> Foo<T> {
    |                             ^^^^^^ help: use the applicable keyword: `Self`
 
 error: unnecessary structure name repetition
-  --> $DIR/use_self.rs:287:13
+  --> $DIR/use_self.rs:286:13
    |
 LL |             Foo::<T> { value }
    |             ^^^^^^^^ help: use the applicable keyword: `Self`
 
 error: unnecessary structure name repetition
-  --> $DIR/use_self.rs:459:13
+  --> $DIR/use_self.rs:458:13
    |
 LL |             A::new::<submod::B>(submod::B {})
    |             ^ help: use the applicable keyword: `Self`
 
 error: unnecessary structure name repetition
-  --> $DIR/use_self.rs:496:13
+  --> $DIR/use_self.rs:495:13
    |
 LL |             S2::new()
    |             ^^ help: use the applicable keyword: `Self`
 
 error: unnecessary structure name repetition
-  --> $DIR/use_self.rs:533:17
+  --> $DIR/use_self.rs:532:17
    |
 LL |                 Foo::Bar => unimplemented!(),
    |                 ^^^ help: use the applicable keyword: `Self`
 
 error: unnecessary structure name repetition
-  --> $DIR/use_self.rs:534:17
+  --> $DIR/use_self.rs:533:17
    |
 LL |                 Foo::Baz => unimplemented!(),
    |                 ^^^ help: use the applicable keyword: `Self`
 
 error: unnecessary structure name repetition
-  --> $DIR/use_self.rs:540:20
+  --> $DIR/use_self.rs:539:20
    |
 LL |             if let Foo::Bar = self {
    |                    ^^^ help: use the applicable keyword: `Self`
 
 error: unnecessary structure name repetition
-  --> $DIR/use_self.rs:564:17
+  --> $DIR/use_self.rs:563:17
    |
 LL |                 Something::Num(n) => *n,
    |                 ^^^^^^^^^ help: use the applicable keyword: `Self`
 
 error: unnecessary structure name repetition
-  --> $DIR/use_self.rs:565:17
+  --> $DIR/use_self.rs:564:17
    |
 LL |                 Something::TupleNums(n, _m) => *n,
    |                 ^^^^^^^^^ help: use the applicable keyword: `Self`
 
 error: unnecessary structure name repetition
-  --> $DIR/use_self.rs:566:17
+  --> $DIR/use_self.rs:565:17
    |
 LL |                 Something::StructNums { one, two: _ } => *one,
    |                 ^^^^^^^^^ help: use the applicable keyword: `Self`
 
 error: unnecessary structure name repetition
-  --> $DIR/use_self.rs:572:17
+  --> $DIR/use_self.rs:571:17
    |
 LL |                 crate::issue8845::Something::Num(n) => *n,
    |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the applicable keyword: `Self`
 
 error: unnecessary structure name repetition
-  --> $DIR/use_self.rs:573:17
+  --> $DIR/use_self.rs:572:17
    |
 LL |                 crate::issue8845::Something::TupleNums(n, _m) => *n,
    |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the applicable keyword: `Self`
 
 error: unnecessary structure name repetition
-  --> $DIR/use_self.rs:574:17
+  --> $DIR/use_self.rs:573:17
    |
 LL |                 crate::issue8845::Something::StructNums { one, two: _ } => *one,
    |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the applicable keyword: `Self`
 
 error: unnecessary structure name repetition
-  --> $DIR/use_self.rs:590:17
+  --> $DIR/use_self.rs:589:17
    |
 LL |             let Foo(x) = self;
    |                 ^^^ help: use the applicable keyword: `Self`
 
 error: unnecessary structure name repetition
-  --> $DIR/use_self.rs:595:17
+  --> $DIR/use_self.rs:594:17
    |
 LL |             let crate::issue8845::Foo(x) = self;
    |                 ^^^^^^^^^^^^^^^^^^^^^ help: use the applicable keyword: `Self`
 
 error: unnecessary structure name repetition
-  --> $DIR/use_self.rs:602:17
+  --> $DIR/use_self.rs:601:17
    |
 LL |             let Bar { x, .. } = self;
    |                 ^^^ help: use the applicable keyword: `Self`
 
 error: unnecessary structure name repetition
-  --> $DIR/use_self.rs:607:17
+  --> $DIR/use_self.rs:606:17
    |
 LL |             let crate::issue8845::Bar { x, .. } = self;
    |                 ^^^^^^^^^^^^^^^^^^^^^ help: use the applicable keyword: `Self`
 
 error: unnecessary structure name repetition
-  --> $DIR/use_self.rs:648:17
+  --> $DIR/use_self.rs:645:17
    |
 LL |                 E::A => {},
    |                 ^ help: use the applicable keyword: `Self`
diff --git a/src/tools/clippy/triagebot.toml b/src/tools/clippy/triagebot.toml
index 80c30393832..acb476ee696 100644
--- a/src/tools/clippy/triagebot.toml
+++ b/src/tools/clippy/triagebot.toml
@@ -4,9 +4,26 @@ allow-unauthenticated = [
     "good-first-issue"
 ]
 
-[assign]
-
 # Allows shortcuts like `@rustbot ready`
 #
 # See https://github.com/rust-lang/triagebot/wiki/Shortcuts
 [shortcut]
+
+[autolabel."S-waiting-on-review"]
+new_pr = true
+
+[assign]
+contributing_url = "https://github.com/rust-lang/rust-clippy/blob/master/CONTRIBUTING.md"
+
+[assign.owners]
+"/.github" = ["@flip1995"]
+"*" = [
+    "@flip1995",
+    "@Manishearth",
+    "@llogiq",
+    "@giraffate",
+    "@xFrednet",
+    "@Alexendoo",
+    "@dswij",
+    "@Jarcho",
+]
diff --git a/src/tools/jsondoclint/src/json_find.rs b/src/tools/jsondoclint/src/json_find.rs
index 95ea8866609..70e7440f730 100644
--- a/src/tools/jsondoclint/src/json_find.rs
+++ b/src/tools/jsondoclint/src/json_find.rs
@@ -2,7 +2,7 @@ use std::fmt::Write;
 
 use serde_json::Value;
 
-#[derive(Debug, Clone)]
+#[derive(Debug, Clone, PartialEq, Eq)]
 pub enum SelectorPart {
     Field(String),
     Index(usize),
@@ -72,3 +72,6 @@ fn find_selector_recursive(
         }
     }
 }
+
+#[cfg(test)]
+mod tests;
diff --git a/src/tools/jsondoclint/src/json_find/tests.rs b/src/tools/jsondoclint/src/json_find/tests.rs
new file mode 100644
index 00000000000..2a533530714
--- /dev/null
+++ b/src/tools/jsondoclint/src/json_find/tests.rs
@@ -0,0 +1,27 @@
+use super::*;
+
+#[test]
+fn basic_find() {
+    use SelectorPart::*;
+
+    let j = serde_json::json!({
+        "index": {
+            "4": {
+                "inner": {
+                    "items": ["1", "2", "3"]
+                }
+            }
+        }
+    });
+
+    let sel = find_selector(&j, &serde_json::json!("1"));
+    let exp: Vec<Vec<SelectorPart>> = vec![vec![
+        Field("index".to_owned()),
+        Field("4".to_owned()),
+        Field("inner".to_owned()),
+        Field("items".to_owned()),
+        Index(0),
+    ]];
+
+    assert_eq!(exp, sel);
+}
diff --git a/src/tools/jsondoclint/src/main.rs b/src/tools/jsondoclint/src/main.rs
index 70d7a82a576..fc54c421b4b 100644
--- a/src/tools/jsondoclint/src/main.rs
+++ b/src/tools/jsondoclint/src/main.rs
@@ -9,13 +9,13 @@ pub(crate) mod item_kind;
 mod json_find;
 mod validator;
 
-#[derive(Debug)]
+#[derive(Debug, PartialEq, Eq)]
 struct Error {
     kind: ErrorKind,
     id: Id,
 }
 
-#[derive(Debug)]
+#[derive(Debug, PartialEq, Eq)]
 enum ErrorKind {
     NotFound,
     Custom(String),
diff --git a/src/tools/miri/src/concurrency/data_race.rs b/src/tools/miri/src/concurrency/data_race.rs
index dd2102043d2..bcbf45a3d24 100644
--- a/src/tools/miri/src/concurrency/data_race.rs
+++ b/src/tools/miri/src/concurrency/data_race.rs
@@ -158,7 +158,7 @@ impl ThreadClockSet {
 
 /// Error returned by finding a data race
 /// should be elaborated upon.
-#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
+#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
 pub struct DataRace;
 
 /// Externally stored memory cell clocks
diff --git a/src/tools/rustc-workspace-hack/Cargo.toml b/src/tools/rustc-workspace-hack/Cargo.toml
index 07cf89f7d33..a5f0c0f320a 100644
--- a/src/tools/rustc-workspace-hack/Cargo.toml
+++ b/src/tools/rustc-workspace-hack/Cargo.toml
@@ -75,6 +75,8 @@ features = [
 bstr = { version = "0.2.17", features = ["default"] }
 clap = { version = "3.1.1", features = ["derive", "clap_derive"]}
 curl-sys = { version = "0.4.13", features = ["http2", "libnghttp2-sys"], optional = true }
+# Ensure `extra_traits` of libc, which is used transitively by Cargo.
+libc = { version = "0.2", features = ["extra_traits"] }
 # Ensure default features of libz-sys, which are disabled in some scenarios.
 libz-sys = { version = "1.1.2" }
 # Ensure default features of regex, which are disabled in some scenarios.
diff --git a/src/tools/rustfmt/src/attr.rs b/src/tools/rustfmt/src/attr.rs
index 23f55db773e..2ac703b957b 100644
--- a/src/tools/rustfmt/src/attr.rs
+++ b/src/tools/rustfmt/src/attr.rs
@@ -260,9 +260,7 @@ impl Rewrite for ast::NestedMetaItem {
     fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option<String> {
         match self {
             ast::NestedMetaItem::MetaItem(ref meta_item) => meta_item.rewrite(context, shape),
-            ast::NestedMetaItem::Literal(ref l) => {
-                rewrite_literal(context, l.token_lit, l.span, shape)
-            }
+            ast::NestedMetaItem::Lit(ref l) => rewrite_literal(context, l.token_lit, l.span, shape),
         }
     }
 }
@@ -527,14 +525,19 @@ pub(crate) trait MetaVisitor<'ast> {
 
     fn visit_meta_word(&mut self, _meta_item: &'ast ast::MetaItem) {}
 
-    fn visit_meta_name_value(&mut self, _meta_item: &'ast ast::MetaItem, _lit: &'ast ast::Lit) {}
+    fn visit_meta_name_value(
+        &mut self,
+        _meta_item: &'ast ast::MetaItem,
+        _lit: &'ast ast::MetaItemLit,
+    ) {
+    }
 
     fn visit_nested_meta_item(&mut self, nm: &'ast ast::NestedMetaItem) {
         match nm {
             ast::NestedMetaItem::MetaItem(ref meta_item) => self.visit_meta_item(meta_item),
-            ast::NestedMetaItem::Literal(ref lit) => self.visit_literal(lit),
+            ast::NestedMetaItem::Lit(ref lit) => self.visit_meta_item_lit(lit),
         }
     }
 
-    fn visit_literal(&mut self, _lit: &'ast ast::Lit) {}
+    fn visit_meta_item_lit(&mut self, _lit: &'ast ast::MetaItemLit) {}
 }
diff --git a/src/tools/rustfmt/src/imports.rs b/src/tools/rustfmt/src/imports.rs
index b6530c69243..d9dc8d004af 100644
--- a/src/tools/rustfmt/src/imports.rs
+++ b/src/tools/rustfmt/src/imports.rs
@@ -490,7 +490,7 @@ impl UseTree {
                 );
                 result.path.push(UseSegment { kind, version });
             }
-            UseTreeKind::Simple(ref rename, ..) => {
+            UseTreeKind::Simple(ref rename) => {
                 // If the path has leading double colons and is composed of only 2 segments, then we
                 // bypass the call to path_to_imported_ident which would get only the ident and
                 // lose the path root, e.g., `that` in `::that`.
diff --git a/src/tools/rustfmt/src/modules/visitor.rs b/src/tools/rustfmt/src/modules/visitor.rs
index ea67977c17a..48431693332 100644
--- a/src/tools/rustfmt/src/modules/visitor.rs
+++ b/src/tools/rustfmt/src/modules/visitor.rs
@@ -84,15 +84,19 @@ impl PathVisitor {
 }
 
 impl<'ast> MetaVisitor<'ast> for PathVisitor {
-    fn visit_meta_name_value(&mut self, meta_item: &'ast ast::MetaItem, lit: &'ast ast::Lit) {
+    fn visit_meta_name_value(
+        &mut self,
+        meta_item: &'ast ast::MetaItem,
+        lit: &'ast ast::MetaItemLit,
+    ) {
         if meta_item.has_name(Symbol::intern("path")) && lit.kind.is_str() {
-            self.paths.push(lit_to_str(lit));
+            self.paths.push(meta_item_lit_to_str(lit));
         }
     }
 }
 
 #[cfg(not(windows))]
-fn lit_to_str(lit: &ast::Lit) -> String {
+fn meta_item_lit_to_str(lit: &ast::MetaItemLit) -> String {
     match lit.kind {
         ast::LitKind::Str(symbol, ..) => symbol.to_string(),
         _ => unreachable!(),
@@ -100,7 +104,7 @@ fn lit_to_str(lit: &ast::Lit) -> String {
 }
 
 #[cfg(windows)]
-fn lit_to_str(lit: &ast::Lit) -> String {
+fn meta_item_lit_to_str(lit: &ast::MetaItemLit) -> String {
     match lit.kind {
         ast::LitKind::Str(symbol, ..) => symbol.as_str().replace("/", "\\"),
         _ => unreachable!(),
diff --git a/src/tools/rustfmt/src/overflow.rs b/src/tools/rustfmt/src/overflow.rs
index 6bf8cd0c70b..af0b95430a1 100644
--- a/src/tools/rustfmt/src/overflow.rs
+++ b/src/tools/rustfmt/src/overflow.rs
@@ -125,7 +125,7 @@ impl<'a> OverflowableItem<'a> {
             OverflowableItem::MacroArg(MacroArg::Keyword(..)) => true,
             OverflowableItem::MacroArg(MacroArg::Expr(expr)) => is_simple_expr(expr),
             OverflowableItem::NestedMetaItem(nested_meta_item) => match nested_meta_item {
-                ast::NestedMetaItem::Literal(..) => true,
+                ast::NestedMetaItem::Lit(..) => true,
                 ast::NestedMetaItem::MetaItem(ref meta_item) => {
                     matches!(meta_item.kind, ast::MetaItemKind::Word)
                 }
@@ -169,7 +169,7 @@ impl<'a> OverflowableItem<'a> {
             },
             OverflowableItem::NestedMetaItem(nested_meta_item) if len == 1 => {
                 match nested_meta_item {
-                    ast::NestedMetaItem::Literal(..) => false,
+                    ast::NestedMetaItem::Lit(..) => false,
                     ast::NestedMetaItem::MetaItem(..) => true,
                 }
             }
diff --git a/src/tools/rustfmt/src/utils.rs b/src/tools/rustfmt/src/utils.rs
index 136a2c7fce2..3e884419f1a 100644
--- a/src/tools/rustfmt/src/utils.rs
+++ b/src/tools/rustfmt/src/utils.rs
@@ -263,7 +263,7 @@ fn is_skip(meta_item: &MetaItem) -> bool {
 fn is_skip_nested(meta_item: &NestedMetaItem) -> bool {
     match meta_item {
         NestedMetaItem::MetaItem(ref mi) => is_skip(mi),
-        NestedMetaItem::Literal(_) => false,
+        NestedMetaItem::Lit(_) => false,
     }
 }
 
diff --git a/src/tools/tidy/src/deps.rs b/src/tools/tidy/src/deps.rs
index 8155ec9dd27..a7f40167284 100644
--- a/src/tools/tidy/src/deps.rs
+++ b/src/tools/tidy/src/deps.rs
@@ -98,6 +98,7 @@ const PERMITTED_RUSTC_DEPENDENCIES: &[&str] = &[
     "chalk-ir",
     "chalk-solve",
     "chrono",
+    "convert_case", // dependency of derive_more
     "compiler_builtins",
     "cpufeatures",
     "crc32fast",
@@ -108,6 +109,7 @@ const PERMITTED_RUSTC_DEPENDENCIES: &[&str] = &[
     "crypto-common",
     "cstr",
     "datafrog",
+    "derive_more",
     "difference",
     "digest",
     "displaydoc",
diff --git a/src/tools/tier-check/src/main.rs b/src/tools/tier-check/src/main.rs
index a41e2d6e3aa..c74d37c61e8 100644
--- a/src/tools/tier-check/src/main.rs
+++ b/src/tools/tier-check/src/main.rs
@@ -44,7 +44,23 @@ fn main() {
             target, filename, src
         );
     }
-    if !missing.is_empty() || !extra.is_empty() {
+    // Check target names for unwanted characters like `.` that can cause problems e.g. in Cargo.
+    // See also Tier 3 target policy.
+    // If desired, target names can ignore this check.
+    let ignore_target_names =
+        vec!["thumbv8m.base-none-eabi", "thumbv8m.main-none-eabi", "thumbv8m.main-none-eabihf"];
+    let mut invalid_target_name_found = false;
+    for target in &target_list {
+        if !ignore_target_names.contains(target)
+            && !target.chars().all(|c| c.is_ascii_alphanumeric() || c == '-' || c == '_')
+        {
+            invalid_target_name_found = true;
+            eprintln!(
+                "error: Target name `{target}` contains other characters than ASCII alphanumeric (a-z, A-Z, 0-9), dash (-) or underscore (_)."
+            );
+        }
+    }
+    if !missing.is_empty() || !extra.is_empty() || invalid_target_name_found {
         std::process::exit(1);
     }
 }
diff --git a/triagebot.toml b/triagebot.toml
index 985e065652d..bc0b88b2bab 100644
--- a/triagebot.toml
+++ b/triagebot.toml
@@ -15,7 +15,7 @@ allow-unauthenticated = [
     "llvm-main",
     "needs-fcp",
     "relnotes",
-    "requires-nightly",
+    "requires-*",
     "regression-*",
     "perf-*",
     "AsyncAwait-OnDeck",
@@ -334,6 +334,13 @@ cc = ["@rust-lang/wg-mir-opt"]
 message = "Some changes occurred in const_evaluatable.rs"
 cc = ["@lcnr"]
 
+[mentions."compiler/rustc_trait_selection/src/traits/engine.rs"]
+message = """
+Some changes occurred in engine.rs, potentially modifying the public API \
+of `ObligationCtxt`.
+"""
+cc = ["@lcnr"]
+
 [mentions."compiler/rustc_error_codes/src/error_codes.rs"]
 message = "Some changes occurred in diagnostic error codes"
 cc = ["@GuillaumeGomez"]
@@ -405,6 +412,9 @@ cc = ["@rust-lang/clippy"]
 message = "The Miri subtree was changed"
 cc = ["@rust-lang/miri"]
 
+[mentions."src/tools/rust-analyzer"]
+cc = ["@rust-lang/wg-rls-2"]
+
 [mentions."src/tools/rustfmt"]
 cc = ["@rust-lang/rustfmt"]
 
@@ -568,6 +578,13 @@ fallback = [
 "/src/bootstrap" =                           ["bootstrap"]
 "/src/ci" =                                  ["infra-ci"]
 "/src/doc" =                                 ["docs"]
+"/src/doc/book" =                            ["@ehuss"]
+"/src/doc/edition-guide" =                   ["@ehuss"]
+"/src/doc/embedded-book" =                   ["@ehuss"]
+"/src/doc/nomicon" =                         ["@ehuss"]
+"/src/doc/reference" =                       ["@ehuss"]
+"/src/doc/rust-by-example" =                 ["@ehuss"]
+"/src/doc/rustc-dev-guide" =                 ["@ehuss"]
 "/src/doc/rustdoc" =                         ["rustdoc"]
 "/src/etc" =                                 ["@Mark-Simulacrum"]
 "/src/librustdoc" =                          ["rustdoc"]