about summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2024-10-22 07:21:22 +0000
committerbors <bors@rust-lang.org>2024-10-22 07:21:22 +0000
commitca51c1e72cebbf0e6979339598b024c400536631 (patch)
treeb8e998e43a98e3cfe11d7b8b8ba0aaf4063ab905 /src
parentfa815a1cbbdb263bb6531d51ceb606e418e32d9a (diff)
parentefc2ba2d902e979770a17e2da6e3d289d6909812 (diff)
Auto merge of #18364 - lnicola:sync-from-rust, r=lnicola
minor: sync from downstream
Diffstat (limited to 'src')
-rwxr-xr-xsrc/bootstrap/configure.py3
-rw-r--r--src/bootstrap/src/core/build_steps/dist.rs43
-rw-r--r--src/bootstrap/src/core/build_steps/run.rs22
-rw-r--r--src/bootstrap/src/core/build_steps/tool.rs1
-rw-r--r--src/bootstrap/src/core/builder/cargo.rs1220
-rw-r--r--src/bootstrap/src/core/builder/mod.rs (renamed from src/bootstrap/src/core/builder.rs)1219
-rw-r--r--src/bootstrap/src/core/builder/tests.rs2
-rw-r--r--src/bootstrap/src/core/config/config.rs5
-rw-r--r--src/bootstrap/src/core/config/flags.rs3
-rw-r--r--src/bootstrap/src/core/config/tests.rs58
-rw-r--r--src/bootstrap/src/utils/change_tracker.rs5
-rw-r--r--src/ci/docker/host-x86_64/dist-various-2/Dockerfile6
-rw-r--r--src/ci/docker/host-x86_64/dist-x86_64-freebsd/Dockerfile8
-rwxr-xr-xsrc/ci/docker/host-x86_64/x86_64-gnu-tools/checktools.sh2
-rwxr-xr-xsrc/ci/docker/scripts/freebsd-toolchain.sh8
-rw-r--r--src/ci/github-actions/jobs.yml31
-rw-r--r--src/doc/rustc/src/platform-support.md18
-rw-r--r--src/doc/rustc/src/platform-support/arm-none-eabi.md15
-rw-r--r--src/doc/rustc/src/platform-support/thumbv7em-none-eabi.md15
-rw-r--r--src/doc/rustc/src/platform-support/thumbv8m.main-none-eabi.md37
-rw-r--r--src/doc/unstable-book/src/language-features/result-ffi-guarantees.md14
-rw-r--r--src/doc/unstable-book/src/language-features/strict-provenance-lints.md (renamed from src/doc/unstable-book/src/language-features/strict-provenance.md)7
-rw-r--r--src/etc/installer/msi/rust.wxs41
-rw-r--r--src/librustdoc/Cargo.toml1
-rw-r--r--src/librustdoc/clean/mod.rs6
-rw-r--r--src/librustdoc/html/markdown.rs84
-rw-r--r--src/librustdoc/html/markdown/footnotes.rs113
-rw-r--r--src/librustdoc/html/static/css/rustdoc.css18
-rw-r--r--src/librustdoc/html/static_files.rs11
-rw-r--r--src/librustdoc/json/conversions.rs4
-rw-r--r--src/librustdoc/passes/collect_intra_doc_links.rs30
-rw-r--r--src/rustdoc-json-types/Cargo.toml5
-rw-r--r--src/rustdoc-json-types/lib.rs50
m---------src/tools/cargo0
-rw-r--r--src/tools/clippy/.github/deploy.sh2
-rw-r--r--src/tools/clippy/.gitignore1
-rw-r--r--src/tools/clippy/CHANGELOG.md42
-rw-r--r--src/tools/clippy/Cargo.toml8
-rw-r--r--src/tools/clippy/book/src/lint_configuration.md2
-rw-r--r--src/tools/clippy/clippy_config/Cargo.toml2
-rw-r--r--src/tools/clippy/clippy_config/src/conf.rs26
-rw-r--r--src/tools/clippy/clippy_config/src/lib.rs2
-rw-r--r--src/tools/clippy/clippy_config/src/msrvs.rs3
-rw-r--r--src/tools/clippy/clippy_dev/Cargo.toml2
-rw-r--r--src/tools/clippy/clippy_dev/src/new_lint.rs24
-rw-r--r--src/tools/clippy/clippy_dev/src/serve.rs4
-rw-r--r--src/tools/clippy/clippy_lints/Cargo.toml3
-rw-r--r--src/tools/clippy/clippy_lints/src/box_default.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/byte_char_slices.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/casts/cast_abs_to_unsigned.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/casts/cast_ptr_alignment.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/create_dir.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/declare_clippy_lint.rs162
-rw-r--r--src/tools/clippy/clippy_lints/src/declared_lints.rs3
-rw-r--r--src/tools/clippy/clippy_lints/src/default.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/doc/mod.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/exit.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/explicit_write.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/floating_point_arithmetic.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/format_impl.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/if_let_mutex.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/implicit_saturating_sub.rs25
-rw-r--r--src/tools/clippy/clippy_lints/src/implied_bounds_in_impls.rs13
-rw-r--r--src/tools/clippy/clippy_lints/src/indexing_slicing.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/item_name_repetitions.rs35
-rw-r--r--src/tools/clippy/clippy_lints/src/large_futures.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/large_stack_arrays.rs19
-rw-r--r--src/tools/clippy/clippy_lints/src/len_zero.rs18
-rw-r--r--src/tools/clippy/clippy_lints/src/lib.rs17
-rw-r--r--src/tools/clippy/clippy_lints/src/lifetimes.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/literal_representation.rs1
-rw-r--r--src/tools/clippy/clippy_lints/src/loops/explicit_iter_loop.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/loops/infinite_loop.rs19
-rw-r--r--src/tools/clippy/clippy_lints/src/loops/manual_memcpy.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/loops/manual_while_let_some.rs11
-rw-r--r--src/tools/clippy/clippy_lints/src/loops/needless_range_loop.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/loops/same_item_push.rs6
-rw-r--r--src/tools/clippy/clippy_lints/src/macro_use.rs1
-rw-r--r--src/tools/clippy/clippy_lints/src/main_recursion.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/manual_bits.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/manual_float_methods.rs22
-rw-r--r--src/tools/clippy/clippy_lints/src/manual_ignore_case_cmp.rs127
-rw-r--r--src/tools/clippy/clippy_lints/src/manual_is_power_of_two.rs10
-rw-r--r--src/tools/clippy/clippy_lints/src/manual_slice_size_calculation.rs18
-rw-r--r--src/tools/clippy/clippy_lints/src/manual_string_new.rs19
-rw-r--r--src/tools/clippy/clippy_lints/src/matches/redundant_pattern_match.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/matches/single_match.rs8
-rw-r--r--src/tools/clippy/clippy_lints/src/matches/try_err.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/clone_on_copy.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/expect_fun_call.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/iter_filter.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/iter_out_of_bounds.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/manual_c_str_literals.rs5
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/manual_saturating_arithmetic.rs3
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/map_clone.rs3
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/mod.rs3
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/needless_collect.rs5
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/or_fun_call.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/read_line_without_trim.rs3
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/readonly_write_lock.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/seek_from_current.rs5
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/seek_to_start_instead_of_rewind.rs5
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/single_char_insert_string.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/single_char_push_string.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/str_splitn.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/uninit_assumed_init.rs3
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/unnecessary_first_then_check.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/useless_asref.rs5
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/utils.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/missing_asserts_for_indexing.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/missing_doc.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/mut_key.rs13
-rw-r--r--src/tools/clippy/clippy_lints/src/mut_reference.rs6
-rw-r--r--src/tools/clippy/clippy_lints/src/needless_maybe_sized.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/non_canonical_impls.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/non_zero_suggestions.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/operators/float_cmp.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/pathbuf_init_then_push.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/ptr.rs8
-rw-r--r--src/tools/clippy/clippy_lints/src/ptr_offset_with_cast.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/question_mark.rs7
-rw-r--r--src/tools/clippy/clippy_lints/src/raw_strings.rs192
-rw-r--r--src/tools/clippy/clippy_lints/src/rc_clone_in_vec_init.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/regex.rs65
-rw-r--r--src/tools/clippy/clippy_lints/src/returns.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/significant_drop_tightening.rs3
-rw-r--r--src/tools/clippy/clippy_lints/src/size_of_in_element_count.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/slow_vector_initialization.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/strings.rs11
-rw-r--r--src/tools/clippy/clippy_lints/src/to_digit_is_some.rs5
-rw-r--r--src/tools/clippy/clippy_lints/src/trailing_empty_array.rs16
-rw-r--r--src/tools/clippy/clippy_lints/src/trait_bounds.rs96
-rw-r--r--src/tools/clippy/clippy_lints/src/tuple_array_conversions.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/unit_types/unit_arg.rs9
-rw-r--r--src/tools/clippy/clippy_lints/src/unnecessary_literal_bound.rs158
-rw-r--r--src/tools/clippy/clippy_lints/src/unnecessary_map_on_constructor.rs10
-rw-r--r--src/tools/clippy/clippy_lints/src/unnecessary_owned_empty_strings.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/unused_io_amount.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/unused_result_ok.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/unwrap.rs9
-rw-r--r--src/tools/clippy/clippy_lints/src/useless_conversion.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/utils/internal_lints/collapsible_calls.rs31
-rw-r--r--src/tools/clippy/clippy_lints/src/utils/internal_lints/lint_without_lint_pass.rs8
-rw-r--r--src/tools/clippy/clippy_lints/src/vec.rs3
-rw-r--r--src/tools/clippy/clippy_utils/Cargo.toml2
-rw-r--r--src/tools/clippy/clippy_utils/src/consts.rs9
-rw-r--r--src/tools/clippy/clippy_utils/src/hir_utils.rs119
-rw-r--r--src/tools/clippy/clippy_utils/src/lib.rs2
-rw-r--r--src/tools/clippy/clippy_utils/src/ty.rs65
-rw-r--r--src/tools/clippy/declare_clippy_lint/Cargo.toml13
-rw-r--r--src/tools/clippy/declare_clippy_lint/src/lib.rs182
-rw-r--r--src/tools/clippy/lintcheck/ci_crates.toml18
-rw-r--r--src/tools/clippy/lintcheck/src/json.rs2
-rw-r--r--src/tools/clippy/rinja.toml3
-rw-r--r--src/tools/clippy/rust-toolchain2
-rw-r--r--src/tools/clippy/rustc_tools_util/CHANGELOG.md9
-rw-r--r--src/tools/clippy/rustc_tools_util/Cargo.toml2
-rw-r--r--src/tools/clippy/rustc_tools_util/README.md4
-rw-r--r--src/tools/clippy/rustc_tools_util/src/lib.rs75
-rw-r--r--src/tools/clippy/tests/compile-test.rs41
-rw-r--r--src/tools/clippy/tests/config-metadata.rs4
-rw-r--r--src/tools/clippy/tests/ui-toml/array_size_threshold/array_size_threshold.stderr14
-rw-r--r--src/tools/clippy/tests/ui-toml/item_name_repetitions/allowed_prefixes/item_name_repetitions.rs2
-rw-r--r--src/tools/clippy/tests/ui-toml/item_name_repetitions/allowed_prefixes_extend/item_name_repetitions.rs2
-rw-r--r--src/tools/clippy/tests/ui/expect_fun_call.stderr30
-rw-r--r--src/tools/clippy/tests/ui/implicit_saturating_sub.fixed6
-rw-r--r--src/tools/clippy/tests/ui/implicit_saturating_sub.rs10
-rw-r--r--src/tools/clippy/tests/ui/implicit_saturating_sub.stderr13
-rw-r--r--src/tools/clippy/tests/ui/infinite_loops.rs38
-rw-r--r--src/tools/clippy/tests/ui/infinite_loops.stderr64
-rw-r--r--src/tools/clippy/tests/ui/invalid_null_ptr_usage.fixed44
-rw-r--r--src/tools/clippy/tests/ui/invalid_null_ptr_usage.stderr44
-rw-r--r--src/tools/clippy/tests/ui/invalid_null_ptr_usage_no_std.fixed57
-rw-r--r--src/tools/clippy/tests/ui/invalid_null_ptr_usage_no_std.rs57
-rw-r--r--src/tools/clippy/tests/ui/invalid_null_ptr_usage_no_std.stderr136
-rw-r--r--src/tools/clippy/tests/ui/large_const_arrays.fixed18
-rw-r--r--src/tools/clippy/tests/ui/large_const_arrays.rs18
-rw-r--r--src/tools/clippy/tests/ui/large_stack_arrays.rs44
-rw-r--r--src/tools/clippy/tests/ui/large_stack_arrays.stderr48
-rw-r--r--src/tools/clippy/tests/ui/manual_c_str_literals.edition2021.fixed (renamed from src/tools/clippy/tests/ui/manual_c_str_literals.fixed)3
-rw-r--r--src/tools/clippy/tests/ui/manual_c_str_literals.edition2021.stderr (renamed from src/tools/clippy/tests/ui/manual_c_str_literals.stderr)26
-rw-r--r--src/tools/clippy/tests/ui/manual_c_str_literals.rs3
-rw-r--r--src/tools/clippy/tests/ui/manual_float_methods.rs5
-rw-r--r--src/tools/clippy/tests/ui/manual_float_methods.stderr8
-rw-r--r--src/tools/clippy/tests/ui/manual_ignore_case_cmp.fixed107
-rw-r--r--src/tools/clippy/tests/ui/manual_ignore_case_cmp.rs107
-rw-r--r--src/tools/clippy/tests/ui/manual_ignore_case_cmp.stderr546
-rw-r--r--src/tools/clippy/tests/ui/manual_slice_size_calculation.fixed4
-rw-r--r--src/tools/clippy/tests/ui/manual_slice_size_calculation.rs4
-rw-r--r--src/tools/clippy/tests/ui/manual_slice_size_calculation.stderr26
-rw-r--r--src/tools/clippy/tests/ui/missing_const_for_fn/could_be_const.fixed8
-rw-r--r--src/tools/clippy/tests/ui/missing_const_for_fn/could_be_const.rs8
-rw-r--r--src/tools/clippy/tests/ui/missing_const_for_fn/could_be_const.stderr12
-rw-r--r--src/tools/clippy/tests/ui/module_name_repetitions.rs18
-rw-r--r--src/tools/clippy/tests/ui/module_name_repetitions.stderr8
-rw-r--r--src/tools/clippy/tests/ui/mut_key.stderr60
-rw-r--r--src/tools/clippy/tests/ui/needless_raw_string.fixed9
-rw-r--r--src/tools/clippy/tests/ui/needless_raw_string.rs9
-rw-r--r--src/tools/clippy/tests/ui/needless_raw_string.stderr38
-rw-r--r--src/tools/clippy/tests/ui/needless_raw_string_hashes.fixed10
-rw-r--r--src/tools/clippy/tests/ui/needless_raw_string_hashes.rs10
-rw-r--r--src/tools/clippy/tests/ui/needless_raw_string_hashes.stderr38
-rw-r--r--src/tools/clippy/tests/ui/or_fun_call.fixed10
-rw-r--r--src/tools/clippy/tests/ui/or_fun_call.rs10
-rw-r--r--src/tools/clippy/tests/ui/or_fun_call.stderr40
-rw-r--r--src/tools/clippy/tests/ui/regex.rs30
-rw-r--r--src/tools/clippy/tests/ui/regex.stderr52
-rw-r--r--src/tools/clippy/tests/ui/trait_duplication_in_bounds.fixed31
-rw-r--r--src/tools/clippy/tests/ui/trait_duplication_in_bounds.rs31
-rw-r--r--src/tools/clippy/tests/ui/trait_duplication_in_bounds.stderr32
-rw-r--r--src/tools/clippy/tests/ui/transmutes_expressible_as_ptr_casts.fixed5
-rw-r--r--src/tools/clippy/tests/ui/transmutes_expressible_as_ptr_casts.rs5
-rw-r--r--src/tools/clippy/tests/ui/unnecessary_literal_bound.fixed65
-rw-r--r--src/tools/clippy/tests/ui/unnecessary_literal_bound.rs65
-rw-r--r--src/tools/clippy/tests/ui/unnecessary_literal_bound.stderr23
-rw-r--r--src/tools/clippy/tests/ui/unwrap_or.fixed4
-rw-r--r--src/tools/clippy/tests/ui/unwrap_or.rs4
-rw-r--r--src/tools/clippy/tests/ui/unwrap_or.stderr4
-rw-r--r--src/tools/clippy/tests/versioncheck.rs1
-rw-r--r--src/tools/clippy/triagebot.toml1
-rw-r--r--src/tools/clippy/util/gh-pages/index.html330
-rw-r--r--src/tools/clippy/util/gh-pages/index_template.html232
-rw-r--r--src/tools/clippy/util/gh-pages/script.js1050
-rw-r--r--src/tools/clippy/util/gh-pages/style.css51
-rw-r--r--src/tools/clippy/util/gh-pages/theme.js56
-rw-r--r--src/tools/compiletest/src/common.rs4
-rw-r--r--src/tools/compiletest/src/lib.rs202
-rw-r--r--src/tools/compiletest/src/main.rs4
-rw-r--r--src/tools/compiletest/src/runtest.rs24
-rw-r--r--src/tools/jsondoclint/Cargo.toml2
-rw-r--r--src/tools/linkchecker/main.rs11
-rw-r--r--src/tools/miri/src/borrow_tracker/stacked_borrows/diagnostics.rs10
-rw-r--r--src/tools/miri/src/borrow_tracker/tree_borrows/diagnostics.rs2
-rw-r--r--src/tools/miri/src/borrow_tracker/tree_borrows/tree.rs4
-rw-r--r--src/tools/miri/src/diagnostics.rs2
-rw-r--r--src/tools/miri/src/intrinsics/simd.rs19
-rw-r--r--src/tools/miri/src/lib.rs2
-rw-r--r--src/tools/miri/src/shims/foreign_items.rs3
-rw-r--r--src/tools/miri/tests/fail/dangling_pointers/dangling_pointer_to_raw_pointer.rs1
-rw-r--r--src/tools/miri/tests/fail/dangling_pointers/deref_dangling_box.rs1
-rw-r--r--src/tools/miri/tests/fail/dangling_pointers/deref_dangling_ref.rs1
-rw-r--r--src/tools/miri/tests/fail/intrinsics/ptr_offset_from_different_ints.rs1
-rw-r--r--src/tools/miri/tests/fail/provenance/int_copy_looses_provenance3.rs1
-rw-r--r--src/tools/miri/tests/fail/provenance/provenance_transmute.rs1
-rw-r--r--src/tools/miri/tests/fail/provenance/ptr_int_unexposed.rs1
-rw-r--r--src/tools/miri/tests/fail/provenance/ptr_invalid.rs1
-rw-r--r--src/tools/miri/tests/fail/provenance/ptr_invalid_offset.rs1
-rw-r--r--src/tools/miri/tests/fail/provenance/strict_provenance_cast.rs1
-rw-r--r--src/tools/miri/tests/fail/stacked_borrows/exposed_only_ro.rs1
-rw-r--r--src/tools/miri/tests/fail/unaligned_pointers/promise_alignment.rs1
-rw-r--r--src/tools/miri/tests/fail/uninit/uninit_alloc_diagnostic_with_provenance.rs1
-rw-r--r--src/tools/miri/tests/pass-dep/libc/libc-affinity.rs1
-rw-r--r--src/tools/miri/tests/pass-dep/libc/libc-epoll-no-blocking.rs1
-rw-r--r--src/tools/miri/tests/pass-dep/libc/libc-mem.rs2
-rw-r--r--src/tools/miri/tests/pass-dep/libc/libc-misc.rs1
-rw-r--r--src/tools/miri/tests/pass-dep/libc/mmap.rs1
-rw-r--r--src/tools/miri/tests/pass/align_offset_symbolic.rs1
-rw-r--r--src/tools/miri/tests/pass/atomic.rs2
-rw-r--r--src/tools/miri/tests/pass/box-custom-alloc-aliasing.rs1
-rw-r--r--src/tools/miri/tests/pass/concurrency/address_reuse_happens_before.rs1
-rw-r--r--src/tools/miri/tests/pass/const-addrs.rs1
-rw-r--r--src/tools/miri/tests/pass/drop_type_without_drop_glue.rs2
-rw-r--r--src/tools/miri/tests/pass/dyn-upcast.rs51
-rw-r--r--src/tools/miri/tests/pass/dyn-upcast.stdout4
-rw-r--r--src/tools/miri/tests/pass/extern_types.rs2
-rw-r--r--src/tools/miri/tests/pass/provenance.rs1
-rw-r--r--src/tools/miri/tests/pass/ptr_int_from_exposed.rs1
-rw-r--r--src/tools/miri/tests/pass/ptr_raw.rs1
-rw-r--r--src/tools/miri/tests/pass/shims/ptr_mask.rs1
-rw-r--r--src/tools/miri/tests/pass/slices.rs1
-rw-r--r--src/tools/miri/tests/pass/stacked-borrows/int-to-ptr.rs1
-rw-r--r--src/tools/miri/tests/pass/stacked-borrows/stack-printing.rs1
-rw-r--r--src/tools/miri/tests/pass/stacked-borrows/unknown-bottom-gc.rs1
-rw-r--r--src/tools/miri/tests/pass/transmute_ptr.rs1
-rw-r--r--src/tools/miri/tests/pass/underscore_pattern.rs1
-rw-r--r--src/tools/miri/tests/pass/zero-sized-accesses-and-offsets.rs1
-rw-r--r--src/tools/miropt-test-tools/src/lib.rs3
-rw-r--r--src/tools/nix-dev-shell/envrc-flake8
-rw-r--r--src/tools/nix-dev-shell/envrc-shell7
-rw-r--r--src/tools/nix-dev-shell/flake.nix33
-rw-r--r--src/tools/nix-dev-shell/shell.nix19
-rw-r--r--src/tools/nix-dev-shell/x/default.nix22
-rw-r--r--src/tools/nix-dev-shell/x/x.rs50
-rw-r--r--src/tools/rust-analyzer/Cargo.lock26
-rw-r--r--src/tools/rust-analyzer/Cargo.toml10
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/layout.rs27
-rw-r--r--src/tools/rust-analyzer/rust-version2
-rw-r--r--src/tools/tidy/Cargo.toml2
-rw-r--r--src/tools/tidy/src/issues.txt1
-rw-r--r--src/tools/unicode-table-generator/Cargo.toml2
-rw-r--r--src/tools/unicode-table-generator/src/range_search.rs6
291 files changed, 6125 insertions, 3451 deletions
diff --git a/src/bootstrap/configure.py b/src/bootstrap/configure.py
index a555a26367d..15137fbb2b5 100755
--- a/src/bootstrap/configure.py
+++ b/src/bootstrap/configure.py
@@ -193,7 +193,8 @@ if '--help' in sys.argv or '-h' in sys.argv:
         if option.value:
             print('\t{:30} {}'.format('--{}=VAL'.format(option.name), option.desc))
         else:
-            print('\t{:30} {}'.format('--enable-{}  OR  --disable-{}'.format(option.name, option.name), option.desc))
+            print('\t--enable-{:25} OR --disable-{}'.format(option.name, option.name))
+            print('\t\t' + option.desc)
     print('')
     print('This configure script is a thin configuration shim over the true')
     print('configuration system, `config.toml`. You can explore the comments')
diff --git a/src/bootstrap/src/core/build_steps/dist.rs b/src/bootstrap/src/core/build_steps/dist.rs
index 90e6a10d9d6..80ba9f44448 100644
--- a/src/bootstrap/src/core/build_steps/dist.rs
+++ b/src/bootstrap/src/core/build_steps/dist.rs
@@ -1591,9 +1591,15 @@ impl Step for Extended {
             prepare("cargo");
             prepare("rust-std");
             prepare("rust-analysis");
-            prepare("clippy");
-            prepare("rust-analyzer");
-            for tool in &["rust-docs", "miri", "rustc-codegen-cranelift"] {
+
+            for tool in &[
+                "clippy",
+                "rustfmt",
+                "rust-analyzer",
+                "rust-docs",
+                "miri",
+                "rustc-codegen-cranelift",
+            ] {
                 if built_tools.contains(tool) {
                     prepare(tool);
                 }
@@ -1633,6 +1639,8 @@ impl Step for Extended {
                     "rust-analyzer-preview".to_string()
                 } else if name == "clippy" {
                     "clippy-preview".to_string()
+                } else if name == "rustfmt" {
+                    "rustfmt-preview".to_string()
                 } else if name == "miri" {
                     "miri-preview".to_string()
                 } else if name == "rustc-codegen-cranelift" {
@@ -1652,7 +1660,7 @@ impl Step for Extended {
             prepare("cargo");
             prepare("rust-analysis");
             prepare("rust-std");
-            for tool in &["clippy", "rust-analyzer", "rust-docs", "miri"] {
+            for tool in &["clippy", "rustfmt", "rust-analyzer", "rust-docs", "miri"] {
                 if built_tools.contains(tool) {
                     prepare(tool);
                 }
@@ -1770,6 +1778,24 @@ impl Step for Extended {
                     .arg(etc.join("msi/remove-duplicates.xsl"))
                     .run(builder);
             }
+            if built_tools.contains("rustfmt") {
+                command(&heat)
+                    .current_dir(&exe)
+                    .arg("dir")
+                    .arg("rustfmt")
+                    .args(heat_flags)
+                    .arg("-cg")
+                    .arg("RustFmtGroup")
+                    .arg("-dr")
+                    .arg("RustFmt")
+                    .arg("-var")
+                    .arg("var.RustFmtDir")
+                    .arg("-out")
+                    .arg(exe.join("RustFmtGroup.wxs"))
+                    .arg("-t")
+                    .arg(etc.join("msi/remove-duplicates.xsl"))
+                    .run(builder);
+            }
             if built_tools.contains("miri") {
                 command(&heat)
                     .current_dir(&exe)
@@ -1841,6 +1867,9 @@ impl Step for Extended {
                 if built_tools.contains("clippy") {
                     cmd.arg("-dClippyDir=clippy");
                 }
+                if built_tools.contains("rustfmt") {
+                    cmd.arg("-dRustFmtDir=rustfmt");
+                }
                 if built_tools.contains("rust-docs") {
                     cmd.arg("-dDocsDir=rust-docs");
                 }
@@ -1867,6 +1896,9 @@ impl Step for Extended {
             if built_tools.contains("clippy") {
                 candle("ClippyGroup.wxs".as_ref());
             }
+            if built_tools.contains("rustfmt") {
+                candle("RustFmtGroup.wxs".as_ref());
+            }
             if built_tools.contains("miri") {
                 candle("MiriGroup.wxs".as_ref());
             }
@@ -1905,6 +1937,9 @@ impl Step for Extended {
             if built_tools.contains("clippy") {
                 cmd.arg("ClippyGroup.wixobj");
             }
+            if built_tools.contains("rustfmt") {
+                cmd.arg("RustFmtGroup.wixobj");
+            }
             if built_tools.contains("miri") {
                 cmd.arg("MiriGroup.wixobj");
             }
diff --git a/src/bootstrap/src/core/build_steps/run.rs b/src/bootstrap/src/core/build_steps/run.rs
index c7bcd76cadd..a6dff7fde80 100644
--- a/src/bootstrap/src/core/build_steps/run.rs
+++ b/src/bootstrap/src/core/build_steps/run.rs
@@ -283,3 +283,25 @@ impl Step for GenerateCompletions {
         run.builder.ensure(GenerateCompletions);
     }
 }
+
+#[derive(Debug, PartialOrd, Ord, Clone, Hash, PartialEq, Eq)]
+pub struct UnicodeTableGenerator;
+
+impl Step for UnicodeTableGenerator {
+    type Output = ();
+    const ONLY_HOSTS: bool = true;
+
+    fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
+        run.path("src/tools/unicode-table-generator")
+    }
+
+    fn make_run(run: RunConfig<'_>) {
+        run.builder.ensure(UnicodeTableGenerator);
+    }
+
+    fn run(self, builder: &Builder<'_>) {
+        let mut cmd = builder.tool_cmd(Tool::UnicodeTableGenerator);
+        cmd.arg(builder.src.join("library/core/src/unicode/unicode_data.rs"));
+        cmd.run(builder);
+    }
+}
diff --git a/src/bootstrap/src/core/build_steps/tool.rs b/src/bootstrap/src/core/build_steps/tool.rs
index f1a10c3296e..f5afa6c4c6c 100644
--- a/src/bootstrap/src/core/build_steps/tool.rs
+++ b/src/bootstrap/src/core/build_steps/tool.rs
@@ -360,6 +360,7 @@ bootstrap_tool!(
     CoverageDump, "src/tools/coverage-dump", "coverage-dump";
     RustcPerfWrapper, "src/tools/rustc-perf-wrapper", "rustc-perf-wrapper";
     WasmComponentLd, "src/tools/wasm-component-ld", "wasm-component-ld", is_unstable_tool = true, allow_features = "min_specialization";
+    UnicodeTableGenerator, "src/tools/unicode-table-generator", "unicode-table-generator";
 );
 
 /// These are the submodules that are required for rustbook to work due to
diff --git a/src/bootstrap/src/core/builder/cargo.rs b/src/bootstrap/src/core/builder/cargo.rs
new file mode 100644
index 00000000000..c3d0994f164
--- /dev/null
+++ b/src/bootstrap/src/core/builder/cargo.rs
@@ -0,0 +1,1220 @@
+use std::env;
+use std::ffi::{OsStr, OsString};
+use std::path::{Path, PathBuf};
+
+use super::{Builder, Kind};
+use crate::core::build_steps::tool::SourceType;
+use crate::core::build_steps::{compile, test};
+use crate::core::config::SplitDebuginfo;
+use crate::core::config::flags::Color;
+use crate::utils::helpers::{
+    self, LldThreads, add_link_lib_path, check_cfg_arg, linker_args, linker_flags,
+};
+use crate::{
+    BootstrapCommand, CLang, Compiler, DocTests, DryRun, EXTRA_CHECK_CFGS, GitRepo, Mode,
+    TargetSelection, command, prepare_behaviour_dump_dir, t,
+};
+
+/// Represents flag values in `String` form with whitespace delimiter to pass it to the compiler
+/// later.
+///
+/// `-Z crate-attr` flags will be applied recursively on the target code using the
+/// `rustc_parse::parser::Parser`. See `rustc_builtin_macros::cmdline_attrs::inject` for more
+/// information.
+#[derive(Debug, Clone)]
+struct Rustflags(String, TargetSelection);
+
+impl Rustflags {
+    fn new(target: TargetSelection) -> Rustflags {
+        let mut ret = Rustflags(String::new(), target);
+        ret.propagate_cargo_env("RUSTFLAGS");
+        ret
+    }
+
+    /// By default, cargo will pick up on various variables in the environment. However, bootstrap
+    /// reuses those variables to pass additional flags to rustdoc, so by default they get
+    /// overridden. Explicitly add back any previous value in the environment.
+    ///
+    /// `prefix` is usually `RUSTFLAGS` or `RUSTDOCFLAGS`.
+    fn propagate_cargo_env(&mut self, prefix: &str) {
+        // Inherit `RUSTFLAGS` by default ...
+        self.env(prefix);
+
+        // ... and also handle target-specific env RUSTFLAGS if they're configured.
+        let target_specific = format!("CARGO_TARGET_{}_{}", crate::envify(&self.1.triple), prefix);
+        self.env(&target_specific);
+    }
+
+    fn env(&mut self, env: &str) {
+        if let Ok(s) = env::var(env) {
+            for part in s.split(' ') {
+                self.arg(part);
+            }
+        }
+    }
+
+    fn arg(&mut self, arg: &str) -> &mut Self {
+        assert_eq!(arg.split(' ').count(), 1);
+        if !self.0.is_empty() {
+            self.0.push(' ');
+        }
+        self.0.push_str(arg);
+        self
+    }
+}
+
+/// Flags that are passed to the `rustc` shim binary. These flags will only be applied when
+/// compiling host code, i.e. when `--target` is unset.
+#[derive(Debug, Default)]
+struct HostFlags {
+    rustc: Vec<String>,
+}
+
+impl HostFlags {
+    const SEPARATOR: &'static str = " ";
+
+    /// Adds a host rustc flag.
+    fn arg<S: Into<String>>(&mut self, flag: S) {
+        let value = flag.into().trim().to_string();
+        assert!(!value.contains(Self::SEPARATOR));
+        self.rustc.push(value);
+    }
+
+    /// Encodes all the flags into a single string.
+    fn encode(self) -> String {
+        self.rustc.join(Self::SEPARATOR)
+    }
+}
+
+#[derive(Debug)]
+pub struct Cargo {
+    command: BootstrapCommand,
+    compiler: Compiler,
+    target: TargetSelection,
+    rustflags: Rustflags,
+    rustdocflags: Rustflags,
+    hostflags: HostFlags,
+    allow_features: String,
+}
+
+impl Cargo {
+    /// Calls [`Builder::cargo`] and [`Cargo::configure_linker`] to prepare an invocation of `cargo`
+    /// to be run.
+    pub fn new(
+        builder: &Builder<'_>,
+        compiler: Compiler,
+        mode: Mode,
+        source_type: SourceType,
+        target: TargetSelection,
+        cmd_kind: Kind,
+    ) -> Cargo {
+        let mut cargo = builder.cargo(compiler, mode, source_type, target, cmd_kind);
+
+        match cmd_kind {
+            // No need to configure the target linker for these command types,
+            // as they don't invoke rustc at all.
+            Kind::Clean | Kind::Suggest | Kind::Format | Kind::Setup => {}
+            _ => {
+                cargo.configure_linker(builder);
+            }
+        }
+
+        cargo
+    }
+
+    pub fn into_cmd(self) -> BootstrapCommand {
+        self.into()
+    }
+
+    /// Same as [`Cargo::new`] except this one doesn't configure the linker with
+    /// [`Cargo::configure_linker`].
+    pub fn new_for_mir_opt_tests(
+        builder: &Builder<'_>,
+        compiler: Compiler,
+        mode: Mode,
+        source_type: SourceType,
+        target: TargetSelection,
+        cmd_kind: Kind,
+    ) -> Cargo {
+        builder.cargo(compiler, mode, source_type, target, cmd_kind)
+    }
+
+    pub fn rustdocflag(&mut self, arg: &str) -> &mut Cargo {
+        self.rustdocflags.arg(arg);
+        self
+    }
+
+    pub fn rustflag(&mut self, arg: &str) -> &mut Cargo {
+        self.rustflags.arg(arg);
+        self
+    }
+
+    pub fn arg(&mut self, arg: impl AsRef<OsStr>) -> &mut Cargo {
+        self.command.arg(arg.as_ref());
+        self
+    }
+
+    pub fn args<I, S>(&mut self, args: I) -> &mut Cargo
+    where
+        I: IntoIterator<Item = S>,
+        S: AsRef<OsStr>,
+    {
+        for arg in args {
+            self.arg(arg.as_ref());
+        }
+        self
+    }
+
+    /// Add an env var to the cargo command instance. Note that `RUSTFLAGS`/`RUSTDOCFLAGS` must go
+    /// through [`Cargo::rustdocflags`] and [`Cargo::rustflags`] because inconsistent `RUSTFLAGS`
+    /// and `RUSTDOCFLAGS` usages will trigger spurious rebuilds.
+    pub fn env(&mut self, key: impl AsRef<OsStr>, value: impl AsRef<OsStr>) -> &mut Cargo {
+        assert_ne!(key.as_ref(), "RUSTFLAGS");
+        assert_ne!(key.as_ref(), "RUSTDOCFLAGS");
+        self.command.env(key.as_ref(), value.as_ref());
+        self
+    }
+
+    pub fn add_rustc_lib_path(&mut self, builder: &Builder<'_>) {
+        builder.add_rustc_lib_path(self.compiler, &mut self.command);
+    }
+
+    pub fn current_dir(&mut self, dir: &Path) -> &mut Cargo {
+        self.command.current_dir(dir);
+        self
+    }
+
+    /// Adds nightly-only features that this invocation is allowed to use.
+    ///
+    /// By default, all nightly features are allowed. Once this is called, it will be restricted to
+    /// the given set.
+    pub fn allow_features(&mut self, features: &str) -> &mut Cargo {
+        if !self.allow_features.is_empty() {
+            self.allow_features.push(',');
+        }
+        self.allow_features.push_str(features);
+        self
+    }
+
+    fn configure_linker(&mut self, builder: &Builder<'_>) -> &mut Cargo {
+        let target = self.target;
+        let compiler = self.compiler;
+
+        // Dealing with rpath here is a little special, so let's go into some
+        // detail. First off, `-rpath` is a linker option on Unix platforms
+        // which adds to the runtime dynamic loader path when looking for
+        // dynamic libraries. We use this by default on Unix platforms to ensure
+        // that our nightlies behave the same on Windows, that is they work out
+        // of the box. This can be disabled by setting `rpath = false` in `[rust]`
+        // table of `config.toml`
+        //
+        // Ok, so the astute might be wondering "why isn't `-C rpath` used
+        // here?" and that is indeed a good question to ask. This codegen
+        // option is the compiler's current interface to generating an rpath.
+        // Unfortunately it doesn't quite suffice for us. The flag currently
+        // takes no value as an argument, so the compiler calculates what it
+        // should pass to the linker as `-rpath`. This unfortunately is based on
+        // the **compile time** directory structure which when building with
+        // Cargo will be very different than the runtime directory structure.
+        //
+        // All that's a really long winded way of saying that if we use
+        // `-Crpath` then the executables generated have the wrong rpath of
+        // something like `$ORIGIN/deps` when in fact the way we distribute
+        // rustc requires the rpath to be `$ORIGIN/../lib`.
+        //
+        // So, all in all, to set up the correct rpath we pass the linker
+        // argument manually via `-C link-args=-Wl,-rpath,...`. Plus isn't it
+        // fun to pass a flag to a tool to pass a flag to pass a flag to a tool
+        // to change a flag in a binary?
+        if builder.config.rpath_enabled(target) && helpers::use_host_linker(target) {
+            let libdir = builder.sysroot_libdir_relative(compiler).to_str().unwrap();
+            let rpath = if target.contains("apple") {
+                // Note that we need to take one extra step on macOS to also pass
+                // `-Wl,-instal_name,@rpath/...` to get things to work right. To
+                // do that we pass a weird flag to the compiler to get it to do
+                // so. Note that this is definitely a hack, and we should likely
+                // flesh out rpath support more fully in the future.
+                self.rustflags.arg("-Zosx-rpath-install-name");
+                Some(format!("-Wl,-rpath,@loader_path/../{libdir}"))
+            } else if !target.is_windows() && !target.contains("aix") && !target.contains("xous") {
+                self.rustflags.arg("-Clink-args=-Wl,-z,origin");
+                Some(format!("-Wl,-rpath,$ORIGIN/../{libdir}"))
+            } else {
+                None
+            };
+            if let Some(rpath) = rpath {
+                self.rustflags.arg(&format!("-Clink-args={rpath}"));
+            }
+        }
+
+        for arg in linker_args(builder, compiler.host, LldThreads::Yes) {
+            self.hostflags.arg(&arg);
+        }
+
+        if let Some(target_linker) = builder.linker(target) {
+            let target = crate::envify(&target.triple);
+            self.command.env(format!("CARGO_TARGET_{target}_LINKER"), target_linker);
+        }
+        // We want to set -Clinker using Cargo, therefore we only call `linker_flags` and not
+        // `linker_args` here.
+        for flag in linker_flags(builder, target, LldThreads::Yes) {
+            self.rustflags.arg(&flag);
+        }
+        for arg in linker_args(builder, target, LldThreads::Yes) {
+            self.rustdocflags.arg(&arg);
+        }
+
+        if !builder.config.dry_run()
+            && builder.cc.borrow()[&target].args().iter().any(|arg| arg == "-gz")
+        {
+            self.rustflags.arg("-Clink-arg=-gz");
+        }
+
+        // Throughout the build Cargo can execute a number of build scripts
+        // compiling C/C++ code and we need to pass compilers, archivers, flags, etc
+        // obtained previously to those build scripts.
+        // Build scripts use either the `cc` crate or `configure/make` so we pass
+        // the options through environment variables that are fetched and understood by both.
+        //
+        // FIXME: the guard against msvc shouldn't need to be here
+        if target.is_msvc() {
+            if let Some(ref cl) = builder.config.llvm_clang_cl {
+                // FIXME: There is a bug in Clang 18 when building for ARM64:
+                // https://github.com/llvm/llvm-project/pull/81849. This is
+                // fixed in LLVM 19, but can't be backported.
+                if !target.starts_with("aarch64") && !target.starts_with("arm64ec") {
+                    self.command.env("CC", cl).env("CXX", cl);
+                }
+            }
+        } else {
+            let ccache = builder.config.ccache.as_ref();
+            let ccacheify = |s: &Path| {
+                let ccache = match ccache {
+                    Some(ref s) => s,
+                    None => return s.display().to_string(),
+                };
+                // FIXME: the cc-rs crate only recognizes the literal strings
+                // `ccache` and `sccache` when doing caching compilations, so we
+                // mirror that here. It should probably be fixed upstream to
+                // accept a new env var or otherwise work with custom ccache
+                // vars.
+                match &ccache[..] {
+                    "ccache" | "sccache" => format!("{} {}", ccache, s.display()),
+                    _ => s.display().to_string(),
+                }
+            };
+            let triple_underscored = target.triple.replace('-', "_");
+            let cc = ccacheify(&builder.cc(target));
+            self.command.env(format!("CC_{triple_underscored}"), &cc);
+
+            let cflags = builder.cflags(target, GitRepo::Rustc, CLang::C).join(" ");
+            self.command.env(format!("CFLAGS_{triple_underscored}"), &cflags);
+
+            if let Some(ar) = builder.ar(target) {
+                let ranlib = format!("{} s", ar.display());
+                self.command
+                    .env(format!("AR_{triple_underscored}"), ar)
+                    .env(format!("RANLIB_{triple_underscored}"), ranlib);
+            }
+
+            if let Ok(cxx) = builder.cxx(target) {
+                let cxx = ccacheify(&cxx);
+                let cxxflags = builder.cflags(target, GitRepo::Rustc, CLang::Cxx).join(" ");
+                self.command
+                    .env(format!("CXX_{triple_underscored}"), &cxx)
+                    .env(format!("CXXFLAGS_{triple_underscored}"), cxxflags);
+            }
+        }
+
+        self
+    }
+}
+
+impl From<Cargo> for BootstrapCommand {
+    fn from(mut cargo: Cargo) -> BootstrapCommand {
+        let rustflags = &cargo.rustflags.0;
+        if !rustflags.is_empty() {
+            cargo.command.env("RUSTFLAGS", rustflags);
+        }
+
+        let rustdocflags = &cargo.rustdocflags.0;
+        if !rustdocflags.is_empty() {
+            cargo.command.env("RUSTDOCFLAGS", rustdocflags);
+        }
+
+        let encoded_hostflags = cargo.hostflags.encode();
+        if !encoded_hostflags.is_empty() {
+            cargo.command.env("RUSTC_HOST_FLAGS", encoded_hostflags);
+        }
+
+        if !cargo.allow_features.is_empty() {
+            cargo.command.env("RUSTC_ALLOW_FEATURES", cargo.allow_features);
+        }
+        cargo.command
+    }
+}
+
+impl Builder<'_> {
+    /// Like [`Builder::cargo`], but only passes flags that are valid for all commands.
+    pub fn bare_cargo(
+        &self,
+        compiler: Compiler,
+        mode: Mode,
+        target: TargetSelection,
+        cmd_kind: Kind,
+    ) -> BootstrapCommand {
+        let mut cargo = match cmd_kind {
+            Kind::Clippy => {
+                let mut cargo = self.cargo_clippy_cmd(compiler);
+                cargo.arg(cmd_kind.as_str());
+                cargo
+            }
+            Kind::MiriSetup => {
+                let mut cargo = self.cargo_miri_cmd(compiler);
+                cargo.arg("miri").arg("setup");
+                cargo
+            }
+            Kind::MiriTest => {
+                let mut cargo = self.cargo_miri_cmd(compiler);
+                cargo.arg("miri").arg("test");
+                cargo
+            }
+            _ => {
+                let mut cargo = command(&self.initial_cargo);
+                cargo.arg(cmd_kind.as_str());
+                cargo
+            }
+        };
+
+        // Run cargo from the source root so it can find .cargo/config.
+        // This matters when using vendoring and the working directory is outside the repository.
+        cargo.current_dir(&self.src);
+
+        let out_dir = self.stage_out(compiler, mode);
+        cargo.env("CARGO_TARGET_DIR", &out_dir);
+
+        // Found with `rg "init_env_logger\("`. If anyone uses `init_env_logger`
+        // from out of tree it shouldn't matter, since x.py is only used for
+        // building in-tree.
+        let color_logs = ["RUSTDOC_LOG_COLOR", "RUSTC_LOG_COLOR", "RUST_LOG_COLOR"];
+        match self.build.config.color {
+            Color::Always => {
+                cargo.arg("--color=always");
+                for log in &color_logs {
+                    cargo.env(log, "always");
+                }
+            }
+            Color::Never => {
+                cargo.arg("--color=never");
+                for log in &color_logs {
+                    cargo.env(log, "never");
+                }
+            }
+            Color::Auto => {} // nothing to do
+        }
+
+        if cmd_kind != Kind::Install {
+            cargo.arg("--target").arg(target.rustc_target_arg());
+        } else {
+            assert_eq!(target, compiler.host);
+        }
+
+        if self.config.rust_optimize.is_release() &&
+        // cargo bench/install do not accept `--release` and miri doesn't want it
+        !matches!(cmd_kind, Kind::Bench | Kind::Install | Kind::Miri | Kind::MiriSetup | Kind::MiriTest)
+        {
+            cargo.arg("--release");
+        }
+
+        // Remove make-related flags to ensure Cargo can correctly set things up
+        cargo.env_remove("MAKEFLAGS");
+        cargo.env_remove("MFLAGS");
+
+        cargo
+    }
+
+    /// This will create a [`BootstrapCommand`] that represents a pending execution of cargo. This
+    /// cargo will be configured to use `compiler` as the actual rustc compiler, its output will be
+    /// scoped by `mode`'s output directory, it will pass the `--target` flag for the specified
+    /// `target`, and will be executing the Cargo command `cmd`. `cmd` can be `miri-cmd` for
+    /// commands to be run with Miri.
+    fn cargo(
+        &self,
+        compiler: Compiler,
+        mode: Mode,
+        source_type: SourceType,
+        target: TargetSelection,
+        cmd_kind: Kind,
+    ) -> Cargo {
+        let mut cargo = self.bare_cargo(compiler, mode, target, cmd_kind);
+        let out_dir = self.stage_out(compiler, mode);
+
+        let mut hostflags = HostFlags::default();
+
+        // Codegen backends are not yet tracked by -Zbinary-dep-depinfo,
+        // so we need to explicitly clear out if they've been updated.
+        for backend in self.codegen_backends(compiler) {
+            self.clear_if_dirty(&out_dir, &backend);
+        }
+
+        if cmd_kind == Kind::Doc {
+            let my_out = match mode {
+                // This is the intended out directory for compiler documentation.
+                Mode::Rustc | Mode::ToolRustc => self.compiler_doc_out(target),
+                Mode::Std => {
+                    if self.config.cmd.json() {
+                        out_dir.join(target).join("json-doc")
+                    } else {
+                        out_dir.join(target).join("doc")
+                    }
+                }
+                _ => panic!("doc mode {mode:?} not expected"),
+            };
+            let rustdoc = self.rustdoc(compiler);
+            self.clear_if_dirty(&my_out, &rustdoc);
+        }
+
+        let profile_var = |name: &str| {
+            let profile = if self.config.rust_optimize.is_release() { "RELEASE" } else { "DEV" };
+            format!("CARGO_PROFILE_{}_{}", profile, name)
+        };
+
+        // See comment in rustc_llvm/build.rs for why this is necessary, largely llvm-config
+        // needs to not accidentally link to libLLVM in stage0/lib.
+        cargo.env("REAL_LIBRARY_PATH_VAR", helpers::dylib_path_var());
+        if let Some(e) = env::var_os(helpers::dylib_path_var()) {
+            cargo.env("REAL_LIBRARY_PATH", e);
+        }
+
+        // Set a flag for `check`/`clippy`/`fix`, so that certain build
+        // scripts can do less work (i.e. not building/requiring LLVM).
+        if matches!(cmd_kind, Kind::Check | Kind::Clippy | Kind::Fix) {
+            // If we've not yet built LLVM, or it's stale, then bust
+            // the rustc_llvm cache. That will always work, even though it
+            // may mean that on the next non-check build we'll need to rebuild
+            // rustc_llvm. But if LLVM is stale, that'll be a tiny amount
+            // of work comparatively, and we'd likely need to rebuild it anyway,
+            // so that's okay.
+            if crate::core::build_steps::llvm::prebuilt_llvm_config(self, target, false)
+                .should_build()
+            {
+                cargo.env("RUST_CHECK", "1");
+            }
+        }
+
+        let stage = if compiler.stage == 0 && self.local_rebuild {
+            // Assume the local-rebuild rustc already has stage1 features.
+            1
+        } else {
+            compiler.stage
+        };
+
+        // We synthetically interpret a stage0 compiler used to build tools as a
+        // "raw" compiler in that it's the exact snapshot we download. Normally
+        // the stage0 build means it uses libraries build by the stage0
+        // compiler, but for tools we just use the precompiled libraries that
+        // we've downloaded
+        let use_snapshot = mode == Mode::ToolBootstrap;
+        assert!(!use_snapshot || stage == 0 || self.local_rebuild);
+
+        let maybe_sysroot = self.sysroot(compiler);
+        let sysroot = if use_snapshot { self.rustc_snapshot_sysroot() } else { &maybe_sysroot };
+        let libdir = self.rustc_libdir(compiler);
+
+        let sysroot_str = sysroot.as_os_str().to_str().expect("sysroot should be UTF-8");
+        if self.is_verbose() && !matches!(self.config.dry_run, DryRun::SelfCheck) {
+            println!("using sysroot {sysroot_str}");
+        }
+
+        let mut rustflags = Rustflags::new(target);
+        if stage != 0 {
+            if let Ok(s) = env::var("CARGOFLAGS_NOT_BOOTSTRAP") {
+                cargo.args(s.split_whitespace());
+            }
+            rustflags.env("RUSTFLAGS_NOT_BOOTSTRAP");
+        } else {
+            if let Ok(s) = env::var("CARGOFLAGS_BOOTSTRAP") {
+                cargo.args(s.split_whitespace());
+            }
+            rustflags.env("RUSTFLAGS_BOOTSTRAP");
+            rustflags.arg("--cfg=bootstrap");
+        }
+
+        if cmd_kind == Kind::Clippy {
+            // clippy overwrites sysroot if we pass it to cargo.
+            // Pass it directly to clippy instead.
+            // NOTE: this can't be fixed in clippy because we explicitly don't set `RUSTC`,
+            // so it has no way of knowing the sysroot.
+            rustflags.arg("--sysroot");
+            rustflags.arg(sysroot_str);
+        }
+
+        let use_new_symbol_mangling = match self.config.rust_new_symbol_mangling {
+            Some(setting) => {
+                // If an explicit setting is given, use that
+                setting
+            }
+            None => {
+                if mode == Mode::Std {
+                    // The standard library defaults to the legacy scheme
+                    false
+                } else {
+                    // The compiler and tools default to the new scheme
+                    true
+                }
+            }
+        };
+
+        // By default, windows-rs depends on a native library that doesn't get copied into the
+        // sysroot. Passing this cfg enables raw-dylib support instead, which makes the native
+        // library unnecessary. This can be removed when windows-rs enables raw-dylib
+        // unconditionally.
+        if let Mode::Rustc | Mode::ToolRustc = mode {
+            rustflags.arg("--cfg=windows_raw_dylib");
+        }
+
+        if use_new_symbol_mangling {
+            rustflags.arg("-Csymbol-mangling-version=v0");
+        } else {
+            rustflags.arg("-Csymbol-mangling-version=legacy");
+        }
+
+        // FIXME: the following components don't build with `-Zrandomize-layout` yet:
+        // - wasm-component-ld, due to the `wast`crate
+        // - rust-analyzer, due to the rowan crate
+        // so we exclude entire categories of steps here due to lack of fine-grained control over
+        // rustflags.
+        if self.config.rust_randomize_layout && mode != Mode::ToolStd && mode != Mode::ToolRustc {
+            rustflags.arg("-Zrandomize-layout");
+        }
+
+        // Enable compile-time checking of `cfg` names, values and Cargo `features`.
+        //
+        // Note: `std`, `alloc` and `core` imports some dependencies by #[path] (like
+        // backtrace, core_simd, std_float, ...), those dependencies have their own
+        // features but cargo isn't involved in the #[path] process and so cannot pass the
+        // complete list of features, so for that reason we don't enable checking of
+        // features for std crates.
+        if mode == Mode::Std {
+            rustflags.arg("--check-cfg=cfg(feature,values(any()))");
+        }
+
+        // Add extra cfg not defined in/by rustc
+        //
+        // Note: Although it would seems that "-Zunstable-options" to `rustflags` is useless as
+        // cargo would implicitly add it, it was discover that sometimes bootstrap only use
+        // `rustflags` without `cargo` making it required.
+        rustflags.arg("-Zunstable-options");
+        for (restricted_mode, name, values) in EXTRA_CHECK_CFGS {
+            if restricted_mode.is_none() || *restricted_mode == Some(mode) {
+                rustflags.arg(&check_cfg_arg(name, *values));
+            }
+        }
+
+        // FIXME(rust-lang/cargo#5754) we shouldn't be using special command arguments
+        // to the host invocation here, but rather Cargo should know what flags to pass rustc
+        // itself.
+        if stage == 0 {
+            hostflags.arg("--cfg=bootstrap");
+        }
+        // Cargo doesn't pass RUSTFLAGS to proc_macros:
+        // https://github.com/rust-lang/cargo/issues/4423
+        // Thus, if we are on stage 0, we explicitly set `--cfg=bootstrap`.
+        // We also declare that the flag is expected, which we need to do to not
+        // get warnings about it being unexpected.
+        hostflags.arg("-Zunstable-options");
+        hostflags.arg("--check-cfg=cfg(bootstrap)");
+
+        // FIXME: It might be better to use the same value for both `RUSTFLAGS` and `RUSTDOCFLAGS`,
+        // but this breaks CI. At the very least, stage0 `rustdoc` needs `--cfg bootstrap`. See
+        // #71458.
+        let mut rustdocflags = rustflags.clone();
+        rustdocflags.propagate_cargo_env("RUSTDOCFLAGS");
+        if stage == 0 {
+            rustdocflags.env("RUSTDOCFLAGS_BOOTSTRAP");
+        } else {
+            rustdocflags.env("RUSTDOCFLAGS_NOT_BOOTSTRAP");
+        }
+
+        if let Ok(s) = env::var("CARGOFLAGS") {
+            cargo.args(s.split_whitespace());
+        }
+
+        match mode {
+            Mode::Std | Mode::ToolBootstrap | Mode::ToolStd => {}
+            Mode::Rustc | Mode::Codegen | Mode::ToolRustc => {
+                // Build proc macros both for the host and the target unless proc-macros are not
+                // supported by the target.
+                if target != compiler.host && cmd_kind != Kind::Check {
+                    let error = command(self.rustc(compiler))
+                        .arg("--target")
+                        .arg(target.rustc_target_arg())
+                        .arg("--print=file-names")
+                        .arg("--crate-type=proc-macro")
+                        .arg("-")
+                        .run_capture(self)
+                        .stderr();
+                    let not_supported = error
+                        .lines()
+                        .any(|line| line.contains("unsupported crate type `proc-macro`"));
+                    if !not_supported {
+                        cargo.arg("-Zdual-proc-macros");
+                        rustflags.arg("-Zdual-proc-macros");
+                    }
+                }
+            }
+        }
+
+        // This tells Cargo (and in turn, rustc) to output more complete
+        // dependency information.  Most importantly for bootstrap, this
+        // includes sysroot artifacts, like libstd, which means that we don't
+        // need to track those in bootstrap (an error prone process!). This
+        // feature is currently unstable as there may be some bugs and such, but
+        // it represents a big improvement in bootstrap's reliability on
+        // rebuilds, so we're using it here.
+        //
+        // For some additional context, see #63470 (the PR originally adding
+        // this), as well as #63012 which is the tracking issue for this
+        // feature on the rustc side.
+        cargo.arg("-Zbinary-dep-depinfo");
+        let allow_features = match mode {
+            Mode::ToolBootstrap | Mode::ToolStd => {
+                // Restrict the allowed features so we don't depend on nightly
+                // accidentally.
+                //
+                // binary-dep-depinfo is used by bootstrap itself for all
+                // compilations.
+                //
+                // Lots of tools depend on proc_macro2 and proc-macro-error.
+                // Those have build scripts which assume nightly features are
+                // available if the `rustc` version is "nighty" or "dev". See
+                // bin/rustc.rs for why that is a problem. Instead of labeling
+                // those features for each individual tool that needs them,
+                // just blanket allow them here.
+                //
+                // If this is ever removed, be sure to add something else in
+                // its place to keep the restrictions in place (or make a way
+                // to unset RUSTC_BOOTSTRAP).
+                "binary-dep-depinfo,proc_macro_span,proc_macro_span_shrink,proc_macro_diagnostic"
+                    .to_string()
+            }
+            Mode::Std | Mode::Rustc | Mode::Codegen | Mode::ToolRustc => String::new(),
+        };
+
+        cargo.arg("-j").arg(self.jobs().to_string());
+
+        // FIXME: Temporary fix for https://github.com/rust-lang/cargo/issues/3005
+        // Force cargo to output binaries with disambiguating hashes in the name
+        let mut metadata = if compiler.stage == 0 {
+            // Treat stage0 like a special channel, whether it's a normal prior-
+            // release rustc or a local rebuild with the same version, so we
+            // never mix these libraries by accident.
+            "bootstrap".to_string()
+        } else {
+            self.config.channel.to_string()
+        };
+        // We want to make sure that none of the dependencies between
+        // std/test/rustc unify with one another. This is done for weird linkage
+        // reasons but the gist of the problem is that if librustc, libtest, and
+        // libstd all depend on libc from crates.io (which they actually do) we
+        // want to make sure they all get distinct versions. Things get really
+        // weird if we try to unify all these dependencies right now, namely
+        // around how many times the library is linked in dynamic libraries and
+        // such. If rustc were a static executable or if we didn't ship dylibs
+        // this wouldn't be a problem, but we do, so it is. This is in general
+        // just here to make sure things build right. If you can remove this and
+        // things still build right, please do!
+        match mode {
+            Mode::Std => metadata.push_str("std"),
+            // When we're building rustc tools, they're built with a search path
+            // that contains things built during the rustc build. For example,
+            // bitflags is built during the rustc build, and is a dependency of
+            // rustdoc as well. We're building rustdoc in a different target
+            // directory, though, which means that Cargo will rebuild the
+            // dependency. When we go on to build rustdoc, we'll look for
+            // bitflags, and find two different copies: one built during the
+            // rustc step and one that we just built. This isn't always a
+            // problem, somehow -- not really clear why -- but we know that this
+            // fixes things.
+            Mode::ToolRustc => metadata.push_str("tool-rustc"),
+            // Same for codegen backends.
+            Mode::Codegen => metadata.push_str("codegen"),
+            _ => {}
+        }
+        cargo.env("__CARGO_DEFAULT_LIB_METADATA", &metadata);
+
+        if cmd_kind == Kind::Clippy {
+            rustflags.arg("-Zforce-unstable-if-unmarked");
+        }
+
+        rustflags.arg("-Zmacro-backtrace");
+
+        let want_rustdoc = self.doc_tests != DocTests::No;
+
+        // Clear the output directory if the real rustc we're using has changed;
+        // Cargo cannot detect this as it thinks rustc is bootstrap/debug/rustc.
+        //
+        // Avoid doing this during dry run as that usually means the relevant
+        // compiler is not yet linked/copied properly.
+        //
+        // Only clear out the directory if we're compiling std; otherwise, we
+        // should let Cargo take care of things for us (via depdep info)
+        if !self.config.dry_run() && mode == Mode::Std && cmd_kind == Kind::Build {
+            self.clear_if_dirty(&out_dir, &self.rustc(compiler));
+        }
+
+        let rustdoc_path = match cmd_kind {
+            Kind::Doc | Kind::Test | Kind::MiriTest => self.rustdoc(compiler),
+            _ => PathBuf::from("/path/to/nowhere/rustdoc/not/required"),
+        };
+
+        // Customize the compiler we're running. Specify the compiler to cargo
+        // as our shim and then pass it some various options used to configure
+        // how the actual compiler itself is called.
+        //
+        // These variables are primarily all read by
+        // src/bootstrap/bin/{rustc.rs,rustdoc.rs}
+        cargo
+            .env("RUSTBUILD_NATIVE_DIR", self.native_dir(target))
+            .env("RUSTC_REAL", self.rustc(compiler))
+            .env("RUSTC_STAGE", stage.to_string())
+            .env("RUSTC_SYSROOT", sysroot)
+            .env("RUSTC_LIBDIR", libdir)
+            .env("RUSTDOC", self.bootstrap_out.join("rustdoc"))
+            .env("RUSTDOC_REAL", rustdoc_path)
+            .env("RUSTC_ERROR_METADATA_DST", self.extended_error_dir())
+            .env("RUSTC_BREAK_ON_ICE", "1");
+
+        // Set RUSTC_WRAPPER to the bootstrap shim, which switches between beta and in-tree
+        // sysroot depending on whether we're building build scripts.
+        // NOTE: we intentionally use RUSTC_WRAPPER so that we can support clippy - RUSTC is not
+        // respected by clippy-driver; RUSTC_WRAPPER happens earlier, before clippy runs.
+        cargo.env("RUSTC_WRAPPER", self.bootstrap_out.join("rustc"));
+        // NOTE: we also need to set RUSTC so cargo can run `rustc -vV`; apparently that ignores RUSTC_WRAPPER >:(
+        cargo.env("RUSTC", self.bootstrap_out.join("rustc"));
+
+        // Someone might have set some previous rustc wrapper (e.g.
+        // sccache) before bootstrap overrode it. Respect that variable.
+        if let Some(existing_wrapper) = env::var_os("RUSTC_WRAPPER") {
+            cargo.env("RUSTC_WRAPPER_REAL", existing_wrapper);
+        }
+
+        // If this is for `miri-test`, prepare the sysroots.
+        if cmd_kind == Kind::MiriTest {
+            self.ensure(compile::Std::new(compiler, compiler.host));
+            let host_sysroot = self.sysroot(compiler);
+            let miri_sysroot = test::Miri::build_miri_sysroot(self, compiler, target);
+            cargo.env("MIRI_SYSROOT", &miri_sysroot);
+            cargo.env("MIRI_HOST_SYSROOT", &host_sysroot);
+        }
+
+        cargo.env(profile_var("STRIP"), self.config.rust_strip.to_string());
+
+        if let Some(stack_protector) = &self.config.rust_stack_protector {
+            rustflags.arg(&format!("-Zstack-protector={stack_protector}"));
+        }
+
+        if !matches!(cmd_kind, Kind::Build | Kind::Check | Kind::Clippy | Kind::Fix) && want_rustdoc
+        {
+            cargo.env("RUSTDOC_LIBDIR", self.rustc_libdir(compiler));
+        }
+
+        let debuginfo_level = match mode {
+            Mode::Rustc | Mode::Codegen => self.config.rust_debuginfo_level_rustc,
+            Mode::Std => self.config.rust_debuginfo_level_std,
+            Mode::ToolBootstrap | Mode::ToolStd | Mode::ToolRustc => {
+                self.config.rust_debuginfo_level_tools
+            }
+        };
+        cargo.env(profile_var("DEBUG"), debuginfo_level.to_string());
+        if let Some(opt_level) = &self.config.rust_optimize.get_opt_level() {
+            cargo.env(profile_var("OPT_LEVEL"), opt_level);
+        }
+        cargo.env(
+            profile_var("DEBUG_ASSERTIONS"),
+            if mode == Mode::Std {
+                self.config.rust_debug_assertions_std.to_string()
+            } else {
+                self.config.rust_debug_assertions.to_string()
+            },
+        );
+        cargo.env(
+            profile_var("OVERFLOW_CHECKS"),
+            if mode == Mode::Std {
+                self.config.rust_overflow_checks_std.to_string()
+            } else {
+                self.config.rust_overflow_checks.to_string()
+            },
+        );
+
+        match self.config.split_debuginfo(target) {
+            SplitDebuginfo::Packed => rustflags.arg("-Csplit-debuginfo=packed"),
+            SplitDebuginfo::Unpacked => rustflags.arg("-Csplit-debuginfo=unpacked"),
+            SplitDebuginfo::Off => rustflags.arg("-Csplit-debuginfo=off"),
+        };
+
+        if self.config.cmd.bless() {
+            // Bless `expect!` tests.
+            cargo.env("UPDATE_EXPECT", "1");
+        }
+
+        if !mode.is_tool() {
+            cargo.env("RUSTC_FORCE_UNSTABLE", "1");
+        }
+
+        if let Some(x) = self.crt_static(target) {
+            if x {
+                rustflags.arg("-Ctarget-feature=+crt-static");
+            } else {
+                rustflags.arg("-Ctarget-feature=-crt-static");
+            }
+        }
+
+        if let Some(x) = self.crt_static(compiler.host) {
+            let sign = if x { "+" } else { "-" };
+            hostflags.arg(format!("-Ctarget-feature={sign}crt-static"));
+        }
+
+        if let Some(map_to) = self.build.debuginfo_map_to(GitRepo::Rustc) {
+            let map = format!("{}={}", self.build.src.display(), map_to);
+            cargo.env("RUSTC_DEBUGINFO_MAP", map);
+
+            // `rustc` needs to know the virtual `/rustc/$hash` we're mapping to,
+            // in order to opportunistically reverse it later.
+            cargo.env("CFG_VIRTUAL_RUST_SOURCE_BASE_DIR", map_to);
+        }
+
+        if self.config.rust_remap_debuginfo {
+            let mut env_var = OsString::new();
+            if self.config.vendor {
+                let vendor = self.build.src.join("vendor");
+                env_var.push(vendor);
+                env_var.push("=/rust/deps");
+            } else {
+                let registry_src = t!(home::cargo_home()).join("registry").join("src");
+                for entry in t!(std::fs::read_dir(registry_src)) {
+                    if !env_var.is_empty() {
+                        env_var.push("\t");
+                    }
+                    env_var.push(t!(entry).path());
+                    env_var.push("=/rust/deps");
+                }
+            }
+            cargo.env("RUSTC_CARGO_REGISTRY_SRC_TO_REMAP", env_var);
+        }
+
+        // Enable usage of unstable features
+        cargo.env("RUSTC_BOOTSTRAP", "1");
+
+        if self.config.dump_bootstrap_shims {
+            prepare_behaviour_dump_dir(self.build);
+
+            cargo
+                .env("DUMP_BOOTSTRAP_SHIMS", self.build.out.join("bootstrap-shims-dump"))
+                .env("BUILD_OUT", &self.build.out)
+                .env("CARGO_HOME", t!(home::cargo_home()));
+        };
+
+        self.add_rust_test_threads(&mut cargo);
+
+        // Almost all of the crates that we compile as part of the bootstrap may
+        // have a build script, including the standard library. To compile a
+        // build script, however, it itself needs a standard library! This
+        // introduces a bit of a pickle when we're compiling the standard
+        // library itself.
+        //
+        // To work around this we actually end up using the snapshot compiler
+        // (stage0) for compiling build scripts of the standard library itself.
+        // The stage0 compiler is guaranteed to have a libstd available for use.
+        //
+        // For other crates, however, we know that we've already got a standard
+        // library up and running, so we can use the normal compiler to compile
+        // build scripts in that situation.
+        if mode == Mode::Std {
+            cargo
+                .env("RUSTC_SNAPSHOT", &self.initial_rustc)
+                .env("RUSTC_SNAPSHOT_LIBDIR", self.rustc_snapshot_libdir());
+        } else {
+            cargo
+                .env("RUSTC_SNAPSHOT", self.rustc(compiler))
+                .env("RUSTC_SNAPSHOT_LIBDIR", self.rustc_libdir(compiler));
+        }
+
+        // Tools that use compiler libraries may inherit the `-lLLVM` link
+        // requirement, but the `-L` library path is not propagated across
+        // separate Cargo projects. We can add LLVM's library path to the
+        // platform-specific environment variable as a workaround.
+        if mode == Mode::ToolRustc || mode == Mode::Codegen {
+            if let Some(llvm_config) = self.llvm_config(target) {
+                let llvm_libdir =
+                    command(llvm_config).arg("--libdir").run_capture_stdout(self).stdout();
+                add_link_lib_path(vec![llvm_libdir.trim().into()], &mut cargo);
+            }
+        }
+
+        // Compile everything except libraries and proc macros with the more
+        // efficient initial-exec TLS model. This doesn't work with `dlopen`,
+        // so we can't use it by default in general, but we can use it for tools
+        // and our own internal libraries.
+        if !mode.must_support_dlopen() && !target.triple.starts_with("powerpc-") {
+            cargo.env("RUSTC_TLS_MODEL_INITIAL_EXEC", "1");
+        }
+
+        // Ignore incremental modes except for stage0, since we're
+        // not guaranteeing correctness across builds if the compiler
+        // is changing under your feet.
+        if self.config.incremental && compiler.stage == 0 {
+            cargo.env("CARGO_INCREMENTAL", "1");
+        } else {
+            // Don't rely on any default setting for incr. comp. in Cargo
+            cargo.env("CARGO_INCREMENTAL", "0");
+        }
+
+        if let Some(ref on_fail) = self.config.on_fail {
+            cargo.env("RUSTC_ON_FAIL", on_fail);
+        }
+
+        if self.config.print_step_timings {
+            cargo.env("RUSTC_PRINT_STEP_TIMINGS", "1");
+        }
+
+        if self.config.print_step_rusage {
+            cargo.env("RUSTC_PRINT_STEP_RUSAGE", "1");
+        }
+
+        if self.config.backtrace_on_ice {
+            cargo.env("RUSTC_BACKTRACE_ON_ICE", "1");
+        }
+
+        if self.is_verbose() {
+            // This provides very useful logs especially when debugging build cache-related stuff.
+            cargo.env("CARGO_LOG", "cargo::core::compiler::fingerprint=info");
+        }
+
+        cargo.env("RUSTC_VERBOSE", self.verbosity.to_string());
+
+        // Downstream forks of the Rust compiler might want to use a custom libc to add support for
+        // targets that are not yet available upstream. Adding a patch to replace libc with a
+        // custom one would cause compilation errors though, because Cargo would interpret the
+        // custom libc as part of the workspace, and apply the check-cfg lints on it.
+        //
+        // The libc build script emits check-cfg flags only when this environment variable is set,
+        // so this line allows the use of custom libcs.
+        cargo.env("LIBC_CHECK_CFG", "1");
+
+        if source_type == SourceType::InTree {
+            let mut lint_flags = Vec::new();
+            // When extending this list, add the new lints to the RUSTFLAGS of the
+            // build_bootstrap function of src/bootstrap/bootstrap.py as well as
+            // some code doesn't go through this `rustc` wrapper.
+            lint_flags.push("-Wrust_2018_idioms");
+            lint_flags.push("-Wunused_lifetimes");
+
+            if self.config.deny_warnings {
+                lint_flags.push("-Dwarnings");
+                rustdocflags.arg("-Dwarnings");
+            }
+
+            // This does not use RUSTFLAGS due to caching issues with Cargo.
+            // Clippy is treated as an "in tree" tool, but shares the same
+            // cache as other "submodule" tools. With these options set in
+            // RUSTFLAGS, that causes *every* shared dependency to be rebuilt.
+            // By injecting this into the rustc wrapper, this circumvents
+            // Cargo's fingerprint detection. This is fine because lint flags
+            // are always ignored in dependencies. Eventually this should be
+            // fixed via better support from Cargo.
+            cargo.env("RUSTC_LINT_FLAGS", lint_flags.join(" "));
+
+            rustdocflags.arg("-Wrustdoc::invalid_codeblock_attributes");
+        }
+
+        if mode == Mode::Rustc {
+            rustflags.arg("-Wrustc::internal");
+            // FIXME(edition_2024): Change this to `-Wrust_2024_idioms` when all
+            // of the individual lints are satisfied.
+            rustflags.arg("-Wkeyword_idents_2024");
+            rustflags.arg("-Wunsafe_op_in_unsafe_fn");
+        }
+
+        if self.config.rust_frame_pointers {
+            rustflags.arg("-Cforce-frame-pointers=true");
+        }
+
+        // If Control Flow Guard is enabled, pass the `control-flow-guard` flag to rustc
+        // when compiling the standard library, since this might be linked into the final outputs
+        // produced by rustc. Since this mitigation is only available on Windows, only enable it
+        // for the standard library in case the compiler is run on a non-Windows platform.
+        // This is not needed for stage 0 artifacts because these will only be used for building
+        // the stage 1 compiler.
+        if cfg!(windows)
+            && mode == Mode::Std
+            && self.config.control_flow_guard
+            && compiler.stage >= 1
+        {
+            rustflags.arg("-Ccontrol-flow-guard");
+        }
+
+        // If EHCont Guard is enabled, pass the `-Zehcont-guard` flag to rustc when compiling the
+        // standard library, since this might be linked into the final outputs produced by rustc.
+        // Since this mitigation is only available on Windows, only enable it for the standard
+        // library in case the compiler is run on a non-Windows platform.
+        // This is not needed for stage 0 artifacts because these will only be used for building
+        // the stage 1 compiler.
+        if cfg!(windows) && mode == Mode::Std && self.config.ehcont_guard && compiler.stage >= 1 {
+            rustflags.arg("-Zehcont-guard");
+        }
+
+        // For `cargo doc` invocations, make rustdoc print the Rust version into the docs
+        // This replaces spaces with tabs because RUSTDOCFLAGS does not
+        // support arguments with regular spaces. Hopefully someday Cargo will
+        // have space support.
+        let rust_version = self.rust_version().replace(' ', "\t");
+        rustdocflags.arg("--crate-version").arg(&rust_version);
+
+        // Environment variables *required* throughout the build
+        //
+        // FIXME: should update code to not require this env var
+
+        // The host this new compiler will *run* on.
+        cargo.env("CFG_COMPILER_HOST_TRIPLE", target.triple);
+        // The host this new compiler is being *built* on.
+        cargo.env("CFG_COMPILER_BUILD_TRIPLE", compiler.host.triple);
+
+        // Set this for all builds to make sure doc builds also get it.
+        cargo.env("CFG_RELEASE_CHANNEL", &self.config.channel);
+
+        // This one's a bit tricky. As of the time of this writing the compiler
+        // links to the `winapi` crate on crates.io. This crate provides raw
+        // bindings to Windows system functions, sort of like libc does for
+        // Unix. This crate also, however, provides "import libraries" for the
+        // MinGW targets. There's an import library per dll in the windows
+        // distribution which is what's linked to. These custom import libraries
+        // are used because the winapi crate can reference Windows functions not
+        // present in the MinGW import libraries.
+        //
+        // For example MinGW may ship libdbghelp.a, but it may not have
+        // references to all the functions in the dbghelp dll. Instead the
+        // custom import library for dbghelp in the winapi crates has all this
+        // information.
+        //
+        // Unfortunately for us though the import libraries are linked by
+        // default via `-ldylib=winapi_foo`. That is, they're linked with the
+        // `dylib` type with a `winapi_` prefix (so the winapi ones don't
+        // conflict with the system MinGW ones). This consequently means that
+        // the binaries we ship of things like rustc_codegen_llvm (aka the rustc_codegen_llvm
+        // DLL) when linked against *again*, for example with procedural macros
+        // or plugins, will trigger the propagation logic of `-ldylib`, passing
+        // `-lwinapi_foo` to the linker again. This isn't actually available in
+        // our distribution, however, so the link fails.
+        //
+        // To solve this problem we tell winapi to not use its bundled import
+        // libraries. This means that it will link to the system MinGW import
+        // libraries by default, and the `-ldylib=foo` directives will still get
+        // passed to the final linker, but they'll look like `-lfoo` which can
+        // be resolved because MinGW has the import library. The downside is we
+        // don't get newer functions from Windows, but we don't use any of them
+        // anyway.
+        if !mode.is_tool() {
+            cargo.env("WINAPI_NO_BUNDLED_LIBRARIES", "1");
+        }
+
+        for _ in 0..self.verbosity {
+            cargo.arg("-v");
+        }
+
+        match (mode, self.config.rust_codegen_units_std, self.config.rust_codegen_units) {
+            (Mode::Std, Some(n), _) | (_, _, Some(n)) => {
+                cargo.env(profile_var("CODEGEN_UNITS"), n.to_string());
+            }
+            _ => {
+                // Don't set anything
+            }
+        }
+
+        if self.config.locked_deps {
+            cargo.arg("--locked");
+        }
+        if self.config.vendor || self.is_sudo {
+            cargo.arg("--frozen");
+        }
+
+        // Try to use a sysroot-relative bindir, in case it was configured absolutely.
+        cargo.env("RUSTC_INSTALL_BINDIR", self.config.bindir_relative());
+
+        cargo.force_coloring_in_ci();
+
+        // When we build Rust dylibs they're all intended for intermediate
+        // usage, so make sure we pass the -Cprefer-dynamic flag instead of
+        // linking all deps statically into the dylib.
+        if matches!(mode, Mode::Std) {
+            rustflags.arg("-Cprefer-dynamic");
+        }
+        if matches!(mode, Mode::Rustc) && !self.link_std_into_rustc_driver(target) {
+            rustflags.arg("-Cprefer-dynamic");
+        }
+
+        cargo.env(
+            "RUSTC_LINK_STD_INTO_RUSTC_DRIVER",
+            if self.link_std_into_rustc_driver(target) { "1" } else { "0" },
+        );
+
+        // When building incrementally we default to a lower ThinLTO import limit
+        // (unless explicitly specified otherwise). This will produce a somewhat
+        // slower code but give way better compile times.
+        {
+            let limit = match self.config.rust_thin_lto_import_instr_limit {
+                Some(limit) => Some(limit),
+                None if self.config.incremental => Some(10),
+                _ => None,
+            };
+
+            if let Some(limit) = limit {
+                if stage == 0
+                    || self.config.default_codegen_backend(target).unwrap_or_default() == "llvm"
+                {
+                    rustflags.arg(&format!("-Cllvm-args=-import-instr-limit={limit}"));
+                }
+            }
+        }
+
+        if matches!(mode, Mode::Std) {
+            if let Some(mir_opt_level) = self.config.rust_validate_mir_opts {
+                rustflags.arg("-Zvalidate-mir");
+                rustflags.arg(&format!("-Zmir-opt-level={mir_opt_level}"));
+            }
+            if self.config.rust_randomize_layout {
+                rustflags.arg("--cfg=randomized_layouts");
+            }
+            // Always enable inlining MIR when building the standard library.
+            // Without this flag, MIR inlining is disabled when incremental compilation is enabled.
+            // That causes some mir-opt tests which inline functions from the standard library to
+            // break when incremental compilation is enabled. So this overrides the "no inlining
+            // during incremental builds" heuristic for the standard library.
+            rustflags.arg("-Zinline-mir");
+
+            // Similarly, we need to keep debug info for functions inlined into other std functions,
+            // even if we're not going to output debuginfo for the crate we're currently building,
+            // so that it'll be available when downstream consumers of std try to use it.
+            rustflags.arg("-Zinline-mir-preserve-debug");
+        }
+
+        if self.config.rustc_parallel
+            && matches!(mode, Mode::ToolRustc | Mode::Rustc | Mode::Codegen)
+        {
+            // keep in sync with `bootstrap/lib.rs:Build::rustc_features`
+            // `cfg` option for rustc, `features` option for cargo, for conditional compilation
+            rustflags.arg("--cfg=parallel_compiler");
+            rustdocflags.arg("--cfg=parallel_compiler");
+        }
+
+        Cargo {
+            command: cargo,
+            compiler,
+            target,
+            rustflags,
+            rustdocflags,
+            hostflags,
+            allow_features,
+        }
+    }
+}
diff --git a/src/bootstrap/src/core/builder.rs b/src/bootstrap/src/core/builder/mod.rs
index 15c6f303f94..8f8ab112857 100644
--- a/src/bootstrap/src/core/builder.rs
+++ b/src/bootstrap/src/core/builder/mod.rs
@@ -1,7 +1,8 @@
+mod cargo;
+
 use std::any::{Any, type_name};
 use std::cell::{Cell, RefCell};
 use std::collections::BTreeSet;
-use std::ffi::{OsStr, OsString};
 use std::fmt::{Debug, Write};
 use std::hash::Hash;
 use std::ops::Deref;
@@ -12,22 +13,17 @@ use std::{env, fs};
 
 use clap::ValueEnum;
 
+pub use self::cargo::Cargo;
 pub use crate::Compiler;
-use crate::core::build_steps::tool::{self, SourceType};
 use crate::core::build_steps::{
-    check, clean, clippy, compile, dist, doc, gcc, install, llvm, run, setup, test, vendor,
+    check, clean, clippy, compile, dist, doc, gcc, install, llvm, run, setup, test, tool, vendor,
 };
-use crate::core::config::flags::{Color, Subcommand};
-use crate::core::config::{DryRun, SplitDebuginfo, TargetSelection};
+use crate::core::config::flags::Subcommand;
+use crate::core::config::{DryRun, TargetSelection};
 use crate::utils::cache::Cache;
 use crate::utils::exec::{BootstrapCommand, command};
-use crate::utils::helpers::{
-    self, LldThreads, add_dylib_path, add_link_lib_path, check_cfg_arg, exe, libdir, linker_args,
-    linker_flags, t,
-};
-use crate::{
-    Build, CLang, Crate, DocTests, EXTRA_CHECK_CFGS, GitRepo, Mode, prepare_behaviour_dump_dir,
-};
+use crate::utils::helpers::{self, LldThreads, add_dylib_path, exe, libdir, linker_args, t};
+use crate::{Build, Crate};
 
 #[cfg(test)]
 mod tests;
@@ -1010,6 +1006,7 @@ impl<'a> Builder<'a> {
                 run::GenerateCopyright,
                 run::GenerateWindowsSys,
                 run::GenerateCompletions,
+                run::UnicodeTableGenerator,
             ),
             Kind::Setup => {
                 describe!(setup::Profile, setup::Hook, setup::Link, setup::Editor)
@@ -1409,870 +1406,6 @@ impl<'a> Builder<'a> {
         None
     }
 
-    /// Like `cargo`, but only passes flags that are valid for all commands.
-    pub fn bare_cargo(
-        &self,
-        compiler: Compiler,
-        mode: Mode,
-        target: TargetSelection,
-        cmd_kind: Kind,
-    ) -> BootstrapCommand {
-        let mut cargo = match cmd_kind {
-            Kind::Clippy => {
-                let mut cargo = self.cargo_clippy_cmd(compiler);
-                cargo.arg(cmd_kind.as_str());
-                cargo
-            }
-            Kind::MiriSetup => {
-                let mut cargo = self.cargo_miri_cmd(compiler);
-                cargo.arg("miri").arg("setup");
-                cargo
-            }
-            Kind::MiriTest => {
-                let mut cargo = self.cargo_miri_cmd(compiler);
-                cargo.arg("miri").arg("test");
-                cargo
-            }
-            _ => {
-                let mut cargo = command(&self.initial_cargo);
-                cargo.arg(cmd_kind.as_str());
-                cargo
-            }
-        };
-
-        // Run cargo from the source root so it can find .cargo/config.
-        // This matters when using vendoring and the working directory is outside the repository.
-        cargo.current_dir(&self.src);
-
-        let out_dir = self.stage_out(compiler, mode);
-        cargo.env("CARGO_TARGET_DIR", &out_dir);
-
-        // Found with `rg "init_env_logger\("`. If anyone uses `init_env_logger`
-        // from out of tree it shouldn't matter, since x.py is only used for
-        // building in-tree.
-        let color_logs = ["RUSTDOC_LOG_COLOR", "RUSTC_LOG_COLOR", "RUST_LOG_COLOR"];
-        match self.build.config.color {
-            Color::Always => {
-                cargo.arg("--color=always");
-                for log in &color_logs {
-                    cargo.env(log, "always");
-                }
-            }
-            Color::Never => {
-                cargo.arg("--color=never");
-                for log in &color_logs {
-                    cargo.env(log, "never");
-                }
-            }
-            Color::Auto => {} // nothing to do
-        }
-
-        if cmd_kind != Kind::Install {
-            cargo.arg("--target").arg(target.rustc_target_arg());
-        } else {
-            assert_eq!(target, compiler.host);
-        }
-
-        if self.config.rust_optimize.is_release() &&
-        // cargo bench/install do not accept `--release` and miri doesn't want it
-        !matches!(cmd_kind, Kind::Bench | Kind::Install | Kind::Miri | Kind::MiriSetup | Kind::MiriTest)
-        {
-            cargo.arg("--release");
-        }
-
-        // Remove make-related flags to ensure Cargo can correctly set things up
-        cargo.env_remove("MAKEFLAGS");
-        cargo.env_remove("MFLAGS");
-
-        cargo
-    }
-
-    /// This will create a `Command` that represents a pending execution of
-    /// Cargo. This cargo will be configured to use `compiler` as the actual
-    /// rustc compiler, its output will be scoped by `mode`'s output directory,
-    /// it will pass the `--target` flag for the specified `target`, and will be
-    /// executing the Cargo command `cmd`. `cmd` can be `miri-cmd` for commands
-    /// to be run with Miri.
-    fn cargo(
-        &self,
-        compiler: Compiler,
-        mode: Mode,
-        source_type: SourceType,
-        target: TargetSelection,
-        cmd_kind: Kind,
-    ) -> Cargo {
-        let mut cargo = self.bare_cargo(compiler, mode, target, cmd_kind);
-        let out_dir = self.stage_out(compiler, mode);
-
-        let mut hostflags = HostFlags::default();
-
-        // Codegen backends are not yet tracked by -Zbinary-dep-depinfo,
-        // so we need to explicitly clear out if they've been updated.
-        for backend in self.codegen_backends(compiler) {
-            self.clear_if_dirty(&out_dir, &backend);
-        }
-
-        if cmd_kind == Kind::Doc {
-            let my_out = match mode {
-                // This is the intended out directory for compiler documentation.
-                Mode::Rustc | Mode::ToolRustc => self.compiler_doc_out(target),
-                Mode::Std => {
-                    if self.config.cmd.json() {
-                        out_dir.join(target).join("json-doc")
-                    } else {
-                        out_dir.join(target).join("doc")
-                    }
-                }
-                _ => panic!("doc mode {mode:?} not expected"),
-            };
-            let rustdoc = self.rustdoc(compiler);
-            self.clear_if_dirty(&my_out, &rustdoc);
-        }
-
-        let profile_var = |name: &str| {
-            let profile = if self.config.rust_optimize.is_release() { "RELEASE" } else { "DEV" };
-            format!("CARGO_PROFILE_{}_{}", profile, name)
-        };
-
-        // See comment in rustc_llvm/build.rs for why this is necessary, largely llvm-config
-        // needs to not accidentally link to libLLVM in stage0/lib.
-        cargo.env("REAL_LIBRARY_PATH_VAR", helpers::dylib_path_var());
-        if let Some(e) = env::var_os(helpers::dylib_path_var()) {
-            cargo.env("REAL_LIBRARY_PATH", e);
-        }
-
-        // Set a flag for `check`/`clippy`/`fix`, so that certain build
-        // scripts can do less work (i.e. not building/requiring LLVM).
-        if matches!(cmd_kind, Kind::Check | Kind::Clippy | Kind::Fix) {
-            // If we've not yet built LLVM, or it's stale, then bust
-            // the rustc_llvm cache. That will always work, even though it
-            // may mean that on the next non-check build we'll need to rebuild
-            // rustc_llvm. But if LLVM is stale, that'll be a tiny amount
-            // of work comparatively, and we'd likely need to rebuild it anyway,
-            // so that's okay.
-            if crate::core::build_steps::llvm::prebuilt_llvm_config(self, target, false)
-                .should_build()
-            {
-                cargo.env("RUST_CHECK", "1");
-            }
-        }
-
-        let stage = if compiler.stage == 0 && self.local_rebuild {
-            // Assume the local-rebuild rustc already has stage1 features.
-            1
-        } else {
-            compiler.stage
-        };
-
-        // We synthetically interpret a stage0 compiler used to build tools as a
-        // "raw" compiler in that it's the exact snapshot we download. Normally
-        // the stage0 build means it uses libraries build by the stage0
-        // compiler, but for tools we just use the precompiled libraries that
-        // we've downloaded
-        let use_snapshot = mode == Mode::ToolBootstrap;
-        assert!(!use_snapshot || stage == 0 || self.local_rebuild);
-
-        let maybe_sysroot = self.sysroot(compiler);
-        let sysroot = if use_snapshot { self.rustc_snapshot_sysroot() } else { &maybe_sysroot };
-        let libdir = self.rustc_libdir(compiler);
-
-        let sysroot_str = sysroot.as_os_str().to_str().expect("sysroot should be UTF-8");
-        if self.is_verbose() && !matches!(self.config.dry_run, DryRun::SelfCheck) {
-            println!("using sysroot {sysroot_str}");
-        }
-
-        let mut rustflags = Rustflags::new(target);
-        if stage != 0 {
-            if let Ok(s) = env::var("CARGOFLAGS_NOT_BOOTSTRAP") {
-                cargo.args(s.split_whitespace());
-            }
-            rustflags.env("RUSTFLAGS_NOT_BOOTSTRAP");
-        } else {
-            if let Ok(s) = env::var("CARGOFLAGS_BOOTSTRAP") {
-                cargo.args(s.split_whitespace());
-            }
-            rustflags.env("RUSTFLAGS_BOOTSTRAP");
-            rustflags.arg("--cfg=bootstrap");
-        }
-
-        if cmd_kind == Kind::Clippy {
-            // clippy overwrites sysroot if we pass it to cargo.
-            // Pass it directly to clippy instead.
-            // NOTE: this can't be fixed in clippy because we explicitly don't set `RUSTC`,
-            // so it has no way of knowing the sysroot.
-            rustflags.arg("--sysroot");
-            rustflags.arg(sysroot_str);
-        }
-
-        let use_new_symbol_mangling = match self.config.rust_new_symbol_mangling {
-            Some(setting) => {
-                // If an explicit setting is given, use that
-                setting
-            }
-            None => {
-                if mode == Mode::Std {
-                    // The standard library defaults to the legacy scheme
-                    false
-                } else {
-                    // The compiler and tools default to the new scheme
-                    true
-                }
-            }
-        };
-
-        // By default, windows-rs depends on a native library that doesn't get copied into the
-        // sysroot. Passing this cfg enables raw-dylib support instead, which makes the native
-        // library unnecessary. This can be removed when windows-rs enables raw-dylib
-        // unconditionally.
-        if let Mode::Rustc | Mode::ToolRustc = mode {
-            rustflags.arg("--cfg=windows_raw_dylib");
-        }
-
-        if use_new_symbol_mangling {
-            rustflags.arg("-Csymbol-mangling-version=v0");
-        } else {
-            rustflags.arg("-Csymbol-mangling-version=legacy");
-        }
-
-        // FIXME: the following components don't build with `-Zrandomize-layout` yet:
-        // - wasm-component-ld, due to the `wast`crate
-        // - rust-analyzer, due to the rowan crate
-        // so we exclude entire categories of steps here due to lack of fine-grained control over
-        // rustflags.
-        if self.config.rust_randomize_layout && mode != Mode::ToolStd && mode != Mode::ToolRustc {
-            rustflags.arg("-Zrandomize-layout");
-        }
-
-        // Enable compile-time checking of `cfg` names, values and Cargo `features`.
-        //
-        // Note: `std`, `alloc` and `core` imports some dependencies by #[path] (like
-        // backtrace, core_simd, std_float, ...), those dependencies have their own
-        // features but cargo isn't involved in the #[path] process and so cannot pass the
-        // complete list of features, so for that reason we don't enable checking of
-        // features for std crates.
-        if mode == Mode::Std {
-            rustflags.arg("--check-cfg=cfg(feature,values(any()))");
-        }
-
-        // Add extra cfg not defined in/by rustc
-        //
-        // Note: Although it would seems that "-Zunstable-options" to `rustflags` is useless as
-        // cargo would implicitly add it, it was discover that sometimes bootstrap only use
-        // `rustflags` without `cargo` making it required.
-        rustflags.arg("-Zunstable-options");
-        for (restricted_mode, name, values) in EXTRA_CHECK_CFGS {
-            if restricted_mode.is_none() || *restricted_mode == Some(mode) {
-                rustflags.arg(&check_cfg_arg(name, *values));
-            }
-        }
-
-        // FIXME(rust-lang/cargo#5754) we shouldn't be using special command arguments
-        // to the host invocation here, but rather Cargo should know what flags to pass rustc
-        // itself.
-        if stage == 0 {
-            hostflags.arg("--cfg=bootstrap");
-        }
-        // Cargo doesn't pass RUSTFLAGS to proc_macros:
-        // https://github.com/rust-lang/cargo/issues/4423
-        // Thus, if we are on stage 0, we explicitly set `--cfg=bootstrap`.
-        // We also declare that the flag is expected, which we need to do to not
-        // get warnings about it being unexpected.
-        hostflags.arg("-Zunstable-options");
-        hostflags.arg("--check-cfg=cfg(bootstrap)");
-
-        // FIXME: It might be better to use the same value for both `RUSTFLAGS` and `RUSTDOCFLAGS`,
-        // but this breaks CI. At the very least, stage0 `rustdoc` needs `--cfg bootstrap`. See
-        // #71458.
-        let mut rustdocflags = rustflags.clone();
-        rustdocflags.propagate_cargo_env("RUSTDOCFLAGS");
-        if stage == 0 {
-            rustdocflags.env("RUSTDOCFLAGS_BOOTSTRAP");
-        } else {
-            rustdocflags.env("RUSTDOCFLAGS_NOT_BOOTSTRAP");
-        }
-
-        if let Ok(s) = env::var("CARGOFLAGS") {
-            cargo.args(s.split_whitespace());
-        }
-
-        match mode {
-            Mode::Std | Mode::ToolBootstrap | Mode::ToolStd => {}
-            Mode::Rustc | Mode::Codegen | Mode::ToolRustc => {
-                // Build proc macros both for the host and the target unless proc-macros are not
-                // supported by the target.
-                if target != compiler.host && cmd_kind != Kind::Check {
-                    let error = command(self.rustc(compiler))
-                        .arg("--target")
-                        .arg(target.rustc_target_arg())
-                        .arg("--print=file-names")
-                        .arg("--crate-type=proc-macro")
-                        .arg("-")
-                        .run_capture(self)
-                        .stderr();
-                    let not_supported = error
-                        .lines()
-                        .any(|line| line.contains("unsupported crate type `proc-macro`"));
-                    if !not_supported {
-                        cargo.arg("-Zdual-proc-macros");
-                        rustflags.arg("-Zdual-proc-macros");
-                    }
-                }
-            }
-        }
-
-        // This tells Cargo (and in turn, rustc) to output more complete
-        // dependency information.  Most importantly for bootstrap, this
-        // includes sysroot artifacts, like libstd, which means that we don't
-        // need to track those in bootstrap (an error prone process!). This
-        // feature is currently unstable as there may be some bugs and such, but
-        // it represents a big improvement in bootstrap's reliability on
-        // rebuilds, so we're using it here.
-        //
-        // For some additional context, see #63470 (the PR originally adding
-        // this), as well as #63012 which is the tracking issue for this
-        // feature on the rustc side.
-        cargo.arg("-Zbinary-dep-depinfo");
-        let allow_features = match mode {
-            Mode::ToolBootstrap | Mode::ToolStd => {
-                // Restrict the allowed features so we don't depend on nightly
-                // accidentally.
-                //
-                // binary-dep-depinfo is used by bootstrap itself for all
-                // compilations.
-                //
-                // Lots of tools depend on proc_macro2 and proc-macro-error.
-                // Those have build scripts which assume nightly features are
-                // available if the `rustc` version is "nighty" or "dev". See
-                // bin/rustc.rs for why that is a problem. Instead of labeling
-                // those features for each individual tool that needs them,
-                // just blanket allow them here.
-                //
-                // If this is ever removed, be sure to add something else in
-                // its place to keep the restrictions in place (or make a way
-                // to unset RUSTC_BOOTSTRAP).
-                "binary-dep-depinfo,proc_macro_span,proc_macro_span_shrink,proc_macro_diagnostic"
-                    .to_string()
-            }
-            Mode::Std | Mode::Rustc | Mode::Codegen | Mode::ToolRustc => String::new(),
-        };
-
-        cargo.arg("-j").arg(self.jobs().to_string());
-
-        // FIXME: Temporary fix for https://github.com/rust-lang/cargo/issues/3005
-        // Force cargo to output binaries with disambiguating hashes in the name
-        let mut metadata = if compiler.stage == 0 {
-            // Treat stage0 like a special channel, whether it's a normal prior-
-            // release rustc or a local rebuild with the same version, so we
-            // never mix these libraries by accident.
-            "bootstrap".to_string()
-        } else {
-            self.config.channel.to_string()
-        };
-        // We want to make sure that none of the dependencies between
-        // std/test/rustc unify with one another. This is done for weird linkage
-        // reasons but the gist of the problem is that if librustc, libtest, and
-        // libstd all depend on libc from crates.io (which they actually do) we
-        // want to make sure they all get distinct versions. Things get really
-        // weird if we try to unify all these dependencies right now, namely
-        // around how many times the library is linked in dynamic libraries and
-        // such. If rustc were a static executable or if we didn't ship dylibs
-        // this wouldn't be a problem, but we do, so it is. This is in general
-        // just here to make sure things build right. If you can remove this and
-        // things still build right, please do!
-        match mode {
-            Mode::Std => metadata.push_str("std"),
-            // When we're building rustc tools, they're built with a search path
-            // that contains things built during the rustc build. For example,
-            // bitflags is built during the rustc build, and is a dependency of
-            // rustdoc as well. We're building rustdoc in a different target
-            // directory, though, which means that Cargo will rebuild the
-            // dependency. When we go on to build rustdoc, we'll look for
-            // bitflags, and find two different copies: one built during the
-            // rustc step and one that we just built. This isn't always a
-            // problem, somehow -- not really clear why -- but we know that this
-            // fixes things.
-            Mode::ToolRustc => metadata.push_str("tool-rustc"),
-            // Same for codegen backends.
-            Mode::Codegen => metadata.push_str("codegen"),
-            _ => {}
-        }
-        cargo.env("__CARGO_DEFAULT_LIB_METADATA", &metadata);
-
-        if cmd_kind == Kind::Clippy {
-            rustflags.arg("-Zforce-unstable-if-unmarked");
-        }
-
-        rustflags.arg("-Zmacro-backtrace");
-
-        let want_rustdoc = self.doc_tests != DocTests::No;
-
-        // Clear the output directory if the real rustc we're using has changed;
-        // Cargo cannot detect this as it thinks rustc is bootstrap/debug/rustc.
-        //
-        // Avoid doing this during dry run as that usually means the relevant
-        // compiler is not yet linked/copied properly.
-        //
-        // Only clear out the directory if we're compiling std; otherwise, we
-        // should let Cargo take care of things for us (via depdep info)
-        if !self.config.dry_run() && mode == Mode::Std && cmd_kind == Kind::Build {
-            self.clear_if_dirty(&out_dir, &self.rustc(compiler));
-        }
-
-        let rustdoc_path = match cmd_kind {
-            Kind::Doc | Kind::Test | Kind::MiriTest => self.rustdoc(compiler),
-            _ => PathBuf::from("/path/to/nowhere/rustdoc/not/required"),
-        };
-
-        // Customize the compiler we're running. Specify the compiler to cargo
-        // as our shim and then pass it some various options used to configure
-        // how the actual compiler itself is called.
-        //
-        // These variables are primarily all read by
-        // src/bootstrap/bin/{rustc.rs,rustdoc.rs}
-        cargo
-            .env("RUSTBUILD_NATIVE_DIR", self.native_dir(target))
-            .env("RUSTC_REAL", self.rustc(compiler))
-            .env("RUSTC_STAGE", stage.to_string())
-            .env("RUSTC_SYSROOT", sysroot)
-            .env("RUSTC_LIBDIR", libdir)
-            .env("RUSTDOC", self.bootstrap_out.join("rustdoc"))
-            .env("RUSTDOC_REAL", rustdoc_path)
-            .env("RUSTC_ERROR_METADATA_DST", self.extended_error_dir())
-            .env("RUSTC_BREAK_ON_ICE", "1");
-
-        // Set RUSTC_WRAPPER to the bootstrap shim, which switches between beta and in-tree
-        // sysroot depending on whether we're building build scripts.
-        // NOTE: we intentionally use RUSTC_WRAPPER so that we can support clippy - RUSTC is not
-        // respected by clippy-driver; RUSTC_WRAPPER happens earlier, before clippy runs.
-        cargo.env("RUSTC_WRAPPER", self.bootstrap_out.join("rustc"));
-        // NOTE: we also need to set RUSTC so cargo can run `rustc -vV`; apparently that ignores RUSTC_WRAPPER >:(
-        cargo.env("RUSTC", self.bootstrap_out.join("rustc"));
-
-        // Someone might have set some previous rustc wrapper (e.g.
-        // sccache) before bootstrap overrode it. Respect that variable.
-        if let Some(existing_wrapper) = env::var_os("RUSTC_WRAPPER") {
-            cargo.env("RUSTC_WRAPPER_REAL", existing_wrapper);
-        }
-
-        // If this is for `miri-test`, prepare the sysroots.
-        if cmd_kind == Kind::MiriTest {
-            self.ensure(compile::Std::new(compiler, compiler.host));
-            let host_sysroot = self.sysroot(compiler);
-            let miri_sysroot = test::Miri::build_miri_sysroot(self, compiler, target);
-            cargo.env("MIRI_SYSROOT", &miri_sysroot);
-            cargo.env("MIRI_HOST_SYSROOT", &host_sysroot);
-        }
-
-        cargo.env(profile_var("STRIP"), self.config.rust_strip.to_string());
-
-        if let Some(stack_protector) = &self.config.rust_stack_protector {
-            rustflags.arg(&format!("-Zstack-protector={stack_protector}"));
-        }
-
-        if !matches!(cmd_kind, Kind::Build | Kind::Check | Kind::Clippy | Kind::Fix) && want_rustdoc
-        {
-            cargo.env("RUSTDOC_LIBDIR", self.rustc_libdir(compiler));
-        }
-
-        let debuginfo_level = match mode {
-            Mode::Rustc | Mode::Codegen => self.config.rust_debuginfo_level_rustc,
-            Mode::Std => self.config.rust_debuginfo_level_std,
-            Mode::ToolBootstrap | Mode::ToolStd | Mode::ToolRustc => {
-                self.config.rust_debuginfo_level_tools
-            }
-        };
-        cargo.env(profile_var("DEBUG"), debuginfo_level.to_string());
-        if let Some(opt_level) = &self.config.rust_optimize.get_opt_level() {
-            cargo.env(profile_var("OPT_LEVEL"), opt_level);
-        }
-        cargo.env(
-            profile_var("DEBUG_ASSERTIONS"),
-            if mode == Mode::Std {
-                self.config.rust_debug_assertions_std.to_string()
-            } else {
-                self.config.rust_debug_assertions.to_string()
-            },
-        );
-        cargo.env(
-            profile_var("OVERFLOW_CHECKS"),
-            if mode == Mode::Std {
-                self.config.rust_overflow_checks_std.to_string()
-            } else {
-                self.config.rust_overflow_checks.to_string()
-            },
-        );
-
-        match self.config.split_debuginfo(target) {
-            SplitDebuginfo::Packed => rustflags.arg("-Csplit-debuginfo=packed"),
-            SplitDebuginfo::Unpacked => rustflags.arg("-Csplit-debuginfo=unpacked"),
-            SplitDebuginfo::Off => rustflags.arg("-Csplit-debuginfo=off"),
-        };
-
-        if self.config.cmd.bless() {
-            // Bless `expect!` tests.
-            cargo.env("UPDATE_EXPECT", "1");
-        }
-
-        if !mode.is_tool() {
-            cargo.env("RUSTC_FORCE_UNSTABLE", "1");
-        }
-
-        if let Some(x) = self.crt_static(target) {
-            if x {
-                rustflags.arg("-Ctarget-feature=+crt-static");
-            } else {
-                rustflags.arg("-Ctarget-feature=-crt-static");
-            }
-        }
-
-        if let Some(x) = self.crt_static(compiler.host) {
-            let sign = if x { "+" } else { "-" };
-            hostflags.arg(format!("-Ctarget-feature={sign}crt-static"));
-        }
-
-        if let Some(map_to) = self.build.debuginfo_map_to(GitRepo::Rustc) {
-            let map = format!("{}={}", self.build.src.display(), map_to);
-            cargo.env("RUSTC_DEBUGINFO_MAP", map);
-
-            // `rustc` needs to know the virtual `/rustc/$hash` we're mapping to,
-            // in order to opportunistically reverse it later.
-            cargo.env("CFG_VIRTUAL_RUST_SOURCE_BASE_DIR", map_to);
-        }
-
-        if self.config.rust_remap_debuginfo {
-            let mut env_var = OsString::new();
-            if self.config.vendor {
-                let vendor = self.build.src.join("vendor");
-                env_var.push(vendor);
-                env_var.push("=/rust/deps");
-            } else {
-                let registry_src = t!(home::cargo_home()).join("registry").join("src");
-                for entry in t!(std::fs::read_dir(registry_src)) {
-                    if !env_var.is_empty() {
-                        env_var.push("\t");
-                    }
-                    env_var.push(t!(entry).path());
-                    env_var.push("=/rust/deps");
-                }
-            }
-            cargo.env("RUSTC_CARGO_REGISTRY_SRC_TO_REMAP", env_var);
-        }
-
-        // Enable usage of unstable features
-        cargo.env("RUSTC_BOOTSTRAP", "1");
-
-        if self.config.dump_bootstrap_shims {
-            prepare_behaviour_dump_dir(self.build);
-
-            cargo
-                .env("DUMP_BOOTSTRAP_SHIMS", self.build.out.join("bootstrap-shims-dump"))
-                .env("BUILD_OUT", &self.build.out)
-                .env("CARGO_HOME", t!(home::cargo_home()));
-        };
-
-        self.add_rust_test_threads(&mut cargo);
-
-        // Almost all of the crates that we compile as part of the bootstrap may
-        // have a build script, including the standard library. To compile a
-        // build script, however, it itself needs a standard library! This
-        // introduces a bit of a pickle when we're compiling the standard
-        // library itself.
-        //
-        // To work around this we actually end up using the snapshot compiler
-        // (stage0) for compiling build scripts of the standard library itself.
-        // The stage0 compiler is guaranteed to have a libstd available for use.
-        //
-        // For other crates, however, we know that we've already got a standard
-        // library up and running, so we can use the normal compiler to compile
-        // build scripts in that situation.
-        if mode == Mode::Std {
-            cargo
-                .env("RUSTC_SNAPSHOT", &self.initial_rustc)
-                .env("RUSTC_SNAPSHOT_LIBDIR", self.rustc_snapshot_libdir());
-        } else {
-            cargo
-                .env("RUSTC_SNAPSHOT", self.rustc(compiler))
-                .env("RUSTC_SNAPSHOT_LIBDIR", self.rustc_libdir(compiler));
-        }
-
-        // Tools that use compiler libraries may inherit the `-lLLVM` link
-        // requirement, but the `-L` library path is not propagated across
-        // separate Cargo projects. We can add LLVM's library path to the
-        // platform-specific environment variable as a workaround.
-        if mode == Mode::ToolRustc || mode == Mode::Codegen {
-            if let Some(llvm_config) = self.llvm_config(target) {
-                let llvm_libdir =
-                    command(llvm_config).arg("--libdir").run_capture_stdout(self).stdout();
-                add_link_lib_path(vec![llvm_libdir.trim().into()], &mut cargo);
-            }
-        }
-
-        // Compile everything except libraries and proc macros with the more
-        // efficient initial-exec TLS model. This doesn't work with `dlopen`,
-        // so we can't use it by default in general, but we can use it for tools
-        // and our own internal libraries.
-        if !mode.must_support_dlopen() && !target.triple.starts_with("powerpc-") {
-            cargo.env("RUSTC_TLS_MODEL_INITIAL_EXEC", "1");
-        }
-
-        // Ignore incremental modes except for stage0, since we're
-        // not guaranteeing correctness across builds if the compiler
-        // is changing under your feet.
-        if self.config.incremental && compiler.stage == 0 {
-            cargo.env("CARGO_INCREMENTAL", "1");
-        } else {
-            // Don't rely on any default setting for incr. comp. in Cargo
-            cargo.env("CARGO_INCREMENTAL", "0");
-        }
-
-        if let Some(ref on_fail) = self.config.on_fail {
-            cargo.env("RUSTC_ON_FAIL", on_fail);
-        }
-
-        if self.config.print_step_timings {
-            cargo.env("RUSTC_PRINT_STEP_TIMINGS", "1");
-        }
-
-        if self.config.print_step_rusage {
-            cargo.env("RUSTC_PRINT_STEP_RUSAGE", "1");
-        }
-
-        if self.config.backtrace_on_ice {
-            cargo.env("RUSTC_BACKTRACE_ON_ICE", "1");
-        }
-
-        if self.is_verbose() {
-            // This provides very useful logs especially when debugging build cache-related stuff.
-            cargo.env("CARGO_LOG", "cargo::core::compiler::fingerprint=info");
-        }
-
-        cargo.env("RUSTC_VERBOSE", self.verbosity.to_string());
-
-        // Downstream forks of the Rust compiler might want to use a custom libc to add support for
-        // targets that are not yet available upstream. Adding a patch to replace libc with a
-        // custom one would cause compilation errors though, because Cargo would interpret the
-        // custom libc as part of the workspace, and apply the check-cfg lints on it.
-        //
-        // The libc build script emits check-cfg flags only when this environment variable is set,
-        // so this line allows the use of custom libcs.
-        cargo.env("LIBC_CHECK_CFG", "1");
-
-        if source_type == SourceType::InTree {
-            let mut lint_flags = Vec::new();
-            // When extending this list, add the new lints to the RUSTFLAGS of the
-            // build_bootstrap function of src/bootstrap/bootstrap.py as well as
-            // some code doesn't go through this `rustc` wrapper.
-            lint_flags.push("-Wrust_2018_idioms");
-            lint_flags.push("-Wunused_lifetimes");
-
-            if self.config.deny_warnings {
-                lint_flags.push("-Dwarnings");
-                rustdocflags.arg("-Dwarnings");
-            }
-
-            // This does not use RUSTFLAGS due to caching issues with Cargo.
-            // Clippy is treated as an "in tree" tool, but shares the same
-            // cache as other "submodule" tools. With these options set in
-            // RUSTFLAGS, that causes *every* shared dependency to be rebuilt.
-            // By injecting this into the rustc wrapper, this circumvents
-            // Cargo's fingerprint detection. This is fine because lint flags
-            // are always ignored in dependencies. Eventually this should be
-            // fixed via better support from Cargo.
-            cargo.env("RUSTC_LINT_FLAGS", lint_flags.join(" "));
-
-            rustdocflags.arg("-Wrustdoc::invalid_codeblock_attributes");
-        }
-
-        if mode == Mode::Rustc {
-            rustflags.arg("-Wrustc::internal");
-            // FIXME(edition_2024): Change this to `-Wrust_2024_idioms` when all
-            // of the individual lints are satisfied.
-            rustflags.arg("-Wkeyword_idents_2024");
-            rustflags.arg("-Wunsafe_op_in_unsafe_fn");
-        }
-
-        if self.config.rust_frame_pointers {
-            rustflags.arg("-Cforce-frame-pointers=true");
-        }
-
-        // If Control Flow Guard is enabled, pass the `control-flow-guard` flag to rustc
-        // when compiling the standard library, since this might be linked into the final outputs
-        // produced by rustc. Since this mitigation is only available on Windows, only enable it
-        // for the standard library in case the compiler is run on a non-Windows platform.
-        // This is not needed for stage 0 artifacts because these will only be used for building
-        // the stage 1 compiler.
-        if cfg!(windows)
-            && mode == Mode::Std
-            && self.config.control_flow_guard
-            && compiler.stage >= 1
-        {
-            rustflags.arg("-Ccontrol-flow-guard");
-        }
-
-        // If EHCont Guard is enabled, pass the `-Zehcont-guard` flag to rustc when compiling the
-        // standard library, since this might be linked into the final outputs produced by rustc.
-        // Since this mitigation is only available on Windows, only enable it for the standard
-        // library in case the compiler is run on a non-Windows platform.
-        // This is not needed for stage 0 artifacts because these will only be used for building
-        // the stage 1 compiler.
-        if cfg!(windows) && mode == Mode::Std && self.config.ehcont_guard && compiler.stage >= 1 {
-            rustflags.arg("-Zehcont-guard");
-        }
-
-        // For `cargo doc` invocations, make rustdoc print the Rust version into the docs
-        // This replaces spaces with tabs because RUSTDOCFLAGS does not
-        // support arguments with regular spaces. Hopefully someday Cargo will
-        // have space support.
-        let rust_version = self.rust_version().replace(' ', "\t");
-        rustdocflags.arg("--crate-version").arg(&rust_version);
-
-        // Environment variables *required* throughout the build
-        //
-        // FIXME: should update code to not require this env var
-
-        // The host this new compiler will *run* on.
-        cargo.env("CFG_COMPILER_HOST_TRIPLE", target.triple);
-        // The host this new compiler is being *built* on.
-        cargo.env("CFG_COMPILER_BUILD_TRIPLE", compiler.host.triple);
-
-        // Set this for all builds to make sure doc builds also get it.
-        cargo.env("CFG_RELEASE_CHANNEL", &self.config.channel);
-
-        // This one's a bit tricky. As of the time of this writing the compiler
-        // links to the `winapi` crate on crates.io. This crate provides raw
-        // bindings to Windows system functions, sort of like libc does for
-        // Unix. This crate also, however, provides "import libraries" for the
-        // MinGW targets. There's an import library per dll in the windows
-        // distribution which is what's linked to. These custom import libraries
-        // are used because the winapi crate can reference Windows functions not
-        // present in the MinGW import libraries.
-        //
-        // For example MinGW may ship libdbghelp.a, but it may not have
-        // references to all the functions in the dbghelp dll. Instead the
-        // custom import library for dbghelp in the winapi crates has all this
-        // information.
-        //
-        // Unfortunately for us though the import libraries are linked by
-        // default via `-ldylib=winapi_foo`. That is, they're linked with the
-        // `dylib` type with a `winapi_` prefix (so the winapi ones don't
-        // conflict with the system MinGW ones). This consequently means that
-        // the binaries we ship of things like rustc_codegen_llvm (aka the rustc_codegen_llvm
-        // DLL) when linked against *again*, for example with procedural macros
-        // or plugins, will trigger the propagation logic of `-ldylib`, passing
-        // `-lwinapi_foo` to the linker again. This isn't actually available in
-        // our distribution, however, so the link fails.
-        //
-        // To solve this problem we tell winapi to not use its bundled import
-        // libraries. This means that it will link to the system MinGW import
-        // libraries by default, and the `-ldylib=foo` directives will still get
-        // passed to the final linker, but they'll look like `-lfoo` which can
-        // be resolved because MinGW has the import library. The downside is we
-        // don't get newer functions from Windows, but we don't use any of them
-        // anyway.
-        if !mode.is_tool() {
-            cargo.env("WINAPI_NO_BUNDLED_LIBRARIES", "1");
-        }
-
-        for _ in 0..self.verbosity {
-            cargo.arg("-v");
-        }
-
-        match (mode, self.config.rust_codegen_units_std, self.config.rust_codegen_units) {
-            (Mode::Std, Some(n), _) | (_, _, Some(n)) => {
-                cargo.env(profile_var("CODEGEN_UNITS"), n.to_string());
-            }
-            _ => {
-                // Don't set anything
-            }
-        }
-
-        if self.config.locked_deps {
-            cargo.arg("--locked");
-        }
-        if self.config.vendor || self.is_sudo {
-            cargo.arg("--frozen");
-        }
-
-        // Try to use a sysroot-relative bindir, in case it was configured absolutely.
-        cargo.env("RUSTC_INSTALL_BINDIR", self.config.bindir_relative());
-
-        cargo.force_coloring_in_ci();
-
-        // When we build Rust dylibs they're all intended for intermediate
-        // usage, so make sure we pass the -Cprefer-dynamic flag instead of
-        // linking all deps statically into the dylib.
-        if matches!(mode, Mode::Std) {
-            rustflags.arg("-Cprefer-dynamic");
-        }
-        if matches!(mode, Mode::Rustc) && !self.link_std_into_rustc_driver(target) {
-            rustflags.arg("-Cprefer-dynamic");
-        }
-
-        cargo.env(
-            "RUSTC_LINK_STD_INTO_RUSTC_DRIVER",
-            if self.link_std_into_rustc_driver(target) { "1" } else { "0" },
-        );
-
-        // When building incrementally we default to a lower ThinLTO import limit
-        // (unless explicitly specified otherwise). This will produce a somewhat
-        // slower code but give way better compile times.
-        {
-            let limit = match self.config.rust_thin_lto_import_instr_limit {
-                Some(limit) => Some(limit),
-                None if self.config.incremental => Some(10),
-                _ => None,
-            };
-
-            if let Some(limit) = limit {
-                if stage == 0
-                    || self.config.default_codegen_backend(target).unwrap_or_default() == "llvm"
-                {
-                    rustflags.arg(&format!("-Cllvm-args=-import-instr-limit={limit}"));
-                }
-            }
-        }
-
-        if matches!(mode, Mode::Std) {
-            if let Some(mir_opt_level) = self.config.rust_validate_mir_opts {
-                rustflags.arg("-Zvalidate-mir");
-                rustflags.arg(&format!("-Zmir-opt-level={mir_opt_level}"));
-            }
-            if self.config.rust_randomize_layout {
-                rustflags.arg("--cfg=randomized_layouts");
-            }
-            // Always enable inlining MIR when building the standard library.
-            // Without this flag, MIR inlining is disabled when incremental compilation is enabled.
-            // That causes some mir-opt tests which inline functions from the standard library to
-            // break when incremental compilation is enabled. So this overrides the "no inlining
-            // during incremental builds" heuristic for the standard library.
-            rustflags.arg("-Zinline-mir");
-
-            // Similarly, we need to keep debug info for functions inlined into other std functions,
-            // even if we're not going to output debuginfo for the crate we're currently building,
-            // so that it'll be available when downstream consumers of std try to use it.
-            rustflags.arg("-Zinline-mir-preserve-debug");
-        }
-
-        if self.config.rustc_parallel
-            && matches!(mode, Mode::ToolRustc | Mode::Rustc | Mode::Codegen)
-        {
-            // keep in sync with `bootstrap/lib.rs:Build::rustc_features`
-            // `cfg` option for rustc, `features` option for cargo, for conditional compilation
-            rustflags.arg("--cfg=parallel_compiler");
-            rustdocflags.arg("--cfg=parallel_compiler");
-        }
-
-        Cargo {
-            command: cargo,
-            compiler,
-            target,
-            rustflags,
-            rustdocflags,
-            hostflags,
-            allow_features,
-        }
-    }
-
     /// Ensure that a given step is built, returning its output. This will
     /// cache the step, so it is safe (and good!) to call this as often as
     /// needed to ensure that all dependencies are built.
@@ -2398,337 +1531,3 @@ impl<'a> Builder<'a> {
         }
     }
 }
-
-/// Represents flag values in `String` form with whitespace delimiter to pass it to the compiler later.
-///
-/// `-Z crate-attr` flags will be applied recursively on the target code using the `rustc_parse::parser::Parser`.
-/// See `rustc_builtin_macros::cmdline_attrs::inject` for more information.
-#[derive(Debug, Clone)]
-struct Rustflags(String, TargetSelection);
-
-impl Rustflags {
-    fn new(target: TargetSelection) -> Rustflags {
-        let mut ret = Rustflags(String::new(), target);
-        ret.propagate_cargo_env("RUSTFLAGS");
-        ret
-    }
-
-    /// By default, cargo will pick up on various variables in the environment. However, bootstrap
-    /// reuses those variables to pass additional flags to rustdoc, so by default they get overridden.
-    /// Explicitly add back any previous value in the environment.
-    ///
-    /// `prefix` is usually `RUSTFLAGS` or `RUSTDOCFLAGS`.
-    fn propagate_cargo_env(&mut self, prefix: &str) {
-        // Inherit `RUSTFLAGS` by default ...
-        self.env(prefix);
-
-        // ... and also handle target-specific env RUSTFLAGS if they're configured.
-        let target_specific = format!("CARGO_TARGET_{}_{}", crate::envify(&self.1.triple), prefix);
-        self.env(&target_specific);
-    }
-
-    fn env(&mut self, env: &str) {
-        if let Ok(s) = env::var(env) {
-            for part in s.split(' ') {
-                self.arg(part);
-            }
-        }
-    }
-
-    fn arg(&mut self, arg: &str) -> &mut Self {
-        assert_eq!(arg.split(' ').count(), 1);
-        if !self.0.is_empty() {
-            self.0.push(' ');
-        }
-        self.0.push_str(arg);
-        self
-    }
-}
-
-/// Flags that are passed to the `rustc` shim binary.
-/// These flags will only be applied when compiling host code, i.e. when
-/// `--target` is unset.
-#[derive(Debug, Default)]
-pub struct HostFlags {
-    rustc: Vec<String>,
-}
-
-impl HostFlags {
-    const SEPARATOR: &'static str = " ";
-
-    /// Adds a host rustc flag.
-    fn arg<S: Into<String>>(&mut self, flag: S) {
-        let value = flag.into().trim().to_string();
-        assert!(!value.contains(Self::SEPARATOR));
-        self.rustc.push(value);
-    }
-
-    /// Encodes all the flags into a single string.
-    fn encode(self) -> String {
-        self.rustc.join(Self::SEPARATOR)
-    }
-}
-
-#[derive(Debug)]
-pub struct Cargo {
-    command: BootstrapCommand,
-    compiler: Compiler,
-    target: TargetSelection,
-    rustflags: Rustflags,
-    rustdocflags: Rustflags,
-    hostflags: HostFlags,
-    allow_features: String,
-}
-
-impl Cargo {
-    /// Calls `Builder::cargo` and `Cargo::configure_linker` to prepare an invocation of `cargo` to be run.
-    pub fn new(
-        builder: &Builder<'_>,
-        compiler: Compiler,
-        mode: Mode,
-        source_type: SourceType,
-        target: TargetSelection,
-        cmd_kind: Kind,
-    ) -> Cargo {
-        let mut cargo = builder.cargo(compiler, mode, source_type, target, cmd_kind);
-
-        match cmd_kind {
-            // No need to configure the target linker for these command types,
-            // as they don't invoke rustc at all.
-            Kind::Clean | Kind::Suggest | Kind::Format | Kind::Setup => {}
-            _ => {
-                cargo.configure_linker(builder);
-            }
-        }
-
-        cargo
-    }
-
-    pub fn into_cmd(self) -> BootstrapCommand {
-        self.into()
-    }
-
-    /// Same as `Cargo::new` except this one doesn't configure the linker with `Cargo::configure_linker`
-    pub fn new_for_mir_opt_tests(
-        builder: &Builder<'_>,
-        compiler: Compiler,
-        mode: Mode,
-        source_type: SourceType,
-        target: TargetSelection,
-        cmd_kind: Kind,
-    ) -> Cargo {
-        builder.cargo(compiler, mode, source_type, target, cmd_kind)
-    }
-
-    pub fn rustdocflag(&mut self, arg: &str) -> &mut Cargo {
-        self.rustdocflags.arg(arg);
-        self
-    }
-
-    pub fn rustflag(&mut self, arg: &str) -> &mut Cargo {
-        self.rustflags.arg(arg);
-        self
-    }
-
-    pub fn arg(&mut self, arg: impl AsRef<OsStr>) -> &mut Cargo {
-        self.command.arg(arg.as_ref());
-        self
-    }
-
-    pub fn args<I, S>(&mut self, args: I) -> &mut Cargo
-    where
-        I: IntoIterator<Item = S>,
-        S: AsRef<OsStr>,
-    {
-        for arg in args {
-            self.arg(arg.as_ref());
-        }
-        self
-    }
-
-    pub fn env(&mut self, key: impl AsRef<OsStr>, value: impl AsRef<OsStr>) -> &mut Cargo {
-        // These are managed through rustflag/rustdocflag interfaces.
-        assert_ne!(key.as_ref(), "RUSTFLAGS");
-        assert_ne!(key.as_ref(), "RUSTDOCFLAGS");
-        self.command.env(key.as_ref(), value.as_ref());
-        self
-    }
-
-    pub fn add_rustc_lib_path(&mut self, builder: &Builder<'_>) {
-        builder.add_rustc_lib_path(self.compiler, &mut self.command);
-    }
-
-    pub fn current_dir(&mut self, dir: &Path) -> &mut Cargo {
-        self.command.current_dir(dir);
-        self
-    }
-
-    /// Adds nightly-only features that this invocation is allowed to use.
-    ///
-    /// By default, all nightly features are allowed. Once this is called, it
-    /// will be restricted to the given set.
-    pub fn allow_features(&mut self, features: &str) -> &mut Cargo {
-        if !self.allow_features.is_empty() {
-            self.allow_features.push(',');
-        }
-        self.allow_features.push_str(features);
-        self
-    }
-
-    fn configure_linker(&mut self, builder: &Builder<'_>) -> &mut Cargo {
-        let target = self.target;
-        let compiler = self.compiler;
-
-        // Dealing with rpath here is a little special, so let's go into some
-        // detail. First off, `-rpath` is a linker option on Unix platforms
-        // which adds to the runtime dynamic loader path when looking for
-        // dynamic libraries. We use this by default on Unix platforms to ensure
-        // that our nightlies behave the same on Windows, that is they work out
-        // of the box. This can be disabled by setting `rpath = false` in `[rust]`
-        // table of `config.toml`
-        //
-        // Ok, so the astute might be wondering "why isn't `-C rpath` used
-        // here?" and that is indeed a good question to ask. This codegen
-        // option is the compiler's current interface to generating an rpath.
-        // Unfortunately it doesn't quite suffice for us. The flag currently
-        // takes no value as an argument, so the compiler calculates what it
-        // should pass to the linker as `-rpath`. This unfortunately is based on
-        // the **compile time** directory structure which when building with
-        // Cargo will be very different than the runtime directory structure.
-        //
-        // All that's a really long winded way of saying that if we use
-        // `-Crpath` then the executables generated have the wrong rpath of
-        // something like `$ORIGIN/deps` when in fact the way we distribute
-        // rustc requires the rpath to be `$ORIGIN/../lib`.
-        //
-        // So, all in all, to set up the correct rpath we pass the linker
-        // argument manually via `-C link-args=-Wl,-rpath,...`. Plus isn't it
-        // fun to pass a flag to a tool to pass a flag to pass a flag to a tool
-        // to change a flag in a binary?
-        if builder.config.rpath_enabled(target) && helpers::use_host_linker(target) {
-            let libdir = builder.sysroot_libdir_relative(compiler).to_str().unwrap();
-            let rpath = if target.contains("apple") {
-                // Note that we need to take one extra step on macOS to also pass
-                // `-Wl,-instal_name,@rpath/...` to get things to work right. To
-                // do that we pass a weird flag to the compiler to get it to do
-                // so. Note that this is definitely a hack, and we should likely
-                // flesh out rpath support more fully in the future.
-                self.rustflags.arg("-Zosx-rpath-install-name");
-                Some(format!("-Wl,-rpath,@loader_path/../{libdir}"))
-            } else if !target.is_windows() && !target.contains("aix") && !target.contains("xous") {
-                self.rustflags.arg("-Clink-args=-Wl,-z,origin");
-                Some(format!("-Wl,-rpath,$ORIGIN/../{libdir}"))
-            } else {
-                None
-            };
-            if let Some(rpath) = rpath {
-                self.rustflags.arg(&format!("-Clink-args={rpath}"));
-            }
-        }
-
-        for arg in linker_args(builder, compiler.host, LldThreads::Yes) {
-            self.hostflags.arg(&arg);
-        }
-
-        if let Some(target_linker) = builder.linker(target) {
-            let target = crate::envify(&target.triple);
-            self.command.env(format!("CARGO_TARGET_{target}_LINKER"), target_linker);
-        }
-        // We want to set -Clinker using Cargo, therefore we only call `linker_flags` and not
-        // `linker_args` here.
-        for flag in linker_flags(builder, target, LldThreads::Yes) {
-            self.rustflags.arg(&flag);
-        }
-        for arg in linker_args(builder, target, LldThreads::Yes) {
-            self.rustdocflags.arg(&arg);
-        }
-
-        if !builder.config.dry_run()
-            && builder.cc.borrow()[&target].args().iter().any(|arg| arg == "-gz")
-        {
-            self.rustflags.arg("-Clink-arg=-gz");
-        }
-
-        // Throughout the build Cargo can execute a number of build scripts
-        // compiling C/C++ code and we need to pass compilers, archivers, flags, etc
-        // obtained previously to those build scripts.
-        // Build scripts use either the `cc` crate or `configure/make` so we pass
-        // the options through environment variables that are fetched and understood by both.
-        //
-        // FIXME: the guard against msvc shouldn't need to be here
-        if target.is_msvc() {
-            if let Some(ref cl) = builder.config.llvm_clang_cl {
-                // FIXME: There is a bug in Clang 18 when building for ARM64:
-                // https://github.com/llvm/llvm-project/pull/81849. This is
-                // fixed in LLVM 19, but can't be backported.
-                if !target.starts_with("aarch64") && !target.starts_with("arm64ec") {
-                    self.command.env("CC", cl).env("CXX", cl);
-                }
-            }
-        } else {
-            let ccache = builder.config.ccache.as_ref();
-            let ccacheify = |s: &Path| {
-                let ccache = match ccache {
-                    Some(ref s) => s,
-                    None => return s.display().to_string(),
-                };
-                // FIXME: the cc-rs crate only recognizes the literal strings
-                // `ccache` and `sccache` when doing caching compilations, so we
-                // mirror that here. It should probably be fixed upstream to
-                // accept a new env var or otherwise work with custom ccache
-                // vars.
-                match &ccache[..] {
-                    "ccache" | "sccache" => format!("{} {}", ccache, s.display()),
-                    _ => s.display().to_string(),
-                }
-            };
-            let triple_underscored = target.triple.replace('-', "_");
-            let cc = ccacheify(&builder.cc(target));
-            self.command.env(format!("CC_{triple_underscored}"), &cc);
-
-            let cflags = builder.cflags(target, GitRepo::Rustc, CLang::C).join(" ");
-            self.command.env(format!("CFLAGS_{triple_underscored}"), &cflags);
-
-            if let Some(ar) = builder.ar(target) {
-                let ranlib = format!("{} s", ar.display());
-                self.command
-                    .env(format!("AR_{triple_underscored}"), ar)
-                    .env(format!("RANLIB_{triple_underscored}"), ranlib);
-            }
-
-            if let Ok(cxx) = builder.cxx(target) {
-                let cxx = ccacheify(&cxx);
-                let cxxflags = builder.cflags(target, GitRepo::Rustc, CLang::Cxx).join(" ");
-                self.command
-                    .env(format!("CXX_{triple_underscored}"), &cxx)
-                    .env(format!("CXXFLAGS_{triple_underscored}"), cxxflags);
-            }
-        }
-
-        self
-    }
-}
-
-impl From<Cargo> for BootstrapCommand {
-    fn from(mut cargo: Cargo) -> BootstrapCommand {
-        let rustflags = &cargo.rustflags.0;
-        if !rustflags.is_empty() {
-            cargo.command.env("RUSTFLAGS", rustflags);
-        }
-
-        let rustdocflags = &cargo.rustdocflags.0;
-        if !rustdocflags.is_empty() {
-            cargo.command.env("RUSTDOCFLAGS", rustdocflags);
-        }
-
-        let encoded_hostflags = cargo.hostflags.encode();
-        if !encoded_hostflags.is_empty() {
-            cargo.command.env("RUSTC_HOST_FLAGS", encoded_hostflags);
-        }
-
-        if !cargo.allow_features.is_empty() {
-            cargo.command.env("RUSTC_ALLOW_FEATURES", cargo.allow_features);
-        }
-        cargo.command
-    }
-}
diff --git a/src/bootstrap/src/core/builder/tests.rs b/src/bootstrap/src/core/builder/tests.rs
index 695a66834d4..21c5f7232a1 100644
--- a/src/bootstrap/src/core/builder/tests.rs
+++ b/src/bootstrap/src/core/builder/tests.rs
@@ -637,7 +637,7 @@ mod dist {
         assert_eq!(first(builder.cache.all::<test::Crate>()), &[test::Crate {
             compiler: Compiler { host, stage: 0 },
             target: host,
-            mode: Mode::Std,
+            mode: crate::Mode::Std,
             crates: vec!["std".to_owned()],
         },]);
     }
diff --git a/src/bootstrap/src/core/config/config.rs b/src/bootstrap/src/core/config/config.rs
index c2ab439891e..aeb81b14638 100644
--- a/src/bootstrap/src/core/config/config.rs
+++ b/src/bootstrap/src/core/config/config.rs
@@ -891,6 +891,7 @@ define_config! {
         metrics: Option<bool> = "metrics",
         android_ndk: Option<PathBuf> = "android-ndk",
         optimized_compiler_builtins: Option<bool> = "optimized-compiler-builtins",
+        jobs: Option<u32> = "jobs",
     }
 }
 
@@ -1289,7 +1290,6 @@ impl Config {
         config.rustc_error_format = flags.rustc_error_format;
         config.json_output = flags.json_output;
         config.on_fail = flags.on_fail;
-        config.jobs = Some(threads_from_config(flags.jobs as u32));
         config.cmd = flags.cmd;
         config.incremental = flags.incremental;
         config.dry_run = if flags.dry_run { DryRun::UserSelected } else { DryRun::Disabled };
@@ -1511,8 +1511,11 @@ impl Config {
             metrics: _,
             android_ndk,
             optimized_compiler_builtins,
+            jobs,
         } = toml.build.unwrap_or_default();
 
+        config.jobs = Some(threads_from_config(flags.jobs.unwrap_or(jobs.unwrap_or(0))));
+
         if let Some(file_build) = build {
             config.build = TargetSelection::from_user(&file_build);
         };
diff --git a/src/bootstrap/src/core/config/flags.rs b/src/bootstrap/src/core/config/flags.rs
index 3aefe517a5b..bfeb811508c 100644
--- a/src/bootstrap/src/core/config/flags.rs
+++ b/src/bootstrap/src/core/config/flags.rs
@@ -110,11 +110,10 @@ pub struct Flags {
         short,
         long,
         value_hint = clap::ValueHint::Other,
-        default_value_t = std::thread::available_parallelism().map_or(1, std::num::NonZeroUsize::get),
         value_name = "JOBS"
     )]
     /// number of jobs to run in parallel
-    pub jobs: usize,
+    pub jobs: Option<u32>,
     // This overrides the deny-warnings configuration option,
     // which passes -Dwarnings to the compiler invocations.
     #[arg(global = true, long)]
diff --git a/src/bootstrap/src/core/config/tests.rs b/src/bootstrap/src/core/config/tests.rs
index 2611b6cf51b..1f02757682c 100644
--- a/src/bootstrap/src/core/config/tests.rs
+++ b/src/bootstrap/src/core/config/tests.rs
@@ -352,3 +352,61 @@ fn parse_rust_std_features_empty() {
 fn parse_rust_std_features_invalid() {
     parse("rust.std-features = \"backtrace\"");
 }
+
+#[test]
+fn parse_jobs() {
+    assert_eq!(parse("build.jobs = 1").jobs, Some(1));
+}
+
+#[test]
+fn jobs_precedence() {
+    // `--jobs` should take precedence over using `--set build.jobs`.
+
+    let config = Config::parse_inner(
+        Flags::parse(&[
+            "check".to_owned(),
+            "--config=/does/not/exist".to_owned(),
+            "--jobs=67890".to_owned(),
+            "--set=build.jobs=12345".to_owned(),
+        ]),
+        |&_| toml::from_str(""),
+    );
+    assert_eq!(config.jobs, Some(67890));
+
+    // `--set build.jobs` should take precedence over `config.toml`.
+    let config = Config::parse_inner(
+        Flags::parse(&[
+            "check".to_owned(),
+            "--config=/does/not/exist".to_owned(),
+            "--set=build.jobs=12345".to_owned(),
+        ]),
+        |&_| {
+            toml::from_str(
+                r#"
+            [build]
+            jobs = 67890
+        "#,
+            )
+        },
+    );
+    assert_eq!(config.jobs, Some(12345));
+
+    // `--jobs` > `--set build.jobs` > `config.toml`
+    let config = Config::parse_inner(
+        Flags::parse(&[
+            "check".to_owned(),
+            "--jobs=123".to_owned(),
+            "--config=/does/not/exist".to_owned(),
+            "--set=build.jobs=456".to_owned(),
+        ]),
+        |&_| {
+            toml::from_str(
+                r#"
+            [build]
+            jobs = 789
+        "#,
+            )
+        },
+    );
+    assert_eq!(config.jobs, Some(123));
+}
diff --git a/src/bootstrap/src/utils/change_tracker.rs b/src/bootstrap/src/utils/change_tracker.rs
index b37786496cb..9169bc90a45 100644
--- a/src/bootstrap/src/utils/change_tracker.rs
+++ b/src/bootstrap/src/utils/change_tracker.rs
@@ -275,4 +275,9 @@ pub const CONFIG_CHANGE_HISTORY: &[ChangeInfo] = &[
         severity: ChangeSeverity::Info,
         summary: "New option `./x setup editor` added, replacing `./x setup vscode` and adding support for vim, emacs and helix.",
     },
+    ChangeInfo {
+        change_id: 131838,
+        severity: ChangeSeverity::Info,
+        summary: "Allow setting `--jobs` in config.toml with `build.jobs`.",
+    },
 ];
diff --git a/src/ci/docker/host-x86_64/dist-various-2/Dockerfile b/src/ci/docker/host-x86_64/dist-various-2/Dockerfile
index 1b98d541693..410f0f92e60 100644
--- a/src/ci/docker/host-x86_64/dist-various-2/Dockerfile
+++ b/src/ci/docker/host-x86_64/dist-various-2/Dockerfile
@@ -56,9 +56,9 @@ ENV \
     CFLAGS_x86_64_fortanix_unknown_sgx="-D__ELF__ -isystem/usr/include/x86_64-linux-gnu -mlvi-hardening -mllvm -x86-experimental-lvi-inline-asm-hardening" \
     CXX_x86_64_fortanix_unknown_sgx=clang++-11 \
     CXXFLAGS_x86_64_fortanix_unknown_sgx="-D__ELF__ -isystem/usr/include/x86_64-linux-gnu -mlvi-hardening -mllvm -x86-experimental-lvi-inline-asm-hardening" \
-    AR_i686_unknown_freebsd=i686-unknown-freebsd12-ar \
-    CC_i686_unknown_freebsd=i686-unknown-freebsd12-clang \
-    CXX_i686_unknown_freebsd=i686-unknown-freebsd12-clang++ \
+    AR_i686_unknown_freebsd=i686-unknown-freebsd13-ar \
+    CC_i686_unknown_freebsd=i686-unknown-freebsd13-clang \
+    CXX_i686_unknown_freebsd=i686-unknown-freebsd13-clang++ \
     CC_aarch64_unknown_uefi=clang-11 \
     CXX_aarch64_unknown_uefi=clang++-11 \
     CC_i686_unknown_uefi=clang-11 \
diff --git a/src/ci/docker/host-x86_64/dist-x86_64-freebsd/Dockerfile b/src/ci/docker/host-x86_64/dist-x86_64-freebsd/Dockerfile
index b3c5f41bdd7..fd0f5da8c49 100644
--- a/src/ci/docker/host-x86_64/dist-x86_64-freebsd/Dockerfile
+++ b/src/ci/docker/host-x86_64/dist-x86_64-freebsd/Dockerfile
@@ -1,4 +1,4 @@
-FROM ubuntu:18.04
+FROM ubuntu:22.04
 
 RUN apt-get update && apt-get install -y --no-install-recommends \
   clang \
@@ -29,9 +29,9 @@ COPY scripts/cmake.sh /scripts/
 RUN /scripts/cmake.sh
 
 ENV \
-    AR_x86_64_unknown_freebsd=x86_64-unknown-freebsd12-ar \
-    CC_x86_64_unknown_freebsd=x86_64-unknown-freebsd12-clang \
-    CXX_x86_64_unknown_freebsd=x86_64-unknown-freebsd12-clang++
+    AR_x86_64_unknown_freebsd=x86_64-unknown-freebsd13-ar \
+    CC_x86_64_unknown_freebsd=x86_64-unknown-freebsd13-clang \
+    CXX_x86_64_unknown_freebsd=x86_64-unknown-freebsd13-clang++
 
 ENV HOSTS=x86_64-unknown-freebsd
 
diff --git a/src/ci/docker/host-x86_64/x86_64-gnu-tools/checktools.sh b/src/ci/docker/host-x86_64/x86_64-gnu-tools/checktools.sh
index 303a2f26c0f..8324d1ec586 100755
--- a/src/ci/docker/host-x86_64/x86_64-gnu-tools/checktools.sh
+++ b/src/ci/docker/host-x86_64/x86_64-gnu-tools/checktools.sh
@@ -59,6 +59,8 @@ case $HOST_TARGET in
     # "error: cannot produce cdylib for ... as the target ... does not support these crate types".
     # Only run "pass" tests, which is quite a bit faster.
     #FIXME: Re-enable this once CI issues are fixed
+    # See <https://github.com/rust-lang/rust/issues/127883>
+    # For now, these tests are moved to `x86_64-msvc-ext2` in `src/ci/github-actions/jobs.yml`.
     #python3 "$X_PY" test --stage 2 src/tools/miri --target aarch64-apple-darwin --test-args pass
     #python3 "$X_PY" test --stage 2 src/tools/miri --target i686-pc-windows-gnu --test-args pass
     ;;
diff --git a/src/ci/docker/scripts/freebsd-toolchain.sh b/src/ci/docker/scripts/freebsd-toolchain.sh
index 0d02636db91..4826b81d56c 100755
--- a/src/ci/docker/scripts/freebsd-toolchain.sh
+++ b/src/ci/docker/scripts/freebsd-toolchain.sh
@@ -5,8 +5,8 @@ set -eux
 
 arch=$1
 binutils_version=2.40
-freebsd_version=12.3
-triple=$arch-unknown-freebsd12
+freebsd_version=13.2
+triple=$arch-unknown-freebsd13
 sysroot=/usr/local/$triple
 
 hide_output() {
@@ -59,7 +59,7 @@ done
 
 # Originally downloaded from:
 # URL=https://download.freebsd.org/ftp/releases/${freebsd_arch}/${freebsd_version}-RELEASE/base.txz
-URL=https://ci-mirrors.rust-lang.org/rustc/2022-05-06-freebsd-${freebsd_version}-${freebsd_arch}-base.txz
+URL=https://ci-mirrors.rust-lang.org/rustc/2024-02-18-freebsd-${freebsd_version}-${freebsd_arch}-base.txz
 curl "$URL" | tar xJf - -C "$sysroot" --wildcards "${files_to_extract[@]}"
 
 # Clang can do cross-builds out of the box, if we give it the right
@@ -68,7 +68,7 @@ curl "$URL" | tar xJf - -C "$sysroot" --wildcards "${files_to_extract[@]}"
 # there might be other problems.)
 #
 # The --target option is last because the cross-build of LLVM uses
-# --target without an OS version ("-freebsd" vs. "-freebsd12").  This
+# --target without an OS version ("-freebsd" vs. "-freebsd13").  This
 # makes Clang default to libstdc++ (which no longer exists), and also
 # controls other features, like GNU-style symbol table hashing and
 # anything predicated on the version number in the __FreeBSD__
diff --git a/src/ci/github-actions/jobs.yml b/src/ci/github-actions/jobs.yml
index 8f49f623afa..2b636604049 100644
--- a/src/ci/github-actions/jobs.yml
+++ b/src/ci/github-actions/jobs.yml
@@ -5,6 +5,11 @@ runners:
     env: { }
 
   - &job-linux-4c
+    os: ubuntu-20.04
+    <<: *base-job
+
+  # Large runner used mainly for its bigger disk capacity
+  - &job-linux-4c-largedisk
     os: ubuntu-20.04-4core-16gb
     <<: *base-job
 
@@ -46,7 +51,7 @@ envs:
     RUSTC_RETRY_LINKER_ON_SEGFAULT: 1
     MACOSX_DEPLOYMENT_TARGET: 10.12
     MACOSX_STD_DEPLOYMENT_TARGET: 10.12
-    SELECT_XCODE: /Applications/Xcode_14.3.1.app
+    SELECT_XCODE: /Applications/Xcode_15.2.app
     NO_LLVM_ASSERTIONS: 1
     NO_DEBUG_ASSERTIONS: 1
     NO_OVERFLOW_CHECKS: 1
@@ -127,7 +132,7 @@ auto:
   - image: dist-aarch64-linux
     env:
       CODEGEN_BACKENDS: llvm,cranelift
-    <<: *job-linux-4c
+    <<: *job-linux-4c-largedisk
 
   - image: dist-android
     <<: *job-linux-4c
@@ -148,28 +153,28 @@ auto:
     <<: *job-linux-4c
 
   - image: dist-loongarch64-linux
-    <<: *job-linux-4c
+    <<: *job-linux-4c-largedisk
 
   - image: dist-loongarch64-musl
-    <<: *job-linux-4c
+    <<: *job-linux-4c-largedisk
 
   - image: dist-ohos
     <<: *job-linux-4c
 
   - image: dist-powerpc-linux
-    <<: *job-linux-4c
+    <<: *job-linux-4c-largedisk
 
   - image: dist-powerpc64-linux
-    <<: *job-linux-4c
+    <<: *job-linux-4c-largedisk
 
   - image: dist-powerpc64le-linux
-    <<: *job-linux-4c
+    <<: *job-linux-4c-largedisk
 
   - image: dist-riscv64-linux
     <<: *job-linux-4c
 
   - image: dist-s390x-linux
-    <<: *job-linux-4c
+    <<: *job-linux-4c-largedisk
 
   - image: dist-various-1
     <<: *job-linux-4c
@@ -282,7 +287,7 @@ auto:
       RUST_CONFIGURE_ARGS: --enable-full-tools --enable-sanitizers --enable-profiler --set rust.jemalloc --set rust.lto=thin --set rust.codegen-units=1
       RUSTC_RETRY_LINKER_ON_SEGFAULT: 1
       MACOSX_DEPLOYMENT_TARGET: 10.12
-      SELECT_XCODE: /Applications/Xcode_14.3.1.app
+      SELECT_XCODE: /Applications/Xcode_15.2.app
       NO_LLVM_ASSERTIONS: 1
       NO_DEBUG_ASSERTIONS: 1
       NO_OVERFLOW_CHECKS: 1
@@ -298,7 +303,7 @@ auto:
       RUST_CONFIGURE_ARGS: --enable-sanitizers --enable-profiler --set rust.jemalloc --set target.aarch64-apple-ios-macabi.sanitizers=false --set target.x86_64-apple-ios-macabi.sanitizers=false
       RUSTC_RETRY_LINKER_ON_SEGFAULT: 1
       MACOSX_DEPLOYMENT_TARGET: 10.12
-      SELECT_XCODE: /Applications/Xcode_14.3.1.app
+      SELECT_XCODE: /Applications/Xcode_15.2.app
       NO_LLVM_ASSERTIONS: 1
       NO_DEBUG_ASSERTIONS: 1
       NO_OVERFLOW_CHECKS: 1
@@ -327,7 +332,7 @@ auto:
         --set llvm.ninja=false
         --set rust.lto=thin
       RUSTC_RETRY_LINKER_ON_SEGFAULT: 1
-      SELECT_XCODE: /Applications/Xcode_14.3.1.app
+      SELECT_XCODE: /Applications/Xcode_15.4.app
       USE_XCODE_CLANG: 1
       MACOSX_DEPLOYMENT_TARGET: 11.0
       MACOSX_STD_DEPLOYMENT_TARGET: 11.0
@@ -347,7 +352,7 @@ auto:
         --enable-profiler
         --set rust.jemalloc
       RUSTC_RETRY_LINKER_ON_SEGFAULT: 1
-      SELECT_XCODE: /Applications/Xcode_14.3.1.app
+      SELECT_XCODE: /Applications/Xcode_15.4.app
       USE_XCODE_CLANG: 1
       MACOSX_DEPLOYMENT_TARGET: 11.0
       MACOSX_STD_DEPLOYMENT_TARGET: 11.0
@@ -381,6 +386,8 @@ auto:
     <<: *job-windows-8c
 
   # Temporary builder to workaround CI issues
+  # See <https://github.com/rust-lang/rust/issues/127883>
+  #FIXME: Remove this, and re-enable the same tests in `checktools.sh`, once CI issues are fixed.
   - image: x86_64-msvc-ext2
     env:
       SCRIPT: >
diff --git a/src/doc/rustc/src/platform-support.md b/src/doc/rustc/src/platform-support.md
index 022fc9da7e0..e9c73ef1c2d 100644
--- a/src/doc/rustc/src/platform-support.md
+++ b/src/doc/rustc/src/platform-support.md
@@ -100,7 +100,7 @@ target | notes
 [`riscv64gc-unknown-linux-gnu`](platform-support/riscv64gc-unknown-linux-gnu.md) | RISC-V Linux (kernel 4.20, glibc 2.29)
 [`riscv64gc-unknown-linux-musl`](platform-support/riscv64gc-unknown-linux-musl.md) | RISC-V Linux (kernel 4.20, musl 1.2.3)
 `s390x-unknown-linux-gnu` | S390x Linux (kernel 3.2, glibc 2.17)
-`x86_64-unknown-freebsd` | 64-bit FreeBSD
+`x86_64-unknown-freebsd` | 64-bit FreeBSD (version 13.2)
 `x86_64-unknown-illumos` | illumos
 `x86_64-unknown-linux-musl` | 64-bit Linux with musl 1.2.3
 [`x86_64-unknown-netbsd`](platform-support/netbsd.md) | NetBSD/amd64
@@ -166,7 +166,7 @@ target | std | notes
 `i586-unknown-linux-musl` | ✓ | 32-bit Linux w/o SSE, musl 1.2.3 [^x86_32-floats-x87]
 [`i686-linux-android`](platform-support/android.md) | ✓ | 32-bit x86 Android [^x86_32-floats-return-ABI]
 [`i686-pc-windows-gnullvm`](platform-support/pc-windows-gnullvm.md) | ✓ | 32-bit x86 MinGW (Windows 10+), LLVM ABI [^x86_32-floats-return-ABI]
-`i686-unknown-freebsd` | ✓ | 32-bit FreeBSD [^x86_32-floats-return-ABI]
+`i686-unknown-freebsd` | ✓ | 32-bit FreeBSD (version 13.2) [^x86_32-floats-return-ABI]
 `i686-unknown-linux-musl` | ✓ | 32-bit Linux with musl 1.2.3 [^x86_32-floats-return-ABI]
 [`i686-unknown-uefi`](platform-support/unknown-uefi.md) | ? | 32-bit UEFI
 [`loongarch64-unknown-none`](platform-support/loongarch-none.md) | * | LoongArch64 Bare-metal (LP64D ABI)
@@ -257,7 +257,7 @@ target | std | host | notes
 [`aarch64-unknown-teeos`](platform-support/aarch64-unknown-teeos.md) | ? |  | ARM64 TEEOS |
 [`aarch64-unknown-nto-qnx700`](platform-support/nto-qnx.md) | ? |  | ARM64 QNX Neutrino 7.0 RTOS |
 [`aarch64-unknown-nto-qnx710`](platform-support/nto-qnx.md) | ✓ |  | ARM64 QNX Neutrino 7.1 RTOS |
-`aarch64-unknown-freebsd` | ✓ | ✓ | ARM64 FreeBSD
+`aarch64-unknown-freebsd` | ✓ | ✓ | ARM64 FreeBSD (version 13.2)
 [`aarch64-unknown-hermit`](platform-support/hermit.md) | ✓ |  | ARM64 Hermit
 `aarch64-unknown-illumos` | ✓ | ✓ | ARM64 illumos
 `aarch64-unknown-linux-gnu_ilp32` | ✓ | ✓ | ARM64 Linux (ILP32 ABI)
@@ -276,14 +276,14 @@ target | std | host | notes
 `armv4t-unknown-linux-gnueabi` | ? |  | Armv4T Linux
 [`armv5te-none-eabi`](platform-support/armv5te-none-eabi.md) | * |  | Bare Armv5TE
 `armv5te-unknown-linux-uclibceabi` | ? |  | Armv5TE Linux with uClibc
-`armv6-unknown-freebsd` | ✓ | ✓ | Armv6 FreeBSD
+`armv6-unknown-freebsd` | ✓ | ✓ | Armv6 FreeBSD (version 13.2)
 [`armv6-unknown-netbsd-eabihf`](platform-support/netbsd.md) | ✓ | ✓ | Armv6 NetBSD w/hard-float
 [`armv6k-nintendo-3ds`](platform-support/armv6k-nintendo-3ds.md) | ? |  | Armv6k Nintendo 3DS, Horizon (Requires devkitARM toolchain)
 [`armv7-rtems-eabihf`](platform-support/armv7-rtems-eabihf.md) | ? |  | RTEMS OS for ARM BSPs
 [`armv7-sony-vita-newlibeabihf`](platform-support/armv7-sony-vita-newlibeabihf.md) | ✓ |  | Armv7-A Cortex-A9 Sony PlayStation Vita (requires VITASDK toolchain)
 [`armv7-unknown-linux-uclibceabi`](platform-support/armv7-unknown-linux-uclibceabi.md) | ✓ | ✓ | Armv7-A Linux with uClibc, softfloat
 [`armv7-unknown-linux-uclibceabihf`](platform-support/armv7-unknown-linux-uclibceabihf.md) | ✓ | ? | Armv7-A Linux with uClibc, hardfloat
-`armv7-unknown-freebsd` | ✓ | ✓ | Armv7-A FreeBSD
+`armv7-unknown-freebsd` | ✓ | ✓ | Armv7-A FreeBSD (version 13.2)
 [`armv7-unknown-netbsd-eabihf`](platform-support/netbsd.md) | ✓ | ✓ | Armv7-A NetBSD w/hard-float
 [`armv7-unknown-trusty`](platform-support/trusty.md) | ? |  |
 [`armv7-wrs-vxworks-eabihf`](platform-support/vxworks.md) | ✓ |  | Armv7-A for VxWorks
@@ -342,9 +342,9 @@ target | std | host | notes
 [`powerpc-unknown-openbsd`](platform-support/powerpc-unknown-openbsd.md) | * |  |
 [`powerpc-wrs-vxworks-spe`](platform-support/vxworks.md) | ✓ |  |
 [`powerpc-wrs-vxworks`](platform-support/vxworks.md) | ✓ |  |
-`powerpc64-unknown-freebsd` | ✓ | ✓ | PPC64 FreeBSD (ELFv1 and ELFv2)
-`powerpc64le-unknown-freebsd` |   |   | PPC64LE FreeBSD
-`powerpc-unknown-freebsd` |   |   | PowerPC FreeBSD
+`powerpc64-unknown-freebsd` | ✓ | ✓ | PPC64 FreeBSD (ELFv1 and ELFv2, version 13.2)
+`powerpc64le-unknown-freebsd` |   |   | PPC64LE FreeBSD (version 13.2)
+`powerpc-unknown-freebsd` |   |   | PowerPC FreeBSD (version 13.2)
 `powerpc64-unknown-linux-musl` | ? |  | 64-bit PowerPC Linux with musl 1.2.3
 [`powerpc64-wrs-vxworks`](platform-support/vxworks.md) | ✓ |  |
 `powerpc64le-unknown-linux-musl` | ? |  | 64-bit PowerPC Linux with musl 1.2.3, Little Endian
@@ -360,7 +360,7 @@ target | std | host | notes
 [`riscv32imafc-esp-espidf`](platform-support/esp-idf.md) | ✓ |  | RISC-V ESP-IDF
 [`riscv32-wrs-vxworks`](platform-support/vxworks.md) | ✓ |  |
 [`riscv64gc-unknown-hermit`](platform-support/hermit.md) | ✓ |   | RISC-V Hermit
-`riscv64gc-unknown-freebsd` |   |   | RISC-V FreeBSD
+`riscv64gc-unknown-freebsd` |   |   | RISC-V FreeBSD (version 13.2)
 `riscv64gc-unknown-fuchsia` |   |   | RISC-V Fuchsia
 [`riscv64gc-unknown-netbsd`](platform-support/netbsd.md) | ✓ | ✓ | RISC-V NetBSD
 [`riscv64gc-unknown-openbsd`](platform-support/openbsd.md) | ✓ | ✓ | OpenBSD/riscv64
diff --git a/src/doc/rustc/src/platform-support/arm-none-eabi.md b/src/doc/rustc/src/platform-support/arm-none-eabi.md
index de0ef322fa6..9732df4be7f 100644
--- a/src/doc/rustc/src/platform-support/arm-none-eabi.md
+++ b/src/doc/rustc/src/platform-support/arm-none-eabi.md
@@ -42,16 +42,15 @@ their own document.
 There are two 32-bit instruction set architectures (ISAs) defined by Arm:
 
 - The [*A32 ISA*][a32-isa], with fixed-width 32-bit instructions. Previously
-  known as the *Arm* ISA, this originated with the original Arm1 of 1985 and has
+  known as the *Arm* ISA, this originated with the original ARM1 of 1985 and has
   been updated by various revisions to the architecture specifications ever
   since.
 - The [*T32 ISA*][t32-isa], with a mix of 16-bit and 32-bit width instructions.
   Note that this term includes both the original 16-bit width *Thumb* ISA
   introduced with the Armv4T architecture in 1994, and the later 16/32-bit sized
-  *Thumb-2* ISA introduced with the Armv6T2 architecture in 2003.
-
-Again, these ISAs have been revised by subsequent revisions to the relevant Arm
-architecture specifications.
+  *Thumb-2* ISA introduced with the Armv6T2 architecture in 2003. Again, these
+  ISAs have been revised by subsequent revisions to the relevant Arm
+  architecture specifications.
 
 There is also a 64-bit ISA with fixed-width 32-bit instructions called the *A64
 ISA*, but targets which implement that instruction set generally start with
@@ -106,10 +105,14 @@ features you do not have available, leaving you with the optimized instruction
 scheduling and support for the features you do have. More details are available
 in the detailed target-specific documentation.
 
-**Note:** Many target-features are currently unstable and subject to change, and
+<div class="warning">
+
+Many target-features are currently unstable and subject to change, and
 if you use them you should disassemble the compiler output and manually inspect
 it to ensure only appropriate instructions for your CPU have been generated.
 
+</div>
+
 If you wish to use the *target-cpu* and *target-feature* options, you can add
 them to your `.cargo/config.toml` file alongside any other flags your project
 uses (likely linker related ones):
diff --git a/src/doc/rustc/src/platform-support/thumbv7em-none-eabi.md b/src/doc/rustc/src/platform-support/thumbv7em-none-eabi.md
index f25ef0383b1..11c9486cb76 100644
--- a/src/doc/rustc/src/platform-support/thumbv7em-none-eabi.md
+++ b/src/doc/rustc/src/platform-support/thumbv7em-none-eabi.md
@@ -35,9 +35,9 @@ to use these flags.
 | CPU        | FPU | DSP | Target CPU  | Target Features |
 | ---------- | --- | --- | ----------- | --------------- |
 | Any        | No  | Yes | None        | None            |
-| Cortex-M4  | No  | Yes | `cortex-m4` | `+soft-float`   |
+| Cortex-M4  | No  | Yes | `cortex-m4` | `-fpregs`       |
 | Cortex-M4F | SP  | Yes | `cortex-m4` | None            |
-| Cortex-M7  | No  | Yes | `cortex-m7` | `+soft-float`   |
+| Cortex-M7  | No  | Yes | `cortex-m7` | `-fpregs`       |
 | Cortex-M7F | SP  | Yes | `cortex-m7` | `-fp64`         |
 | Cortex-M7F | DP  | Yes | `cortex-m7` | None            |
 
@@ -50,6 +50,13 @@ to use these flags.
 | Cortex-M7F | SP  | Yes | `cortex-m7` | `-fp64`         |
 | Cortex-M7F | DP  | Yes | `cortex-m7` | None            |
 
+<div class="warning">
+
+Never use the `-fpregs` *target-feature* with the `thumbv7em-none-eabihf` target
+as it will cause compilation units to have different ABIs, which is unsound.
+
+</div>
+
 ### Arm Cortex-M4 and Arm Cortex-M4F
 
 The target CPU is `cortex-m4`.
@@ -59,7 +66,7 @@ The target CPU is `cortex-m4`.
   * enabled by default with this *target*
 * Cortex-M4F has a single precision FPU
   * support is enabled by default with this *target-cpu*
-  * disable support using the `+soft-float` feature (`eabi` only)
+  * disable support using the `-fpregs` *target-feature* (`eabi` only)
 
 ### Arm Cortex-M7 and Arm Cortex-M7F
 
@@ -71,4 +78,4 @@ The target CPU is `cortex-m7`.
 * Cortex-M7F have either a single-precision or double-precision FPU
   * double-precision support is enabled by default with this *target-cpu*
     * opt-out by using the `-f64` *target-feature*
-  * disable support entirely using the `+soft-float` feature (`eabi` only)
+  * disable support entirely using the `-fpregs` *target-feature* (`eabi` only)
diff --git a/src/doc/rustc/src/platform-support/thumbv8m.main-none-eabi.md b/src/doc/rustc/src/platform-support/thumbv8m.main-none-eabi.md
index 4e696f9c304..82fdc5b21cf 100644
--- a/src/doc/rustc/src/platform-support/thumbv8m.main-none-eabi.md
+++ b/src/doc/rustc/src/platform-support/thumbv8m.main-none-eabi.md
@@ -39,22 +39,22 @@ to use these flags.
 | CPU         | FPU | DSP | MVE       | Target CPU    | Target Features       |
 | ----------- | --- | --- | --------- | ------------- | --------------------- |
 | Unspecified | No  | No  | No        | None          | None                  |
-| Cortex-M33  | No  | No  | No        | `cortex-m33`  | `+soft-float,-dsp`    |
-| Cortex-M33  | No  | Yes | No        | `cortex-m33`  | `+soft-float`         |
+| Cortex-M33  | No  | No  | No        | `cortex-m33`  | `-fpregs,-dsp`        |
+| Cortex-M33  | No  | Yes | No        | `cortex-m33`  | `-fpregs`             |
 | Cortex-M33  | SP  | No  | No        | `cortex-m33`  | `-dsp`                |
 | Cortex-M33  | SP  | Yes | No        | `cortex-m33`  | None                  |
-| Cortex-M35P | No  | No  | No        | `cortex-m35p` | `+soft-float,-dsp`    |
-| Cortex-M35P | No  | Yes | No        | `cortex-m35p` | `+soft-float`         |
+| Cortex-M35P | No  | No  | No        | `cortex-m35p` | `-fpregs,-dsp`        |
+| Cortex-M35P | No  | Yes | No        | `cortex-m35p` | `-fpregs`             |
 | Cortex-M35P | SP  | No  | No        | `cortex-m35p` | `-dsp`                |
 | Cortex-M35P | SP  | Yes | No        | `cortex-m35p` | None                  |
-| Cortex-M55  | No  | Yes | No        | `cortex-m55`  | `+soft-float,-mve`    |
+| Cortex-M55  | No  | Yes | No        | `cortex-m55`  | `-fpregs,-mve`        |
 | Cortex-M55  | DP  | Yes | No        | `cortex-m55`  | `-mve`                |
-| Cortex-M55  | No  | Yes | Int       | `cortex-m55`  | `+soft-float,-mve.fp` |
+| Cortex-M55  | No  | Yes | Int       | `cortex-m55`  | `-fpregs,-mve.fp,+mve`|
 | Cortex-M55  | DP  | Yes | Int       | `cortex-m55`  | `-mve.fp`             |
 | Cortex-M55  | DP  | Yes | Int+Float | `cortex-m55`  | None                  |
-| Cortex-M85  | No  | Yes | No        | `cortex-m85`  | `+soft-float,-mve`    |
+| Cortex-M85  | No  | Yes | No        | `cortex-m85`  | `-fpregs,-mve`        |
 | Cortex-M85  | DP  | Yes | No        | `cortex-m85`  | `-mve`                |
-| Cortex-M85  | No  | Yes | Int       | `cortex-m85`  | `+soft-float,-mve.fp` |
+| Cortex-M85  | No  | Yes | Int       | `cortex-m85`  | `-fpregs,-mve.fp,+mve`|
 | Cortex-M85  | DP  | Yes | Int       | `cortex-m85`  | `-mve.fp`             |
 | Cortex-M85  | DP  | Yes | Int+Float | `cortex-m85`  | None                  |
 
@@ -74,6 +74,19 @@ to use these flags.
 | Cortex-M85  | DP  | Yes | Int       | `cortex-m85`  | `-mve.fp`             |
 | Cortex-M85  | DP  | Yes | Int+Float | `cortex-m85`  | None                  |
 
+*Technically* you can use this hard-float ABI on a CPU which has no FPU but does
+have Integer MVE, because MVE provides the same set of registers as the FPU
+(including `s0` and `d0`). The particular set of flags that might enable this
+unusual scenario are currently not recorded here.
+
+<div class="warning">
+
+Never use the `-fpregs` *target-feature* with the `thumbv8m.main-none-eabihf`
+target as it will cause compilation units to have different ABIs, which is
+unsound.
+
+</div>
+
 ### Arm Cortex-M33
 
 The target CPU is `cortex-m33`.
@@ -83,7 +96,7 @@ The target CPU is `cortex-m33`.
   * enabled by default with this *target-cpu*
 * Has an optional single precision FPU
   * support is enabled by default with this *target-cpu*
-  * disable support using the `+soft-float` feature (`eabi` only)
+  * disable support using the `-fpregs` *target-feature* (`eabi` only)
 
 ### Arm Cortex-M35P
 
@@ -94,7 +107,7 @@ The target CPU is `cortex-m35p`.
   * enabled by default with this *target-cpu*
 * Has an optional single precision FPU
   * support is enabled by default with this *target-cpu*
-  * disable support using the `+soft-float` feature (`eabi` only)
+  * disable support using the `-fpregs` *target-feature* (`eabi` only)
 
 ### Arm Cortex-M55
 
@@ -106,7 +119,7 @@ The target CPU is `cortex-m55`.
 * Has an optional double-precision FPU that also supports half-precision FP16
   values
   * support is enabled by default with this *target-cpu*
-  * disable support using the `+soft-float` feature (`eabi` only)
+  * disable support using the `-fpregs` *target-feature* (`eabi` only)
 * Has optional support for M-Profile Vector Extensions
   * Also known as *Helium Technology*
   * Available with only integer support, or both integer/float support
@@ -125,7 +138,7 @@ The target CPU is `cortex-m85`.
 * Has an optional double-precision FPU that also supports half-precision FP16
   values
   * support is enabled by default with this *target-cpu*
-  * disable support using the `+soft-float` feature (`eabi` only)
+  * disable support using the `-fpregs` *target-feature* (`eabi` only)
 * Has optional support for M-Profile Vector Extensions
   * Also known as *Helium Technology*
   * Available with only integer support, or both integer/float support
diff --git a/src/doc/unstable-book/src/language-features/result-ffi-guarantees.md b/src/doc/unstable-book/src/language-features/result-ffi-guarantees.md
deleted file mode 100644
index dc9c196524e..00000000000
--- a/src/doc/unstable-book/src/language-features/result-ffi-guarantees.md
+++ /dev/null
@@ -1,14 +0,0 @@
-# `result_ffi_guarantees`
-
-The tracking issue for this feature is: [#110503]
-
-[#110503]: https://github.com/rust-lang/rust/issues/110503
-
-------------------------
-
-This feature adds the possibility of using `Result<T, E>` in FFI if T's niche
-value can be used to describe E or vise-versa.
-
-See [RFC 3391] for more information.
-
-[RFC 3391]: https://github.com/rust-lang/rfcs/blob/master/text/3391-result_ffi_guarantees.md
diff --git a/src/doc/unstable-book/src/language-features/strict-provenance.md b/src/doc/unstable-book/src/language-features/strict-provenance-lints.md
index dc60f3f375d..81bdf07a86a 100644
--- a/src/doc/unstable-book/src/language-features/strict-provenance.md
+++ b/src/doc/unstable-book/src/language-features/strict-provenance-lints.md
@@ -1,18 +1,17 @@
-# `strict_provenance`
+# `strict_provenance_lints`
 
 The tracking issue for this feature is: [#95228]
 
 [#95228]: https://github.com/rust-lang/rust/issues/95228
 -----
 
-The `strict_provenance` feature allows to enable the `fuzzy_provenance_casts` and `lossy_provenance_casts` lints.
+The `strict_provenance_lints` feature allows to enable the `fuzzy_provenance_casts` and `lossy_provenance_casts` lints.
 These lint on casts between integers and pointers, that are recommended against or invalid in the strict provenance model.
-The same feature gate is also used for the experimental strict provenance API in `std` (actually `core`).
 
 ## Example
 
 ```rust
-#![feature(strict_provenance)]
+#![feature(strict_provenance_lints)]
 #![warn(fuzzy_provenance_casts)]
 
 fn main() {
diff --git a/src/etc/installer/msi/rust.wxs b/src/etc/installer/msi/rust.wxs
index f29e1e4d27a..2d155bf0b10 100644
--- a/src/etc/installer/msi/rust.wxs
+++ b/src/etc/installer/msi/rust.wxs
@@ -172,6 +172,11 @@
                     <!-- tool-rust-docs-end -->
                     <Directory Id="Cargo" Name="." />
                     <Directory Id="Std" Name="." />
+                    <Directory Id="RustFmt" Name="." />
+                    <Directory Id="RustAnalyzer" Name="." />
+                    <Directory Id="Miri" Name="." />
+                    <Directory Id="Analysis" Name="." />
+                    <Directory Id="Clippy" Name="." />
                 </Directory>
             </Directory>
 
@@ -279,7 +284,41 @@
                  <ComponentRef Id="PathEnvPerMachine" />
                  <ComponentRef Id="PathEnvPerUser" />
         </Feature>
-
+        <Feature Id="RustFmt"
+                 Title="Formatter for rust"
+                 Display="7"
+                 Level="1"
+                 AllowAdvertise="no">
+                 <ComponentGroupRef Id="RustFmtGroup" />
+        </Feature>
+        <Feature Id="Clippy"
+                 Title="Formatter and checker for rust"
+                 Display="8"
+                 Level="1"
+                 AllowAdvertise="no">
+                 <ComponentGroupRef Id="ClippyGroup" />
+        </Feature>
+        <Feature Id="Miri"
+                 Title="Soundness checker for rust"
+                 Display="9"
+                 Level="1"
+                 AllowAdvertise="no">
+                 <ComponentGroupRef Id="MiriGroup" />
+        </Feature>
+        <Feature Id="RustAnalyzer"
+                 Title="Analyzer for rust"
+                 Display="10"
+                 Level="1"
+                 AllowAdvertise="no">
+                 <ComponentGroupRef Id="RustAnalyzerGroup" />
+        </Feature>
+        <Feature Id="Analysis"
+                 Title="Analysis for rust"
+                 Display="11"
+                 Level="1"
+                 AllowAdvertise="no">
+                 <ComponentGroupRef Id="AnalysisGroup" />
+        </Feature>
         <UIRef Id="RustUI" />
     </Product>
 </Wix>
diff --git a/src/librustdoc/Cargo.toml b/src/librustdoc/Cargo.toml
index 34332de80b3..42df0b28381 100644
--- a/src/librustdoc/Cargo.toml
+++ b/src/librustdoc/Cargo.toml
@@ -24,6 +24,7 @@ tracing = "0.1"
 tracing-tree = "0.3.0"
 threadpool = "1.8.1"
 unicode-segmentation = "1.9"
+sha2 = "0.10.8"
 
 [dependencies.tracing-subscriber]
 version = "0.3.3"
diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs
index 1ddad917b78..7d4d8d8941d 100644
--- a/src/librustdoc/clean/mod.rs
+++ b/src/librustdoc/clean/mod.rs
@@ -1817,7 +1817,7 @@ pub(crate) fn clean_ty<'tcx>(ty: &hir::Ty<'tcx>, cx: &mut DocContext<'tcx>) -> T
                         // Only anon consts can implicitly capture params.
                         // FIXME: is this correct behavior?
                         let param_env = cx.tcx.param_env(*def_id);
-                        ct.normalize(cx.tcx, param_env)
+                        cx.tcx.normalize_erasing_regions(param_env, ct)
                     } else {
                         ct
                     };
@@ -2033,8 +2033,8 @@ pub(crate) fn clean_middle_ty<'tcx>(
             Box::new(clean_middle_ty(bound_ty.rebind(ty), cx, None, None)),
             format!("{pat:?}").into_boxed_str(),
         ),
-        ty::Array(ty, mut n) => {
-            n = n.normalize(cx.tcx, ty::ParamEnv::reveal_all());
+        ty::Array(ty, n) => {
+            let n = cx.tcx.normalize_erasing_regions(cx.param_env, n);
             let n = print_const(cx, n);
             Array(Box::new(clean_middle_ty(bound_ty.rebind(ty), cx, None, None)), n.into())
         }
diff --git a/src/librustdoc/html/markdown.rs b/src/librustdoc/html/markdown.rs
index 315b7742a4c..7826a5d8394 100644
--- a/src/librustdoc/html/markdown.rs
+++ b/src/librustdoc/html/markdown.rs
@@ -37,7 +37,7 @@ use std::sync::OnceLock;
 use pulldown_cmark::{
     BrokenLink, CodeBlockKind, CowStr, Event, LinkType, Options, Parser, Tag, TagEnd, html,
 };
-use rustc_data_structures::fx::{FxHashMap, FxIndexMap};
+use rustc_data_structures::fx::FxHashMap;
 use rustc_errors::{Diag, DiagMessage};
 use rustc_hir::def_id::LocalDefId;
 use rustc_middle::ty::TyCtxt;
@@ -57,6 +57,7 @@ use crate::html::length_limit::HtmlWithLimit;
 use crate::html::render::small_url_encode;
 use crate::html::toc::{Toc, TocBuilder};
 
+mod footnotes;
 #[cfg(test)]
 mod tests;
 
@@ -646,81 +647,6 @@ impl<'a, I: Iterator<Item = Event<'a>>> Iterator for SummaryLine<'a, I> {
     }
 }
 
-/// Moves all footnote definitions to the end and add back links to the
-/// references.
-struct Footnotes<'a, I> {
-    inner: I,
-    footnotes: FxIndexMap<String, (Vec<Event<'a>>, u16)>,
-}
-
-impl<'a, I> Footnotes<'a, I> {
-    fn new(iter: I) -> Self {
-        Footnotes { inner: iter, footnotes: FxIndexMap::default() }
-    }
-
-    fn get_entry(&mut self, key: &str) -> &mut (Vec<Event<'a>>, u16) {
-        let new_id = self.footnotes.len() + 1;
-        let key = key.to_owned();
-        self.footnotes.entry(key).or_insert((Vec::new(), new_id as u16))
-    }
-}
-
-impl<'a, I: Iterator<Item = SpannedEvent<'a>>> Iterator for Footnotes<'a, I> {
-    type Item = SpannedEvent<'a>;
-
-    fn next(&mut self) -> Option<Self::Item> {
-        loop {
-            match self.inner.next() {
-                Some((Event::FootnoteReference(ref reference), range)) => {
-                    let entry = self.get_entry(reference);
-                    let reference = format!(
-                        "<sup id=\"fnref{0}\"><a href=\"#fn{0}\">{0}</a></sup>",
-                        (*entry).1
-                    );
-                    return Some((Event::Html(reference.into()), range));
-                }
-                Some((Event::Start(Tag::FootnoteDefinition(def)), _)) => {
-                    let mut content = Vec::new();
-                    for (event, _) in &mut self.inner {
-                        if let Event::End(TagEnd::FootnoteDefinition) = event {
-                            break;
-                        }
-                        content.push(event);
-                    }
-                    let entry = self.get_entry(&def);
-                    (*entry).0 = content;
-                }
-                Some(e) => return Some(e),
-                None => {
-                    if !self.footnotes.is_empty() {
-                        let mut v: Vec<_> = self.footnotes.drain(..).map(|(_, x)| x).collect();
-                        v.sort_by(|a, b| a.1.cmp(&b.1));
-                        let mut ret = String::from("<div class=\"footnotes\"><hr><ol>");
-                        for (mut content, id) in v {
-                            write!(ret, "<li id=\"fn{id}\">").unwrap();
-                            let mut is_paragraph = false;
-                            if let Some(&Event::End(TagEnd::Paragraph)) = content.last() {
-                                content.pop();
-                                is_paragraph = true;
-                            }
-                            html::push_html(&mut ret, content.into_iter());
-                            write!(ret, "&nbsp;<a href=\"#fnref{id}\">↩</a>").unwrap();
-                            if is_paragraph {
-                                ret.push_str("</p>");
-                            }
-                            ret.push_str("</li>");
-                        }
-                        ret.push_str("</ol></div>");
-                        return Some((Event::Html(ret.into()), 0..0));
-                    } else {
-                        return None;
-                    }
-                }
-            }
-        }
-    }
-}
-
 /// A newtype that represents a relative line number in Markdown.
 ///
 /// In other words, this represents an offset from the first line of Markdown
@@ -1408,7 +1334,7 @@ impl Markdown<'_> {
         let mut s = String::with_capacity(md.len() * 3 / 2);
 
         let p = HeadingLinks::new(p, None, ids, heading_offset);
-        let p = Footnotes::new(p);
+        let p = footnotes::Footnotes::new(p);
         let p = LinkReplacer::new(p.map(|(ev, _)| ev), links);
         let p = TableWrapper::new(p);
         let p = CodeBlocks::new(p, codes, edition, playground);
@@ -1443,7 +1369,7 @@ impl MarkdownWithToc<'_> {
 
         {
             let p = HeadingLinks::new(p, Some(&mut toc), ids, HeadingOffset::H1);
-            let p = Footnotes::new(p);
+            let p = footnotes::Footnotes::new(p);
             let p = TableWrapper::new(p.map(|(ev, _)| ev));
             let p = CodeBlocks::new(p, codes, edition, playground);
             html::push_html(&mut s, p);
@@ -1476,7 +1402,7 @@ impl MarkdownItemInfo<'_> {
         let mut s = String::with_capacity(md.len() * 3 / 2);
 
         let p = HeadingLinks::new(p, None, ids, HeadingOffset::H1);
-        let p = Footnotes::new(p);
+        let p = footnotes::Footnotes::new(p);
         let p = TableWrapper::new(p.map(|(ev, _)| ev));
         let p = p.filter(|event| {
             !matches!(event, Event::Start(Tag::Paragraph) | Event::End(TagEnd::Paragraph))
diff --git a/src/librustdoc/html/markdown/footnotes.rs b/src/librustdoc/html/markdown/footnotes.rs
new file mode 100644
index 00000000000..3f0e586b8e3
--- /dev/null
+++ b/src/librustdoc/html/markdown/footnotes.rs
@@ -0,0 +1,113 @@
+//! Markdown footnote handling.
+use std::fmt::Write as _;
+
+use pulldown_cmark::{Event, Tag, TagEnd, html};
+use rustc_data_structures::fx::FxIndexMap;
+
+use super::SpannedEvent;
+
+/// Moves all footnote definitions to the end and add back links to the
+/// references.
+pub(super) struct Footnotes<'a, I> {
+    inner: I,
+    footnotes: FxIndexMap<String, FootnoteDef<'a>>,
+}
+
+/// The definition of a single footnote.
+struct FootnoteDef<'a> {
+    content: Vec<Event<'a>>,
+    /// The number that appears in the footnote reference and list.
+    id: u16,
+}
+
+impl<'a, I> Footnotes<'a, I> {
+    pub(super) fn new(iter: I) -> Self {
+        Footnotes { inner: iter, footnotes: FxIndexMap::default() }
+    }
+
+    fn get_entry(&mut self, key: &str) -> (&mut Vec<Event<'a>>, u16) {
+        let new_id = self.footnotes.len() + 1;
+        let key = key.to_owned();
+        let FootnoteDef { content, id } = self
+            .footnotes
+            .entry(key)
+            .or_insert(FootnoteDef { content: Vec::new(), id: new_id as u16 });
+        // Don't allow changing the ID of existing entrys, but allow changing the contents.
+        (content, *id)
+    }
+}
+
+impl<'a, I: Iterator<Item = SpannedEvent<'a>>> Iterator for Footnotes<'a, I> {
+    type Item = SpannedEvent<'a>;
+
+    fn next(&mut self) -> Option<Self::Item> {
+        loop {
+            match self.inner.next() {
+                Some((Event::FootnoteReference(ref reference), range)) => {
+                    // When we see a reference (to a footnote we may not know) the definition of,
+                    // reserve a number for it, and emit a link to that number.
+                    let (_, id) = self.get_entry(reference);
+                    let reference =
+                        format!("<sup id=\"fnref{0}\"><a href=\"#fn{0}\">{0}</a></sup>", id);
+                    return Some((Event::Html(reference.into()), range));
+                }
+                Some((Event::Start(Tag::FootnoteDefinition(def)), _)) => {
+                    // When we see a footnote definition, collect the assocated content, and store
+                    // that for rendering later.
+                    let content = collect_footnote_def(&mut self.inner);
+                    let (entry_content, _) = self.get_entry(&def);
+                    *entry_content = content;
+                }
+                Some(e) => return Some(e),
+                None => {
+                    if !self.footnotes.is_empty() {
+                        // After all the markdown is emmited, emit an <hr> then all the footnotes
+                        // in a list.
+                        let defs: Vec<_> = self.footnotes.drain(..).map(|(_, x)| x).collect();
+                        let defs_html = render_footnotes_defs(defs);
+                        return Some((Event::Html(defs_html.into()), 0..0));
+                    } else {
+                        return None;
+                    }
+                }
+            }
+        }
+    }
+}
+
+fn collect_footnote_def<'a>(events: impl Iterator<Item = SpannedEvent<'a>>) -> Vec<Event<'a>> {
+    let mut content = Vec::new();
+    for (event, _) in events {
+        if let Event::End(TagEnd::FootnoteDefinition) = event {
+            break;
+        }
+        content.push(event);
+    }
+    content
+}
+
+fn render_footnotes_defs(mut footnotes: Vec<FootnoteDef<'_>>) -> String {
+    let mut ret = String::from("<div class=\"footnotes\"><hr><ol>");
+
+    // Footnotes must listed in order of id, so the numbers the
+    // browser generated for <li> are right.
+    footnotes.sort_by_key(|x| x.id);
+
+    for FootnoteDef { mut content, id } in footnotes {
+        write!(ret, "<li id=\"fn{id}\">").unwrap();
+        let mut is_paragraph = false;
+        if let Some(&Event::End(TagEnd::Paragraph)) = content.last() {
+            content.pop();
+            is_paragraph = true;
+        }
+        html::push_html(&mut ret, content.into_iter());
+        write!(ret, "&nbsp;<a href=\"#fnref{id}\">↩</a>").unwrap();
+        if is_paragraph {
+            ret.push_str("</p>");
+        }
+        ret.push_str("</li>");
+    }
+    ret.push_str("</ol></div>");
+
+    ret
+}
diff --git a/src/librustdoc/html/static/css/rustdoc.css b/src/librustdoc/html/static/css/rustdoc.css
index df9776ff5f8..2c17fd54006 100644
--- a/src/librustdoc/html/static/css/rustdoc.css
+++ b/src/librustdoc/html/static/css/rustdoc.css
@@ -44,7 +44,7 @@ xmlns="http://www.w3.org/2000/svg" fill="black" height="18px">\
 	font-style: normal;
 	font-weight: 400;
 	src: local('Fira Sans'),
-		url("FiraSans-Regular-018c141bf0843ffd.woff2") format("woff2");
+		url("FiraSans-Regular-0fe48ade.woff2") format("woff2");
 	font-display: swap;
 }
 @font-face {
@@ -52,7 +52,7 @@ xmlns="http://www.w3.org/2000/svg" fill="black" height="18px">\
 	font-style: normal;
 	font-weight: 500;
 	src: local('Fira Sans Medium'),
-		url("FiraSans-Medium-8f9a781e4970d388.woff2") format("woff2");
+		url("FiraSans-Medium-e1aa3f0a.woff2") format("woff2");
 	font-display: swap;
 }
 
@@ -62,7 +62,7 @@ xmlns="http://www.w3.org/2000/svg" fill="black" height="18px">\
 	font-style: normal;
 	font-weight: 400;
 	src: local('Source Serif 4'),
-		url("SourceSerif4-Regular-46f98efaafac5295.ttf.woff2") format("woff2");
+		url("SourceSerif4-Regular-6b053e98.ttf.woff2") format("woff2");
 	font-display: swap;
 }
 @font-face {
@@ -70,7 +70,7 @@ xmlns="http://www.w3.org/2000/svg" fill="black" height="18px">\
 	font-style: italic;
 	font-weight: 400;
 	src: local('Source Serif 4 Italic'),
-		url("SourceSerif4-It-acdfaf1a8af734b1.ttf.woff2") format("woff2");
+		url("SourceSerif4-It-ca3b17ed.ttf.woff2") format("woff2");
 	font-display: swap;
 }
 @font-face {
@@ -78,7 +78,7 @@ xmlns="http://www.w3.org/2000/svg" fill="black" height="18px">\
 	font-style: normal;
 	font-weight: 700;
 	src: local('Source Serif 4 Bold'),
-		url("SourceSerif4-Bold-a2c9cd1067f8b328.ttf.woff2") format("woff2");
+		url("SourceSerif4-Bold-6d4fd4c0.ttf.woff2") format("woff2");
 	font-display: swap;
 }
 
@@ -89,28 +89,28 @@ xmlns="http://www.w3.org/2000/svg" fill="black" height="18px">\
 	font-weight: 400;
 	/* Avoid using locally installed font because bad versions are in circulation:
 	 * see https://github.com/rust-lang/rust/issues/24355 */
-	src: url("SourceCodePro-Regular-562dcc5011b6de7d.ttf.woff2") format("woff2");
+	src: url("SourceCodePro-Regular-8badfe75.ttf.woff2") format("woff2");
 	font-display: swap;
 }
 @font-face {
 	font-family: 'Source Code Pro';
 	font-style: italic;
 	font-weight: 400;
-	src: url("SourceCodePro-It-1cc31594bf4f1f79.ttf.woff2") format("woff2");
+	src: url("SourceCodePro-It-fc8b9304.ttf.woff2") format("woff2");
 	font-display: swap;
 }
 @font-face {
 	font-family: 'Source Code Pro';
 	font-style: normal;
 	font-weight: 600;
-	src: url("SourceCodePro-Semibold-d899c5a5c4aeb14a.ttf.woff2") format("woff2");
+	src: url("SourceCodePro-Semibold-aa29a496.ttf.woff2") format("woff2");
 	font-display: swap;
 }
 
 /* Avoid using legacy CJK serif fonts in Windows like Batang. */
 @font-face {
 	font-family: 'NanumBarunGothic';
-	src: url("NanumBarunGothic-0f09457c7a19b7c6.ttf.woff2") format("woff2");
+	src: url("NanumBarunGothic-13b3dcba.ttf.woff2") format("woff2");
 	font-display: swap;
 	unicode-range: U+AC00-D7AF, U+1100-11FF, U+3130-318F, U+A960-A97F, U+D7B0-D7FF;
 }
diff --git a/src/librustdoc/html/static_files.rs b/src/librustdoc/html/static_files.rs
index 6157598ba38..9e0803f5d3f 100644
--- a/src/librustdoc/html/static_files.rs
+++ b/src/librustdoc/html/static_files.rs
@@ -3,12 +3,9 @@
 //! All the static files are included here for centralized access in case anything other than the
 //! HTML rendering code (say, the theme checker) needs to access one of these files.
 
-use std::hash::Hasher;
 use std::path::{Path, PathBuf};
 use std::{fmt, str};
 
-use rustc_data_structures::fx::FxHasher;
-
 pub(crate) struct StaticFile {
     pub(crate) filename: PathBuf,
     pub(crate) bytes: &'static [u8],
@@ -64,9 +61,11 @@ pub(crate) fn static_filename(filename: &str, contents: &[u8]) -> PathBuf {
 }
 
 fn static_suffix(bytes: &[u8]) -> String {
-    let mut hasher = FxHasher::default();
-    hasher.write(bytes);
-    format!("-{:016x}", hasher.finish())
+    use sha2::Digest;
+    let bytes = sha2::Sha256::digest(bytes);
+    let mut digest = format!("-{bytes:x}");
+    digest.truncate(9);
+    digest
 }
 
 macro_rules! static_files {
diff --git a/src/librustdoc/json/conversions.rs b/src/librustdoc/json/conversions.rs
index 77e7d83090b..0130f2ce517 100644
--- a/src/librustdoc/json/conversions.rs
+++ b/src/librustdoc/json/conversions.rs
@@ -672,12 +672,12 @@ impl FromClean<clean::Trait> for Trait {
         let tcx = renderer.tcx;
         let is_auto = trait_.is_auto(tcx);
         let is_unsafe = trait_.safety(tcx) == rustc_hir::Safety::Unsafe;
-        let is_object_safe = trait_.is_dyn_compatible(tcx);
+        let is_dyn_compatible = trait_.is_dyn_compatible(tcx);
         let clean::Trait { items, generics, bounds, .. } = trait_;
         Trait {
             is_auto,
             is_unsafe,
-            is_object_safe,
+            is_dyn_compatible,
             items: renderer.ids(items),
             generics: generics.into_json(renderer),
             bounds: bounds.into_json(renderer),
diff --git a/src/librustdoc/passes/collect_intra_doc_links.rs b/src/librustdoc/passes/collect_intra_doc_links.rs
index cbc6e351fac..2f0ea8d618c 100644
--- a/src/librustdoc/passes/collect_intra_doc_links.rs
+++ b/src/librustdoc/passes/collect_intra_doc_links.rs
@@ -1091,7 +1091,6 @@ impl LinkCollector<'_, '_> {
             // resolutions are cached, for other links we want to report an error every
             // time so they are not cached.
             matches!(ori_link.kind, LinkType::Reference | LinkType::Shortcut),
-            false,
         )?;
 
         if resolved.len() > 1 {
@@ -1404,9 +1403,6 @@ impl LinkCollector<'_, '_> {
         // If errors are cached then they are only reported on first occurrence
         // which we want in some cases but not in others.
         cache_errors: bool,
-        // If this call is intended to be recoverable, then pass true to silence.
-        // This is only recoverable when path is failed to resolved.
-        recoverable: bool,
     ) -> Option<Vec<(Res, Option<UrlFragment>)>> {
         if let Some(res) = self.visited_links.get(&key) {
             if res.is_some() || cache_errors {
@@ -1414,7 +1410,7 @@ impl LinkCollector<'_, '_> {
             }
         }
 
-        let mut candidates = self.resolve_with_disambiguator(&key, diag.clone(), recoverable);
+        let mut candidates = self.resolve_with_disambiguator(&key, diag.clone());
 
         // FIXME: it would be nice to check that the feature gate was enabled in the original crate, not just ignore it altogether.
         // However I'm not sure how to check that across crates.
@@ -1463,14 +1459,10 @@ impl LinkCollector<'_, '_> {
     }
 
     /// After parsing the disambiguator, resolve the main part of the link.
-    // FIXME(jynelson): wow this is just so much
     fn resolve_with_disambiguator(
         &mut self,
         key: &ResolutionInfo,
         diag: DiagnosticInfo<'_>,
-        // If this call is intended to be recoverable, then pass true to silence.
-        // This is only recoverable when path is failed to resolved.
-        recoverable: bool,
     ) -> Vec<(Res, Option<DefId>)> {
         let disambiguator = key.dis;
         let path_str = &key.path_str;
@@ -1500,9 +1492,7 @@ impl LinkCollector<'_, '_> {
                                 }
                             }
                         }
-                        if !recoverable {
-                            resolution_failure(self, diag, path_str, disambiguator, smallvec![err]);
-                        }
+                        resolution_failure(self, diag, path_str, disambiguator, smallvec![err]);
                         return vec![];
                     }
                 }
@@ -1539,15 +1529,13 @@ impl LinkCollector<'_, '_> {
                     .fold(0, |acc, res| if let Ok(res) = res { acc + res.len() } else { acc });
 
                 if len == 0 {
-                    if !recoverable {
-                        resolution_failure(
-                            self,
-                            diag,
-                            path_str,
-                            disambiguator,
-                            candidates.into_iter().filter_map(|res| res.err()).collect(),
-                        );
-                    }
+                    resolution_failure(
+                        self,
+                        diag,
+                        path_str,
+                        disambiguator,
+                        candidates.into_iter().filter_map(|res| res.err()).collect(),
+                    );
                     return vec![];
                 } else if len == 1 {
                     candidates.into_iter().filter_map(|res| res.ok()).flatten().collect::<Vec<_>>()
diff --git a/src/rustdoc-json-types/Cargo.toml b/src/rustdoc-json-types/Cargo.toml
index d3548036d4c..14ff1d08816 100644
--- a/src/rustdoc-json-types/Cargo.toml
+++ b/src/rustdoc-json-types/Cargo.toml
@@ -6,9 +6,12 @@ edition = "2021"
 [lib]
 path = "lib.rs"
 
+[features]
+default = ["rustc-hash"]
+
 [dependencies]
 serde = { version = "1.0", features = ["derive"] }
-rustc-hash = "1.1.0"
+rustc-hash = { version = "2.0", optional = true }
 
 [dev-dependencies]
 serde_json = "1.0"
diff --git a/src/rustdoc-json-types/lib.rs b/src/rustdoc-json-types/lib.rs
index fc64bc98bb9..f553a78d766 100644
--- a/src/rustdoc-json-types/lib.rs
+++ b/src/rustdoc-json-types/lib.rs
@@ -2,18 +2,35 @@
 //!
 //! These types are the public API exposed through the `--output-format json` flag. The [`Crate`]
 //! struct is the root of the JSON blob and all other items are contained within.
+//!
+//! We expose a `rustc-hash` feature that is disabled by default. This feature switches the
+//! [`std::collections::HashMap`] for [`rustc_hash::FxHashMap`] to improve the performance of said
+//! `HashMap` in specific situations.
+//!
+//! `cargo-semver-checks` for example, saw a [-3% improvement][1] when benchmarking using the
+//! `aws_sdk_ec2` JSON output (~500MB of JSON). As always, we recommend measuring the impact before
+//! turning this feature on, as [`FxHashMap`][2] only concerns itself with hash speed, and may
+//! increase the number of collisions.
+//!
+//! [1]: https://rust-lang.zulipchat.com/#narrow/channel/266220-t-rustdoc/topic/rustc-hash.20and.20performance.20of.20rustdoc-types/near/474855731
+//! [2]: https://crates.io/crates/rustc-hash
 
+#[cfg(not(feature = "rustc-hash"))]
+use std::collections::HashMap;
 use std::path::PathBuf;
 
-pub use rustc_hash::FxHashMap;
+#[cfg(feature = "rustc-hash")]
+use rustc_hash::FxHashMap as HashMap;
 use serde::{Deserialize, Serialize};
 
+pub type FxHashMap<K, V> = HashMap<K, V>; // re-export for use in src/librustdoc
+
 /// The version of JSON output that this crate represents.
 ///
 /// This integer is incremented with every breaking change to the API,
 /// and is returned along with the JSON blob as [`Crate::format_version`].
 /// Consuming code should assert that this value matches the format version(s) that it supports.
-pub const FORMAT_VERSION: u32 = 35;
+pub const FORMAT_VERSION: u32 = 36;
 
 /// The root of the emitted JSON blob.
 ///
@@ -30,11 +47,11 @@ pub struct Crate {
     pub includes_private: bool,
     /// A collection of all items in the local crate as well as some external traits and their
     /// items that are referenced locally.
-    pub index: FxHashMap<Id, Item>,
+    pub index: HashMap<Id, Item>,
     /// Maps IDs to fully qualified paths and other info helpful for generating links.
-    pub paths: FxHashMap<Id, ItemSummary>,
+    pub paths: HashMap<Id, ItemSummary>,
     /// Maps `crate_id` of items to a crate name and html_root_url if it exists.
-    pub external_crates: FxHashMap<u32, ExternalCrate>,
+    pub external_crates: HashMap<u32, ExternalCrate>,
     /// A single version number to be used in the future when making backwards incompatible changes
     /// to the JSON output.
     pub format_version: u32,
@@ -95,7 +112,7 @@ pub struct Item {
     /// Some("") if there is some documentation but it is empty (EG `#[doc = ""]`).
     pub docs: Option<String>,
     /// This mapping resolves [intra-doc links](https://github.com/rust-lang/rfcs/blob/master/text/1946-intra-rustdoc-links.md) from the docstring to their IDs
-    pub links: FxHashMap<String, Id>,
+    pub links: HashMap<String, Id>,
     /// Stringified versions of the attributes on this item (e.g. `"#[inline]"`)
     pub attrs: Vec<String>,
     /// Information about the item’s deprecation, if present.
@@ -300,10 +317,10 @@ pub enum AssocItemConstraintKind {
 // FIXME(aDotInTheVoid): Consider making this non-public in rustdoc-types.
 pub struct Id(pub u32);
 
-/// The fundamental kind of an item. Unlike [`ItemEnum`], this does not carry any aditional info.
+/// The fundamental kind of an item. Unlike [`ItemEnum`], this does not carry any additional info.
 ///
 /// Part of [`ItemSummary`].
-#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
+#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
 #[serde(rename_all = "snake_case")]
 pub enum ItemKind {
     /// A module declaration, e.g. `mod foo;` or `mod foo {}`
@@ -693,7 +710,7 @@ pub enum Abi {
     Aapcs { unwind: bool },
     /// Can be specified as `extern "win64"`.
     Win64 { unwind: bool },
-    /// Can be specifed as `extern "sysv64"`.
+    /// Can be specified as `extern "sysv64"`.
     SysV64 { unwind: bool },
     /// Can be specified as `extern "system"`.
     System { unwind: bool },
@@ -887,7 +904,7 @@ pub enum GenericBound {
 }
 
 /// A set of modifiers applied to a trait.
-#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
+#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
 #[serde(rename_all = "snake_case")]
 pub enum TraitBoundModifier {
     /// Marks the absence of a modifier.
@@ -991,7 +1008,7 @@ pub enum Type {
     QualifiedPath {
         /// The name of the associated type in the parent type.
         ///
-        /// ```ignore (incomplete expresssion)
+        /// ```ignore (incomplete expression)
         /// <core::array::IntoIter<u32, 42> as Iterator>::Item
         /// //                                            ^^^^
         /// ```
@@ -1078,12 +1095,15 @@ pub struct FunctionSignature {
 #[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
 pub struct Trait {
     /// Whether the trait is marked `auto` and is thus implemented automatically
-    /// for all aplicable types.
+    /// for all applicable types.
     pub is_auto: bool,
     /// Whether the trait is marked as `unsafe`.
     pub is_unsafe: bool,
-    /// Whether the trait is [object safe](https://doc.rust-lang.org/reference/items/traits.html#object-safety).
-    pub is_object_safe: bool,
+    // FIXME(dyn_compat_renaming): Update the URL once the Reference is updated and hits stable.
+    /// Whether the trait is [dyn compatible](https://doc.rust-lang.org/reference/items/traits.html#object-safety)[^1].
+    ///
+    /// [^1]: Formerly known as "object safe".
+    pub is_dyn_compatible: bool,
     /// Associated [`Item`]s that can/must be implemented by the `impl` blocks.
     pub items: Vec<Id>,
     /// Information about the type parameters and `where` clauses of the trait.
@@ -1185,7 +1205,7 @@ pub struct ProcMacro {
 }
 
 /// The way a [`ProcMacro`] is declared to be used.
-#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
+#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
 #[serde(rename_all = "snake_case")]
 pub enum MacroKind {
     /// A bang macro `foo!()`.
diff --git a/src/tools/cargo b/src/tools/cargo
-Subproject 8c30ce53688e25f7e9d860b33cc914fb2957ca9
+Subproject cf53cc54bb593b5ec3dc2be4b1702f50c36d24d
diff --git a/src/tools/clippy/.github/deploy.sh b/src/tools/clippy/.github/deploy.sh
index 5b4b4be4e36..ea118a3b6fc 100644
--- a/src/tools/clippy/.github/deploy.sh
+++ b/src/tools/clippy/.github/deploy.sh
@@ -8,8 +8,8 @@ rm -rf out/master/ || exit 0
 echo "Making the docs for master"
 mkdir out/master/
 cp util/gh-pages/index.html out/master
+cp util/gh-pages/theme.js out/master
 cp util/gh-pages/script.js out/master
-cp util/gh-pages/lints.json out/master
 cp util/gh-pages/style.css out/master
 
 if [[ -n $TAG_NAME ]]; then
diff --git a/src/tools/clippy/.gitignore b/src/tools/clippy/.gitignore
index 181b71a658b..a7c25b29021 100644
--- a/src/tools/clippy/.gitignore
+++ b/src/tools/clippy/.gitignore
@@ -34,6 +34,7 @@ out
 
 # gh pages docs
 util/gh-pages/lints.json
+util/gh-pages/index.html
 
 # rustfmt backups
 *.rs.bk
diff --git a/src/tools/clippy/CHANGELOG.md b/src/tools/clippy/CHANGELOG.md
index 5d253d52531..4bdbc91db93 100644
--- a/src/tools/clippy/CHANGELOG.md
+++ b/src/tools/clippy/CHANGELOG.md
@@ -6,11 +6,46 @@ document.
 
 ## Unreleased / Beta / In Rust Nightly
 
-[b794b8e0...master](https://github.com/rust-lang/rust-clippy/compare/b794b8e0...master)
+[0f8eabd6...master](https://github.com/rust-lang/rust-clippy/compare/0f8eabd6...master)
+
+## Rust 1.82
+
+Current stable, released 2024-10-17
+
+[View all 108 merged pull requests](https://github.com/rust-lang/rust-clippy/pulls?q=merged%3A2024-07-11T20%3A12%3A07Z..2024-08-24T20%3A55%3A35Z+base%3Amaster)
+
+### New Lints
+
+* Added [`too_long_first_doc_paragraph`] to `nursery`
+  [#12993](https://github.com/rust-lang/rust-clippy/pull/12993)
+* Added [`unused_result_ok`] to `restriction`
+  [#12150](https://github.com/rust-lang/rust-clippy/pull/12150)
+* Added [`pathbuf_init_then_push`] to `restriction`
+  [#11700](https://github.com/rust-lang/rust-clippy/pull/11700)
+
+### Enhancements
+
+* [`explicit_iter_loop`]: Now respects the `msrv` configuration
+  [#13288](https://github.com/rust-lang/rust-clippy/pull/13288)
+* [`assigning_clones`]: No longer lints in test code
+  [#13273](https://github.com/rust-lang/rust-clippy/pull/13273)
+* [`inconsistent_struct_constructor`]: Lint attributes now work on the struct definition
+  [#13211](https://github.com/rust-lang/rust-clippy/pull/13211)
+* [`set_contains_or_insert`]: Now also checks for `BTreeSet`
+  [#13053](https://github.com/rust-lang/rust-clippy/pull/13053)
+* [`doc_markdown`]: Added the following identifiers to [`doc-valid-idents`]: AccessKit,
+  CoreFoundation, CoreGraphics, CoreText, Direct2D, Direct3D, DirectWrite, PostScript,
+  OpenAL, OpenType, WebRTC, WebSocket, WebTransport, NetBSD, and OpenBSD
+  [#13093](https://github.com/rust-lang/rust-clippy/pull/13093)
+
+### ICE Fixes
+
+* [`uninit_vec`]
+  [rust#128720](https://github.com/rust-lang/rust/pull/128720)
 
 ## Rust 1.81
 
-Current stable, released 2024-09-05
+Released 2024-09-05
 
 ### New Lints
 
@@ -5621,6 +5656,7 @@ Released 2018-09-13
 [`manual_find_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_find_map
 [`manual_flatten`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_flatten
 [`manual_hash_one`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_hash_one
+[`manual_ignore_case_cmp`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_ignore_case_cmp
 [`manual_inspect`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_inspect
 [`manual_instant_elapsed`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_instant_elapsed
 [`manual_is_ascii_check`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_is_ascii_check
@@ -5874,6 +5910,7 @@ Released 2018-09-13
 [`ref_option`]: https://rust-lang.github.io/rust-clippy/master/index.html#ref_option
 [`ref_option_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#ref_option_ref
 [`ref_patterns`]: https://rust-lang.github.io/rust-clippy/master/index.html#ref_patterns
+[`regex_creation_in_loops`]: https://rust-lang.github.io/rust-clippy/master/index.html#regex_creation_in_loops
 [`regex_macro`]: https://rust-lang.github.io/rust-clippy/master/index.html#regex_macro
 [`renamed_function_params`]: https://rust-lang.github.io/rust-clippy/master/index.html#renamed_function_params
 [`repeat_once`]: https://rust-lang.github.io/rust-clippy/master/index.html#repeat_once
@@ -6027,6 +6064,7 @@ Released 2018-09-13
 [`unnecessary_get_then_check`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_get_then_check
 [`unnecessary_join`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_join
 [`unnecessary_lazy_evaluations`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_lazy_evaluations
+[`unnecessary_literal_bound`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_literal_bound
 [`unnecessary_literal_unwrap`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_literal_unwrap
 [`unnecessary_map_on_constructor`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_map_on_constructor
 [`unnecessary_min_or_max`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_min_or_max
diff --git a/src/tools/clippy/Cargo.toml b/src/tools/clippy/Cargo.toml
index cf810798d8c..1f7784fc489 100644
--- a/src/tools/clippy/Cargo.toml
+++ b/src/tools/clippy/Cargo.toml
@@ -1,6 +1,6 @@
 [package]
 name = "clippy"
-version = "0.1.83"
+version = "0.1.84"
 description = "A bunch of helpful lints to avoid common pitfalls in Rust"
 repository = "https://github.com/rust-lang/rust-clippy"
 readme = "README.md"
@@ -23,7 +23,7 @@ path = "src/driver.rs"
 [dependencies]
 clippy_config = { path = "clippy_config" }
 clippy_lints = { path = "clippy_lints" }
-rustc_tools_util = "0.3.0"
+rustc_tools_util = "0.4.0"
 tempfile = { version = "3.3", optional = true }
 termize = "0.1"
 color-print = "0.3.4"
@@ -39,6 +39,8 @@ toml = "0.7.3"
 walkdir = "2.3"
 filetime = "0.2.9"
 itertools = "0.12"
+pulldown-cmark = "0.11"
+rinja = { version = "0.3", default-features = false, features = ["config"] }
 
 # UI test dependencies
 clippy_utils = { path = "clippy_utils" }
@@ -50,7 +52,7 @@ parking_lot = "0.12"
 tokio = { version = "1", features = ["io-util"] }
 
 [build-dependencies]
-rustc_tools_util = "0.3.0"
+rustc_tools_util = "0.4.0"
 
 [features]
 integration = ["tempfile"]
diff --git a/src/tools/clippy/book/src/lint_configuration.md b/src/tools/clippy/book/src/lint_configuration.md
index 07a56fb33df..43b551ae216 100644
--- a/src/tools/clippy/book/src/lint_configuration.md
+++ b/src/tools/clippy/book/src/lint_configuration.md
@@ -329,7 +329,7 @@ arithmetic-side-effects-allowed-unary = ["SomeType", "AnotherType"]
 ## `array-size-threshold`
 The maximum allowed size for arrays on the stack
 
-**Default Value:** `512000`
+**Default Value:** `16384`
 
 ---
 **Affected lints:**
diff --git a/src/tools/clippy/clippy_config/Cargo.toml b/src/tools/clippy/clippy_config/Cargo.toml
index 9da7112345d..d21df202dca 100644
--- a/src/tools/clippy/clippy_config/Cargo.toml
+++ b/src/tools/clippy/clippy_config/Cargo.toml
@@ -1,6 +1,6 @@
 [package]
 name = "clippy_config"
-version = "0.1.83"
+version = "0.1.84"
 edition = "2021"
 
 # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
diff --git a/src/tools/clippy/clippy_config/src/conf.rs b/src/tools/clippy/clippy_config/src/conf.rs
index e4e2c97fdc1..4757c0b1339 100644
--- a/src/tools/clippy/clippy_config/src/conf.rs
+++ b/src/tools/clippy/clippy_config/src/conf.rs
@@ -97,6 +97,30 @@ impl ConfError {
     }
 }
 
+// Remove code tags and code behind '# 's, as they are not needed for the lint docs and --explain
+pub fn sanitize_explanation(raw_docs: &str) -> String {
+    // Remove tags and hidden code:
+    let mut explanation = String::with_capacity(128);
+    let mut in_code = false;
+    for line in raw_docs.lines().map(str::trim) {
+        if let Some(lang) = line.strip_prefix("```") {
+            let tag = lang.split_once(',').map_or(lang, |(left, _)| left);
+            if !in_code && matches!(tag, "" | "rust" | "ignore" | "should_panic" | "no_run" | "compile_fail") {
+                explanation += "```rust\n";
+            } else {
+                explanation += line;
+                explanation.push('\n');
+            }
+            in_code = !in_code;
+        } else if !(in_code && line.starts_with("# ")) {
+            explanation += line;
+            explanation.push('\n');
+        }
+    }
+
+    explanation
+}
+
 macro_rules! wrap_option {
     () => {
         None
@@ -366,7 +390,7 @@ define_Conf! {
     arithmetic_side_effects_allowed_unary: Vec<String> = <_>::default(),
     /// The maximum allowed size for arrays on the stack
     #[lints(large_const_arrays, large_stack_arrays)]
-    array_size_threshold: u64 = 512_000,
+    array_size_threshold: u64 = 16 * 1024,
     /// Suppress lints whenever the suggested change would cause breakage for other crates.
     #[lints(
         box_collection,
diff --git a/src/tools/clippy/clippy_config/src/lib.rs b/src/tools/clippy/clippy_config/src/lib.rs
index c63d98a0a13..42651521f8d 100644
--- a/src/tools/clippy/clippy_config/src/lib.rs
+++ b/src/tools/clippy/clippy_config/src/lib.rs
@@ -26,5 +26,5 @@ mod metadata;
 pub mod msrvs;
 pub mod types;
 
-pub use conf::{Conf, get_configuration_metadata, lookup_conf_file};
+pub use conf::{Conf, get_configuration_metadata, lookup_conf_file, sanitize_explanation};
 pub use metadata::ClippyConfiguration;
diff --git a/src/tools/clippy/clippy_config/src/msrvs.rs b/src/tools/clippy/clippy_config/src/msrvs.rs
index 68a3b11d384..2f4da4cba3d 100644
--- a/src/tools/clippy/clippy_config/src/msrvs.rs
+++ b/src/tools/clippy/clippy_config/src/msrvs.rs
@@ -17,8 +17,7 @@ macro_rules! msrv_aliases {
 
 // names may refer to stabilized feature flags or library items
 msrv_aliases! {
-    1,83,0 { CONST_EXTERN_FN }
-    1,83,0 { CONST_FLOAT_BITS_CONV }
+    1,83,0 { CONST_EXTERN_FN, CONST_FLOAT_BITS_CONV, CONST_FLOAT_CLASSIFY }
     1,82,0 { IS_NONE_OR }
     1,81,0 { LINT_REASONS_STABILIZATION }
     1,80,0 { BOX_INTO_ITER}
diff --git a/src/tools/clippy/clippy_dev/Cargo.toml b/src/tools/clippy/clippy_dev/Cargo.toml
index a5d72c3a559..952a8711fb4 100644
--- a/src/tools/clippy/clippy_dev/Cargo.toml
+++ b/src/tools/clippy/clippy_dev/Cargo.toml
@@ -9,7 +9,7 @@ aho-corasick = "1.0"
 clap = { version = "4.4", features = ["derive"] }
 indoc = "1.0"
 itertools = "0.12"
-opener = "0.6"
+opener = "0.7"
 shell-escape = "0.1"
 walkdir = "2.3"
 
diff --git a/src/tools/clippy/clippy_dev/src/new_lint.rs b/src/tools/clippy/clippy_dev/src/new_lint.rs
index 5fd65017cfb..e15ba339717 100644
--- a/src/tools/clippy/clippy_dev/src/new_lint.rs
+++ b/src/tools/clippy/clippy_dev/src/new_lint.rs
@@ -207,13 +207,13 @@ pub(crate) fn get_stabilization_version() -> String {
 
 fn get_test_file_contents(lint_name: &str, msrv: bool) -> String {
     let mut test = formatdoc!(
-        r#"
+        r"
         #![warn(clippy::{lint_name})]
 
         fn main() {{
             // test code goes here
         }}
-    "#
+    "
     );
 
     if msrv {
@@ -272,23 +272,23 @@ fn get_lint_file_contents(lint: &LintData<'_>, enable_msrv: bool) -> String {
 
     result.push_str(&if enable_msrv {
         formatdoc!(
-            r#"
+            r"
             use clippy_config::msrvs::{{self, Msrv}};
             use clippy_config::Conf;
             {pass_import}
             use rustc_lint::{{{context_import}, {pass_type}, LintContext}};
             use rustc_session::impl_lint_pass;
 
-        "#
+        "
         )
     } else {
         formatdoc!(
-            r#"
+            r"
             {pass_import}
             use rustc_lint::{{{context_import}, {pass_type}}};
             use rustc_session::declare_lint_pass;
 
-        "#
+        "
         )
     });
 
@@ -296,7 +296,7 @@ fn get_lint_file_contents(lint: &LintData<'_>, enable_msrv: bool) -> String {
 
     result.push_str(&if enable_msrv {
         formatdoc!(
-            r#"
+            r"
             pub struct {name_camel} {{
                 msrv: Msrv,
             }}
@@ -315,15 +315,15 @@ fn get_lint_file_contents(lint: &LintData<'_>, enable_msrv: bool) -> String {
 
             // TODO: Add MSRV level to `clippy_config/src/msrvs.rs` if needed.
             // TODO: Update msrv config comment in `clippy_config/src/conf.rs`
-        "#
+        "
         )
     } else {
         formatdoc!(
-            r#"
+            r"
             declare_lint_pass!({name_camel} => [{name_upper}]);
 
             impl {pass_type}{pass_lifetimes} for {name_camel} {{}}
-        "#
+        "
         )
     });
 
@@ -416,7 +416,7 @@ fn create_lint_for_ty(lint: &LintData<'_>, enable_msrv: bool, ty: &str) -> io::R
     } else {
         let _: fmt::Result = writedoc!(
             lint_file_contents,
-            r#"
+            r"
                 use rustc_lint::{{{context_import}, LintContext}};
 
                 use super::{name_upper};
@@ -425,7 +425,7 @@ fn create_lint_for_ty(lint: &LintData<'_>, enable_msrv: bool, ty: &str) -> io::R
                 pub(super) fn check(cx: &{context_import}{pass_lifetimes}) {{
                     todo!();
                 }}
-           "#
+           "
         );
     }
 
diff --git a/src/tools/clippy/clippy_dev/src/serve.rs b/src/tools/clippy/clippy_dev/src/serve.rs
index cc14cd8dae6..d367fefec61 100644
--- a/src/tools/clippy/clippy_dev/src/serve.rs
+++ b/src/tools/clippy/clippy_dev/src/serve.rs
@@ -19,7 +19,9 @@ pub fn run(port: u16, lint: Option<String>) -> ! {
     });
 
     loop {
-        if mtime("util/gh-pages/lints.json") < mtime("clippy_lints/src") {
+        let index_time = mtime("util/gh-pages/index.html");
+
+        if index_time < mtime("clippy_lints/src") || index_time < mtime("util/gh-pages/index_template.html") {
             Command::new(env::var("CARGO").unwrap_or("cargo".into()))
                 .arg("collect-metadata")
                 .spawn()
diff --git a/src/tools/clippy/clippy_lints/Cargo.toml b/src/tools/clippy/clippy_lints/Cargo.toml
index d1188940b46..63ea6faf60d 100644
--- a/src/tools/clippy/clippy_lints/Cargo.toml
+++ b/src/tools/clippy/clippy_lints/Cargo.toml
@@ -1,6 +1,6 @@
 [package]
 name = "clippy_lints"
-version = "0.1.83"
+version = "0.1.84"
 description = "A bunch of helpful lints to avoid common pitfalls in Rust"
 repository = "https://github.com/rust-lang/rust-clippy"
 readme = "README.md"
@@ -13,7 +13,6 @@ arrayvec = { version = "0.7", default-features = false }
 cargo_metadata = "0.18"
 clippy_config = { path = "../clippy_config" }
 clippy_utils = { path = "../clippy_utils" }
-declare_clippy_lint = { path = "../declare_clippy_lint" }
 itertools = "0.12"
 quine-mc_cluskey = "0.2"
 regex-syntax = "0.8"
diff --git a/src/tools/clippy/clippy_lints/src/box_default.rs b/src/tools/clippy/clippy_lints/src/box_default.rs
index 40d154c0bdf..bf1d077fec2 100644
--- a/src/tools/clippy/clippy_lints/src/box_default.rs
+++ b/src/tools/clippy/clippy_lints/src/box_default.rs
@@ -47,7 +47,7 @@ impl LateLintPass<'_> for BoxDefault {
             // And the call is that of a `Box` method
             && path_def_id(cx, ty).map_or(false, |id| Some(id) == cx.tcx.lang_items().owned_box())
             // And the single argument to the call is another function call
-            // This is the `T::default()` of `Box::new(T::default())`
+            // This is the `T::default()` (or default equivalent) of `Box::new(T::default())`
             && let ExprKind::Call(arg_path, _) = arg.kind
             // And we are not in a foreign crate's macro
             && !in_external_macro(cx.sess(), expr.span)
diff --git a/src/tools/clippy/clippy_lints/src/byte_char_slices.rs b/src/tools/clippy/clippy_lints/src/byte_char_slices.rs
index dd2620b0b9d..d88c0711b39 100644
--- a/src/tools/clippy/clippy_lints/src/byte_char_slices.rs
+++ b/src/tools/clippy/clippy_lints/src/byte_char_slices.rs
@@ -41,7 +41,7 @@ impl EarlyLintPass for ByteCharSlice {
                 "can be more succinctly written as a byte str",
                 "try",
                 format!("b\"{slice}\""),
-                Applicability::MaybeIncorrect,
+                Applicability::MachineApplicable,
             );
         }
     }
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 d4d5ee37bcc..b7b63250864 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
@@ -19,7 +19,7 @@ pub(super) fn check(
     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
+        && let ExprKind::MethodCall(method_path, receiver, [], _) = cast_expr.kind
         && method_path.ident.name.as_str() == "abs"
     {
         let span = if from.bit_width() == to.bit_width() {
diff --git a/src/tools/clippy/clippy_lints/src/casts/cast_ptr_alignment.rs b/src/tools/clippy/clippy_lints/src/casts/cast_ptr_alignment.rs
index 960c81045e3..b11b967f8bc 100644
--- a/src/tools/clippy/clippy_lints/src/casts/cast_ptr_alignment.rs
+++ b/src/tools/clippy/clippy_lints/src/casts/cast_ptr_alignment.rs
@@ -19,7 +19,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>) {
             cx.typeck_results().expr_ty(expr),
         );
         lint_cast_ptr_alignment(cx, expr, cast_from, cast_to);
-    } else if let ExprKind::MethodCall(method_path, self_arg, ..) = &expr.kind {
+    } else if let ExprKind::MethodCall(method_path, self_arg, [], _) = &expr.kind {
         if method_path.ident.name == sym!(cast)
             && let Some(generic_args) = method_path.args
             && let [GenericArg::Type(cast_to)] = generic_args.args
diff --git a/src/tools/clippy/clippy_lints/src/create_dir.rs b/src/tools/clippy/clippy_lints/src/create_dir.rs
index 24570d8f440..b43906903a0 100644
--- a/src/tools/clippy/clippy_lints/src/create_dir.rs
+++ b/src/tools/clippy/clippy_lints/src/create_dir.rs
@@ -34,7 +34,7 @@ declare_lint_pass!(CreateDir => [CREATE_DIR]);
 
 impl LateLintPass<'_> for CreateDir {
     fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) {
-        if let ExprKind::Call(func, [arg, ..]) = expr.kind
+        if let ExprKind::Call(func, [arg]) = expr.kind
             && let ExprKind::Path(ref path) = func.kind
             && let Some(def_id) = cx.qpath_res(path, func.hir_id).opt_def_id()
             && cx.tcx.is_diagnostic_item(sym::fs_create_dir, def_id)
diff --git a/src/tools/clippy/clippy_lints/src/declare_clippy_lint.rs b/src/tools/clippy/clippy_lints/src/declare_clippy_lint.rs
new file mode 100644
index 00000000000..b1e39c70baa
--- /dev/null
+++ b/src/tools/clippy/clippy_lints/src/declare_clippy_lint.rs
@@ -0,0 +1,162 @@
+#[macro_export]
+#[allow(clippy::crate_in_macro_def)]
+macro_rules! declare_clippy_lint {
+    (@
+        $(#[doc = $lit:literal])*
+        pub $lint_name:ident,
+        $category:ident,
+        $lintcategory:expr,
+        $desc:literal,
+        $version_expr:expr,
+        $version_lit:literal
+    ) => {
+        rustc_session::declare_tool_lint! {
+            $(#[doc = $lit])*
+            #[clippy::version = $version_lit]
+            pub clippy::$lint_name,
+            $category,
+            $desc,
+            report_in_external_macro:true
+        }
+
+        pub(crate) static ${concat($lint_name, _INFO)}: &'static crate::LintInfo = &crate::LintInfo {
+            lint: &$lint_name,
+            category:  $lintcategory,
+            explanation: concat!($($lit,"\n",)*),
+            location: concat!(file!(), "#L", line!()),
+            version: $version_expr
+        };
+    };
+    (
+        $(#[doc = $lit:literal])*
+        #[clippy::version = $version:literal]
+        pub $lint_name:ident,
+        restriction,
+        $desc:literal
+    ) => {
+        declare_clippy_lint! {@
+            $(#[doc = $lit])*
+            pub $lint_name, Allow, crate::LintCategory::Restriction, $desc,
+            Some($version), $version
+        }
+    };
+    (
+        $(#[doc = $lit:literal])*
+        #[clippy::version = $version:literal]
+        pub $lint_name:ident,
+        style,
+        $desc:literal
+    ) => {
+        declare_clippy_lint! {@
+            $(#[doc = $lit])*
+            pub $lint_name, Warn, crate::LintCategory::Style, $desc,
+            Some($version), $version
+
+        }
+    };
+    (
+        $(#[doc = $lit:literal])*
+        #[clippy::version = $version:literal]
+        pub $lint_name:ident,
+        correctness,
+        $desc:literal
+    ) => {
+        declare_clippy_lint! {@
+            $(#[doc = $lit])*
+            pub $lint_name, Deny, crate::LintCategory::Correctness, $desc,
+            Some($version), $version
+
+        }
+    };
+    (
+        $(#[doc = $lit:literal])*
+        #[clippy::version = $version:literal]
+        pub $lint_name:ident,
+        perf,
+        $desc:literal
+    ) => {
+        declare_clippy_lint! {@
+            $(#[doc = $lit])*
+            pub $lint_name, Warn, crate::LintCategory::Perf, $desc,
+            Some($version), $version
+        }
+    };
+    (
+        $(#[doc = $lit:literal])*
+        #[clippy::version = $version:literal]
+        pub $lint_name:ident,
+        complexity,
+        $desc:literal
+    ) => {
+        declare_clippy_lint! {@
+            $(#[doc = $lit])*
+            pub $lint_name, Warn, crate::LintCategory::Complexity, $desc,
+            Some($version), $version
+        }
+    };
+    (
+        $(#[doc = $lit:literal])*
+        #[clippy::version = $version:literal]
+        pub $lint_name:ident,
+        suspicious,
+        $desc:literal
+    ) => {
+        declare_clippy_lint! {@
+            $(#[doc = $lit])*
+            pub $lint_name, Warn, crate::LintCategory::Suspicious, $desc,
+            Some($version), $version
+        }
+    };
+    (
+        $(#[doc = $lit:literal])*
+        #[clippy::version = $version:literal]
+        pub $lint_name:ident,
+        nursery,
+        $desc:literal
+    ) => {
+        declare_clippy_lint! {@
+            $(#[doc = $lit])*
+            pub $lint_name, Allow, crate::LintCategory::Nursery, $desc,
+            Some($version), $version
+        }
+    };
+    (
+        $(#[doc = $lit:literal])*
+        #[clippy::version = $version:literal]
+        pub $lint_name:ident,
+        pedantic,
+        $desc:literal
+    ) => {
+        declare_clippy_lint! {@
+            $(#[doc = $lit])*
+            pub $lint_name, Allow, crate::LintCategory::Pedantic, $desc,
+            Some($version), $version
+        }
+    };
+    (
+        $(#[doc = $lit:literal])*
+        #[clippy::version = $version:literal]
+        pub $lint_name:ident,
+        cargo,
+        $desc:literal
+    ) => {
+        declare_clippy_lint! {@
+            $(#[doc = $lit])*
+            pub $lint_name, Allow, crate::LintCategory::Cargo, $desc,
+            Some($version), $version
+        }
+    };
+
+    (
+        $(#[doc = $lit:literal])*
+        pub $lint_name:ident,
+        internal,
+        $desc:literal
+    ) => {
+        declare_clippy_lint! {@
+            $(#[doc = $lit])*
+            pub $lint_name, Allow, crate::LintCategory::Internal, $desc,
+            None, "0.0.0"
+        }
+    };
+}
diff --git a/src/tools/clippy/clippy_lints/src/declared_lints.rs b/src/tools/clippy/clippy_lints/src/declared_lints.rs
index 9cec672beb0..3c4e75df8ab 100644
--- a/src/tools/clippy/clippy_lints/src/declared_lints.rs
+++ b/src/tools/clippy/clippy_lints/src/declared_lints.rs
@@ -306,6 +306,7 @@ pub static LINTS: &[&crate::LintInfo] = &[
     crate::manual_float_methods::MANUAL_IS_FINITE_INFO,
     crate::manual_float_methods::MANUAL_IS_INFINITE_INFO,
     crate::manual_hash_one::MANUAL_HASH_ONE_INFO,
+    crate::manual_ignore_case_cmp::MANUAL_IGNORE_CASE_CMP_INFO,
     crate::manual_is_ascii_check::MANUAL_IS_ASCII_CHECK_INFO,
     crate::manual_is_power_of_two::MANUAL_IS_POWER_OF_TWO_INFO,
     crate::manual_let_else::MANUAL_LET_ELSE_INFO,
@@ -639,6 +640,7 @@ pub static LINTS: &[&crate::LintInfo] = &[
     crate::ref_patterns::REF_PATTERNS_INFO,
     crate::reference::DEREF_ADDROF_INFO,
     crate::regex::INVALID_REGEX_INFO,
+    crate::regex::REGEX_CREATION_IN_LOOPS_INFO,
     crate::regex::TRIVIAL_REGEX_INFO,
     crate::repeat_vec_with_capacity::REPEAT_VEC_WITH_CAPACITY_INFO,
     crate::reserve_after_initialization::RESERVE_AFTER_INITIALIZATION_INFO,
@@ -736,6 +738,7 @@ pub static LINTS: &[&crate::LintInfo] = &[
     crate::unit_types::UNIT_CMP_INFO,
     crate::unnamed_address::FN_ADDRESS_COMPARISONS_INFO,
     crate::unnecessary_box_returns::UNNECESSARY_BOX_RETURNS_INFO,
+    crate::unnecessary_literal_bound::UNNECESSARY_LITERAL_BOUND_INFO,
     crate::unnecessary_map_on_constructor::UNNECESSARY_MAP_ON_CONSTRUCTOR_INFO,
     crate::unnecessary_owned_empty_strings::UNNECESSARY_OWNED_EMPTY_STRINGS_INFO,
     crate::unnecessary_self_imports::UNNECESSARY_SELF_IMPORTS_INFO,
diff --git a/src/tools/clippy/clippy_lints/src/default.rs b/src/tools/clippy/clippy_lints/src/default.rs
index dc10b64698b..de775b64795 100644
--- a/src/tools/clippy/clippy_lints/src/default.rs
+++ b/src/tools/clippy/clippy_lints/src/default.rs
@@ -83,7 +83,7 @@ impl<'tcx> LateLintPass<'tcx> for Default {
         if !expr.span.from_expansion()
             // Avoid cases already linted by `field_reassign_with_default`
             && !self.reassigned_linted.contains(&expr.span)
-            && let ExprKind::Call(path, ..) = expr.kind
+            && let ExprKind::Call(path, []) = expr.kind
             && !in_automatically_derived(cx.tcx, expr.hir_id)
             && let ExprKind::Path(ref qpath) = path.kind
             && let Some(def_id) = cx.qpath_res(qpath, path.hir_id).opt_def_id()
@@ -253,7 +253,7 @@ impl<'tcx> LateLintPass<'tcx> for Default {
 
 /// Checks if the given expression is the `default` method belonging to the `Default` trait.
 fn is_expr_default<'tcx>(expr: &'tcx Expr<'tcx>, cx: &LateContext<'tcx>) -> bool {
-    if let ExprKind::Call(fn_expr, _) = &expr.kind
+    if let ExprKind::Call(fn_expr, []) = &expr.kind
         && let ExprKind::Path(qpath) = &fn_expr.kind
         && let Res::Def(_, def_id) = cx.qpath_res(qpath, fn_expr.hir_id)
     {
diff --git a/src/tools/clippy/clippy_lints/src/doc/mod.rs b/src/tools/clippy/clippy_lints/src/doc/mod.rs
index e090644ae44..89c6a4e08dc 100644
--- a/src/tools/clippy/clippy_lints/src/doc/mod.rs
+++ b/src/tools/clippy/clippy_lints/src/doc/mod.rs
@@ -452,7 +452,7 @@ declare_clippy_lint! {
     /// ```
     #[clippy::version = "1.82.0"]
     pub TOO_LONG_FIRST_DOC_PARAGRAPH,
-    style,
+    nursery,
     "ensure that the first line of a documentation paragraph isn't too long"
 }
 
diff --git a/src/tools/clippy/clippy_lints/src/exit.rs b/src/tools/clippy/clippy_lints/src/exit.rs
index f37d11f7eb9..3c235fab009 100644
--- a/src/tools/clippy/clippy_lints/src/exit.rs
+++ b/src/tools/clippy/clippy_lints/src/exit.rs
@@ -43,7 +43,7 @@ declare_lint_pass!(Exit => [EXIT]);
 
 impl<'tcx> LateLintPass<'tcx> for Exit {
     fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) {
-        if let ExprKind::Call(path_expr, _args) = e.kind
+        if let ExprKind::Call(path_expr, [_]) = e.kind
             && let ExprKind::Path(ref path) = path_expr.kind
             && let Some(def_id) = cx.qpath_res(path, path_expr.hir_id).opt_def_id()
             && cx.tcx.is_diagnostic_item(sym::process_exit, def_id)
diff --git a/src/tools/clippy/clippy_lints/src/explicit_write.rs b/src/tools/clippy/clippy_lints/src/explicit_write.rs
index 5b423a96918..4e4434ec7d1 100644
--- a/src/tools/clippy/clippy_lints/src/explicit_write.rs
+++ b/src/tools/clippy/clippy_lints/src/explicit_write.rs
@@ -57,7 +57,7 @@ impl<'tcx> LateLintPass<'tcx> for ExplicitWrite {
             && unwrap_fun.ident.name == sym::unwrap
             // match call to write_fmt
             && let ExprKind::MethodCall(write_fun, write_recv, [write_arg], _) = *look_in_block(cx, &write_call.kind)
-            && let ExprKind::Call(write_recv_path, _) = write_recv.kind
+            && let ExprKind::Call(write_recv_path, []) = write_recv.kind
             && write_fun.ident.name == sym!(write_fmt)
             && let Some(def_id) = path_def_id(cx, write_recv_path)
         {
diff --git a/src/tools/clippy/clippy_lints/src/floating_point_arithmetic.rs b/src/tools/clippy/clippy_lints/src/floating_point_arithmetic.rs
index 8da6623f34d..daa199779e3 100644
--- a/src/tools/clippy/clippy_lints/src/floating_point_arithmetic.rs
+++ b/src/tools/clippy/clippy_lints/src/floating_point_arithmetic.rs
@@ -436,12 +436,12 @@ fn check_expm1(cx: &LateContext<'_>, expr: &Expr<'_>) {
         lhs,
         rhs,
     ) = expr.kind
+        && let ExprKind::MethodCall(path, self_arg, [], _) = &lhs.kind
+        && path.ident.name.as_str() == "exp"
         && cx.typeck_results().expr_ty(lhs).is_floating_point()
         && let Some(value) = ConstEvalCtxt::new(cx).eval(rhs)
         && (F32(1.0) == value || F64(1.0) == value)
-        && let ExprKind::MethodCall(path, self_arg, ..) = &lhs.kind
         && cx.typeck_results().expr_ty(self_arg).is_floating_point()
-        && path.ident.name.as_str() == "exp"
     {
         span_lint_and_sugg(
             cx,
diff --git a/src/tools/clippy/clippy_lints/src/format_impl.rs b/src/tools/clippy/clippy_lints/src/format_impl.rs
index 7c0515b8c56..5619cb0ab1b 100644
--- a/src/tools/clippy/clippy_lints/src/format_impl.rs
+++ b/src/tools/clippy/clippy_lints/src/format_impl.rs
@@ -151,7 +151,7 @@ struct FormatImplExpr<'a, 'tcx> {
 impl FormatImplExpr<'_, '_> {
     fn check_to_string_in_display(&self) {
         if self.format_trait_impl.name == sym::Display
-            && let ExprKind::MethodCall(path, self_arg, ..) = self.expr.kind
+            && let ExprKind::MethodCall(path, self_arg, [], _) = self.expr.kind
             // Get the hir_id of the object we are calling the method on
             // Is the method to_string() ?
             && path.ident.name == sym::to_string
diff --git a/src/tools/clippy/clippy_lints/src/if_let_mutex.rs b/src/tools/clippy/clippy_lints/src/if_let_mutex.rs
index 1c52514a330..ba80c099a01 100644
--- a/src/tools/clippy/clippy_lints/src/if_let_mutex.rs
+++ b/src/tools/clippy/clippy_lints/src/if_let_mutex.rs
@@ -82,7 +82,7 @@ fn mutex_lock_call<'tcx>(
     expr: &'tcx Expr<'_>,
     op_mutex: Option<&'tcx Expr<'_>>,
 ) -> ControlFlow<&'tcx Expr<'tcx>> {
-    if let ExprKind::MethodCall(path, self_arg, ..) = &expr.kind
+    if let ExprKind::MethodCall(path, self_arg, [], _) = &expr.kind
         && path.ident.as_str() == "lock"
         && let ty = cx.typeck_results().expr_ty(self_arg).peel_refs()
         && is_type_diagnostic_item(cx, ty, sym::Mutex)
diff --git a/src/tools/clippy/clippy_lints/src/implicit_saturating_sub.rs b/src/tools/clippy/clippy_lints/src/implicit_saturating_sub.rs
index f4a64f5c20b..3b84b569c3e 100644
--- a/src/tools/clippy/clippy_lints/src/implicit_saturating_sub.rs
+++ b/src/tools/clippy/clippy_lints/src/implicit_saturating_sub.rs
@@ -139,6 +139,13 @@ fn check_manual_check<'tcx>(
                 if_block,
                 else_block,
                 msrv,
+                matches!(
+                    clippy_utils::get_parent_expr(cx, expr),
+                    Some(Expr {
+                        kind: ExprKind::If(..),
+                        ..
+                    })
+                ),
             ),
             BinOpKind::Lt | BinOpKind::Le => check_gt(
                 cx,
@@ -149,6 +156,13 @@ fn check_manual_check<'tcx>(
                 if_block,
                 else_block,
                 msrv,
+                matches!(
+                    clippy_utils::get_parent_expr(cx, expr),
+                    Some(Expr {
+                        kind: ExprKind::If(..),
+                        ..
+                    })
+                ),
             ),
             _ => {},
         }
@@ -165,6 +179,7 @@ fn check_gt(
     if_block: &Expr<'_>,
     else_block: &Expr<'_>,
     msrv: &Msrv,
+    is_composited: bool,
 ) {
     if let Some(big_var) = Var::new(big_var)
         && let Some(little_var) = Var::new(little_var)
@@ -178,6 +193,7 @@ fn check_gt(
             if_block,
             else_block,
             msrv,
+            is_composited,
         );
     }
 }
@@ -206,6 +222,7 @@ fn check_subtraction(
     if_block: &Expr<'_>,
     else_block: &Expr<'_>,
     msrv: &Msrv,
+    is_composited: bool,
 ) {
     let if_block = peel_blocks(if_block);
     let else_block = peel_blocks(else_block);
@@ -226,6 +243,7 @@ fn check_subtraction(
             else_block,
             if_block,
             msrv,
+            is_composited,
         );
         return;
     }
@@ -242,13 +260,18 @@ fn check_subtraction(
                 && let Some(little_var_snippet) = snippet_opt(cx, little_var.span)
                 && (!is_in_const_context(cx) || msrv.meets(msrvs::SATURATING_SUB_CONST))
             {
+                let sugg = format!(
+                    "{}{big_var_snippet}.saturating_sub({little_var_snippet}){}",
+                    if is_composited { "{ " } else { "" },
+                    if is_composited { " }" } else { "" }
+                );
                 span_lint_and_sugg(
                     cx,
                     IMPLICIT_SATURATING_SUB,
                     expr_span,
                     "manual arithmetic check found",
                     "replace it with",
-                    format!("{big_var_snippet}.saturating_sub({little_var_snippet})"),
+                    sugg,
                     Applicability::MachineApplicable,
                 );
             }
diff --git a/src/tools/clippy/clippy_lints/src/implied_bounds_in_impls.rs b/src/tools/clippy/clippy_lints/src/implied_bounds_in_impls.rs
index 590d9afd1b4..00f83237224 100644
--- a/src/tools/clippy/clippy_lints/src/implied_bounds_in_impls.rs
+++ b/src/tools/clippy/clippy_lints/src/implied_bounds_in_impls.rs
@@ -3,8 +3,8 @@ use clippy_utils::source::snippet;
 use rustc_errors::{Applicability, SuggestionStyle};
 use rustc_hir::def_id::DefId;
 use rustc_hir::{
-    AssocItemConstraint, GenericArg, GenericBound, GenericBounds, PredicateOrigin, TraitBoundModifier,
-    TyKind, WherePredicate,
+    AssocItemConstraint, GenericArg, GenericBound, GenericBounds, PredicateOrigin, TraitBoundModifier, TyKind,
+    WherePredicate,
 };
 use rustc_hir_analysis::lower_ty;
 use rustc_lint::{LateContext, LateLintPass};
@@ -190,15 +190,6 @@ fn is_same_generics<'tcx>(
         .enumerate()
         .skip(1) // skip `Self` implicit arg
         .all(|(arg_index, arg)| {
-            if [
-                implied_by_generics.host_effect_index,
-                implied_generics.host_effect_index,
-            ]
-            .contains(&Some(arg_index))
-            {
-                // skip host effect params in determining whether generics are same
-                return true;
-            }
             if let Some(ty) = arg.as_type() {
                 if let &ty::Param(ty::ParamTy { index, .. }) = ty.kind()
                     // `index == 0` means that it's referring to `Self`,
diff --git a/src/tools/clippy/clippy_lints/src/indexing_slicing.rs b/src/tools/clippy/clippy_lints/src/indexing_slicing.rs
index 22e9674714f..ae2c3e0491f 100644
--- a/src/tools/clippy/clippy_lints/src/indexing_slicing.rs
+++ b/src/tools/clippy/clippy_lints/src/indexing_slicing.rs
@@ -114,7 +114,7 @@ impl<'tcx> LateLintPass<'tcx> for IndexingSlicing {
             if let Some(range) = higher::Range::hir(index) {
                 // Ranged indexes, i.e., &x[n..m], &x[n..], &x[..n] and &x[..]
                 if let ty::Array(_, s) = ty.kind() {
-                    let size: u128 = if let Some(size) = s.try_eval_target_usize(cx.tcx, cx.param_env) {
+                    let size: u128 = if let Some(size) = s.try_to_target_usize(cx.tcx) {
                         size.into()
                     } else {
                         return;
@@ -183,7 +183,7 @@ impl<'tcx> LateLintPass<'tcx> for IndexingSlicing {
                             && let ty::Uint(utype) = cx.typeck_results().expr_ty(index).kind()
                             && *utype == ty::UintTy::Usize
                             && let ty::Array(_, s) = ty.kind()
-                            && let Some(size) = s.try_eval_target_usize(cx.tcx, cx.param_env)
+                            && let Some(size) = s.try_to_target_usize(cx.tcx)
                         {
                             // get constant offset and check whether it is in bounds
                             let off = usize::try_from(off).unwrap();
diff --git a/src/tools/clippy/clippy_lints/src/item_name_repetitions.rs b/src/tools/clippy/clippy_lints/src/item_name_repetitions.rs
index 66a8a3167a4..dd90e2a6e94 100644
--- a/src/tools/clippy/clippy_lints/src/item_name_repetitions.rs
+++ b/src/tools/clippy/clippy_lints/src/item_name_repetitions.rs
@@ -50,11 +50,28 @@ declare_clippy_lint! {
 
 declare_clippy_lint! {
     /// ### What it does
-    /// Detects type names that are prefixed or suffixed by the
-    /// containing module's name.
+    /// Detects public item names that are prefixed or suffixed by the
+    /// containing public module's name.
     ///
     /// ### Why is this bad?
-    /// It requires the user to type the module name twice.
+    /// It requires the user to type the module name twice in each usage,
+    /// especially if they choose to import the module rather than its contents.
+    ///
+    /// Lack of such repetition is also the style used in the Rust standard library;
+    /// e.g. `io::Error` and `fmt::Error` rather than `io::IoError` and `fmt::FmtError`;
+    /// and `array::from_ref` rather than `array::array_from_ref`.
+    ///
+    /// ### Known issues
+    /// Glob re-exports are ignored; e.g. this will not warn even though it should:
+    ///
+    /// ```no_run
+    /// pub mod foo {
+    ///     mod iteration {
+    ///         pub struct FooIter {}
+    ///     }
+    ///     pub use iteration::*; // creates the path `foo::FooIter`
+    /// }
+    /// ```
     ///
     /// ### Example
     /// ```no_run
@@ -71,7 +88,7 @@ declare_clippy_lint! {
     /// ```
     #[clippy::version = "1.33.0"]
     pub MODULE_NAME_REPETITIONS,
-    pedantic,
+    restriction,
     "type names prefixed/postfixed with their containing module's name"
 }
 
@@ -389,12 +406,12 @@ impl LateLintPass<'_> for ItemNameRepetitions {
         let item_name = item.ident.name.as_str();
         let item_camel = to_camel_case(item_name);
         if !item.span.from_expansion() && is_present_in_source(cx, item.span) {
-            if let [.., (mod_name, mod_camel, owner_id)] = &*self.modules {
+            if let [.., (mod_name, mod_camel, mod_owner_id)] = &*self.modules {
                 // constants don't have surrounding modules
                 if !mod_camel.is_empty() {
                     if mod_name == &item.ident.name
                         && let ItemKind::Mod(..) = item.kind
-                        && (!self.allow_private_module_inception || cx.tcx.visibility(owner_id.def_id).is_public())
+                        && (!self.allow_private_module_inception || cx.tcx.visibility(mod_owner_id.def_id).is_public())
                     {
                         span_lint(
                             cx,
@@ -403,9 +420,13 @@ impl LateLintPass<'_> for ItemNameRepetitions {
                             "module has the same name as its containing module",
                         );
                     }
+
                     // The `module_name_repetitions` lint should only trigger if the item has the module in its
                     // name. Having the same name is accepted.
-                    if cx.tcx.visibility(item.owner_id).is_public() && item_camel.len() > mod_camel.len() {
+                    if cx.tcx.visibility(item.owner_id).is_public()
+                        && cx.tcx.visibility(mod_owner_id.def_id).is_public()
+                        && item_camel.len() > mod_camel.len()
+                    {
                         let matching = count_match_start(mod_camel, &item_camel);
                         let rmatching = count_match_end(mod_camel, &item_camel);
                         let nchars = mod_camel.chars().count();
diff --git a/src/tools/clippy/clippy_lints/src/large_futures.rs b/src/tools/clippy/clippy_lints/src/large_futures.rs
index 6f5065e4936..25f9be8b2d7 100644
--- a/src/tools/clippy/clippy_lints/src/large_futures.rs
+++ b/src/tools/clippy/clippy_lints/src/large_futures.rs
@@ -57,7 +57,7 @@ impl_lint_pass!(LargeFuture => [LARGE_FUTURES]);
 impl<'tcx> LateLintPass<'tcx> for LargeFuture {
     fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) {
         if let ExprKind::Match(scrutinee, _, MatchSource::AwaitDesugar) = expr.kind
-            && let ExprKind::Call(func, [arg, ..]) = scrutinee.kind
+            && let ExprKind::Call(func, [arg]) = scrutinee.kind
             && let ExprKind::Path(QPath::LangItem(LangItem::IntoFutureIntoFuture, ..)) = func.kind
             && !expr.span.from_expansion()
             && let ty = cx.typeck_results().expr_ty(arg)
diff --git a/src/tools/clippy/clippy_lints/src/large_stack_arrays.rs b/src/tools/clippy/clippy_lints/src/large_stack_arrays.rs
index 0f061d6de50..4ef881f11d5 100644
--- a/src/tools/clippy/clippy_lints/src/large_stack_arrays.rs
+++ b/src/tools/clippy/clippy_lints/src/large_stack_arrays.rs
@@ -1,3 +1,5 @@
+use std::num::Saturating;
+
 use clippy_config::Conf;
 use clippy_utils::diagnostics::span_lint_and_then;
 use clippy_utils::is_from_proc_macro;
@@ -30,6 +32,7 @@ declare_clippy_lint! {
 pub struct LargeStackArrays {
     maximum_allowed_size: u64,
     prev_vec_macro_callsite: Option<Span>,
+    const_item_counter: Saturating<u16>,
 }
 
 impl LargeStackArrays {
@@ -37,6 +40,7 @@ impl LargeStackArrays {
         Self {
             maximum_allowed_size: conf.array_size_threshold,
             prev_vec_macro_callsite: None,
+            const_item_counter: Saturating(0),
         }
     }
 
@@ -60,8 +64,21 @@ impl LargeStackArrays {
 impl_lint_pass!(LargeStackArrays => [LARGE_STACK_ARRAYS]);
 
 impl<'tcx> LateLintPass<'tcx> for LargeStackArrays {
+    fn check_item(&mut self, _: &LateContext<'tcx>, item: &'tcx Item<'tcx>) {
+        if matches!(item.kind, ItemKind::Static(..) | ItemKind::Const(..)) {
+            self.const_item_counter += 1;
+        }
+    }
+
+    fn check_item_post(&mut self, _: &LateContext<'tcx>, item: &'tcx Item<'tcx>) {
+        if matches!(item.kind, ItemKind::Static(..) | ItemKind::Const(..)) {
+            self.const_item_counter -= 1;
+        }
+    }
+
     fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &Expr<'tcx>) {
-        if let ExprKind::Repeat(_, _) | ExprKind::Array(_) = expr.kind
+        if self.const_item_counter.0 == 0
+            && let ExprKind::Repeat(_, _) | ExprKind::Array(_) = expr.kind
             && !self.is_from_vec_macro(cx, expr.span)
             && let ty::Array(element_type, cst) = cx.typeck_results().expr_ty(expr).kind()
             && let ConstKind::Value(_, ty::ValTree::Leaf(element_count)) = cst.kind()
diff --git a/src/tools/clippy/clippy_lints/src/len_zero.rs b/src/tools/clippy/clippy_lints/src/len_zero.rs
index 035ee40348c..47c65ee6d0b 100644
--- a/src/tools/clippy/clippy_lints/src/len_zero.rs
+++ b/src/tools/clippy/clippy_lints/src/len_zero.rs
@@ -513,7 +513,7 @@ fn check_cmp(cx: &LateContext<'_>, span: Span, method: &Expr<'_>, lit: &Expr<'_>
         return;
     }
 
-    if let (&ExprKind::MethodCall(method_path, receiver, args, _), ExprKind::Lit(lit)) = (&method.kind, &lit.kind) {
+    if let (&ExprKind::MethodCall(method_path, receiver, [], _), ExprKind::Lit(lit)) = (&method.kind, &lit.kind) {
         // check if we are in an is_empty() method
         if let Some(name) = get_item_name(cx, method) {
             if name.as_str() == "is_empty" {
@@ -521,29 +521,17 @@ fn check_cmp(cx: &LateContext<'_>, span: Span, method: &Expr<'_>, lit: &Expr<'_>
             }
         }
 
-        check_len(
-            cx,
-            span,
-            method_path.ident.name,
-            receiver,
-            args,
-            &lit.node,
-            op,
-            compare_to,
-        );
+        check_len(cx, span, method_path.ident.name, receiver, &lit.node, op, compare_to);
     } else {
         check_empty_expr(cx, span, method, lit, op);
     }
 }
 
-// FIXME(flip1995): Figure out how to reduce the number of arguments
-#[allow(clippy::too_many_arguments)]
 fn check_len(
     cx: &LateContext<'_>,
     span: Span,
     method_name: Symbol,
     receiver: &Expr<'_>,
-    args: &[Expr<'_>],
     lit: &LitKind,
     op: &str,
     compare_to: u32,
@@ -554,7 +542,7 @@ fn check_len(
             return;
         }
 
-        if method_name == sym::len && args.is_empty() && has_is_empty(cx, receiver) {
+        if method_name == sym::len && has_is_empty(cx, receiver) {
             let mut applicability = Applicability::MachineApplicable;
             span_lint_and_sugg(
                 cx,
diff --git a/src/tools/clippy/clippy_lints/src/lib.rs b/src/tools/clippy/clippy_lints/src/lib.rs
index 6ee064a6124..6e29dde2211 100644
--- a/src/tools/clippy/clippy_lints/src/lib.rs
+++ b/src/tools/clippy/clippy_lints/src/lib.rs
@@ -1,6 +1,7 @@
 #![feature(array_windows)]
 #![feature(binary_heap_into_iter_sorted)]
 #![feature(box_patterns)]
+#![feature(macro_metavar_expr_concat)]
 #![feature(f128)]
 #![feature(f16)]
 #![feature(if_let_guard)]
@@ -56,9 +57,10 @@ extern crate rustc_trait_selection;
 extern crate thin_vec;
 
 #[macro_use]
-extern crate clippy_utils;
+mod declare_clippy_lint;
+
 #[macro_use]
-extern crate declare_clippy_lint;
+extern crate clippy_utils;
 
 #[cfg_attr(feature = "internal", allow(clippy::missing_clippy_version_attribute))]
 mod utils;
@@ -203,6 +205,7 @@ mod manual_clamp;
 mod manual_div_ceil;
 mod manual_float_methods;
 mod manual_hash_one;
+mod manual_ignore_case_cmp;
 mod manual_is_ascii_check;
 mod manual_is_power_of_two;
 mod manual_let_else;
@@ -360,6 +363,7 @@ mod unit_return_expecting_ord;
 mod unit_types;
 mod unnamed_address;
 mod unnecessary_box_returns;
+mod unnecessary_literal_bound;
 mod unnecessary_map_on_constructor;
 mod unnecessary_owned_empty_strings;
 mod unnecessary_self_imports;
@@ -391,7 +395,7 @@ mod zero_sized_map_values;
 mod zombie_processes;
 // end lints modules, do not remove this comment, it’s used in `update_lints`
 
-use clippy_config::{Conf, get_configuration_metadata};
+use clippy_config::{Conf, get_configuration_metadata, sanitize_explanation};
 use clippy_utils::macros::FormatArgsStorage;
 use rustc_data_structures::fx::FxHashSet;
 use rustc_lint::{Lint, LintId};
@@ -519,8 +523,9 @@ impl LintInfo {
 
 pub fn explain(name: &str) -> i32 {
     let target = format!("clippy::{}", name.to_ascii_uppercase());
+
     if let Some(info) = declared_lints::LINTS.iter().find(|info| info.lint.name == target) {
-        println!("{}", info.explanation);
+        println!("{}", sanitize_explanation(info.explanation));
         // Check if the lint has configuration
         let mut mdconf = get_configuration_metadata();
         let name = name.to_ascii_lowercase();
@@ -896,7 +901,7 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) {
     store.register_late_pass(|_| Box::new(manual_range_patterns::ManualRangePatterns));
     store.register_early_pass(|| Box::new(visibility::Visibility));
     store.register_late_pass(move |_| Box::new(tuple_array_conversions::TupleArrayConversions::new(conf)));
-    store.register_late_pass(|_| Box::new(manual_float_methods::ManualFloatMethods));
+    store.register_late_pass(move |_| Box::new(manual_float_methods::ManualFloatMethods::new(conf)));
     store.register_late_pass(|_| Box::new(four_forward_slashes::FourForwardSlashes));
     store.register_late_pass(|_| Box::new(error_impl_error::ErrorImplError));
     store.register_late_pass(move |_| Box::new(absolute_paths::AbsolutePaths::new(conf)));
@@ -941,5 +946,7 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) {
     store.register_late_pass(|_| Box::new(manual_is_power_of_two::ManualIsPowerOfTwo));
     store.register_late_pass(|_| Box::new(non_zero_suggestions::NonZeroSuggestions));
     store.register_late_pass(move |_| Box::new(unused_trait_names::UnusedTraitNames::new(conf)));
+    store.register_late_pass(|_| Box::new(manual_ignore_case_cmp::ManualIgnoreCaseCmp));
+    store.register_late_pass(|_| Box::new(unnecessary_literal_bound::UnnecessaryLiteralBound));
     // 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 a7c48eb216a..5a3930b8bb8 100644
--- a/src/tools/clippy/clippy_lints/src/lifetimes.rs
+++ b/src/tools/clippy/clippy_lints/src/lifetimes.rs
@@ -6,8 +6,8 @@ use rustc_errors::Applicability;
 use rustc_hir::FnRetTy::Return;
 use rustc_hir::intravisit::nested_filter::{self as hir_nested_filter, NestedFilter};
 use rustc_hir::intravisit::{
-    Visitor, walk_fn_decl, walk_generic_args, walk_generics, walk_impl_item_ref, walk_param_bound,
-    walk_poly_trait_ref, walk_trait_ref, walk_ty, walk_where_predicate,
+    Visitor, walk_fn_decl, walk_generic_args, walk_generics, walk_impl_item_ref, walk_param_bound, walk_poly_trait_ref,
+    walk_trait_ref, walk_ty, walk_where_predicate,
 };
 use rustc_hir::{
     BareFnTy, BodyId, FnDecl, FnSig, GenericArg, GenericArgs, GenericBound, GenericParam, GenericParamKind, Generics,
diff --git a/src/tools/clippy/clippy_lints/src/literal_representation.rs b/src/tools/clippy/clippy_lints/src/literal_representation.rs
index 81f2a03fb55..e2dcb20f906 100644
--- a/src/tools/clippy/clippy_lints/src/literal_representation.rs
+++ b/src/tools/clippy/clippy_lints/src/literal_representation.rs
@@ -412,7 +412,6 @@ impl LiteralDigitGrouping {
     }
 }
 
-#[expect(clippy::module_name_repetitions)]
 pub struct DecimalLiteralRepresentation {
     threshold: u64,
 }
diff --git a/src/tools/clippy/clippy_lints/src/loops/explicit_iter_loop.rs b/src/tools/clippy/clippy_lints/src/loops/explicit_iter_loop.rs
index b134af500f5..022c6bcc70b 100644
--- a/src/tools/clippy/clippy_lints/src/loops/explicit_iter_loop.rs
+++ b/src/tools/clippy/clippy_lints/src/loops/explicit_iter_loop.rs
@@ -30,7 +30,7 @@ pub(super) fn check(
                 return;
             }
         } else if count
-            .try_eval_target_usize(cx.tcx, cx.param_env)
+            .try_to_target_usize(cx.tcx)
             .map_or(true, |x| x > 32)
             && !msrv.meets(msrvs::ARRAY_IMPL_ANY_LEN)
         {
diff --git a/src/tools/clippy/clippy_lints/src/loops/infinite_loop.rs b/src/tools/clippy/clippy_lints/src/loops/infinite_loop.rs
index 858e3be5093..e25c03db534 100644
--- a/src/tools/clippy/clippy_lints/src/loops/infinite_loop.rs
+++ b/src/tools/clippy/clippy_lints/src/loops/infinite_loop.rs
@@ -42,6 +42,7 @@ pub(super) fn check<'tcx>(
     let mut loop_visitor = LoopVisitor {
         cx,
         label,
+        inner_labels: label.into_iter().collect(),
         is_finite: false,
         loop_depth: 0,
     };
@@ -93,6 +94,7 @@ fn get_parent_fn_ret_ty<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'_>) -> Option
 struct LoopVisitor<'hir, 'tcx> {
     cx: &'hir LateContext<'tcx>,
     label: Option<Label>,
+    inner_labels: Vec<Label>,
     loop_depth: usize,
     is_finite: bool,
 }
@@ -108,11 +110,24 @@ impl<'hir> Visitor<'hir> for LoopVisitor<'hir, '_> {
                     self.is_finite = true;
                 }
             },
+            ExprKind::Continue(hir::Destination { label, .. }) => {
+                // Check whether we are leaving this loop by continuing into an outer loop
+                // whose label we did not encounter.
+                if label.is_some_and(|label| !self.inner_labels.contains(&label)) {
+                    self.is_finite = true;
+                }
+            },
             ExprKind::Ret(..) => self.is_finite = true,
-            ExprKind::Loop(..) => {
+            ExprKind::Loop(_, label, _, _) => {
+                if let Some(label) = label {
+                    self.inner_labels.push(*label);
+                }
                 self.loop_depth += 1;
                 walk_expr(self, ex);
-                self.loop_depth = self.loop_depth.saturating_sub(1);
+                self.loop_depth -= 1;
+                if label.is_some() {
+                    self.inner_labels.pop();
+                }
             },
             _ => {
                 // Calls to a function that never return
diff --git a/src/tools/clippy/clippy_lints/src/loops/manual_memcpy.rs b/src/tools/clippy/clippy_lints/src/loops/manual_memcpy.rs
index 68d063ad5e5..af089451759 100644
--- a/src/tools/clippy/clippy_lints/src/loops/manual_memcpy.rs
+++ b/src/tools/clippy/clippy_lints/src/loops/manual_memcpy.rs
@@ -472,7 +472,7 @@ fn is_array_length_equal_to_range(cx: &LateContext<'_>, start: &Expr<'_>, end: &
     let arr_ty = cx.typeck_results().expr_ty(arr).peel_refs();
 
     if let ty::Array(_, s) = arr_ty.kind() {
-        let size: u128 = if let Some(size) = s.try_eval_target_usize(cx.tcx, cx.param_env) {
+        let size: u128 = if let Some(size) = s.try_to_target_usize(cx.tcx) {
             size.into()
         } else {
             return false;
diff --git a/src/tools/clippy/clippy_lints/src/loops/manual_while_let_some.rs b/src/tools/clippy/clippy_lints/src/loops/manual_while_let_some.rs
index 7476a87267f..4473a3343c7 100644
--- a/src/tools/clippy/clippy_lints/src/loops/manual_while_let_some.rs
+++ b/src/tools/clippy/clippy_lints/src/loops/manual_while_let_some.rs
@@ -47,8 +47,9 @@ fn report_lint(cx: &LateContext<'_>, pop_span: Span, pop_stmt_kind: PopStmt<'_>,
     );
 }
 
-fn match_method_call(cx: &LateContext<'_>, expr: &Expr<'_>, method: Symbol) -> bool {
-    if let ExprKind::MethodCall(..) = expr.kind
+fn match_method_call<const ARGS_COUNT: usize>(cx: &LateContext<'_>, expr: &Expr<'_>, method: Symbol) -> bool {
+    if let ExprKind::MethodCall(_, _, args, _) = expr.kind
+        && args.len() == ARGS_COUNT
         && let Some(id) = cx.typeck_results().type_dependent_def_id(expr.hir_id)
     {
         cx.tcx.is_diagnostic_item(method, id)
@@ -58,9 +59,9 @@ fn match_method_call(cx: &LateContext<'_>, expr: &Expr<'_>, method: Symbol) -> b
 }
 
 fn is_vec_pop_unwrap(cx: &LateContext<'_>, expr: &Expr<'_>, is_empty_recv: &Expr<'_>) -> bool {
-    if (match_method_call(cx, expr, sym::option_unwrap) || match_method_call(cx, expr, sym::option_expect))
+    if (match_method_call::<0>(cx, expr, sym::option_unwrap) || match_method_call::<1>(cx, expr, sym::option_expect))
         && let ExprKind::MethodCall(_, unwrap_recv, ..) = expr.kind
-        && match_method_call(cx, unwrap_recv, sym::vec_pop)
+        && match_method_call::<0>(cx, unwrap_recv, sym::vec_pop)
         && let ExprKind::MethodCall(_, pop_recv, ..) = unwrap_recv.kind
     {
         // make sure they're the same `Vec`
@@ -96,7 +97,7 @@ fn check_call_arguments(cx: &LateContext<'_>, stmt: &Stmt<'_>, is_empty_recv: &E
 pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, full_cond: &'tcx Expr<'_>, body: &'tcx Expr<'_>, loop_span: Span) {
     if let ExprKind::Unary(UnOp::Not, cond) = full_cond.kind
         && let ExprKind::MethodCall(_, is_empty_recv, _, _) = cond.kind
-        && match_method_call(cx, cond, sym::vec_is_empty)
+        && match_method_call::<0>(cx, cond, sym::vec_is_empty)
         && let ExprKind::Block(body, _) = body.kind
         && let Some(stmt) = body.stmts.first()
     {
diff --git a/src/tools/clippy/clippy_lints/src/loops/needless_range_loop.rs b/src/tools/clippy/clippy_lints/src/loops/needless_range_loop.rs
index 214b8b0f379..2e6442156ef 100644
--- a/src/tools/clippy/clippy_lints/src/loops/needless_range_loop.rs
+++ b/src/tools/clippy/clippy_lints/src/loops/needless_range_loop.rs
@@ -207,7 +207,7 @@ fn is_end_eq_array_len<'tcx>(
     if let ExprKind::Lit(lit) = end.kind
         && let ast::LitKind::Int(end_int, _) = lit.node
         && let ty::Array(_, arr_len_const) = indexed_ty.kind()
-        && let Some(arr_len) = arr_len_const.try_eval_target_usize(cx.tcx, cx.param_env)
+        && let Some(arr_len) = arr_len_const.try_to_target_usize(cx.tcx)
     {
         return match limits {
             ast::RangeLimits::Closed => end_int.get() + 1 >= arr_len.into(),
diff --git a/src/tools/clippy/clippy_lints/src/loops/same_item_push.rs b/src/tools/clippy/clippy_lints/src/loops/same_item_push.rs
index f8659897ffe..d255fea3af2 100644
--- a/src/tools/clippy/clippy_lints/src/loops/same_item_push.rs
+++ b/src/tools/clippy/clippy_lints/src/loops/same_item_push.rs
@@ -172,10 +172,8 @@ fn get_vec_push<'tcx>(
     stmt: &'tcx Stmt<'_>,
 ) -> Option<(&'tcx Expr<'tcx>, &'tcx Expr<'tcx>, SyntaxContext)> {
     if let StmtKind::Semi(semi_stmt) = &stmt.kind
-            // Extract method being called
-            && let ExprKind::MethodCall(path, self_expr, args, _) = &semi_stmt.kind
-            // Figure out the parameters for the method call
-            && let Some(pushed_item) = args.first()
+            // Extract method being called and figure out the parameters for the method call
+            && let ExprKind::MethodCall(path, self_expr, [pushed_item], _) = &semi_stmt.kind
             // Check that the method being called is push() on a Vec
             && is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(self_expr), sym::Vec)
             && path.ident.name.as_str() == "push"
diff --git a/src/tools/clippy/clippy_lints/src/macro_use.rs b/src/tools/clippy/clippy_lints/src/macro_use.rs
index 50680331fbc..22aa681b681 100644
--- a/src/tools/clippy/clippy_lints/src/macro_use.rs
+++ b/src/tools/clippy/clippy_lints/src/macro_use.rs
@@ -43,7 +43,6 @@ impl MacroRefData {
 }
 
 #[derive(Default)]
-#[expect(clippy::module_name_repetitions)]
 pub struct MacroUseImports {
     /// the actual import path used and the span of the attribute above it. The value is
     /// the location, where the lint should be emitted.
diff --git a/src/tools/clippy/clippy_lints/src/main_recursion.rs b/src/tools/clippy/clippy_lints/src/main_recursion.rs
index 72807b4b284..01ea2f5debe 100644
--- a/src/tools/clippy/clippy_lints/src/main_recursion.rs
+++ b/src/tools/clippy/clippy_lints/src/main_recursion.rs
@@ -42,7 +42,7 @@ impl LateLintPass<'_> for MainRecursion {
             return;
         }
 
-        if let ExprKind::Call(func, _) = &expr.kind
+        if let ExprKind::Call(func, []) = &expr.kind
             && let ExprKind::Path(QPath::Resolved(_, path)) = &func.kind
             && let Some(def_id) = path.res.opt_def_id()
             && is_entrypoint_fn(cx, def_id)
diff --git a/src/tools/clippy/clippy_lints/src/manual_bits.rs b/src/tools/clippy/clippy_lints/src/manual_bits.rs
index c0e87e8a1fa..1bd8813e348 100644
--- a/src/tools/clippy/clippy_lints/src/manual_bits.rs
+++ b/src/tools/clippy/clippy_lints/src/manual_bits.rs
@@ -95,7 +95,7 @@ fn get_one_size_of_ty<'tcx>(
 }
 
 fn get_size_of_ty<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option<(&'tcx rustc_hir::Ty<'tcx>, Ty<'tcx>)> {
-    if let ExprKind::Call(count_func, _func_args) = expr.kind
+    if let ExprKind::Call(count_func, []) = expr.kind
         && let ExprKind::Path(ref count_func_qpath) = count_func.kind
         && let QPath::Resolved(_, count_func_path) = count_func_qpath
         && let Some(segment_zero) = count_func_path.segments.first()
diff --git a/src/tools/clippy/clippy_lints/src/manual_float_methods.rs b/src/tools/clippy/clippy_lints/src/manual_float_methods.rs
index 9d3ddab60bb..a269ea11397 100644
--- a/src/tools/clippy/clippy_lints/src/manual_float_methods.rs
+++ b/src/tools/clippy/clippy_lints/src/manual_float_methods.rs
@@ -1,3 +1,5 @@
+use clippy_config::msrvs::Msrv;
+use clippy_config::{Conf, msrvs};
 use clippy_utils::consts::{ConstEvalCtxt, Constant};
 use clippy_utils::diagnostics::span_lint_and_then;
 use clippy_utils::source::SpanRangeExt;
@@ -6,7 +8,7 @@ use rustc_errors::Applicability;
 use rustc_hir::{BinOpKind, Constness, Expr, ExprKind};
 use rustc_lint::{LateContext, LateLintPass, Lint, LintContext};
 use rustc_middle::lint::in_external_macro;
-use rustc_session::declare_lint_pass;
+use rustc_session::impl_lint_pass;
 
 declare_clippy_lint! {
     /// ### What it does
@@ -56,7 +58,7 @@ declare_clippy_lint! {
     style,
     "use dedicated method to check if a float is finite"
 }
-declare_lint_pass!(ManualFloatMethods => [MANUAL_IS_INFINITE, MANUAL_IS_FINITE]);
+impl_lint_pass!(ManualFloatMethods => [MANUAL_IS_INFINITE, MANUAL_IS_FINITE]);
 
 #[derive(Clone, Copy)]
 enum Variant {
@@ -80,6 +82,18 @@ impl Variant {
     }
 }
 
+pub struct ManualFloatMethods {
+    msrv: Msrv,
+}
+
+impl ManualFloatMethods {
+    pub fn new(conf: &'static Conf) -> Self {
+        Self {
+            msrv: conf.msrv.clone(),
+        }
+    }
+}
+
 impl<'tcx> LateLintPass<'tcx> for ManualFloatMethods {
     fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) {
         if let ExprKind::Binary(kind, lhs, rhs) = expr.kind
@@ -92,7 +106,7 @@ impl<'tcx> LateLintPass<'tcx> for ManualFloatMethods {
             && !in_external_macro(cx.sess(), expr.span)
             && (
                 matches!(cx.tcx.constness(cx.tcx.hir().enclosing_body_owner(expr.hir_id)), Constness::NotConst)
-                    || cx.tcx.features().declared(sym!(const_float_classify))
+                    || self.msrv.meets(msrvs::CONST_FLOAT_CLASSIFY)
             )
             && let [first, second, const_1, const_2] = exprs
             && let ecx = ConstEvalCtxt::new(cx)
@@ -150,6 +164,8 @@ impl<'tcx> LateLintPass<'tcx> for ManualFloatMethods {
             });
         }
     }
+
+    extract_msrv_attr!(LateContext);
 }
 
 fn is_infinity(constant: &Constant<'_>) -> bool {
diff --git a/src/tools/clippy/clippy_lints/src/manual_ignore_case_cmp.rs b/src/tools/clippy/clippy_lints/src/manual_ignore_case_cmp.rs
new file mode 100644
index 00000000000..dabfac3f613
--- /dev/null
+++ b/src/tools/clippy/clippy_lints/src/manual_ignore_case_cmp.rs
@@ -0,0 +1,127 @@
+use crate::manual_ignore_case_cmp::MatchType::{Literal, ToAscii};
+use clippy_utils::diagnostics::span_lint_and_then;
+use clippy_utils::source::snippet_with_applicability;
+use clippy_utils::ty::{get_type_diagnostic_name, is_type_diagnostic_item, is_type_lang_item};
+use rustc_ast::LitKind;
+use rustc_errors::Applicability;
+use rustc_hir::ExprKind::{Binary, Lit, MethodCall};
+use rustc_hir::{BinOpKind, Expr, LangItem};
+use rustc_lint::{LateContext, LateLintPass};
+use rustc_middle::ty;
+use rustc_middle::ty::{Ty, UintTy};
+use rustc_session::declare_lint_pass;
+use rustc_span::{Span, sym};
+
+declare_clippy_lint! {
+    /// ### What it does
+    /// Checks for manual case-insensitive ASCII comparison.
+    ///
+    /// ### Why is this bad?
+    /// The `eq_ignore_ascii_case` method is faster because it does not allocate
+    /// memory for the new strings, and it is more readable.
+    ///
+    /// ### Example
+    /// ```no_run
+    /// fn compare(a: &str, b: &str) -> bool {
+    ///     a.to_ascii_lowercase() == b.to_ascii_lowercase() || a.to_ascii_lowercase() == "abc"
+    /// }
+    /// ```
+    /// Use instead:
+    /// ```no_run
+    /// fn compare(a: &str, b: &str) -> bool {
+    ///    a.eq_ignore_ascii_case(b) || a.eq_ignore_ascii_case("abc")
+    /// }
+    /// ```
+    #[clippy::version = "1.82.0"]
+    pub MANUAL_IGNORE_CASE_CMP,
+    perf,
+    "manual case-insensitive ASCII comparison"
+}
+
+declare_lint_pass!(ManualIgnoreCaseCmp => [MANUAL_IGNORE_CASE_CMP]);
+
+enum MatchType<'a, 'b> {
+    ToAscii(bool, Ty<'a>),
+    Literal(&'b LitKind),
+}
+
+fn get_ascii_type<'a, 'b>(cx: &LateContext<'a>, kind: rustc_hir::ExprKind<'b>) -> Option<(Span, MatchType<'a, 'b>)> {
+    if let MethodCall(path, expr, _, _) = kind {
+        let is_lower = match path.ident.name.as_str() {
+            "to_ascii_lowercase" => true,
+            "to_ascii_uppercase" => false,
+            _ => return None,
+        };
+        let ty_raw = cx.typeck_results().expr_ty(expr);
+        let ty = ty_raw.peel_refs();
+        if needs_ref_to_cmp(cx, ty)
+            || ty.is_str()
+            || ty.is_slice()
+            || matches!(get_type_diagnostic_name(cx, ty), Some(sym::OsStr | sym::OsString))
+        {
+            return Some((expr.span, ToAscii(is_lower, ty_raw)));
+        }
+    } else if let Lit(expr) = kind {
+        return Some((expr.span, Literal(&expr.node)));
+    }
+    None
+}
+
+/// Returns true if the type needs to be dereferenced to be compared
+fn needs_ref_to_cmp(cx: &LateContext<'_>, ty: Ty<'_>) -> bool {
+    ty.is_char()
+        || *ty.kind() == ty::Uint(UintTy::U8)
+        || is_type_diagnostic_item(cx, ty, sym::Vec)
+        || is_type_lang_item(cx, ty, LangItem::String)
+}
+
+impl LateLintPass<'_> for ManualIgnoreCaseCmp {
+    fn check_expr(&mut self, cx: &LateContext<'_>, expr: &'_ Expr<'_>) {
+        // check if expression represents a comparison of two strings
+        // using .to_ascii_lowercase() or .to_ascii_uppercase() methods,
+        // or one of the sides is a literal
+        // Offer to replace it with .eq_ignore_ascii_case() method
+        if let Binary(op, left, right) = &expr.kind
+            && (op.node == BinOpKind::Eq || op.node == BinOpKind::Ne)
+            && let Some((left_span, left_val)) = get_ascii_type(cx, left.kind)
+            && let Some((right_span, right_val)) = get_ascii_type(cx, right.kind)
+            && match (&left_val, &right_val) {
+                (ToAscii(l_lower, ..), ToAscii(r_lower, ..)) if l_lower == r_lower => true,
+                (ToAscii(..), Literal(..)) | (Literal(..), ToAscii(..)) => true,
+                _ => false,
+            }
+        {
+            let deref = match right_val {
+                ToAscii(_, ty) if needs_ref_to_cmp(cx, ty) => "&",
+                ToAscii(..) => "",
+                Literal(ty) => {
+                    if let LitKind::Char(_) | LitKind::Byte(_) = ty {
+                        "&"
+                    } else {
+                        ""
+                    }
+                },
+            };
+            let neg = if op.node == BinOpKind::Ne { "!" } else { "" };
+            span_lint_and_then(
+                cx,
+                MANUAL_IGNORE_CASE_CMP,
+                expr.span,
+                "manual case-insensitive ASCII comparison",
+                |diag| {
+                    let mut app = Applicability::MachineApplicable;
+                    diag.span_suggestion_verbose(
+                        expr.span,
+                        "consider using `.eq_ignore_ascii_case()` instead",
+                        format!(
+                            "{neg}{}.eq_ignore_ascii_case({deref}{})",
+                            snippet_with_applicability(cx, left_span, "_", &mut app),
+                            snippet_with_applicability(cx, right_span, "_", &mut app)
+                        ),
+                        app,
+                    );
+                },
+            );
+        }
+    }
+}
diff --git a/src/tools/clippy/clippy_lints/src/manual_is_power_of_two.rs b/src/tools/clippy/clippy_lints/src/manual_is_power_of_two.rs
index da2a982ee17..a11d3e4624c 100644
--- a/src/tools/clippy/clippy_lints/src/manual_is_power_of_two.rs
+++ b/src/tools/clippy/clippy_lints/src/manual_is_power_of_two.rs
@@ -11,10 +11,12 @@ use rustc_session::declare_lint_pass;
 
 declare_clippy_lint! {
     /// ### What it does
-    /// Checks for expressions like `x.count_ones() == 1` or `x & (x - 1) == 0`, with x and unsigned integer, which are manual
+    /// Checks for expressions like `x.count_ones() == 1` or `x & (x - 1) == 0`, with x and unsigned integer, which may be manual
     /// reimplementations of `x.is_power_of_two()`.
+    ///
     /// ### Why is this bad?
     /// Manual reimplementations of `is_power_of_two` increase code complexity for little benefit.
+    ///
     /// ### Example
     /// ```no_run
     /// let a: u32 = 4;
@@ -27,7 +29,7 @@ declare_clippy_lint! {
     /// ```
     #[clippy::version = "1.82.0"]
     pub MANUAL_IS_POWER_OF_TWO,
-    complexity,
+    pedantic,
     "manually reimplementing `is_power_of_two`"
 }
 
@@ -41,7 +43,7 @@ impl LateLintPass<'_> for ManualIsPowerOfTwo {
             && bin_op.node == BinOpKind::Eq
         {
             // a.count_ones() == 1
-            if let ExprKind::MethodCall(method_name, reciever, _, _) = left.kind
+            if let ExprKind::MethodCall(method_name, reciever, [], _) = left.kind
                 && method_name.ident.as_str() == "count_ones"
                 && let &Uint(_) = cx.typeck_results().expr_ty(reciever).kind()
                 && check_lit(right, 1)
@@ -50,7 +52,7 @@ impl LateLintPass<'_> for ManualIsPowerOfTwo {
             }
 
             // 1 == a.count_ones()
-            if let ExprKind::MethodCall(method_name, reciever, _, _) = right.kind
+            if let ExprKind::MethodCall(method_name, reciever, [], _) = right.kind
                 && method_name.ident.as_str() == "count_ones"
                 && let &Uint(_) = cx.typeck_results().expr_ty(reciever).kind()
                 && check_lit(left, 1)
diff --git a/src/tools/clippy/clippy_lints/src/manual_slice_size_calculation.rs b/src/tools/clippy/clippy_lints/src/manual_slice_size_calculation.rs
index b24a0f4695a..18901f7399d 100644
--- a/src/tools/clippy/clippy_lints/src/manual_slice_size_calculation.rs
+++ b/src/tools/clippy/clippy_lints/src/manual_slice_size_calculation.rs
@@ -45,10 +45,11 @@ impl<'tcx> LateLintPass<'tcx> for ManualSliceSizeCalculation {
             && !expr.span.from_expansion()
             // Does not apply inside const because size_of_val is not cost in stable.
             && !is_in_const_context(cx)
-            && let Some(receiver) = simplify(cx, left, right)
+            && let Some((receiver, refs_count)) = simplify(cx, left, right)
         {
             let ctxt = expr.span.ctxt();
             let mut app = Applicability::MachineApplicable;
+            let deref = "*".repeat(refs_count - 1);
             let val_name = snippet_with_context(cx, receiver.span, ctxt, "slice", &mut app).0;
             let Some(sugg) = std_or_core(cx) else { return };
 
@@ -58,7 +59,7 @@ impl<'tcx> LateLintPass<'tcx> for ManualSliceSizeCalculation {
                 expr.span,
                 "manual slice size calculation",
                 "try",
-                format!("{sugg}::mem::size_of_val({val_name})"),
+                format!("{sugg}::mem::size_of_val({deref}{val_name})"),
                 app,
             );
         }
@@ -69,7 +70,7 @@ fn simplify<'tcx>(
     cx: &LateContext<'tcx>,
     expr1: &'tcx Expr<'tcx>,
     expr2: &'tcx Expr<'tcx>,
-) -> Option<&'tcx Expr<'tcx>> {
+) -> Option<(&'tcx Expr<'tcx>, usize)> {
     let expr1 = expr_or_init(cx, expr1);
     let expr2 = expr_or_init(cx, expr2);
 
@@ -80,15 +81,16 @@ fn simplify_half<'tcx>(
     cx: &LateContext<'tcx>,
     expr1: &'tcx Expr<'tcx>,
     expr2: &'tcx Expr<'tcx>,
-) -> Option<&'tcx Expr<'tcx>> {
+) -> Option<(&'tcx Expr<'tcx>, usize)> {
     if !expr1.span.from_expansion()
         // expr1 is `[T1].len()`?
-        && let ExprKind::MethodCall(method_path, receiver, _, _) = expr1.kind
+        && let ExprKind::MethodCall(method_path, receiver, [], _) = expr1.kind
         && method_path.ident.name == sym::len
         && let receiver_ty = cx.typeck_results().expr_ty(receiver)
-        && let ty::Slice(ty1) = receiver_ty.peel_refs().kind()
+        && let (receiver_ty, refs_count) = clippy_utils::ty::walk_ptrs_ty_depth(receiver_ty)
+        && let ty::Slice(ty1) = receiver_ty.kind()
         // expr2 is `size_of::<T2>()`?
-        && let ExprKind::Call(func, _) = expr2.kind
+        && let ExprKind::Call(func, []) = expr2.kind
         && let ExprKind::Path(ref func_qpath) = func.kind
         && let Some(def_id) = cx.qpath_res(func_qpath, func.hir_id).opt_def_id()
         && cx.tcx.is_diagnostic_item(sym::mem_size_of, def_id)
@@ -96,7 +98,7 @@ fn simplify_half<'tcx>(
         // T1 == T2?
         && *ty1 == ty2
     {
-        Some(receiver)
+        Some((receiver, refs_count))
     } else {
         None
     }
diff --git a/src/tools/clippy/clippy_lints/src/manual_string_new.rs b/src/tools/clippy/clippy_lints/src/manual_string_new.rs
index 198f7aaddc7..5c2a711b5cb 100644
--- a/src/tools/clippy/clippy_lints/src/manual_string_new.rs
+++ b/src/tools/clippy/clippy_lints/src/manual_string_new.rs
@@ -52,8 +52,8 @@ impl LateLintPass<'_> for ManualStringNew {
         }
 
         match expr.kind {
-            ExprKind::Call(func, args) => {
-                parse_call(cx, expr.span, func, args);
+            ExprKind::Call(func, [arg]) => {
+                parse_call(cx, expr.span, func, arg);
             },
             ExprKind::MethodCall(path_segment, receiver, ..) => {
                 parse_method_call(cx, expr.span, path_segment, receiver);
@@ -93,20 +93,15 @@ fn parse_method_call(cx: &LateContext<'_>, span: Span, path_segment: &PathSegmen
     let method_arg_kind = &receiver.kind;
     if ["to_string", "to_owned", "into"].contains(&ident) && is_expr_kind_empty_str(method_arg_kind) {
         warn_then_suggest(cx, span);
-    } else if let ExprKind::Call(func, args) = method_arg_kind {
+    } else if let ExprKind::Call(func, [arg]) = method_arg_kind {
         // If our first argument is a function call itself, it could be an `unwrap`-like function.
         // E.g. String::try_from("hello").unwrap(), TryFrom::try_from("").expect("hello"), etc.
-        parse_call(cx, span, func, args);
+        parse_call(cx, span, func, arg);
     }
 }
 
 /// Tries to parse an expression as a function call, emitting the warning if necessary.
-fn parse_call(cx: &LateContext<'_>, span: Span, func: &Expr<'_>, args: &[Expr<'_>]) {
-    if args.len() != 1 {
-        return;
-    }
-
-    let arg_kind = &args[0].kind;
+fn parse_call(cx: &LateContext<'_>, span: Span, func: &Expr<'_>, arg: &Expr<'_>) {
     if let ExprKind::Path(qpath) = &func.kind {
         // String::from(...) or String::try_from(...)
         if let QPath::TypeRelative(ty, path_seg) = qpath
@@ -115,13 +110,13 @@ fn parse_call(cx: &LateContext<'_>, span: Span, func: &Expr<'_>, args: &[Expr<'_
             && let QPath::Resolved(_, path) = qpath
             && let [path_seg] = path.segments
             && path_seg.ident.name == sym::String
-            && is_expr_kind_empty_str(arg_kind)
+            && is_expr_kind_empty_str(&arg.kind)
         {
             warn_then_suggest(cx, span);
         } else if let QPath::Resolved(_, path) = qpath {
             // From::from(...) or TryFrom::try_from(...)
             if let [path_seg1, path_seg2] = path.segments
-                && is_expr_kind_empty_str(arg_kind)
+                && is_expr_kind_empty_str(&arg.kind)
                 && ((path_seg1.ident.name == sym::From && path_seg2.ident.name == sym::from)
                     || (path_seg1.ident.name == sym::TryFrom && path_seg2.ident.name == sym::try_from))
             {
diff --git a/src/tools/clippy/clippy_lints/src/matches/redundant_pattern_match.rs b/src/tools/clippy/clippy_lints/src/matches/redundant_pattern_match.rs
index b646e87a439..9ca75fb2615 100644
--- a/src/tools/clippy/clippy_lints/src/matches/redundant_pattern_match.rs
+++ b/src/tools/clippy/clippy_lints/src/matches/redundant_pattern_match.rs
@@ -210,7 +210,7 @@ fn find_method_sugg_for_if_let<'tcx>(
 
     // check that `while_let_on_iterator` lint does not trigger
     if keyword == "while"
-        && let ExprKind::MethodCall(method_path, ..) = let_expr.kind
+        && let ExprKind::MethodCall(method_path, _, [], _) = let_expr.kind
         && method_path.ident.name == sym::next
         && is_trait_method(cx, let_expr, sym::Iterator)
     {
diff --git a/src/tools/clippy/clippy_lints/src/matches/single_match.rs b/src/tools/clippy/clippy_lints/src/matches/single_match.rs
index 2eda238ae8c..047d070a131 100644
--- a/src/tools/clippy/clippy_lints/src/matches/single_match.rs
+++ b/src/tools/clippy/clippy_lints/src/matches/single_match.rs
@@ -11,7 +11,7 @@ use rustc_hir::def::{DefKind, Res};
 use rustc_hir::intravisit::{Visitor, walk_pat};
 use rustc_hir::{Arm, Expr, ExprKind, HirId, Node, Pat, PatKind, QPath, StmtKind};
 use rustc_lint::LateContext;
-use rustc_middle::ty::{self, AdtDef, ParamEnv, TyCtxt, TypeckResults, VariantDef};
+use rustc_middle::ty::{self, AdtDef, TyCtxt, TypeckResults, VariantDef};
 use rustc_span::{Span, sym};
 
 use super::{MATCH_BOOL, SINGLE_MATCH, SINGLE_MATCH_ELSE};
@@ -67,7 +67,6 @@ pub(crate) fn check<'tcx>(cx: &LateContext<'tcx>, ex: &'tcx Expr<'_>, arms: &'tc
             if v.has_enum {
                 let cx = PatCtxt {
                     tcx: cx.tcx,
-                    param_env: cx.param_env,
                     typeck,
                     arena: DroplessArena::default(),
                 };
@@ -185,7 +184,6 @@ impl<'tcx> Visitor<'tcx> for PatVisitor<'tcx> {
 /// The context needed to manipulate a `PatState`.
 struct PatCtxt<'tcx> {
     tcx: TyCtxt<'tcx>,
-    param_env: ParamEnv<'tcx>,
     typeck: &'tcx TypeckResults<'tcx>,
     arena: DroplessArena,
 }
@@ -334,7 +332,7 @@ impl<'a> PatState<'a> {
                 if match *cx.typeck.pat_ty(pat).peel_refs().kind() {
                     ty::Adt(adt, _) => adt.is_enum() || (adt.is_struct() && !adt.non_enum_variant().fields.is_empty()),
                     ty::Tuple(tys) => !tys.is_empty(),
-                    ty::Array(_, len) => len.try_eval_target_usize(cx.tcx, cx.param_env) != Some(1),
+                    ty::Array(_, len) => len.try_to_target_usize(cx.tcx) != Some(1),
                     ty::Slice(..) => true,
                     _ => false,
                 } =>
@@ -353,7 +351,7 @@ impl<'a> PatState<'a> {
             },
             PatKind::Slice([sub_pat], _, []) | PatKind::Slice([], _, [sub_pat])
                 if let ty::Array(_, len) = *cx.typeck.pat_ty(pat).kind()
-                    && len.try_eval_target_usize(cx.tcx, cx.param_env) == Some(1) =>
+                    && len.try_to_target_usize(cx.tcx) == Some(1) =>
             {
                 self.add_pat(cx, sub_pat)
             },
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 b7ffa8b8a78..c7e1b70d19e 100644
--- a/src/tools/clippy/clippy_lints/src/matches/try_err.rs
+++ b/src/tools/clippy/clippy_lints/src/matches/try_err.rs
@@ -21,10 +21,10 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, scrutine
     //         #[allow(unreachable_code)]
     //         val,
     // };
-    if let ExprKind::Call(match_fun, [try_arg, ..]) = scrutinee.kind
+    if let ExprKind::Call(match_fun, [try_arg]) = scrutinee.kind
         && let ExprKind::Path(ref match_fun_path) = match_fun.kind
         && matches!(match_fun_path, QPath::LangItem(LangItem::TryTraitBranch, ..))
-        && let ExprKind::Call(err_fun, [err_arg, ..]) = try_arg.kind
+        && let ExprKind::Call(err_fun, [err_arg]) = try_arg.kind
         && is_res_lang_ctor(cx, path_res(cx, err_fun), ResultErr)
         && let Some(return_ty) = find_return_type(cx, &expr.kind)
     {
diff --git a/src/tools/clippy/clippy_lints/src/methods/clone_on_copy.rs b/src/tools/clippy/clippy_lints/src/methods/clone_on_copy.rs
index 79a473e0e6f..c9604c7b2e2 100644
--- a/src/tools/clippy/clippy_lints/src/methods/clone_on_copy.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/clone_on_copy.rs
@@ -58,7 +58,7 @@ pub(super) fn check(
                     return;
                 },
                 // ? is a Call, makes sure not to rec *x?, but rather (*x)?
-                ExprKind::Call(hir_callee, _) => matches!(
+                ExprKind::Call(hir_callee, [_]) => matches!(
                     hir_callee.kind,
                     ExprKind::Path(QPath::LangItem(rustc_hir::LangItem::TryTraitBranch, ..))
                 ),
diff --git a/src/tools/clippy/clippy_lints/src/methods/expect_fun_call.rs b/src/tools/clippy/clippy_lints/src/methods/expect_fun_call.rs
index 2922086522c..c288dbdabe9 100644
--- a/src/tools/clippy/clippy_lints/src/methods/expect_fun_call.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/expect_fun_call.rs
@@ -143,7 +143,7 @@ pub(super) fn check<'tcx>(
                 cx,
                 EXPECT_FUN_CALL,
                 span_replace_word,
-                format!("use of `{name}` followed by a function call"),
+                format!("function call inside of `{name}`"),
                 "try",
                 format!("unwrap_or_else({closure_args} panic!({sugg}))"),
                 applicability,
@@ -161,7 +161,7 @@ pub(super) fn check<'tcx>(
         cx,
         EXPECT_FUN_CALL,
         span_replace_word,
-        format!("use of `{name}` followed by a function call"),
+        format!("function call inside of `{name}`"),
         "try",
         format!("unwrap_or_else({closure_args} {{ panic!(\"{{}}\", {arg_root_snippet}) }})"),
         applicability,
diff --git a/src/tools/clippy/clippy_lints/src/methods/iter_filter.rs b/src/tools/clippy/clippy_lints/src/methods/iter_filter.rs
index f6612c984a7..30387ba62a7 100644
--- a/src/tools/clippy/clippy_lints/src/methods/iter_filter.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/iter_filter.rs
@@ -106,9 +106,9 @@ fn is_method(
 
 fn parent_is_map(cx: &LateContext<'_>, expr: &hir::Expr<'_>) -> bool {
     if let Some(expr) = get_parent_expr(cx, expr)
-        && is_trait_method(cx, expr, sym::Iterator)
-        && let ExprKind::MethodCall(path, _, _, _) = expr.kind
+        && let ExprKind::MethodCall(path, _, [_], _) = expr.kind
         && path.ident.name == sym::map
+        && is_trait_method(cx, expr, sym::Iterator)
     {
         return true;
     }
diff --git a/src/tools/clippy/clippy_lints/src/methods/iter_out_of_bounds.rs b/src/tools/clippy/clippy_lints/src/methods/iter_out_of_bounds.rs
index f198849c5c0..9d462bd1845 100644
--- a/src/tools/clippy/clippy_lints/src/methods/iter_out_of_bounds.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/iter_out_of_bounds.rs
@@ -31,14 +31,14 @@ fn get_iterator_length<'tcx>(cx: &LateContext<'tcx>, iter: &'tcx Expr<'tcx>) ->
         // parameter.
         substs
             .const_at(1)
-            .try_eval_target_usize(cx.tcx, cx.param_env)
+            .try_to_target_usize(cx.tcx)
             .map(u128::from)
     } else if cx.tcx.is_diagnostic_item(sym::SliceIter, did)
         && let ExprKind::MethodCall(_, recv, ..) = iter.kind
     {
         if let ty::Array(_, len) = cx.typeck_results().expr_ty(recv).peel_refs().kind() {
             // For slice::Iter<'_, T>, the receiver might be an array literal: [1,2,3].iter().skip(..)
-            len.try_eval_target_usize(cx.tcx, cx.param_env).map(u128::from)
+            len.try_to_target_usize(cx.tcx).map(u128::from)
         } else if let Some(args) = VecArgs::hir(cx, expr_or_init(cx, recv)) {
             match args {
                 VecArgs::Vec(vec) => vec.len().try_into().ok(),
diff --git a/src/tools/clippy/clippy_lints/src/methods/manual_c_str_literals.rs b/src/tools/clippy/clippy_lints/src/methods/manual_c_str_literals.rs
index 96af9db1af7..22f4748de70 100644
--- a/src/tools/clippy/clippy_lints/src/methods/manual_c_str_literals.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/manual_c_str_literals.rs
@@ -6,6 +6,7 @@ use rustc_ast::{LitKind, StrStyle};
 use rustc_errors::Applicability;
 use rustc_hir::{Expr, ExprKind, Node, QPath, TyKind};
 use rustc_lint::LateContext;
+use rustc_span::edition::Edition::Edition2021;
 use rustc_span::{Span, Symbol, sym};
 
 use super::MANUAL_C_STR_LITERALS;
@@ -25,6 +26,7 @@ pub(super) fn check_as_ptr<'tcx>(
 ) {
     if let ExprKind::Lit(lit) = receiver.kind
         && let LitKind::ByteStr(_, StrStyle::Cooked) | LitKind::Str(_, StrStyle::Cooked) = lit.node
+        && cx.tcx.sess.edition() >= Edition2021
         && let casts_removed = peel_ptr_cast_ancestors(cx, expr)
         && !get_parent_expr(cx, casts_removed).is_some_and(
             |parent| matches!(parent.kind, ExprKind::Call(func, _) if is_c_str_function(cx, func).is_some()),
@@ -66,6 +68,7 @@ fn is_c_str_function(cx: &LateContext<'_>, func: &Expr<'_>) -> Option<Symbol> {
 pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, func: &Expr<'_>, args: &[Expr<'_>], msrv: &Msrv) {
     if let Some(fn_name) = is_c_str_function(cx, func)
         && let [arg] = args
+        && cx.tcx.sess.edition() >= Edition2021
         && msrv.meets(msrvs::C_STR_LITERALS)
     {
         match fn_name.as_str() {
@@ -84,7 +87,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, func: &Expr<'_>, args
 
 /// Checks `CStr::from_ptr(b"foo\0".as_ptr().cast())`
 fn check_from_ptr(cx: &LateContext<'_>, expr: &Expr<'_>, arg: &Expr<'_>) {
-    if let ExprKind::MethodCall(method, lit, ..) = peel_ptr_cast(arg).kind
+    if let ExprKind::MethodCall(method, lit, [], _) = peel_ptr_cast(arg).kind
         && method.ident.name == sym::as_ptr
         && !lit.span.from_expansion()
         && let ExprKind::Lit(lit) = lit.kind
diff --git a/src/tools/clippy/clippy_lints/src/methods/manual_saturating_arithmetic.rs b/src/tools/clippy/clippy_lints/src/methods/manual_saturating_arithmetic.rs
index 9e3b313156e..13918ed11b8 100644
--- a/src/tools/clippy/clippy_lints/src/methods/manual_saturating_arithmetic.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/manual_saturating_arithmetic.rs
@@ -68,8 +68,7 @@ enum MinMax {
 
 fn is_min_or_max(cx: &LateContext<'_>, expr: &hir::Expr<'_>) -> Option<MinMax> {
     // `T::max_value()` `T::min_value()` inherent methods
-    if let hir::ExprKind::Call(func, args) = &expr.kind
-        && args.is_empty()
+    if let hir::ExprKind::Call(func, []) = &expr.kind
         && let hir::ExprKind::Path(hir::QPath::TypeRelative(_, segment)) = &func.kind
     {
         match segment.ident.as_str() {
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 ac378ff3702..515d4a11ed5 100644
--- a/src/tools/clippy/clippy_lints/src/methods/map_clone.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/map_clone.rs
@@ -86,9 +86,8 @@ pub(super) fn check(cx: &LateContext<'_>, e: &hir::Expr<'_>, recv: &hir::Expr<'_
                                     }
                                 }
                             },
-                            hir::ExprKind::Call(call, args) => {
+                            hir::ExprKind::Call(call, [arg]) => {
                                 if let hir::ExprKind::Path(qpath) = call.kind
-                                    && let [arg] = args
                                     && ident_eq(name, arg)
                                 {
                                     handle_path(cx, call, &qpath, e, recv);
diff --git a/src/tools/clippy/clippy_lints/src/methods/mod.rs b/src/tools/clippy/clippy_lints/src/methods/mod.rs
index 7696dd16b25..2a391870d70 100644
--- a/src/tools/clippy/clippy_lints/src/methods/mod.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/mod.rs
@@ -4046,7 +4046,7 @@ declare_clippy_lint! {
     /// Checks the usage of `.get().is_some()` or `.get().is_none()` on std map types.
     ///
     /// ### Why is this bad?
-    /// It can be done in one call with `.contains()`/`.contains_keys()`.
+    /// It can be done in one call with `.contains()`/`.contains_key()`.
     ///
     /// ### Example
     /// ```no_run
@@ -5182,6 +5182,7 @@ impl ShouldImplTraitCase {
 }
 
 #[rustfmt::skip]
+#[expect(clippy::large_const_arrays, reason = "`Span` is not sync, so this can't be static")]
 const TRAIT_METHODS: [ShouldImplTraitCase; 30] = [
     ShouldImplTraitCase::new("std::ops::Add", "add",  2,  FN_HEADER,  SelfKind::Value,  OutType::Any, true),
     ShouldImplTraitCase::new("std::convert::AsMut", "as_mut",  1,  FN_HEADER,  SelfKind::RefMut,  OutType::Ref, true),
diff --git a/src/tools/clippy/clippy_lints/src/methods/needless_collect.rs b/src/tools/clippy/clippy_lints/src/methods/needless_collect.rs
index c58e27e37ad..96a31812ca4 100644
--- a/src/tools/clippy/clippy_lints/src/methods/needless_collect.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/needless_collect.rs
@@ -321,7 +321,10 @@ impl<'tcx> Visitor<'tcx> for IterFunctionVisitor<'_, 'tcx> {
     fn visit_expr(&mut self, expr: &'tcx Expr<'tcx>) {
         // Check function calls on our collection
         if let ExprKind::MethodCall(method_name, recv, args, _) = &expr.kind {
-            if method_name.ident.name == sym!(collect) && is_trait_method(self.cx, expr, sym::Iterator) {
+            if args.is_empty()
+                && method_name.ident.name == sym!(collect)
+                && is_trait_method(self.cx, expr, sym::Iterator)
+            {
                 self.current_mutably_captured_ids = get_captured_ids(self.cx, self.cx.typeck_results().expr_ty(recv));
                 self.visit_expr(recv);
                 return;
diff --git a/src/tools/clippy/clippy_lints/src/methods/or_fun_call.rs b/src/tools/clippy/clippy_lints/src/methods/or_fun_call.rs
index b971f60d416..b685a466b72 100644
--- a/src/tools/clippy/clippy_lints/src/methods/or_fun_call.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/or_fun_call.rs
@@ -183,7 +183,7 @@ pub(super) fn check<'tcx>(
                 cx,
                 OR_FUN_CALL,
                 span_replace_word,
-                format!("use of `{name}` followed by a function call"),
+                format!("function call inside of `{name}`"),
                 "try",
                 format!("{name}_{suffix}({sugg})"),
                 app,
@@ -259,7 +259,7 @@ fn closure_body_returns_empty_to_string(cx: &LateContext<'_>, e: &hir::Expr<'_>)
 
         if body.params.is_empty()
             && let hir::Expr { kind, .. } = &body.value
-            && let hir::ExprKind::MethodCall(hir::PathSegment { ident, .. }, self_arg, _, _) = kind
+            && let hir::ExprKind::MethodCall(hir::PathSegment { ident, .. }, self_arg, [], _) = kind
             && ident.name == sym::to_string
             && let hir::Expr { kind, .. } = self_arg
             && let hir::ExprKind::Lit(lit) = kind
diff --git a/src/tools/clippy/clippy_lints/src/methods/read_line_without_trim.rs b/src/tools/clippy/clippy_lints/src/methods/read_line_without_trim.rs
index 0c8b6284284..65e545ed03a 100644
--- a/src/tools/clippy/clippy_lints/src/methods/read_line_without_trim.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/read_line_without_trim.rs
@@ -43,7 +43,8 @@ pub fn check(cx: &LateContext<'_>, call: &Expr<'_>, recv: &Expr<'_>, arg: &Expr<
         for_each_local_use_after_expr(cx, local_id, call.hir_id, |expr| {
             if let Some(parent) = get_parent_expr(cx, expr) {
                 let data = if let ExprKind::MethodCall(segment, recv, args, span) = parent.kind {
-                    if segment.ident.name == sym!(parse)
+                    if args.is_empty()
+                        && segment.ident.name == sym!(parse)
                         && let parse_result_ty = cx.typeck_results().expr_ty(parent)
                         && is_type_diagnostic_item(cx, parse_result_ty, sym::Result)
                         && let ty::Adt(_, substs) = parse_result_ty.kind()
diff --git a/src/tools/clippy/clippy_lints/src/methods/readonly_write_lock.rs b/src/tools/clippy/clippy_lints/src/methods/readonly_write_lock.rs
index 774aaec1afd..40b6becd453 100644
--- a/src/tools/clippy/clippy_lints/src/methods/readonly_write_lock.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/readonly_write_lock.rs
@@ -10,7 +10,7 @@ use rustc_middle::mir::{Location, START_BLOCK};
 use rustc_span::sym;
 
 fn is_unwrap_call(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
-    if let ExprKind::MethodCall(path, receiver, ..) = expr.kind
+    if let ExprKind::MethodCall(path, receiver, [], _) = expr.kind
         && path.ident.name == sym::unwrap
     {
         is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(receiver).peel_refs(), sym::Result)
diff --git a/src/tools/clippy/clippy_lints/src/methods/seek_from_current.rs b/src/tools/clippy/clippy_lints/src/methods/seek_from_current.rs
index 7ef07fe899c..d318462e584 100644
--- a/src/tools/clippy/clippy_lints/src/methods/seek_from_current.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/seek_from_current.rs
@@ -34,14 +34,13 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, recv: &'
 }
 
 fn arg_is_seek_from_current<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> bool {
-    if let ExprKind::Call(f, args) = expr.kind
+    if let ExprKind::Call(f, [arg]) = expr.kind
         && let ExprKind::Path(ref path) = f.kind
         && let Some(ctor_call_id) = cx.qpath_res(path, f.hir_id).opt_def_id()
         && is_enum_variant_ctor(cx, sym::SeekFrom, sym!(Current), ctor_call_id)
     {
         // check if argument of `SeekFrom::Current` is `0`
-        if args.len() == 1
-            && let ExprKind::Lit(lit) = args[0].kind
+        if let ExprKind::Lit(lit) = arg.kind
             && let LitKind::Int(Pu128(0), LitIntType::Unsuffixed) = lit.node
         {
             return true;
diff --git a/src/tools/clippy/clippy_lints/src/methods/seek_to_start_instead_of_rewind.rs b/src/tools/clippy/clippy_lints/src/methods/seek_to_start_instead_of_rewind.rs
index 9c966f010f1..7b1dd9e58c5 100644
--- a/src/tools/clippy/clippy_lints/src/methods/seek_to_start_instead_of_rewind.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/seek_to_start_instead_of_rewind.rs
@@ -26,12 +26,11 @@ pub(super) fn check<'tcx>(
 
     if let Some(seek_trait_id) = cx.tcx.get_diagnostic_item(sym::IoSeek)
         && implements_trait(cx, ty, seek_trait_id, &[])
-        && let ExprKind::Call(func, args1) = arg.kind
+        && let ExprKind::Call(func, [arg]) = arg.kind
         && let ExprKind::Path(ref path) = func.kind
         && let Some(ctor_call_id) = cx.qpath_res(path, func.hir_id).opt_def_id()
         && is_enum_variant_ctor(cx, sym::SeekFrom, sym!(Start), ctor_call_id)
-        && args1.len() == 1
-        && let ExprKind::Lit(lit) = args1[0].kind
+        && let ExprKind::Lit(lit) = arg.kind
         && let LitKind::Int(Pu128(0), LitIntType::Unsuffixed) = lit.node
     {
         let method_call_span = expr.span.with_lo(name_span.lo());
diff --git a/src/tools/clippy/clippy_lints/src/methods/single_char_insert_string.rs b/src/tools/clippy/clippy_lints/src/methods/single_char_insert_string.rs
index e2f76ac114c..4a1d25deade 100644
--- a/src/tools/clippy/clippy_lints/src/methods/single_char_insert_string.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/single_char_insert_string.rs
@@ -27,7 +27,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, receiver: &hir::
     }
 
     if let ExprKind::AddrOf(BorrowKind::Ref, _, arg) = &args[1].kind
-        && let ExprKind::MethodCall(path_segment, method_arg, _, _) = &arg.kind
+        && let ExprKind::MethodCall(path_segment, method_arg, [], _) = &arg.kind
         && path_segment.ident.name == rustc_span::sym::to_string
         && (is_ref_char(cx, method_arg) || is_char(cx, method_arg))
     {
diff --git a/src/tools/clippy/clippy_lints/src/methods/single_char_push_string.rs b/src/tools/clippy/clippy_lints/src/methods/single_char_push_string.rs
index 4ae8634305d..bc271d59392 100644
--- a/src/tools/clippy/clippy_lints/src/methods/single_char_push_string.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/single_char_push_string.rs
@@ -26,7 +26,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, receiver: &hir::
     }
 
     if let ExprKind::AddrOf(BorrowKind::Ref, _, arg) = &args[0].kind
-        && let ExprKind::MethodCall(path_segment, method_arg, _, _) = &arg.kind
+        && let ExprKind::MethodCall(path_segment, method_arg, [], _) = &arg.kind
         && path_segment.ident.name == rustc_span::sym::to_string
         && (is_ref_char(cx, method_arg) || is_char(cx, method_arg))
     {
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 69032776b2b..a2a7de905ca 100644
--- a/src/tools/clippy/clippy_lints/src/methods/str_splitn.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/str_splitn.rs
@@ -333,7 +333,7 @@ fn parse_iter_usage<'tcx>(
                     kind: ExprKind::Path(QPath::LangItem(LangItem::TryTraitBranch, ..)),
                     ..
                 },
-                _,
+                [_],
             ) => {
                 let parent_span = e.span.parent_callsite().unwrap();
                 if parent_span.ctxt() == ctxt {
diff --git a/src/tools/clippy/clippy_lints/src/methods/uninit_assumed_init.rs b/src/tools/clippy/clippy_lints/src/methods/uninit_assumed_init.rs
index 1ee655d61e1..6371fe64428 100644
--- a/src/tools/clippy/clippy_lints/src/methods/uninit_assumed_init.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/uninit_assumed_init.rs
@@ -9,8 +9,7 @@ use super::UNINIT_ASSUMED_INIT;
 
 /// lint for `MaybeUninit::uninit().assume_init()` (we already have the latter)
 pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, recv: &hir::Expr<'_>) {
-    if let hir::ExprKind::Call(callee, args) = recv.kind
-        && args.is_empty()
+    if let hir::ExprKind::Call(callee, []) = recv.kind
         && is_path_diagnostic_item(cx, callee, sym::maybe_uninit_uninit)
         && !is_uninit_value_valid_for_ty(cx, cx.typeck_results().expr_ty_adjusted(expr))
     {
diff --git a/src/tools/clippy/clippy_lints/src/methods/unnecessary_first_then_check.rs b/src/tools/clippy/clippy_lints/src/methods/unnecessary_first_then_check.rs
index 7ae1bb54e60..d322909bef3 100644
--- a/src/tools/clippy/clippy_lints/src/methods/unnecessary_first_then_check.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/unnecessary_first_then_check.rs
@@ -50,7 +50,7 @@ pub(super) fn check(
             ),
             "replace this with",
             suggestion,
-            Applicability::MaybeIncorrect,
+            Applicability::MachineApplicable,
         );
     }
 }
diff --git a/src/tools/clippy/clippy_lints/src/methods/useless_asref.rs b/src/tools/clippy/clippy_lints/src/methods/useless_asref.rs
index eafe7486bb0..c309e778116 100644
--- a/src/tools/clippy/clippy_lints/src/methods/useless_asref.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/useless_asref.rs
@@ -86,12 +86,11 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, call_name: &str,
             // changing the type, then we can move forward.
             && rcv_ty.peel_refs() == res_ty.peel_refs()
             && let Some(parent) = get_parent_expr(cx, expr)
-            && let hir::ExprKind::MethodCall(segment, _, args, _) = parent.kind
+            // Check that it only has one argument.
+            && let hir::ExprKind::MethodCall(segment, _, [arg], _) = parent.kind
             && segment.ident.span != expr.span
             // We check that the called method name is `map`.
             && segment.ident.name == sym::map
-            // And that it only has one argument.
-            && let [arg] = args
             && is_calling_clone(cx, arg)
             // And that we are not recommending recv.clone() over Arc::clone() or similar
             && !should_call_clone_as_function(cx, rcv_ty)
diff --git a/src/tools/clippy/clippy_lints/src/methods/utils.rs b/src/tools/clippy/clippy_lints/src/methods/utils.rs
index cf0ee569f13..6e39e7be2c4 100644
--- a/src/tools/clippy/clippy_lints/src/methods/utils.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/utils.rs
@@ -18,7 +18,7 @@ pub(super) fn derefs_to_slice<'tcx>(
             ty::Slice(_) => true,
             ty::Adt(..) if let Some(boxed) = ty.boxed_ty() => may_slice(cx, boxed),
             ty::Adt(..) => is_type_diagnostic_item(cx, ty, sym::Vec),
-            ty::Array(_, size) => size.try_eval_target_usize(cx.tcx, cx.param_env).is_some(),
+            ty::Array(_, size) => size.try_to_target_usize(cx.tcx).is_some(),
             ty::Ref(_, inner, _) => may_slice(cx, *inner),
             _ => false,
         }
diff --git a/src/tools/clippy/clippy_lints/src/missing_asserts_for_indexing.rs b/src/tools/clippy/clippy_lints/src/missing_asserts_for_indexing.rs
index c56a4014b34..d78299fe08b 100644
--- a/src/tools/clippy/clippy_lints/src/missing_asserts_for_indexing.rs
+++ b/src/tools/clippy/clippy_lints/src/missing_asserts_for_indexing.rs
@@ -139,7 +139,7 @@ fn assert_len_expr<'hir>(
         && let ExprKind::Binary(bin_op, left, right) = &condition.kind
 
         && let Some((cmp, asserted_len, slice_len)) = len_comparison(*bin_op, left, right)
-        && let ExprKind::MethodCall(method, recv, ..) = &slice_len.kind
+        && let ExprKind::MethodCall(method, recv, [], _) = &slice_len.kind
         && cx.typeck_results().expr_ty_adjusted(recv).peel_refs().is_slice()
         && method.ident.name == sym::len
 
diff --git a/src/tools/clippy/clippy_lints/src/missing_doc.rs b/src/tools/clippy/clippy_lints/src/missing_doc.rs
index 007bcebdff6..ce508d85d63 100644
--- a/src/tools/clippy/clippy_lints/src/missing_doc.rs
+++ b/src/tools/clippy/clippy_lints/src/missing_doc.rs
@@ -193,7 +193,7 @@ impl<'tcx> LateLintPass<'tcx> for MissingDoc {
             | hir::ItemKind::Trait(..)
             | hir::ItemKind::TraitAlias(..)
             | hir::ItemKind::TyAlias(..)
-            | hir::ItemKind::Union(..) => {}
+            | hir::ItemKind::Union(..) => {},
             hir::ItemKind::ExternCrate(..)
             | hir::ItemKind::ForeignMod { .. }
             | hir::ItemKind::GlobalAsm(..)
diff --git a/src/tools/clippy/clippy_lints/src/mut_key.rs b/src/tools/clippy/clippy_lints/src/mut_key.rs
index 8118c14bd4a..745f81d1c51 100644
--- a/src/tools/clippy/clippy_lints/src/mut_key.rs
+++ b/src/tools/clippy/clippy_lints/src/mut_key.rs
@@ -1,9 +1,10 @@
 use clippy_config::Conf;
-use clippy_utils::diagnostics::span_lint;
+use clippy_utils::diagnostics::span_lint_and_then;
 use clippy_utils::trait_ref_of_method;
 use clippy_utils::ty::InteriorMut;
 use rustc_hir as hir;
 use rustc_lint::{LateContext, LateLintPass};
+use rustc_middle::ty::print::with_forced_trimmed_paths;
 use rustc_middle::ty::{self, Ty, TyCtxt};
 use rustc_session::impl_lint_pass;
 use rustc_span::Span;
@@ -132,8 +133,14 @@ impl<'tcx> MutableKeyType<'tcx> {
             )
         {
             let subst_ty = args.type_at(0);
-            if self.interior_mut.is_interior_mut_ty(cx, subst_ty) {
-                span_lint(cx, MUTABLE_KEY_TYPE, span, "mutable key type");
+            if let Some(chain) = self.interior_mut.interior_mut_ty_chain(cx, subst_ty) {
+                span_lint_and_then(cx, MUTABLE_KEY_TYPE, span, "mutable key type", |diag| {
+                    for ty in chain.iter().rev() {
+                        diag.note(with_forced_trimmed_paths!(format!(
+                            "... because it contains `{ty}`, which has interior mutability"
+                        )));
+                    }
+                });
             }
         }
     }
diff --git a/src/tools/clippy/clippy_lints/src/mut_reference.rs b/src/tools/clippy/clippy_lints/src/mut_reference.rs
index 3c0f06f66d1..c382fb8fce1 100644
--- a/src/tools/clippy/clippy_lints/src/mut_reference.rs
+++ b/src/tools/clippy/clippy_lints/src/mut_reference.rs
@@ -47,7 +47,7 @@ impl<'tcx> LateLintPass<'tcx> for UnnecessaryMutPassed {
                 if let ExprKind::Path(ref path) = fn_expr.kind {
                     check_arguments(
                         cx,
-                        arguments.iter().collect(),
+                        &mut arguments.iter(),
                         cx.typeck_results().expr_ty(fn_expr),
                         &rustc_hir_pretty::qpath_to_string(&cx.tcx, path),
                         "function",
@@ -60,7 +60,7 @@ impl<'tcx> LateLintPass<'tcx> for UnnecessaryMutPassed {
                 let method_type = cx.tcx.type_of(def_id).instantiate(cx.tcx, args);
                 check_arguments(
                     cx,
-                    iter::once(receiver).chain(arguments.iter()).collect(),
+                    &mut iter::once(receiver).chain(arguments.iter()),
                     method_type,
                     path.ident.as_str(),
                     "method",
@@ -73,7 +73,7 @@ impl<'tcx> LateLintPass<'tcx> for UnnecessaryMutPassed {
 
 fn check_arguments<'tcx>(
     cx: &LateContext<'tcx>,
-    arguments: Vec<&Expr<'_>>,
+    arguments: &mut dyn Iterator<Item = &'tcx Expr<'tcx>>,
     type_definition: Ty<'tcx>,
     name: &str,
     fn_kind: &str,
diff --git a/src/tools/clippy/clippy_lints/src/needless_maybe_sized.rs b/src/tools/clippy/clippy_lints/src/needless_maybe_sized.rs
index a56024f08d5..68c9af07465 100644
--- a/src/tools/clippy/clippy_lints/src/needless_maybe_sized.rs
+++ b/src/tools/clippy/clippy_lints/src/needless_maybe_sized.rs
@@ -64,7 +64,7 @@ fn type_param_bounds<'tcx>(generics: &'tcx Generics<'tcx>) -> impl Iterator<Item
                     .iter()
                     .enumerate()
                     .filter_map(move |(bound_pos, bound)| match bound {
-                        &GenericBound::Trait(ref trait_bound) => Some(Bound {
+                        GenericBound::Trait(trait_bound) => Some(Bound {
                             param,
                             ident,
                             trait_bound,
diff --git a/src/tools/clippy/clippy_lints/src/non_canonical_impls.rs b/src/tools/clippy/clippy_lints/src/non_canonical_impls.rs
index de6a1a36f3e..94855c46567 100644
--- a/src/tools/clippy/clippy_lints/src/non_canonical_impls.rs
+++ b/src/tools/clippy/clippy_lints/src/non_canonical_impls.rs
@@ -281,7 +281,7 @@ fn self_cmp_call<'tcx>(
     needs_fully_qualified: &mut bool,
 ) -> bool {
     match cmp_expr.kind {
-        ExprKind::Call(path, [_self, _other]) => path_res(cx, path)
+        ExprKind::Call(path, [_, _]) => path_res(cx, path)
             .opt_def_id()
             .is_some_and(|def_id| cx.tcx.is_diagnostic_item(sym::ord_cmp_method, def_id)),
         ExprKind::MethodCall(_, _, [_other], ..) => {
diff --git a/src/tools/clippy/clippy_lints/src/non_zero_suggestions.rs b/src/tools/clippy/clippy_lints/src/non_zero_suggestions.rs
index 90a9f2e994b..aefb665b52e 100644
--- a/src/tools/clippy/clippy_lints/src/non_zero_suggestions.rs
+++ b/src/tools/clippy/clippy_lints/src/non_zero_suggestions.rs
@@ -71,7 +71,7 @@ fn check_non_zero_conversion(cx: &LateContext<'_>, expr: &Expr<'_>, applicabilit
     if let ExprKind::Call(func, [arg]) = expr.kind
         && let ExprKind::Path(qpath) = &func.kind
         && let Some(def_id) = cx.qpath_res(qpath, func.hir_id).opt_def_id()
-        && let ExprKind::MethodCall(rcv_path, receiver, _, _) = &arg.kind
+        && let ExprKind::MethodCall(rcv_path, receiver, [], _) = &arg.kind
         && rcv_path.ident.name.as_str() == "get"
     {
         let fn_name = cx.tcx.item_name(def_id);
diff --git a/src/tools/clippy/clippy_lints/src/operators/float_cmp.rs b/src/tools/clippy/clippy_lints/src/operators/float_cmp.rs
index df6e6745596..8e394944c21 100644
--- a/src/tools/clippy/clippy_lints/src/operators/float_cmp.rs
+++ b/src/tools/clippy/clippy_lints/src/operators/float_cmp.rs
@@ -106,7 +106,7 @@ fn is_signum(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
         return is_signum(cx, child_expr);
     }
 
-    if let ExprKind::MethodCall(method_name, self_arg, ..) = expr.kind
+    if let ExprKind::MethodCall(method_name, self_arg, [], _) = expr.kind
         && sym!(signum) == method_name.ident.name
     // Check that the receiver of the signum() is a float (expressions[0] is the receiver of
     // the method call)
diff --git a/src/tools/clippy/clippy_lints/src/pathbuf_init_then_push.rs b/src/tools/clippy/clippy_lints/src/pathbuf_init_then_push.rs
index 9f84686a0b1..d2529d4d9f8 100644
--- a/src/tools/clippy/clippy_lints/src/pathbuf_init_then_push.rs
+++ b/src/tools/clippy/clippy_lints/src/pathbuf_init_then_push.rs
@@ -37,7 +37,7 @@ declare_clippy_lint! {
     /// // or
     /// let path_buf = PathBuf::new().join("foo");
     /// ```
-    #[clippy::version = "1.81.0"]
+    #[clippy::version = "1.82.0"]
     pub PATHBUF_INIT_THEN_PUSH,
     restriction,
     "`push` immediately after `PathBuf` creation"
diff --git a/src/tools/clippy/clippy_lints/src/ptr.rs b/src/tools/clippy/clippy_lints/src/ptr.rs
index bb8a9b6fca8..f5fcf521b96 100644
--- a/src/tools/clippy/clippy_lints/src/ptr.rs
+++ b/src/tools/clippy/clippy_lints/src/ptr.rs
@@ -1,7 +1,7 @@
 use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg, span_lint_and_then, span_lint_hir_and_then};
 use clippy_utils::source::SpanRangeExt;
 use clippy_utils::visitors::contains_unsafe_block;
-use clippy_utils::{get_expr_use_or_unification_node, is_lint_allowed, path_def_id, path_to_local};
+use clippy_utils::{get_expr_use_or_unification_node, is_lint_allowed, path_def_id, path_to_local, std_or_core};
 use hir::LifetimeName;
 use rustc_errors::{Applicability, MultiSpan};
 use rustc_hir::hir_id::{HirId, HirIdMap};
@@ -294,14 +294,16 @@ fn check_invalid_ptr_usage<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
         };
 
         for &arg_idx in arg_indices {
-            if let Some(arg) = args.get(arg_idx).filter(|arg| is_null_path(cx, arg)) {
+            if let Some(arg) = args.get(arg_idx).filter(|arg| is_null_path(cx, arg))
+                && let Some(std_or_core) = std_or_core(cx)
+            {
                 span_lint_and_sugg(
                     cx,
                     INVALID_NULL_PTR_USAGE,
                     arg.span,
                     "pointer must be non-null",
                     "change this to",
-                    "core::ptr::NonNull::dangling().as_ptr()".to_string(),
+                    format!("{std_or_core}::ptr::NonNull::dangling().as_ptr()"),
                     Applicability::MachineApplicable,
                 );
             }
diff --git a/src/tools/clippy/clippy_lints/src/ptr_offset_with_cast.rs b/src/tools/clippy/clippy_lints/src/ptr_offset_with_cast.rs
index 87a52cb2186..56d07aeae17 100644
--- a/src/tools/clippy/clippy_lints/src/ptr_offset_with_cast.rs
+++ b/src/tools/clippy/clippy_lints/src/ptr_offset_with_cast.rs
@@ -91,7 +91,7 @@ fn expr_as_ptr_offset_call<'tcx>(
     cx: &LateContext<'tcx>,
     expr: &'tcx Expr<'_>,
 ) -> Option<(&'tcx Expr<'tcx>, &'tcx Expr<'tcx>, Method)> {
-    if let ExprKind::MethodCall(path_segment, arg_0, [arg_1, ..], _) = &expr.kind {
+    if let ExprKind::MethodCall(path_segment, arg_0, [arg_1], _) = &expr.kind {
         if is_expr_ty_raw_ptr(cx, arg_0) {
             if path_segment.ident.name == sym::offset {
                 return Some((arg_0, arg_1, Method::Offset));
diff --git a/src/tools/clippy/clippy_lints/src/question_mark.rs b/src/tools/clippy/clippy_lints/src/question_mark.rs
index aa9a9001afb..9344cb41993 100644
--- a/src/tools/clippy/clippy_lints/src/question_mark.rs
+++ b/src/tools/clippy/clippy_lints/src/question_mark.rs
@@ -206,12 +206,11 @@ fn expr_return_none_or_err(
             sym::Result => path_to_local(expr).is_some() && path_to_local(expr) == path_to_local(cond_expr),
             _ => false,
         },
-        ExprKind::Call(call_expr, args_expr) => {
+        ExprKind::Call(call_expr, [arg]) => {
             if smbl == sym::Result
                 && let ExprKind::Path(QPath::Resolved(_, path)) = &call_expr.kind
                 && let Some(segment) = path.segments.first()
                 && let Some(err_sym) = err_sym
-                && let Some(arg) = args_expr.first()
                 && let ExprKind::Path(QPath::Resolved(_, arg_path)) = &arg.kind
                 && let Some(PathSegment { ident, .. }) = arg_path.segments.first()
             {
@@ -241,7 +240,7 @@ fn expr_return_none_or_err(
 fn check_is_none_or_err_and_early_return<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'tcx>) {
     if let Some(higher::If { cond, then, r#else }) = higher::If::hir(expr)
         && !is_else_clause(cx.tcx, expr)
-        && let ExprKind::MethodCall(segment, caller, ..) = &cond.kind
+        && let ExprKind::MethodCall(segment, caller, [], _) = &cond.kind
         && let caller_ty = cx.typeck_results().expr_ty(caller)
         && let if_block = IfBlockType::IfIs(caller, caller_ty, segment.ident.name, then)
         && (is_early_return(sym::Option, cx, &if_block) || is_early_return(sym::Result, cx, &if_block))
@@ -332,7 +331,7 @@ impl QuestionMark {
 
 fn is_try_block(cx: &LateContext<'_>, bl: &Block<'_>) -> bool {
     if let Some(expr) = bl.expr
-        && let ExprKind::Call(callee, _) = expr.kind
+        && let ExprKind::Call(callee, [_]) = expr.kind
     {
         is_path_lang_item(cx, callee, LangItem::TryTraitFromOutput)
     } else {
diff --git a/src/tools/clippy/clippy_lints/src/raw_strings.rs b/src/tools/clippy/clippy_lints/src/raw_strings.rs
index 3c19ee3522d..23d0e768c2f 100644
--- a/src/tools/clippy/clippy_lints/src/raw_strings.rs
+++ b/src/tools/clippy/clippy_lints/src/raw_strings.rs
@@ -1,6 +1,6 @@
 use clippy_config::Conf;
 use clippy_utils::diagnostics::span_lint_and_then;
-use clippy_utils::source::SpanRangeExt;
+use clippy_utils::source::{SpanRangeExt, snippet_opt};
 use rustc_ast::ast::{Expr, ExprKind};
 use rustc_ast::token::LitKind;
 use rustc_errors::Applicability;
@@ -71,6 +71,23 @@ impl RawStrings {
 
 impl EarlyLintPass for RawStrings {
     fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &Expr) {
+        if let ExprKind::FormatArgs(format_args) = &expr.kind
+            && !in_external_macro(cx.sess(), format_args.span)
+            && format_args.span.check_source_text(cx, |src| src.starts_with('r'))
+            && let Some(str) = snippet_opt(cx.sess(), format_args.span)
+            && let count_hash = str.bytes().skip(1).take_while(|b| *b == b'#').count()
+            && let Some(str) = str.get(count_hash + 2..str.len() - count_hash - 1)
+        {
+            self.check_raw_string(
+                cx,
+                str,
+                format_args.span,
+                "r",
+                u8::try_from(count_hash).unwrap(),
+                "string",
+            );
+        }
+
         if let ExprKind::Lit(lit) = expr.kind
             && let (prefix, max) = match lit.kind {
                 LitKind::StrRaw(max) => ("r", max),
@@ -81,94 +98,105 @@ impl EarlyLintPass for RawStrings {
             && !in_external_macro(cx.sess(), expr.span)
             && expr.span.check_source_text(cx, |src| src.starts_with(prefix))
         {
-            let str = lit.symbol.as_str();
-            let descr = lit.kind.descr();
-
-            if !str.contains(['\\', '"']) {
-                span_lint_and_then(
-                    cx,
-                    NEEDLESS_RAW_STRINGS,
-                    expr.span,
-                    "unnecessary raw string literal",
-                    |diag| {
-                        let (start, end) = hash_spans(expr.span, prefix.len(), 0, max);
-
-                        // BytePos: skip over the `b` in `br`, we checked the prefix appears in the source text
-                        let r_pos = expr.span.lo() + BytePos::from_usize(prefix.len() - 1);
-                        let start = start.with_lo(r_pos);
-
-                        let mut remove = vec![(start, String::new())];
-                        // avoid debug ICE from empty suggestions
-                        if !end.is_empty() {
-                            remove.push((end, String::new()));
-                        }
+            self.check_raw_string(cx, lit.symbol.as_str(), expr.span, prefix, max, lit.kind.descr());
+        }
+    }
+}
 
-                        diag.multipart_suggestion_verbose(
-                            format!("use a plain {descr} literal instead"),
-                            remove,
-                            Applicability::MachineApplicable,
-                        );
-                    },
-                );
-                if !matches!(cx.get_lint_level(NEEDLESS_RAW_STRINGS), rustc_lint::Allow) {
-                    return;
-                }
+impl RawStrings {
+    fn check_raw_string(
+        &mut self,
+        cx: &EarlyContext<'_>,
+        str: &str,
+        lit_span: Span,
+        prefix: &str,
+        max: u8,
+        descr: &str,
+    ) {
+        if !str.contains(['\\', '"']) {
+            span_lint_and_then(
+                cx,
+                NEEDLESS_RAW_STRINGS,
+                lit_span,
+                "unnecessary raw string literal",
+                |diag| {
+                    let (start, end) = hash_spans(lit_span, prefix.len(), 0, max);
+
+                    // BytePos: skip over the `b` in `br`, we checked the prefix appears in the source text
+                    let r_pos = lit_span.lo() + BytePos::from_usize(prefix.len() - 1);
+                    let start = start.with_lo(r_pos);
+
+                    let mut remove = vec![(start, String::new())];
+                    // avoid debug ICE from empty suggestions
+                    if !end.is_empty() {
+                        remove.push((end, String::new()));
+                    }
+
+                    diag.multipart_suggestion_verbose(
+                        format!("use a plain {descr} literal instead"),
+                        remove,
+                        Applicability::MachineApplicable,
+                    );
+                },
+            );
+            if !matches!(cx.get_lint_level(NEEDLESS_RAW_STRINGS), rustc_lint::Allow) {
+                return;
             }
+        }
 
-            let mut req = {
-                let mut following_quote = false;
-                let mut req = 0;
-                // `once` so a raw string ending in hashes is still checked
-                let num = str.as_bytes().iter().chain(once(&0)).try_fold(0u8, |acc, &b| {
-                    match b {
-                        b'"' if !following_quote => (following_quote, req) = (true, 1),
-                        b'#' => req += u8::from(following_quote),
-                        _ => {
-                            if following_quote {
-                                following_quote = false;
-
-                                if req == max {
-                                    return ControlFlow::Break(req);
-                                }
-
-                                return ControlFlow::Continue(acc.max(req));
+        let mut req = {
+            let mut following_quote = false;
+            let mut req = 0;
+            // `once` so a raw string ending in hashes is still checked
+            let num = str.as_bytes().iter().chain(once(&0)).try_fold(0u8, |acc, &b| {
+                match b {
+                    b'"' if !following_quote => (following_quote, req) = (true, 1),
+                    b'#' => req += u8::from(following_quote),
+                    _ => {
+                        if following_quote {
+                            following_quote = false;
+
+                            if req == max {
+                                return ControlFlow::Break(req);
                             }
-                        },
-                    }
 
-                    ControlFlow::Continue(acc)
-                });
-
-                match num {
-                    ControlFlow::Continue(num) | ControlFlow::Break(num) => num,
-                }
-            };
-            if self.allow_one_hash_in_raw_strings {
-                req = req.max(1);
-            }
-            if req < max {
-                span_lint_and_then(
-                    cx,
-                    NEEDLESS_RAW_STRING_HASHES,
-                    expr.span,
-                    "unnecessary hashes around raw string literal",
-                    |diag| {
-                        let (start, end) = hash_spans(expr.span, prefix.len(), req, max);
-
-                        let message = match max - req {
-                            _ if req == 0 => format!("remove all the hashes around the {descr} literal"),
-                            1 => format!("remove one hash from both sides of the {descr} literal"),
-                            n => format!("remove {n} hashes from both sides of the {descr} literal"),
-                        };
-
-                        diag.multipart_suggestion(
-                            message,
-                            vec![(start, String::new()), (end, String::new())],
-                            Applicability::MachineApplicable,
-                        );
+                            return ControlFlow::Continue(acc.max(req));
+                        }
                     },
-                );
+                }
+
+                ControlFlow::Continue(acc)
+            });
+
+            match num {
+                ControlFlow::Continue(num) | ControlFlow::Break(num) => num,
             }
+        };
+        if self.allow_one_hash_in_raw_strings {
+            req = req.max(1);
+        }
+        if req < max {
+            span_lint_and_then(
+                cx,
+                NEEDLESS_RAW_STRING_HASHES,
+                lit_span,
+                "unnecessary hashes around raw string literal",
+                |diag| {
+                    let (start, end) = hash_spans(lit_span, prefix.len(), req, max);
+
+                    let message = match max - req {
+                        _ if req == 0 => format!("remove all the hashes around the {descr} literal"),
+                        1 => format!("remove one hash from both sides of the {descr} literal"),
+                        n => format!("remove {n} hashes from both sides of the {descr} literal"),
+                    };
+
+                    diag.multipart_suggestion(
+                        message,
+                        vec![(start, String::new()), (end, String::new())],
+                        Applicability::MachineApplicable,
+                    );
+                },
+            );
         }
     }
 }
diff --git a/src/tools/clippy/clippy_lints/src/rc_clone_in_vec_init.rs b/src/tools/clippy/clippy_lints/src/rc_clone_in_vec_init.rs
index e877f5d6ed4..6bb7650a7e1 100644
--- a/src/tools/clippy/clippy_lints/src/rc_clone_in_vec_init.rs
+++ b/src/tools/clippy/clippy_lints/src/rc_clone_in_vec_init.rs
@@ -65,11 +65,11 @@ impl LateLintPass<'_> for RcCloneInVecInit {
 
 fn loop_init_suggestion(elem: &str, len: &str, indent: &str) -> String {
     format!(
-        r#"{{
+        r"{{
 {indent}    let mut v = Vec::with_capacity({len});
 {indent}    (0..{len}).for_each(|_| v.push({elem}));
 {indent}    v
-{indent}}}"#
+{indent}}}"
     )
 }
 
diff --git a/src/tools/clippy/clippy_lints/src/regex.rs b/src/tools/clippy/clippy_lints/src/regex.rs
index 12cbdb854ef..6a5bf1b8045 100644
--- a/src/tools/clippy/clippy_lints/src/regex.rs
+++ b/src/tools/clippy/clippy_lints/src/regex.rs
@@ -6,7 +6,7 @@ use clippy_utils::source::SpanRangeExt;
 use clippy_utils::{def_path_res_with_base, find_crates, path_def_id, paths};
 use rustc_ast::ast::{LitKind, StrStyle};
 use rustc_hir::def_id::DefIdMap;
-use rustc_hir::{BorrowKind, Expr, ExprKind};
+use rustc_hir::{BorrowKind, Expr, ExprKind, OwnerId};
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_session::impl_lint_pass;
 use rustc_span::{BytePos, Span};
@@ -55,6 +55,44 @@ declare_clippy_lint! {
     "trivial regular expressions"
 }
 
+declare_clippy_lint! {
+    /// ### What it does
+    ///
+    /// Checks for [regex](https://crates.io/crates/regex) compilation inside a loop with a literal.
+    ///
+    /// ### Why is this bad?
+    ///
+    /// Compiling a regex is a much more expensive operation than using one, and a compiled regex can be used multiple times.
+    /// This is documented as an antipattern [on the regex documentation](https://docs.rs/regex/latest/regex/#avoid-re-compiling-regexes-especially-in-a-loop)
+    ///
+    /// ### Example
+    /// ```no_run
+    /// # let haystacks = [""];
+    /// # const MY_REGEX: &str = "a.b";
+    /// for haystack in haystacks {
+    ///     let regex = regex::Regex::new(MY_REGEX).unwrap();
+    ///     if regex.is_match(haystack) {
+    ///         // Perform operation
+    ///     }
+    /// }
+    /// ```
+    /// can be replaced with
+    /// ```no_run
+    /// # let haystacks = [""];
+    /// # const MY_REGEX: &str = "a.b";
+    /// let regex = regex::Regex::new(MY_REGEX).unwrap();
+    /// for haystack in haystacks {
+    ///     if regex.is_match(haystack) {
+    ///         // Perform operation
+    ///     }
+    /// }
+    /// ```
+    #[clippy::version = "1.83.0"]
+    pub REGEX_CREATION_IN_LOOPS,
+    perf,
+    "regular expression compilation performed in a loop"
+}
+
 #[derive(Copy, Clone)]
 enum RegexKind {
     Unicode,
@@ -66,9 +104,10 @@ enum RegexKind {
 #[derive(Default)]
 pub struct Regex {
     definitions: DefIdMap<RegexKind>,
+    loop_stack: Vec<(OwnerId, Span)>,
 }
 
-impl_lint_pass!(Regex => [INVALID_REGEX, TRIVIAL_REGEX]);
+impl_lint_pass!(Regex => [INVALID_REGEX, TRIVIAL_REGEX, REGEX_CREATION_IN_LOOPS]);
 
 impl<'tcx> LateLintPass<'tcx> for Regex {
     fn check_crate(&mut self, cx: &LateContext<'tcx>) {
@@ -99,12 +138,34 @@ impl<'tcx> LateLintPass<'tcx> for Regex {
             && let Some(def_id) = path_def_id(cx, fun)
             && let Some(regex_kind) = self.definitions.get(&def_id)
         {
+            if let Some(&(loop_item_id, loop_span)) = self.loop_stack.last()
+                && loop_item_id == fun.hir_id.owner
+                && (matches!(arg.kind, ExprKind::Lit(_)) || const_str(cx, arg).is_some())
+            {
+                span_lint_and_help(
+                    cx,
+                    REGEX_CREATION_IN_LOOPS,
+                    fun.span,
+                    "compiling a regex in a loop",
+                    Some(loop_span),
+                    "move the regex construction outside this loop",
+                );
+            }
+
             match regex_kind {
                 RegexKind::Unicode => check_regex(cx, arg, true),
                 RegexKind::UnicodeSet => check_set(cx, arg, true),
                 RegexKind::Bytes => check_regex(cx, arg, false),
                 RegexKind::BytesSet => check_set(cx, arg, false),
             }
+        } else if let ExprKind::Loop(block, _, _, span) = expr.kind {
+            self.loop_stack.push((block.hir_id.owner, span));
+        }
+    }
+
+    fn check_expr_post(&mut self, _: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
+        if matches!(expr.kind, ExprKind::Loop(..)) {
+            self.loop_stack.pop();
         }
     }
 }
diff --git a/src/tools/clippy/clippy_lints/src/returns.rs b/src/tools/clippy/clippy_lints/src/returns.rs
index 662745e4b5d..110dea8fb8e 100644
--- a/src/tools/clippy/clippy_lints/src/returns.rs
+++ b/src/tools/clippy/clippy_lints/src/returns.rs
@@ -357,7 +357,7 @@ fn check_final_expr<'tcx>(
 
             let replacement = if let Some(inner_expr) = inner {
                 // if desugar of `do yeet`, don't lint
-                if let ExprKind::Call(path_expr, _) = inner_expr.kind
+                if let ExprKind::Call(path_expr, [_]) = inner_expr.kind
                     && let ExprKind::Path(QPath::LangItem(LangItem::TryTraitFromYeet, ..)) = path_expr.kind
                 {
                     return;
diff --git a/src/tools/clippy/clippy_lints/src/significant_drop_tightening.rs b/src/tools/clippy/clippy_lints/src/significant_drop_tightening.rs
index 0eece922143..abd8363456d 100644
--- a/src/tools/clippy/clippy_lints/src/significant_drop_tightening.rs
+++ b/src/tools/clippy/clippy_lints/src/significant_drop_tightening.rs
@@ -421,11 +421,10 @@ fn dummy_stmt_expr<'any>(expr: &'any hir::Expr<'any>) -> hir::Stmt<'any> {
 }
 
 fn has_drop(expr: &hir::Expr<'_>, first_bind_ident: &Ident, lcx: &LateContext<'_>) -> bool {
-    if let hir::ExprKind::Call(fun, args) = expr.kind
+    if let hir::ExprKind::Call(fun, [first_arg]) = expr.kind
         && let hir::ExprKind::Path(hir::QPath::Resolved(_, fun_path)) = &fun.kind
         && let Res::Def(DefKind::Fn, did) = fun_path.res
         && lcx.tcx.is_diagnostic_item(sym::mem_drop, did)
-        && let [first_arg, ..] = args
     {
         let has_ident = |local_expr: &hir::Expr<'_>| {
             if let hir::ExprKind::Path(hir::QPath::Resolved(_, arg_path)) = &local_expr.kind
diff --git a/src/tools/clippy/clippy_lints/src/size_of_in_element_count.rs b/src/tools/clippy/clippy_lints/src/size_of_in_element_count.rs
index 7750d8909d3..db1c75fc3de 100644
--- a/src/tools/clippy/clippy_lints/src/size_of_in_element_count.rs
+++ b/src/tools/clippy/clippy_lints/src/size_of_in_element_count.rs
@@ -34,7 +34,7 @@ declare_lint_pass!(SizeOfInElementCount => [SIZE_OF_IN_ELEMENT_COUNT]);
 
 fn get_size_of_ty<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, inverted: bool) -> Option<Ty<'tcx>> {
     match expr.kind {
-        ExprKind::Call(count_func, _func_args) => {
+        ExprKind::Call(count_func, _) => {
             if !inverted
                 && let ExprKind::Path(ref count_func_qpath) = count_func.kind
                 && let Some(def_id) = cx.qpath_res(count_func_qpath, count_func.hir_id).opt_def_id()
diff --git a/src/tools/clippy/clippy_lints/src/slow_vector_initialization.rs b/src/tools/clippy/clippy_lints/src/slow_vector_initialization.rs
index fc799cad67e..ec6e45256d1 100644
--- a/src/tools/clippy/clippy_lints/src/slow_vector_initialization.rs
+++ b/src/tools/clippy/clippy_lints/src/slow_vector_initialization.rs
@@ -152,7 +152,7 @@ impl SlowVectorInit {
             && is_path_diagnostic_item(cx, func, sym::vec_with_capacity)
         {
             Some(InitializedSize::Initialized(len_expr))
-        } else if matches!(expr.kind, ExprKind::Call(func, _) if is_path_diagnostic_item(cx, func, sym::vec_new)) {
+        } else if matches!(expr.kind, ExprKind::Call(func, []) if is_path_diagnostic_item(cx, func, sym::vec_new)) {
             Some(InitializedSize::Uninitialized)
         } else {
             None
@@ -268,7 +268,7 @@ impl<'tcx> VectorInitializationVisitor<'_, 'tcx> {
 
     /// Returns `true` if give expression is `repeat(0).take(...)`
     fn is_repeat_take(&mut self, expr: &'tcx Expr<'tcx>) -> bool {
-        if let ExprKind::MethodCall(take_path, recv, [len_arg, ..], _) = expr.kind
+        if let ExprKind::MethodCall(take_path, recv, [len_arg], _) = expr.kind
             && take_path.ident.name == sym!(take)
             // Check that take is applied to `repeat(0)`
             && self.is_repeat_zero(recv)
diff --git a/src/tools/clippy/clippy_lints/src/strings.rs b/src/tools/clippy/clippy_lints/src/strings.rs
index 1fb82b66ab8..bf49bb60162 100644
--- a/src/tools/clippy/clippy_lints/src/strings.rs
+++ b/src/tools/clippy/clippy_lints/src/strings.rs
@@ -253,18 +253,17 @@ impl<'tcx> LateLintPass<'tcx> for StringLitAsBytes {
     fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) {
         use rustc_ast::LitKind;
 
-        if let ExprKind::Call(fun, args) = e.kind
+        if let ExprKind::Call(fun, [bytes_arg]) = e.kind
             // Find std::str::converts::from_utf8
             && is_path_diagnostic_item(cx, fun, sym::str_from_utf8)
 
             // Find string::as_bytes
-            && let ExprKind::AddrOf(BorrowKind::Ref, _, args) = args[0].kind
+            && let ExprKind::AddrOf(BorrowKind::Ref, _, args) = bytes_arg.kind
             && let ExprKind::Index(left, right, _) = args.kind
             && let (method_names, expressions, _) = method_calls(left, 1)
-            && method_names.len() == 1
+            && method_names == [sym!(as_bytes)]
             && expressions.len() == 1
             && expressions[0].1.is_empty()
-            && method_names[0] == sym!(as_bytes)
 
             // Check for slicer
             && let ExprKind::Struct(QPath::LangItem(LangItem::Range, ..), _, _) = right.kind
@@ -393,7 +392,7 @@ impl<'tcx> LateLintPass<'tcx> for StrToString {
             return;
         }
 
-        if let ExprKind::MethodCall(path, self_arg, ..) = &expr.kind
+        if let ExprKind::MethodCall(path, self_arg, [], _) = &expr.kind
             && path.ident.name == sym::to_string
             && let ty = cx.typeck_results().expr_ty(self_arg)
             && let ty::Ref(_, ty, ..) = ty.kind()
@@ -449,7 +448,7 @@ impl<'tcx> LateLintPass<'tcx> for StringToString {
             return;
         }
 
-        if let ExprKind::MethodCall(path, self_arg, ..) = &expr.kind
+        if let ExprKind::MethodCall(path, self_arg, [], _) = &expr.kind
             && path.ident.name == sym::to_string
             && let ty = cx.typeck_results().expr_ty(self_arg)
             && is_type_lang_item(cx, ty, LangItem::String)
diff --git a/src/tools/clippy/clippy_lints/src/to_digit_is_some.rs b/src/tools/clippy/clippy_lints/src/to_digit_is_some.rs
index 4f96a566b63..569812d8106 100644
--- a/src/tools/clippy/clippy_lints/src/to_digit_is_some.rs
+++ b/src/tools/clippy/clippy_lints/src/to_digit_is_some.rs
@@ -51,9 +51,8 @@ impl<'tcx> LateLintPass<'tcx> for ToDigitIsSome {
                         None
                     }
                 },
-                hir::ExprKind::Call(to_digits_call, to_digit_args) => {
-                    if let [char_arg, radix_arg] = *to_digit_args
-                        && let hir::ExprKind::Path(to_digits_path) = &to_digits_call.kind
+                hir::ExprKind::Call(to_digits_call, [char_arg, radix_arg]) => {
+                    if let hir::ExprKind::Path(to_digits_path) = &to_digits_call.kind
                         && let to_digits_call_res = cx.qpath_res(to_digits_path, to_digits_call.hir_id)
                         && let Some(to_digits_def_id) = to_digits_call_res.opt_def_id()
                         && match_def_path(cx, to_digits_def_id, &[
diff --git a/src/tools/clippy/clippy_lints/src/trailing_empty_array.rs b/src/tools/clippy/clippy_lints/src/trailing_empty_array.rs
index db8c63892b8..52bb7c4bd68 100644
--- a/src/tools/clippy/clippy_lints/src/trailing_empty_array.rs
+++ b/src/tools/clippy/clippy_lints/src/trailing_empty_array.rs
@@ -2,7 +2,7 @@ use clippy_utils::diagnostics::span_lint_and_help;
 use clippy_utils::has_repr_attr;
 use rustc_hir::{Item, ItemKind};
 use rustc_lint::{LateContext, LateLintPass};
-use rustc_middle::ty::{Const, FeedConstTy};
+use rustc_middle::ty;
 use rustc_session::declare_lint_pass;
 
 declare_clippy_lint! {
@@ -55,16 +55,14 @@ impl<'tcx> LateLintPass<'tcx> for TrailingEmptyArray {
 
 fn is_struct_with_trailing_zero_sized_array<'tcx>(cx: &LateContext<'tcx>, item: &Item<'tcx>) -> bool {
     if let ItemKind::Struct(data, _) = &item.kind
-        // First check if last field is an array
         && let Some(last_field) = data.fields().last()
-        && let rustc_hir::TyKind::Array(_, rustc_hir::ArrayLen::Body(length)) = last_field.ty.kind
-
-        // Then check if that array is zero-sized
-        && let length = Const::from_const_arg(cx.tcx, length, FeedConstTy::No)
-        && let length = length.try_eval_target_usize(cx.tcx, cx.param_env)
-        && let Some(length) = length
+        && let field_ty = cx
+            .tcx
+            .normalize_erasing_regions(cx.param_env, cx.tcx.type_of(last_field.def_id).instantiate_identity())
+        && let ty::Array(_, array_len) = *field_ty.kind()
+        && let Some(0) = array_len.try_to_target_usize(cx.tcx)
     {
-        length == 0
+        true
     } else {
         false
     }
diff --git a/src/tools/clippy/clippy_lints/src/trait_bounds.rs b/src/tools/clippy/clippy_lints/src/trait_bounds.rs
index 38befdee574..7f528b9d17b 100644
--- a/src/tools/clippy/clippy_lints/src/trait_bounds.rs
+++ b/src/tools/clippy/clippy_lints/src/trait_bounds.rs
@@ -10,7 +10,7 @@ use rustc_data_structures::unhash::UnhashMap;
 use rustc_errors::Applicability;
 use rustc_hir::def::Res;
 use rustc_hir::{
-    GenericArg, GenericBound, Generics, Item, ItemKind, LangItem, Node, Path, PathSegment, PredicateOrigin, QPath,
+    GenericBound, Generics, Item, ItemKind, LangItem, Node, Path, PathSegment, PredicateOrigin, QPath,
     TraitBoundModifier, TraitItem, TraitRef, Ty, TyKind, WherePredicate,
 };
 use rustc_lint::{LateContext, LateLintPass};
@@ -152,7 +152,10 @@ impl<'tcx> LateLintPass<'tcx> for TraitBounds {
                     .filter_map(get_trait_info_from_bound)
                     .for_each(|(trait_item_res, trait_item_segments, span)| {
                         if let Some(self_segments) = self_bounds_map.get(&trait_item_res) {
-                            if SpanlessEq::new(cx).eq_path_segments(self_segments, trait_item_segments) {
+                            if SpanlessEq::new(cx)
+                                .paths_by_resolution()
+                                .eq_path_segments(self_segments, trait_item_segments)
+                            {
                                 span_lint_and_help(
                                     cx,
                                     TRAIT_DUPLICATION_IN_BOUNDS,
@@ -302,7 +305,7 @@ impl TraitBounds {
     }
 }
 
-fn check_trait_bound_duplication(cx: &LateContext<'_>, generics: &'_ Generics<'_>) {
+fn check_trait_bound_duplication<'tcx>(cx: &LateContext<'tcx>, generics: &'_ Generics<'tcx>) {
     if generics.span.from_expansion() {
         return;
     }
@@ -314,6 +317,7 @@ fn check_trait_bound_duplication(cx: &LateContext<'_>, generics: &'_ Generics<'_
     //       |
     // collects each of these where clauses into a set keyed by generic name and comparable trait
     // eg. (T, Clone)
+    #[expect(clippy::mutable_key_type)]
     let where_predicates = generics
         .predicates
         .iter()
@@ -367,11 +371,27 @@ fn check_trait_bound_duplication(cx: &LateContext<'_>, generics: &'_ Generics<'_
     }
 }
 
-#[derive(Clone, PartialEq, Eq, Hash, Debug)]
-struct ComparableTraitRef(Res, Vec<Res>);
-impl Default for ComparableTraitRef {
-    fn default() -> Self {
-        Self(Res::Err, Vec::new())
+struct ComparableTraitRef<'a, 'tcx> {
+    cx: &'a LateContext<'tcx>,
+    trait_ref: &'tcx TraitRef<'tcx>,
+    modifier: TraitBoundModifier,
+}
+
+impl PartialEq for ComparableTraitRef<'_, '_> {
+    fn eq(&self, other: &Self) -> bool {
+        self.modifier == other.modifier
+            && SpanlessEq::new(self.cx)
+                .paths_by_resolution()
+                .eq_path(self.trait_ref.path, other.trait_ref.path)
+    }
+}
+impl Eq for ComparableTraitRef<'_, '_> {}
+impl Hash for ComparableTraitRef<'_, '_> {
+    fn hash<H: Hasher>(&self, state: &mut H) {
+        let mut s = SpanlessHash::new(self.cx).paths_by_resolution();
+        s.hash_path(self.trait_ref.path);
+        state.write_u64(s.finish());
+        self.modifier.hash(state);
     }
 }
 
@@ -392,69 +412,41 @@ fn get_trait_info_from_bound<'a>(bound: &'a GenericBound<'_>) -> Option<(Res, &'
     }
 }
 
-fn get_ty_res(ty: Ty<'_>) -> Option<Res> {
-    match ty.kind {
-        TyKind::Path(QPath::Resolved(_, path)) => Some(path.res),
-        TyKind::Path(QPath::TypeRelative(ty, _)) => get_ty_res(*ty),
-        _ => None,
-    }
-}
-
-// FIXME: ComparableTraitRef does not support nested bounds needed for associated_type_bounds
-fn into_comparable_trait_ref(trait_ref: &TraitRef<'_>) -> ComparableTraitRef {
-    ComparableTraitRef(
-        trait_ref.path.res,
-        trait_ref
-            .path
-            .segments
-            .iter()
-            .filter_map(|segment| {
-                // get trait bound type arguments
-                Some(segment.args?.args.iter().filter_map(|arg| {
-                    if let GenericArg::Type(ty) = arg {
-                        return get_ty_res(**ty);
-                    }
-                    None
-                }))
-            })
-            .flatten()
-            .collect(),
-    )
-}
-
-fn rollup_traits(
-    cx: &LateContext<'_>,
-    bounds: &[GenericBound<'_>],
+fn rollup_traits<'cx, 'tcx>(
+    cx: &'cx LateContext<'tcx>,
+    bounds: &'tcx [GenericBound<'tcx>],
     msg: &'static str,
-) -> Vec<(ComparableTraitRef, Span)> {
+) -> Vec<(ComparableTraitRef<'cx, 'tcx>, Span)> {
+    // Source order is needed for joining spans
     let mut map = FxIndexMap::default();
     let mut repeated_res = false;
 
-    let only_comparable_trait_refs = |bound: &GenericBound<'_>| {
+    let only_comparable_trait_refs = |bound: &'tcx GenericBound<'tcx>| {
         if let GenericBound::Trait(t) = bound {
-            Some((into_comparable_trait_ref(&t.trait_ref), t.span))
+            Some((
+                ComparableTraitRef {
+                    cx,
+                    trait_ref: &t.trait_ref,
+                    modifier: t.modifiers,
+                },
+                t.span,
+            ))
         } else {
             None
         }
     };
 
-    let mut i = 0usize;
     for bound in bounds.iter().filter_map(only_comparable_trait_refs) {
         let (comparable_bound, span_direct) = bound;
         match map.entry(comparable_bound) {
             IndexEntry::Occupied(_) => repeated_res = true,
             IndexEntry::Vacant(e) => {
-                e.insert((span_direct, i));
-                i += 1;
+                e.insert(span_direct);
             },
         }
     }
 
-    // Put bounds in source order
-    let mut comparable_bounds = vec![Default::default(); map.len()];
-    for (k, (v, i)) in map {
-        comparable_bounds[i] = (k, v);
-    }
+    let comparable_bounds: Vec<_> = map.into_iter().collect();
 
     if repeated_res && let [first_trait, .., last_trait] = bounds {
         let all_trait_span = first_trait.span().to(last_trait.span());
diff --git a/src/tools/clippy/clippy_lints/src/tuple_array_conversions.rs b/src/tools/clippy/clippy_lints/src/tuple_array_conversions.rs
index 3da8a449a7c..07d0f59b91c 100644
--- a/src/tools/clippy/clippy_lints/src/tuple_array_conversions.rs
+++ b/src/tools/clippy/clippy_lints/src/tuple_array_conversions.rs
@@ -190,7 +190,7 @@ fn all_bindings_are_for_conv<'tcx>(
                     tys.len() == elements.len() && tys.iter().chain(final_tys.iter().copied()).all_equal()
                 },
                 (ToType::Tuple, ty::Array(ty, len)) => {
-                    let Some(len) = len.try_eval_target_usize(cx.tcx, cx.param_env) else { return false };
+                    let Some(len) = len.try_to_target_usize(cx.tcx) else { return false };
                     len as usize == elements.len() && final_tys.iter().chain(once(ty)).all_equal()
                 },
                 _ => false,
diff --git a/src/tools/clippy/clippy_lints/src/unit_types/unit_arg.rs b/src/tools/clippy/clippy_lints/src/unit_types/unit_arg.rs
index 41b2ca5d268..e7d26fa238e 100644
--- a/src/tools/clippy/clippy_lints/src/unit_types/unit_arg.rs
+++ b/src/tools/clippy/clippy_lints/src/unit_types/unit_arg.rs
@@ -25,14 +25,15 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) {
         return;
     }
 
-    let args: Vec<_> = match expr.kind {
-        ExprKind::Call(_, args) => args.iter().collect(),
-        ExprKind::MethodCall(_, receiver, args, _) => std::iter::once(receiver).chain(args.iter()).collect(),
+    let (reciever, args) = match expr.kind {
+        ExprKind::Call(_, args) => (None, args),
+        ExprKind::MethodCall(_, receiver, args, _) => (Some(receiver), args),
         _ => return,
     };
 
-    let args_to_recover = args
+    let args_to_recover = reciever
         .into_iter()
+        .chain(args)
         .filter(|arg| {
             if cx.typeck_results().expr_ty(arg).is_unit() && !utils::is_unit_literal(arg) {
                 !matches!(
diff --git a/src/tools/clippy/clippy_lints/src/unnecessary_literal_bound.rs b/src/tools/clippy/clippy_lints/src/unnecessary_literal_bound.rs
new file mode 100644
index 00000000000..80ce6711126
--- /dev/null
+++ b/src/tools/clippy/clippy_lints/src/unnecessary_literal_bound.rs
@@ -0,0 +1,158 @@
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::path_res;
+use rustc_ast::ast::LitKind;
+use rustc_errors::Applicability;
+use rustc_hir::def::Res;
+use rustc_hir::intravisit::{FnKind, Visitor};
+use rustc_hir::{Body, Expr, ExprKind, FnDecl, FnRetTy, Lit, MutTy, Mutability, PrimTy, Ty, TyKind, intravisit};
+use rustc_lint::{LateContext, LateLintPass};
+use rustc_session::declare_lint_pass;
+use rustc_span::Span;
+use rustc_span::def_id::LocalDefId;
+
+declare_clippy_lint! {
+    /// ### What it does
+    ///
+    /// Detects functions that are written to return `&str` that could return `&'static str` but instead return a `&'a str`.
+    ///
+    /// ### Why is this bad?
+    ///
+    /// This leaves the caller unable to use the `&str` as `&'static str`, causing unneccessary allocations or confusion.
+    /// This is also most likely what you meant to write.
+    ///
+    /// ### Example
+    /// ```no_run
+    /// # struct MyType;
+    /// impl MyType {
+    ///     fn returns_literal(&self) -> &str {
+    ///         "Literal"
+    ///     }
+    /// }
+    /// ```
+    /// Use instead:
+    /// ```no_run
+    /// # struct MyType;
+    /// impl MyType {
+    ///     fn returns_literal(&self) -> &'static str {
+    ///         "Literal"
+    ///     }
+    /// }
+    /// ```
+    /// Or, in case you may return a non-literal `str` in future:
+    /// ```no_run
+    /// # struct MyType;
+    /// impl MyType {
+    ///     fn returns_literal<'a>(&'a self) -> &'a str {
+    ///         "Literal"
+    ///     }
+    /// }
+    /// ```
+    #[clippy::version = "1.83.0"]
+    pub UNNECESSARY_LITERAL_BOUND,
+    pedantic,
+    "detects &str that could be &'static str in function return types"
+}
+
+declare_lint_pass!(UnnecessaryLiteralBound => [UNNECESSARY_LITERAL_BOUND]);
+
+fn extract_anonymous_ref<'tcx>(hir_ty: &Ty<'tcx>) -> Option<&'tcx Ty<'tcx>> {
+    let TyKind::Ref(lifetime, MutTy { ty, mutbl }) = hir_ty.kind else {
+        return None;
+    };
+
+    if !lifetime.is_anonymous() || !matches!(mutbl, Mutability::Not) {
+        return None;
+    }
+
+    Some(ty)
+}
+
+fn is_str_literal(expr: &Expr<'_>) -> bool {
+    matches!(
+        expr.kind,
+        ExprKind::Lit(Lit {
+            node: LitKind::Str(..),
+            ..
+        }),
+    )
+}
+
+struct FindNonLiteralReturn;
+
+impl<'hir> Visitor<'hir> for FindNonLiteralReturn {
+    type Result = std::ops::ControlFlow<()>;
+    type NestedFilter = intravisit::nested_filter::None;
+
+    fn visit_expr(&mut self, expr: &'hir Expr<'hir>) -> Self::Result {
+        if let ExprKind::Ret(Some(ret_val_expr)) = expr.kind
+            && !is_str_literal(ret_val_expr)
+        {
+            Self::Result::Break(())
+        } else {
+            intravisit::walk_expr(self, expr)
+        }
+    }
+}
+
+fn check_implicit_returns_static_str(body: &Body<'_>) -> bool {
+    // TODO: Improve this to the same complexity as the Visitor to catch more implicit return cases.
+    if let ExprKind::Block(block, _) = body.value.kind
+        && let Some(implicit_ret) = block.expr
+    {
+        return is_str_literal(implicit_ret);
+    }
+
+    false
+}
+
+fn check_explicit_returns_static_str(expr: &Expr<'_>) -> bool {
+    let mut visitor = FindNonLiteralReturn;
+    visitor.visit_expr(expr).is_continue()
+}
+
+impl<'tcx> LateLintPass<'tcx> for UnnecessaryLiteralBound {
+    fn check_fn(
+        &mut self,
+        cx: &LateContext<'tcx>,
+        kind: FnKind<'tcx>,
+        decl: &'tcx FnDecl<'_>,
+        body: &'tcx Body<'_>,
+        span: Span,
+        _: LocalDefId,
+    ) {
+        if span.from_expansion() {
+            return;
+        }
+
+        // Checking closures would be a little silly
+        if matches!(kind, FnKind::Closure) {
+            return;
+        }
+
+        // Check for `-> &str`
+        let FnRetTy::Return(ret_hir_ty) = decl.output else {
+            return;
+        };
+
+        let Some(inner_hir_ty) = extract_anonymous_ref(ret_hir_ty) else {
+            return;
+        };
+
+        if path_res(cx, inner_hir_ty) != Res::PrimTy(PrimTy::Str) {
+            return;
+        }
+
+        // Check for all return statements returning literals
+        if check_explicit_returns_static_str(body.value) && check_implicit_returns_static_str(body) {
+            span_lint_and_sugg(
+                cx,
+                UNNECESSARY_LITERAL_BOUND,
+                ret_hir_ty.span,
+                "returning a `str` unnecessarily tied to the lifetime of arguments",
+                "try",
+                "&'static str".into(), // how ironic, a lint about `&'static str` requiring a `String` alloc...
+                Applicability::MachineApplicable,
+            );
+        }
+    }
+}
diff --git a/src/tools/clippy/clippy_lints/src/unnecessary_map_on_constructor.rs b/src/tools/clippy/clippy_lints/src/unnecessary_map_on_constructor.rs
index 8f1eb5019f0..d3700d05b01 100644
--- a/src/tools/clippy/clippy_lints/src/unnecessary_map_on_constructor.rs
+++ b/src/tools/clippy/clippy_lints/src/unnecessary_map_on_constructor.rs
@@ -38,13 +38,11 @@ impl<'tcx> LateLintPass<'tcx> for UnnecessaryMapOnConstructor {
         if expr.span.from_expansion() {
             return;
         }
-        if let hir::ExprKind::MethodCall(path, recv, args, ..) = expr.kind
+        if let hir::ExprKind::MethodCall(path, recv, [map_arg], ..) = expr.kind
             && let Some(sym::Option | sym::Result) = get_type_diagnostic_name(cx, cx.typeck_results().expr_ty(recv))
         {
-            let (constructor_path, constructor_item) = if let hir::ExprKind::Call(constructor, constructor_args) =
-                recv.kind
+            let (constructor_path, constructor_item) = if let hir::ExprKind::Call(constructor, [arg, ..]) = recv.kind
                 && let hir::ExprKind::Path(constructor_path) = constructor.kind
-                && let Some(arg) = constructor_args.first()
             {
                 if constructor.span.from_expansion() || arg.span.from_expansion() {
                     return;
@@ -70,9 +68,7 @@ impl<'tcx> LateLintPass<'tcx> for UnnecessaryMapOnConstructor {
                 _ => return,
             }
 
-            if let Some(map_arg) = args.first()
-                && let hir::ExprKind::Path(fun) = map_arg.kind
-            {
+            if let hir::ExprKind::Path(fun) = map_arg.kind {
                 if map_arg.span.from_expansion() {
                     return;
                 }
diff --git a/src/tools/clippy/clippy_lints/src/unnecessary_owned_empty_strings.rs b/src/tools/clippy/clippy_lints/src/unnecessary_owned_empty_strings.rs
index f01cb457af8..7d996775a58 100644
--- a/src/tools/clippy/clippy_lints/src/unnecessary_owned_empty_strings.rs
+++ b/src/tools/clippy/clippy_lints/src/unnecessary_owned_empty_strings.rs
@@ -52,8 +52,8 @@ impl<'tcx> LateLintPass<'tcx> for UnnecessaryOwnedEmptyStrings {
                     Applicability::MachineApplicable,
                 );
             } else if cx.tcx.is_diagnostic_item(sym::from_fn, fun_def_id)
-                && let [.., last_arg] = args
-                && let ExprKind::Lit(spanned) = &last_arg.kind
+                && let [arg] = args
+                && let ExprKind::Lit(spanned) = &arg.kind
                 && let LitKind::Str(symbol, _) = spanned.node
                 && symbol.is_empty()
                 && let inner_expr_type = cx.typeck_results().expr_ty(inner_expr)
diff --git a/src/tools/clippy/clippy_lints/src/unused_io_amount.rs b/src/tools/clippy/clippy_lints/src/unused_io_amount.rs
index d2a21b11ef4..cf406b817da 100644
--- a/src/tools/clippy/clippy_lints/src/unused_io_amount.rs
+++ b/src/tools/clippy/clippy_lints/src/unused_io_amount.rs
@@ -222,7 +222,7 @@ fn unpack_call_chain<'a>(mut expr: &'a hir::Expr<'a>) -> &'a hir::Expr<'a> {
 }
 
 fn unpack_try<'a>(mut expr: &'a hir::Expr<'a>) -> &'a hir::Expr<'a> {
-    while let ExprKind::Call(func, [ref arg_0, ..]) = expr.kind
+    while let ExprKind::Call(func, [ref arg_0]) = expr.kind
         && matches!(
             func.kind,
             ExprKind::Path(hir::QPath::LangItem(hir::LangItem::TryTraitBranch, ..))
@@ -244,7 +244,7 @@ fn unpack_match<'a>(mut expr: &'a hir::Expr<'a>) -> &'a hir::Expr<'a> {
 /// waited on.  Otherwise return None.
 fn unpack_await<'a>(expr: &'a hir::Expr<'a>) -> &'a hir::Expr<'a> {
     if let ExprKind::Match(expr, _, hir::MatchSource::AwaitDesugar) = expr.kind {
-        if let ExprKind::Call(func, [ref arg_0, ..]) = expr.kind {
+        if let ExprKind::Call(func, [ref arg_0]) = expr.kind {
             if matches!(
                 func.kind,
                 ExprKind::Path(hir::QPath::LangItem(hir::LangItem::IntoFutureIntoFuture, ..))
diff --git a/src/tools/clippy/clippy_lints/src/unused_result_ok.rs b/src/tools/clippy/clippy_lints/src/unused_result_ok.rs
index 297288db0a5..0c0d10eac5b 100644
--- a/src/tools/clippy/clippy_lints/src/unused_result_ok.rs
+++ b/src/tools/clippy/clippy_lints/src/unused_result_ok.rs
@@ -26,7 +26,7 @@ declare_clippy_lint! {
     /// # fn some_function() -> Result<(), ()> { Ok(()) }
     /// let _ = some_function();
     /// ```
-    #[clippy::version = "1.70.0"]
+    #[clippy::version = "1.82.0"]
     pub UNUSED_RESULT_OK,
     restriction,
     "Use of `.ok()` to silence `Result`'s `#[must_use]` is misleading. Use `let _ =` instead."
diff --git a/src/tools/clippy/clippy_lints/src/unwrap.rs b/src/tools/clippy/clippy_lints/src/unwrap.rs
index 6fe660b6a47..096b3ff9a2e 100644
--- a/src/tools/clippy/clippy_lints/src/unwrap.rs
+++ b/src/tools/clippy/clippy_lints/src/unwrap.rs
@@ -153,13 +153,12 @@ fn collect_unwrap_info<'tcx>(
         }
     } else if let ExprKind::Unary(UnOp::Not, expr) = &expr.kind {
         return collect_unwrap_info(cx, if_expr, expr, branch, !invert, false);
-    } else if let ExprKind::MethodCall(method_name, receiver, args, _) = &expr.kind
+    } else if let ExprKind::MethodCall(method_name, receiver, [], _) = &expr.kind
         && let Some(local_id) = path_to_local(receiver)
         && let ty = cx.typeck_results().expr_ty(receiver)
         && let name = method_name.ident.as_str()
         && (is_relevant_option_call(cx, ty, name) || is_relevant_result_call(cx, ty, name))
     {
-        assert!(args.is_empty());
         let unwrappable = match name {
             "is_some" | "is_ok" => true,
             "is_err" | "is_none" => false,
@@ -208,7 +207,7 @@ struct MutationVisitor<'tcx> {
 /// expression: that will be where the actual method call is.
 fn is_option_as_mut_use(tcx: TyCtxt<'_>, expr_id: HirId) -> bool {
     if let Node::Expr(mutating_expr) = tcx.parent_hir_node(expr_id)
-        && let ExprKind::MethodCall(path, ..) = mutating_expr.kind
+        && let ExprKind::MethodCall(path, _, [], _) = mutating_expr.kind
     {
         path.ident.name.as_str() == "as_mut"
     } else {
@@ -275,7 +274,7 @@ enum AsRefKind {
 /// Checks if the expression is a method call to `as_{ref,mut}` and returns the receiver of it.
 /// If it isn't, the expression itself is returned.
 fn consume_option_as_ref<'tcx>(expr: &'tcx Expr<'tcx>) -> (&'tcx Expr<'tcx>, Option<AsRefKind>) {
-    if let ExprKind::MethodCall(path, recv, ..) = expr.kind {
+    if let ExprKind::MethodCall(path, recv, [], _) = expr.kind {
         if path.ident.name == sym::as_ref {
             (recv, Some(AsRefKind::AsRef))
         } else if path.ident.name.as_str() == "as_mut" {
@@ -303,7 +302,7 @@ impl<'tcx> Visitor<'tcx> for UnwrappableVariablesVisitor<'_, 'tcx> {
                 self.visit_branch(expr, cond, else_inner, true);
             }
         } else {
-            // find `unwrap[_err]()` calls:
+            // find `unwrap[_err]()` or `expect("...")` calls:
             if let ExprKind::MethodCall(method_name, self_arg, ..) = expr.kind
                 && let (self_arg, as_ref_kind) = consume_option_as_ref(self_arg)
                 && let Some(id) = path_to_local(self_arg)
diff --git a/src/tools/clippy/clippy_lints/src/useless_conversion.rs b/src/tools/clippy/clippy_lints/src/useless_conversion.rs
index 29a7949b343..ec3a693d2ef 100644
--- a/src/tools/clippy/clippy_lints/src/useless_conversion.rs
+++ b/src/tools/clippy/clippy_lints/src/useless_conversion.rs
@@ -129,7 +129,7 @@ fn into_iter_bound<'tcx>(
 
 /// Extracts the receiver of a `.into_iter()` method call.
 fn into_iter_call<'hir>(cx: &LateContext<'_>, expr: &'hir Expr<'hir>) -> Option<&'hir Expr<'hir>> {
-    if let ExprKind::MethodCall(name, recv, _, _) = expr.kind
+    if let ExprKind::MethodCall(name, recv, [], _) = expr.kind
         && is_trait_method(cx, expr, sym::IntoIterator)
         && name.ident.name == sym::into_iter
     {
@@ -173,7 +173,7 @@ impl<'tcx> LateLintPass<'tcx> for UselessConversion {
                 }
             },
 
-            ExprKind::MethodCall(name, recv, ..) => {
+            ExprKind::MethodCall(name, recv, [], _) => {
                 if is_trait_method(cx, e, sym::Into) && name.ident.as_str() == "into" {
                     let a = cx.typeck_results().expr_ty(e);
                     let b = cx.typeck_results().expr_ty(recv);
diff --git a/src/tools/clippy/clippy_lints/src/utils/internal_lints/collapsible_calls.rs b/src/tools/clippy/clippy_lints/src/utils/internal_lints/collapsible_calls.rs
index d8f101a8614..a3f9abe4f96 100644
--- a/src/tools/clippy/clippy_lints/src/utils/internal_lints/collapsible_calls.rs
+++ b/src/tools/clippy/clippy_lints/src/utils/internal_lints/collapsible_calls.rs
@@ -5,6 +5,7 @@ use rustc_errors::Applicability;
 use rustc_hir::{Closure, Expr, ExprKind};
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_session::declare_lint_pass;
+use rustc_span::Span;
 
 use std::borrow::{Borrow, Cow};
 
@@ -76,19 +77,19 @@ impl<'tcx> LateLintPass<'tcx> for CollapsibleCalls {
             return;
         }
 
-        if let ExprKind::Call(func, and_then_args) = expr.kind
+        if let ExprKind::Call(func, [call_cx, call_lint, call_sp, call_msg, call_f]) = expr.kind
             && is_expr_path_def_path(cx, func, &["clippy_utils", "diagnostics", "span_lint_and_then"])
-            && and_then_args.len() == 5
-            && let ExprKind::Closure(&Closure { body, .. }) = &and_then_args[4].kind
+            && let ExprKind::Closure(&Closure { body, .. }) = &call_f.kind
             && let body = cx.tcx.hir().body(body)
             && let only_expr = peel_blocks_with_stmt(body.value)
             && let ExprKind::MethodCall(ps, recv, span_call_args, _) = &only_expr.kind
             && let ExprKind::Path(..) = recv.kind
         {
-            let and_then_snippets = get_and_then_snippets(cx, and_then_args);
+            let and_then_snippets =
+                get_and_then_snippets(cx, call_cx.span, call_lint.span, call_sp.span, call_msg.span);
             let mut sle = SpanlessEq::new(cx).deny_side_effects();
             match ps.ident.as_str() {
-                "span_suggestion" if sle.eq_expr(&and_then_args[2], &span_call_args[0]) => {
+                "span_suggestion" if sle.eq_expr(call_sp, &span_call_args[0]) => {
                     suggest_suggestion(
                         cx,
                         expr,
@@ -96,11 +97,11 @@ impl<'tcx> LateLintPass<'tcx> for CollapsibleCalls {
                         &span_suggestion_snippets(cx, span_call_args),
                     );
                 },
-                "span_help" if sle.eq_expr(&and_then_args[2], &span_call_args[0]) => {
+                "span_help" if sle.eq_expr(call_sp, &span_call_args[0]) => {
                     let help_snippet = snippet(cx, span_call_args[1].span, r#""...""#);
                     suggest_help(cx, expr, &and_then_snippets, help_snippet.borrow(), true);
                 },
-                "span_note" if sle.eq_expr(&and_then_args[2], &span_call_args[0]) => {
+                "span_note" if sle.eq_expr(call_sp, &span_call_args[0]) => {
                     let note_snippet = snippet(cx, span_call_args[1].span, r#""...""#);
                     suggest_note(cx, expr, &and_then_snippets, note_snippet.borrow(), true);
                 },
@@ -125,11 +126,17 @@ struct AndThenSnippets<'a> {
     msg: Cow<'a, str>,
 }
 
-fn get_and_then_snippets<'a, 'hir>(cx: &LateContext<'_>, and_then_snippets: &'hir [Expr<'hir>]) -> AndThenSnippets<'a> {
-    let cx_snippet = snippet(cx, and_then_snippets[0].span, "cx");
-    let lint_snippet = snippet(cx, and_then_snippets[1].span, "..");
-    let span_snippet = snippet(cx, and_then_snippets[2].span, "span");
-    let msg_snippet = snippet(cx, and_then_snippets[3].span, r#""...""#);
+fn get_and_then_snippets(
+    cx: &LateContext<'_>,
+    cx_span: Span,
+    lint_span: Span,
+    span_span: Span,
+    msg_span: Span,
+) -> AndThenSnippets<'static> {
+    let cx_snippet = snippet(cx, cx_span, "cx");
+    let lint_snippet = snippet(cx, lint_span, "..");
+    let span_snippet = snippet(cx, span_span, "span");
+    let msg_snippet = snippet(cx, msg_span, r#""...""#);
 
     AndThenSnippets {
         cx: cx_snippet,
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 dd456022212..51235de9f29 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
@@ -2,7 +2,7 @@ use clippy_utils::diagnostics::{span_lint, span_lint_and_help};
 use clippy_utils::macros::root_macro_call_first_node;
 use clippy_utils::{is_lint_allowed, match_def_path, paths};
 use rustc_ast::ast::LitKind;
-use rustc_data_structures::fx::{FxHashMap, FxHashSet};
+use rustc_data_structures::fx::{FxIndexMap, FxIndexSet};
 use rustc_hir::def::{DefKind, Res};
 use rustc_hir::hir_id::CRATE_HIR_ID;
 use rustc_hir::intravisit::Visitor;
@@ -87,8 +87,8 @@ declare_clippy_lint! {
 
 #[derive(Clone, Debug, Default)]
 pub struct LintWithoutLintPass {
-    declared_lints: FxHashMap<Symbol, Span>,
-    registered_lints: FxHashSet<Symbol>,
+    declared_lints: FxIndexMap<Symbol, Span>,
+    registered_lints: FxIndexSet<Symbol>,
 }
 
 impl_lint_pass!(LintWithoutLintPass => [
@@ -266,7 +266,7 @@ pub(super) fn extract_clippy_version_value(cx: &LateContext<'_>, item: &'_ Item<
 }
 
 struct LintCollector<'a, 'tcx> {
-    output: &'a mut FxHashSet<Symbol>,
+    output: &'a mut FxIndexSet<Symbol>,
     cx: &'a LateContext<'tcx>,
 }
 
diff --git a/src/tools/clippy/clippy_lints/src/vec.rs b/src/tools/clippy/clippy_lints/src/vec.rs
index ce4f41e854d..9bcff9d7bce 100644
--- a/src/tools/clippy/clippy_lints/src/vec.rs
+++ b/src/tools/clippy/clippy_lints/src/vec.rs
@@ -17,7 +17,6 @@ use rustc_middle::ty::layout::LayoutOf;
 use rustc_session::impl_lint_pass;
 use rustc_span::{DesugaringKind, Span, sym};
 
-#[expect(clippy::module_name_repetitions)]
 pub struct UselessVec {
     too_large_for_stack: u64,
     msrv: Msrv,
@@ -244,7 +243,7 @@ fn adjusts_to_slice(cx: &LateContext<'_>, e: &Expr<'_>) -> bool {
 pub fn is_allowed_vec_method(cx: &LateContext<'_>, e: &Expr<'_>) -> bool {
     const ALLOWED_METHOD_NAMES: &[&str] = &["len", "as_ptr", "is_empty"];
 
-    if let ExprKind::MethodCall(path, ..) = e.kind {
+    if let ExprKind::MethodCall(path, _, [], _) = e.kind {
         ALLOWED_METHOD_NAMES.contains(&path.ident.name.as_str())
     } else {
         is_trait_method(cx, e, sym::IntoIterator)
diff --git a/src/tools/clippy/clippy_utils/Cargo.toml b/src/tools/clippy/clippy_utils/Cargo.toml
index fe30b10c435..d8d5733da1c 100644
--- a/src/tools/clippy/clippy_utils/Cargo.toml
+++ b/src/tools/clippy/clippy_utils/Cargo.toml
@@ -1,6 +1,6 @@
 [package]
 name = "clippy_utils"
-version = "0.1.83"
+version = "0.1.84"
 edition = "2021"
 publish = false
 
diff --git a/src/tools/clippy/clippy_utils/src/consts.rs b/src/tools/clippy/clippy_utils/src/consts.rs
index 510034876e0..67c31abbdda 100644
--- a/src/tools/clippy/clippy_utils/src/consts.rs
+++ b/src/tools/clippy/clippy_utils/src/consts.rs
@@ -472,7 +472,7 @@ impl<'tcx> ConstEvalCtxt<'tcx> {
             ExprKind::Tup(tup) => self.multi(tup).map(Constant::Tuple),
             ExprKind::Repeat(value, _) => {
                 let n = match self.typeck.expr_ty(e).kind() {
-                    ty::Array(_, n) => n.try_eval_target_usize(self.tcx, self.param_env)?,
+                    ty::Array(_, n) => n.try_to_target_usize(self.tcx)?,
                     _ => span_bug!(e.span, "typeck error"),
                 };
                 self.expr(value).map(|v| Constant::Repeat(Box::new(v), n))
@@ -484,10 +484,9 @@ impl<'tcx> ConstEvalCtxt<'tcx> {
             }),
             ExprKind::If(cond, then, ref otherwise) => self.ifthenelse(cond, then, *otherwise),
             ExprKind::Binary(op, left, right) => self.binop(op, left, right),
-            ExprKind::Call(callee, args) => {
+            ExprKind::Call(callee, []) => {
                 // We only handle a few const functions for now.
-                if args.is_empty()
-                    && let ExprKind::Path(qpath) = &callee.kind
+                if let ExprKind::Path(qpath) = &callee.kind
                     && let Some(did) = self.typeck.qpath_res(qpath, callee.hir_id).opt_def_id()
                 {
                     match self.tcx.get_diagnostic_name(did) {
@@ -554,7 +553,7 @@ impl<'tcx> ConstEvalCtxt<'tcx> {
             ExprKind::Array(vec) => self.multi(vec).map(|v| v.is_empty()),
             ExprKind::Repeat(..) => {
                 if let ty::Array(_, n) = self.typeck.expr_ty(e).kind() {
-                    Some(n.try_eval_target_usize(self.tcx, self.param_env)? == 0)
+                    Some(n.try_to_target_usize(self.tcx)? == 0)
                 } else {
                     span_bug!(e.span, "typeck error");
                 }
diff --git a/src/tools/clippy/clippy_utils/src/hir_utils.rs b/src/tools/clippy/clippy_utils/src/hir_utils.rs
index a19c1555d16..27c57808ece 100644
--- a/src/tools/clippy/clippy_utils/src/hir_utils.rs
+++ b/src/tools/clippy/clippy_utils/src/hir_utils.rs
@@ -5,7 +5,7 @@ use crate::tokenize_with_text;
 use rustc_ast::ast::InlineAsmTemplatePiece;
 use rustc_data_structures::fx::FxHasher;
 use rustc_hir::MatchSource::TryDesugar;
-use rustc_hir::def::Res;
+use rustc_hir::def::{DefKind, Res};
 use rustc_hir::{
     ArrayLen, AssocItemConstraint, BinOpKind, BindingMode, Block, BodyId, Closure, ConstArg, ConstArgKind, Expr,
     ExprField, ExprKind, FnRetTy, GenericArg, GenericArgs, HirId, HirIdMap, InlineAsmOperand, LetExpr, Lifetime,
@@ -17,11 +17,33 @@ use rustc_middle::ty::TypeckResults;
 use rustc_span::{BytePos, ExpnKind, MacroKind, Symbol, SyntaxContext, sym};
 use std::hash::{Hash, Hasher};
 use std::ops::Range;
+use std::slice;
 
 /// Callback that is called when two expressions are not equal in the sense of `SpanlessEq`, but
 /// other conditions would make them equal.
 type SpanlessEqCallback<'a> = dyn FnMut(&Expr<'_>, &Expr<'_>) -> bool + 'a;
 
+/// Determines how paths are hashed and compared for equality.
+#[derive(Copy, Clone, Debug, Default)]
+pub enum PathCheck {
+    /// Paths must match exactly and are hashed by their exact HIR tree.
+    ///
+    /// Thus, `std::iter::Iterator` and `Iterator` are not considered equal even though they refer
+    /// to the same item.
+    #[default]
+    Exact,
+    /// Paths are compared and hashed based on their resolution.
+    ///
+    /// They can appear different in the HIR tree but are still considered equal
+    /// and have equal hashes as long as they refer to the same item.
+    ///
+    /// Note that this is currently only partially implemented specifically for paths that are
+    /// resolved before type-checking, i.e. the final segment must have a non-error resolution.
+    /// If a path with an error resolution is encountered, it falls back to the default exact
+    /// matching behavior.
+    Resolution,
+}
+
 /// Type used to check whether two ast are the same. This is different from the
 /// operator `==` on ast types as this operator would compare true equality with
 /// ID and span.
@@ -33,6 +55,7 @@ pub struct SpanlessEq<'a, 'tcx> {
     maybe_typeck_results: Option<(&'tcx TypeckResults<'tcx>, &'tcx TypeckResults<'tcx>)>,
     allow_side_effects: bool,
     expr_fallback: Option<Box<SpanlessEqCallback<'a>>>,
+    path_check: PathCheck,
 }
 
 impl<'a, 'tcx> SpanlessEq<'a, 'tcx> {
@@ -42,6 +65,7 @@ impl<'a, 'tcx> SpanlessEq<'a, 'tcx> {
             maybe_typeck_results: cx.maybe_typeck_results().map(|x| (x, x)),
             allow_side_effects: true,
             expr_fallback: None,
+            path_check: PathCheck::default(),
         }
     }
 
@@ -54,6 +78,16 @@ impl<'a, 'tcx> SpanlessEq<'a, 'tcx> {
         }
     }
 
+    /// Check paths by their resolution instead of exact equality. See [`PathCheck`] for more
+    /// details.
+    #[must_use]
+    pub fn paths_by_resolution(self) -> Self {
+        Self {
+            path_check: PathCheck::Resolution,
+            ..self
+        }
+    }
+
     #[must_use]
     pub fn expr_fallback(self, expr_fallback: impl FnMut(&Expr<'_>, &Expr<'_>) -> bool + 'a) -> Self {
         Self {
@@ -498,7 +532,7 @@ impl HirEqInterExpr<'_, '_, '_> {
         match (left.res, right.res) {
             (Res::Local(l), Res::Local(r)) => l == r || self.locals.get(&l) == Some(&r),
             (Res::Local(_), _) | (_, Res::Local(_)) => false,
-            _ => over(left.segments, right.segments, |l, r| self.eq_path_segment(l, r)),
+            _ => self.eq_path_segments(left.segments, right.segments),
         }
     }
 
@@ -511,17 +545,39 @@ impl HirEqInterExpr<'_, '_, '_> {
         }
     }
 
-    pub fn eq_path_segments(&mut self, left: &[PathSegment<'_>], right: &[PathSegment<'_>]) -> bool {
-        left.len() == right.len() && left.iter().zip(right).all(|(l, r)| self.eq_path_segment(l, r))
+    pub fn eq_path_segments<'tcx>(
+        &mut self,
+        mut left: &'tcx [PathSegment<'tcx>],
+        mut right: &'tcx [PathSegment<'tcx>],
+    ) -> bool {
+        if let PathCheck::Resolution = self.inner.path_check
+            && let Some(left_seg) = generic_path_segments(left)
+            && let Some(right_seg) = generic_path_segments(right)
+        {
+            // If we compare by resolution, then only check the last segments that could possibly have generic
+            // arguments
+            left = left_seg;
+            right = right_seg;
+        }
+
+        over(left, right, |l, r| self.eq_path_segment(l, r))
     }
 
     pub fn eq_path_segment(&mut self, left: &PathSegment<'_>, right: &PathSegment<'_>) -> bool {
-        // The == of idents doesn't work with different contexts,
-        // we have to be explicit about hygiene
-        left.ident.name == right.ident.name
-            && both(left.args.as_ref(), right.args.as_ref(), |l, r| {
-                self.eq_path_parameters(l, r)
-            })
+        if !self.eq_path_parameters(left.args(), right.args()) {
+            return false;
+        }
+
+        if let PathCheck::Resolution = self.inner.path_check
+            && left.res != Res::Err
+            && right.res != Res::Err
+        {
+            left.res == right.res
+        } else {
+            // The == of idents doesn't work with different contexts,
+            // we have to be explicit about hygiene
+            left.ident.name == right.ident.name
+        }
     }
 
     pub fn eq_ty(&mut self, left: &Ty<'_>, right: &Ty<'_>) -> bool {
@@ -684,6 +740,21 @@ pub fn eq_expr_value(cx: &LateContext<'_>, left: &Expr<'_>, right: &Expr<'_>) ->
     SpanlessEq::new(cx).deny_side_effects().eq_expr(left, right)
 }
 
+/// Returns the segments of a path that might have generic parameters.
+/// Usually just the last segment for free items, except for when the path resolves to an associated
+/// item, in which case it is the last two
+fn generic_path_segments<'tcx>(segments: &'tcx [PathSegment<'tcx>]) -> Option<&'tcx [PathSegment<'tcx>]> {
+    match segments.last()?.res {
+        Res::Def(DefKind::AssocConst | DefKind::AssocFn | DefKind::AssocTy, _) => {
+            // <Ty as module::Trait<T>>::assoc::<U>
+            //        ^^^^^^^^^^^^^^^^   ^^^^^^^^^^ segments: [module, Trait<T>, assoc<U>]
+            Some(&segments[segments.len().checked_sub(2)?..])
+        },
+        Res::Err => None,
+        _ => Some(slice::from_ref(segments.last()?)),
+    }
+}
+
 /// Type used to hash an ast element. This is different from the `Hash` trait
 /// on ast types as this
 /// trait would consider IDs and spans.
@@ -694,6 +765,7 @@ pub struct SpanlessHash<'a, 'tcx> {
     cx: &'a LateContext<'tcx>,
     maybe_typeck_results: Option<&'tcx TypeckResults<'tcx>>,
     s: FxHasher,
+    path_check: PathCheck,
 }
 
 impl<'a, 'tcx> SpanlessHash<'a, 'tcx> {
@@ -701,10 +773,21 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> {
         Self {
             cx,
             maybe_typeck_results: cx.maybe_typeck_results(),
+            path_check: PathCheck::default(),
             s: FxHasher::default(),
         }
     }
 
+    /// Check paths by their resolution instead of exact equality. See [`PathCheck`] for more
+    /// details.
+    #[must_use]
+    pub fn paths_by_resolution(self) -> Self {
+        Self {
+            path_check: PathCheck::Resolution,
+            ..self
+        }
+    }
+
     pub fn finish(self) -> u64 {
         self.s.finish()
     }
@@ -1042,9 +1125,19 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> {
             // even though the binding names are different and they have different `HirId`s.
             Res::Local(_) => 1_usize.hash(&mut self.s),
             _ => {
-                for seg in path.segments {
-                    self.hash_name(seg.ident.name);
-                    self.hash_generic_args(seg.args().args);
+                if let PathCheck::Resolution = self.path_check
+                    && let [.., last] = path.segments
+                    && let Some(segments) = generic_path_segments(path.segments)
+                {
+                    for seg in segments {
+                        self.hash_generic_args(seg.args().args);
+                    }
+                    last.res.hash(&mut self.s);
+                } else {
+                    for seg in path.segments {
+                        self.hash_name(seg.ident.name);
+                        self.hash_generic_args(seg.args().args);
+                    }
                 }
             },
         }
diff --git a/src/tools/clippy/clippy_utils/src/lib.rs b/src/tools/clippy/clippy_utils/src/lib.rs
index 10e258444a6..ad85dfa2d1e 100644
--- a/src/tools/clippy/clippy_utils/src/lib.rs
+++ b/src/tools/clippy/clippy_utils/src/lib.rs
@@ -3,6 +3,7 @@
 #![feature(f128)]
 #![feature(f16)]
 #![feature(if_let_guard)]
+#![feature(macro_metavar_expr_concat)]
 #![feature(let_chains)]
 #![feature(never_type)]
 #![feature(rustc_private)]
@@ -128,7 +129,6 @@ use crate::consts::{ConstEvalCtxt, Constant, mir_to_const};
 use crate::higher::Range;
 use crate::ty::{adt_and_variant_of_res, can_partially_move_ty, expr_sig, is_copy, is_recursively_primitive_type};
 use crate::visitors::for_each_expr_without_closures;
-
 use rustc_middle::hir::nested_filter;
 
 #[macro_export]
diff --git a/src/tools/clippy/clippy_utils/src/ty.rs b/src/tools/clippy/clippy_utils/src/ty.rs
index 21c2b19f4bd..07c3d0eada0 100644
--- a/src/tools/clippy/clippy_utils/src/ty.rs
+++ b/src/tools/clippy/clippy_utils/src/ty.rs
@@ -274,23 +274,10 @@ pub fn implements_trait_with_env_from_iter<'tcx>(
         .map(|arg| arg.into().unwrap_or_else(|| infcx.next_ty_var(DUMMY_SP).into()))
         .collect::<Vec<_>>();
 
-    // If an effect arg was not specified, we need to specify it.
-    let effect_arg = if tcx
-        .generics_of(trait_id)
-        .host_effect_index
-        .is_some_and(|x| args.get(x - 1).is_none())
-    {
-        Some(GenericArg::from(callee_id.map_or(tcx.consts.true_, |def_id| {
-            tcx.expected_host_effect_param_for_body(def_id)
-        })))
-    } else {
-        None
-    };
-
     let trait_ref = TraitRef::new(
         tcx,
         trait_id,
-        [GenericArg::from(ty)].into_iter().chain(args).chain(effect_arg),
+        [GenericArg::from(ty)].into_iter().chain(args),
     );
 
     debug_assert_matches!(
@@ -989,7 +976,7 @@ pub fn approx_ty_size<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> u64 {
         (Ok(size), _) => size,
         (Err(_), ty::Tuple(list)) => list.iter().map(|t| approx_ty_size(cx, t)).sum(),
         (Err(_), ty::Array(t, n)) => {
-            n.try_eval_target_usize(cx.tcx, cx.param_env).unwrap_or_default() * approx_ty_size(cx, *t)
+            n.try_to_target_usize(cx.tcx).unwrap_or_default() * approx_ty_size(cx, *t)
         },
         (Err(_), ty::Adt(def, subst)) if def.is_struct() => def
             .variants()
@@ -1168,7 +1155,7 @@ pub fn make_normalized_projection<'tcx>(
 pub struct InteriorMut<'tcx> {
     ignored_def_ids: FxHashSet<DefId>,
     ignore_pointers: bool,
-    tys: FxHashMap<Ty<'tcx>, Option<bool>>,
+    tys: FxHashMap<Ty<'tcx>, Option<&'tcx ty::List<Ty<'tcx>>>>,
 }
 
 impl<'tcx> InteriorMut<'tcx> {
@@ -1194,25 +1181,24 @@ impl<'tcx> InteriorMut<'tcx> {
         }
     }
 
-    /// Check if given type has inner mutability such as [`std::cell::Cell`] or
-    /// [`std::cell::RefCell`] etc.
-    pub fn is_interior_mut_ty(&mut self, cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool {
+    /// Check if given type has interior mutability such as [`std::cell::Cell`] or
+    /// [`std::cell::RefCell`] etc. and if it does, returns a chain of types that causes
+    /// this type to be interior mutable
+    pub fn interior_mut_ty_chain(&mut self, cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Option<&'tcx ty::List<Ty<'tcx>>> {
         match self.tys.entry(ty) {
-            Entry::Occupied(o) => return *o.get() == Some(true),
+            Entry::Occupied(o) => return *o.get(),
             // Temporarily insert a `None` to break cycles
             Entry::Vacant(v) => v.insert(None),
         };
 
-        let interior_mut = match *ty.kind() {
-            ty::RawPtr(inner_ty, _) if !self.ignore_pointers => self.is_interior_mut_ty(cx, inner_ty),
-            ty::Ref(_, inner_ty, _) | ty::Slice(inner_ty) => self.is_interior_mut_ty(cx, inner_ty),
-            ty::Array(inner_ty, size) => {
-                size.try_eval_target_usize(cx.tcx, cx.param_env)
-                    .map_or(true, |u| u != 0)
-                    && self.is_interior_mut_ty(cx, inner_ty)
+        let chain = match *ty.kind() {
+            ty::RawPtr(inner_ty, _) if !self.ignore_pointers => self.interior_mut_ty_chain(cx, inner_ty),
+            ty::Ref(_, inner_ty, _) | ty::Slice(inner_ty) => self.interior_mut_ty_chain(cx, inner_ty),
+            ty::Array(inner_ty, size) if size.try_to_target_usize(cx.tcx) != Some(0) => {
+                self.interior_mut_ty_chain(cx, inner_ty)
             },
-            ty::Tuple(fields) => fields.iter().any(|ty| self.is_interior_mut_ty(cx, ty)),
-            ty::Adt(def, _) if def.is_unsafe_cell() => true,
+            ty::Tuple(fields) => fields.iter().find_map(|ty| self.interior_mut_ty_chain(cx, ty)),
+            ty::Adt(def, _) if def.is_unsafe_cell() => Some(ty::List::empty()),
             ty::Adt(def, args) => {
                 let is_std_collection = matches!(
                     cx.tcx.get_diagnostic_name(def.did()),
@@ -1231,19 +1217,28 @@ impl<'tcx> InteriorMut<'tcx> {
 
                 if is_std_collection || def.is_box() {
                     // Include the types from std collections that are behind pointers internally
-                    args.types().any(|ty| self.is_interior_mut_ty(cx, ty))
+                    args.types().find_map(|ty| self.interior_mut_ty_chain(cx, ty))
                 } else if self.ignored_def_ids.contains(&def.did()) || def.is_phantom_data() {
-                    false
+                    None
                 } else {
                     def.all_fields()
-                        .any(|f| self.is_interior_mut_ty(cx, f.ty(cx.tcx, args)))
+                        .find_map(|f| self.interior_mut_ty_chain(cx, f.ty(cx.tcx, args)))
                 }
             },
-            _ => false,
+            _ => None,
         };
 
-        self.tys.insert(ty, Some(interior_mut));
-        interior_mut
+        chain.map(|chain| {
+            let list = cx.tcx.mk_type_list_from_iter(chain.iter().chain([ty]));
+            self.tys.insert(ty, Some(list));
+            list
+        })
+    }
+
+    /// Check if given type has interior mutability such as [`std::cell::Cell`] or
+    /// [`std::cell::RefCell`] etc.
+    pub fn is_interior_mut_ty(&mut self, cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool {
+        self.interior_mut_ty_chain(cx, ty).is_some()
     }
 }
 
diff --git a/src/tools/clippy/declare_clippy_lint/Cargo.toml b/src/tools/clippy/declare_clippy_lint/Cargo.toml
deleted file mode 100644
index 67a1f7cc72c..00000000000
--- a/src/tools/clippy/declare_clippy_lint/Cargo.toml
+++ /dev/null
@@ -1,13 +0,0 @@
-[package]
-name = "declare_clippy_lint"
-version = "0.1.83"
-edition = "2021"
-publish = false
-
-[lib]
-proc-macro = true
-
-[dependencies]
-itertools = "0.12"
-quote = "1.0.21"
-syn = "2.0"
diff --git a/src/tools/clippy/declare_clippy_lint/src/lib.rs b/src/tools/clippy/declare_clippy_lint/src/lib.rs
deleted file mode 100644
index fefc1a0a6c4..00000000000
--- a/src/tools/clippy/declare_clippy_lint/src/lib.rs
+++ /dev/null
@@ -1,182 +0,0 @@
-#![feature(let_chains, proc_macro_span)]
-// warn on lints, that are included in `rust-lang/rust`s bootstrap
-#![warn(rust_2018_idioms, unused_lifetimes)]
-
-use proc_macro::TokenStream;
-use quote::{format_ident, quote};
-use syn::parse::{Parse, ParseStream};
-use syn::{Attribute, Error, Expr, ExprLit, Ident, Lit, LitStr, Meta, Result, Token, parse_macro_input};
-
-fn parse_attr<const LEN: usize>(path: [&'static str; LEN], attr: &Attribute) -> Option<LitStr> {
-    if let Meta::NameValue(name_value) = &attr.meta {
-        let path_idents = name_value.path.segments.iter().map(|segment| &segment.ident);
-
-        if itertools::equal(path_idents, path)
-            && let Expr::Lit(ExprLit { lit: Lit::Str(s), .. }) = &name_value.value
-        {
-            return Some(s.clone());
-        }
-    }
-
-    None
-}
-
-struct ClippyLint {
-    attrs: Vec<Attribute>,
-    version: Option<LitStr>,
-    explanation: String,
-    name: Ident,
-    category: Ident,
-    description: LitStr,
-}
-
-impl Parse for ClippyLint {
-    fn parse(input: ParseStream<'_>) -> Result<Self> {
-        let attrs = input.call(Attribute::parse_outer)?;
-
-        let mut in_code = false;
-        let mut explanation = String::new();
-        let mut version = None;
-        for attr in &attrs {
-            if let Some(lit) = parse_attr(["doc"], attr) {
-                let value = lit.value();
-                let line = value.strip_prefix(' ').unwrap_or(&value);
-
-                if let Some(lang) = line.strip_prefix("```") {
-                    let tag = lang.split_once(',').map_or(lang, |(left, _)| left);
-                    if !in_code && matches!(tag, "" | "rust" | "ignore" | "should_panic" | "no_run" | "compile_fail") {
-                        explanation += "```rust\n";
-                    } else {
-                        explanation += line;
-                        explanation.push('\n');
-                    }
-                    in_code = !in_code;
-                } else if !(in_code && line.starts_with("# ")) {
-                    explanation += line;
-                    explanation.push('\n');
-                }
-            } else if let Some(lit) = parse_attr(["clippy", "version"], attr) {
-                if let Some(duplicate) = version.replace(lit) {
-                    return Err(Error::new_spanned(duplicate, "duplicate clippy::version"));
-                }
-            } else {
-                return Err(Error::new_spanned(attr, "unexpected attribute"));
-            }
-        }
-
-        input.parse::<Token![pub]>()?;
-        let name = input.parse()?;
-        input.parse::<Token![,]>()?;
-
-        let category = input.parse()?;
-        input.parse::<Token![,]>()?;
-
-        let description = input.parse()?;
-
-        Ok(Self {
-            attrs,
-            version,
-            explanation,
-            name,
-            category,
-            description,
-        })
-    }
-}
-
-/// Macro used to declare a Clippy lint.
-///
-/// Every lint declaration consists of 4 parts:
-///
-/// 1. The documentation, which is used for the website and `cargo clippy --explain`
-/// 2. The `LINT_NAME`. See [lint naming][lint_naming] on lint naming conventions.
-/// 3. The `lint_level`, which is a mapping from *one* of our lint groups to `Allow`, `Warn` or
-///    `Deny`. The lint level here has nothing to do with what lint groups the lint is a part of.
-/// 4. The `description` that contains a short explanation on what's wrong with code where the lint
-///    is triggered.
-///
-/// Currently the categories `style`, `correctness`, `suspicious`, `complexity` and `perf` are
-/// enabled by default. As said in the README.md of this repository, if the lint level mapping
-/// changes, please update README.md.
-///
-/// # Example
-///
-/// ```ignore
-/// use rustc_session::declare_tool_lint;
-///
-/// declare_clippy_lint! {
-///     /// ### What it does
-///     /// Checks for ... (describe what the lint matches).
-///     ///
-///     /// ### Why is this bad?
-///     /// Supply the reason for linting the code.
-///     ///
-///     /// ### Example
-///     /// ```rust
-///     /// Insert a short example of code that triggers the lint
-///     /// ```
-///     ///
-///     /// Use instead:
-///     /// ```rust
-///     /// Insert a short example of improved code that doesn't trigger the lint
-///     /// ```
-///     #[clippy::version = "1.65.0"]
-///     pub LINT_NAME,
-///     pedantic,
-///     "description"
-/// }
-/// ```
-/// [lint_naming]: https://rust-lang.github.io/rfcs/0344-conventions-galore.html#lints
-#[proc_macro]
-pub fn declare_clippy_lint(input: TokenStream) -> TokenStream {
-    let ClippyLint {
-        attrs,
-        version,
-        explanation,
-        name,
-        category,
-        description,
-    } = parse_macro_input!(input as ClippyLint);
-
-    let mut category = category.to_string();
-
-    let level = format_ident!("{}", match category.as_str() {
-        "correctness" => "Deny",
-        "style" | "suspicious" | "complexity" | "perf" => "Warn",
-        "pedantic" | "restriction" | "cargo" | "nursery" | "internal" => "Allow",
-        _ => panic!("unknown category {category}"),
-    },);
-
-    let info_name = format_ident!("{name}_INFO");
-
-    (&mut category[0..1]).make_ascii_uppercase();
-    let category_variant = format_ident!("{category}");
-
-    let name_span = name.span().unwrap();
-    let location = format!("{}#L{}", name_span.source_file().path().display(), name_span.line());
-
-    let version = match version {
-        Some(version) => quote!(Some(#version)),
-        None => quote!(None),
-    };
-
-    let output = quote! {
-        rustc_session::declare_tool_lint! {
-            #(#attrs)*
-            pub clippy::#name,
-            #level,
-            #description,
-            report_in_external_macro: true
-        }
-
-        pub(crate) static #info_name: &'static crate::LintInfo = &crate::LintInfo {
-            lint: &#name,
-            category: crate::LintCategory::#category_variant,
-            explanation: #explanation,
-            location: #location,
-            version: #version,
-        };
-    };
-
-    TokenStream::from(output)
-}
diff --git a/src/tools/clippy/lintcheck/ci_crates.toml b/src/tools/clippy/lintcheck/ci_crates.toml
index 9e3dbef6a9e..6299823451d 100644
--- a/src/tools/clippy/lintcheck/ci_crates.toml
+++ b/src/tools/clippy/lintcheck/ci_crates.toml
@@ -88,17 +88,17 @@ errno = { name = 'errno', version = '0.3.9' }
 uuid = { name = 'uuid', version = '1.10.0' }
 unicode-normalization = { name = 'unicode-normalization', version = '0.1.23' }
 ppv-lite86 = { name = 'ppv-lite86', version = '0.2.17' }
-futures-core = { name = 'futures-core', version = '0.3.30' }
+futures-core = { name = 'futures-core', version = '0.3.31' }
 http-body = { name = 'http-body', version = '1.0.1' }
 tinyvec = { name = 'tinyvec', version = '1.8.0' }
-futures-util = { name = 'futures-util', version = '0.3.30' }
-futures-task = { name = 'futures-task', version = '0.3.30' }
+futures-util = { name = 'futures-util', version = '0.3.31' }
+futures-task = { name = 'futures-task', version = '0.3.31' }
 sha2 = { name = 'sha2', version = '0.11.0-pre.3' }
 ring = { name = 'ring', version = '0.17.8' }
 slab = { name = 'slab', version = '0.4.9' }
 chrono = { name = 'chrono', version = '0.4.38' }
-futures-sink = { name = 'futures-sink', version = '0.3.30' }
-futures-channel = { name = 'futures-channel', version = '0.3.30' }
+futures-sink = { name = 'futures-sink', version = '0.3.31' }
+futures-channel = { name = 'futures-channel', version = '0.3.31' }
 num_cpus = { name = 'num_cpus', version = '1.16.0' }
 untrusted = { name = 'untrusted', version = '0.9.0' }
 tinyvec_macros = { name = 'tinyvec_macros', version = '0.1.1' }
@@ -106,7 +106,7 @@ mio = { name = 'mio', version = '1.0.0' }
 byteorder = { name = 'byteorder', version = '1.5.0' }
 form_urlencoded = { name = 'form_urlencoded', version = '1.2.1' }
 unicode-bidi = { name = 'unicode-bidi', version = '0.3.15' }
-futures-io = { name = 'futures-io', version = '0.3.30' }
+futures-io = { name = 'futures-io', version = '0.3.31' }
 tokio-util = { name = 'tokio-util', version = '0.7.11' }
 rustls-pemfile = { name = 'rustls-pemfile', version = '2.1.2' }
 generic-array = { name = 'generic-array', version = '1.1.0' }
@@ -116,7 +116,7 @@ tracing-core = { name = 'tracing-core', version = '0.1.32' }
 pin-utils = { name = 'pin-utils', version = '0.1.0' }
 tempfile = { name = 'tempfile', version = '3.10.1' }
 h2 = { name = 'h2', version = '0.4.5' }
-futures = { name = 'futures', version = '0.3.30' }
+futures = { name = 'futures', version = '0.3.31' }
 typenum = { name = 'typenum', version = '1.17.0' }
 winnow = { name = 'winnow', version = '0.6.13' }
 cpufeatures = { name = 'cpufeatures', version = '0.2.12' }
@@ -131,9 +131,9 @@ pkg-config = { name = 'pkg-config', version = '0.3.30' }
 redox_syscall = { name = 'redox_syscall', version = '0.5.3' }
 nom = { name = 'nom', version = '8.0.0-alpha2' }
 rustc_version = { name = 'rustc_version', version = '0.4.0' }
-futures-macro = { name = 'futures-macro', version = '0.3.30' }
+futures-macro = { name = 'futures-macro', version = '0.3.31' }
 clap_derive = { name = 'clap_derive', version = '4.5.8' }
-futures-executor = { name = 'futures-executor', version = '0.3.30' }
+futures-executor = { name = 'futures-executor', version = '0.3.31' }
 event-listener = { name = 'event-listener', version = '5.3.1' }
 num-integer = { name = 'num-integer', version = '0.1.46' }
 time-macros = { name = 'time-macros', version = '0.2.18' }
diff --git a/src/tools/clippy/lintcheck/src/json.rs b/src/tools/clippy/lintcheck/src/json.rs
index ee0c80aea52..3a68f2c9243 100644
--- a/src/tools/clippy/lintcheck/src/json.rs
+++ b/src/tools/clippy/lintcheck/src/json.rs
@@ -133,7 +133,7 @@ fn print_lint_warnings(lint: &LintWarnings, truncate_after: usize) {
     println!();
 
     print!(
-        r##"{}, {}, {}"##,
+        r"{}, {}, {}",
         count_string(name, "added", lint.added.len()),
         count_string(name, "removed", lint.removed.len()),
         count_string(name, "changed", lint.changed.len()),
diff --git a/src/tools/clippy/rinja.toml b/src/tools/clippy/rinja.toml
new file mode 100644
index 00000000000..a10da6e1f28
--- /dev/null
+++ b/src/tools/clippy/rinja.toml
@@ -0,0 +1,3 @@
+[general]
+dirs = ["util/gh-pages/"]
+whitespace = "suppress"
diff --git a/src/tools/clippy/rust-toolchain b/src/tools/clippy/rust-toolchain
index f0c8651efce..e11ab40b9de 100644
--- a/src/tools/clippy/rust-toolchain
+++ b/src/tools/clippy/rust-toolchain
@@ -1,4 +1,4 @@
 [toolchain]
-channel = "nightly-2024-10-03"
+channel = "nightly-2024-10-18"
 components = ["cargo", "llvm-tools", "rust-src", "rust-std", "rustc", "rustc-dev", "rustfmt"]
 profile = "minimal"
diff --git a/src/tools/clippy/rustc_tools_util/CHANGELOG.md b/src/tools/clippy/rustc_tools_util/CHANGELOG.md
index 1b351da2e7b..7f628178ea6 100644
--- a/src/tools/clippy/rustc_tools_util/CHANGELOG.md
+++ b/src/tools/clippy/rustc_tools_util/CHANGELOG.md
@@ -1,5 +1,14 @@
 # Changelog
 
+## Version 0.4.0
+
+* The commit hashes are now always 10 characters long [#13222](https://github.com/rust-lang/rust-clippy/pull/13222)
+* `get_commit_date` and `get_commit_hash` now return `None` if the `git` command fails instead of `Some("")`
+  [#13217](https://github.com/rust-lang/rust-clippy/pull/13217)
+* `setup_version_info` will now re-run when the git commit changes
+  [#13329](https://github.com/rust-lang/rust-clippy/pull/13329)
+* New `rerun_if_git_changes` function was added [#13329](https://github.com/rust-lang/rust-clippy/pull/13329)
+
 ## Version 0.3.0
 
 * Added `setup_version_info!();` macro for automated scripts.
diff --git a/src/tools/clippy/rustc_tools_util/Cargo.toml b/src/tools/clippy/rustc_tools_util/Cargo.toml
index 37b592da132..b63632916ba 100644
--- a/src/tools/clippy/rustc_tools_util/Cargo.toml
+++ b/src/tools/clippy/rustc_tools_util/Cargo.toml
@@ -1,6 +1,6 @@
 [package]
 name = "rustc_tools_util"
-version = "0.3.0"
+version = "0.4.0"
 description = "small helper to generate version information for git packages"
 repository = "https://github.com/rust-lang/rust-clippy"
 readme = "README.md"
diff --git a/src/tools/clippy/rustc_tools_util/README.md b/src/tools/clippy/rustc_tools_util/README.md
index 56f62b867a6..1b11dfe0619 100644
--- a/src/tools/clippy/rustc_tools_util/README.md
+++ b/src/tools/clippy/rustc_tools_util/README.md
@@ -13,10 +13,10 @@ build = "build.rs"
 List rustc_tools_util as regular AND build dependency.
 ````toml
 [dependencies]
-rustc_tools_util = "0.3.0"
+rustc_tools_util = "0.4.0"
 
 [build-dependencies]
-rustc_tools_util = "0.3.0"
+rustc_tools_util = "0.4.0"
 ````
 
 In `build.rs`, generate the data in your `main()`
diff --git a/src/tools/clippy/rustc_tools_util/src/lib.rs b/src/tools/clippy/rustc_tools_util/src/lib.rs
index 2cc38130472..16be02f4a40 100644
--- a/src/tools/clippy/rustc_tools_util/src/lib.rs
+++ b/src/tools/clippy/rustc_tools_util/src/lib.rs
@@ -1,3 +1,5 @@
+use std::path::PathBuf;
+use std::process::Command;
 use std::str;
 
 /// This macro creates the version string during compilation from the
@@ -32,6 +34,7 @@ macro_rules! get_version_info {
 #[macro_export]
 macro_rules! setup_version_info {
     () => {{
+        let _ = $crate::rerun_if_git_changes();
         println!(
             "cargo:rustc-env=GIT_HASH={}",
             $crate::get_commit_hash().unwrap_or_default()
@@ -100,24 +103,52 @@ impl std::fmt::Debug for VersionInfo {
 }
 
 #[must_use]
-pub fn get_commit_hash() -> Option<String> {
-    let output = std::process::Command::new("git")
-        .args(["rev-parse", "HEAD"])
-        .output()
-        .ok()?;
+fn get_output(cmd: &str, args: &[&str]) -> Option<String> {
+    let output = Command::new(cmd).args(args).output().ok()?;
     let mut stdout = output.status.success().then_some(output.stdout)?;
-    stdout.truncate(10);
+    // Remove trailing newlines.
+    while stdout.last().copied() == Some(b'\n') {
+        stdout.pop();
+    }
     String::from_utf8(stdout).ok()
 }
 
 #[must_use]
+pub fn rerun_if_git_changes() -> Option<()> {
+    // Make sure we get rerun when the git commit changes.
+    // We want to watch two files: HEAD, which tracks which branch we are on,
+    // and the file for that branch that tracks which commit is is on.
+
+    // First, find the `HEAD` file. This should work even with worktrees.
+    let git_head_file = PathBuf::from(get_output("git", &["rev-parse", "--git-path", "HEAD"])?);
+    if git_head_file.exists() {
+        println!("cargo::rerun-if-changed={}", git_head_file.display());
+    }
+
+    // Determine the name of the current ref.
+    // This will quit if HEAD is detached.
+    let git_head_ref = get_output("git", &["symbolic-ref", "-q", "HEAD"])?;
+    // Ask git where this ref is stored.
+    let git_head_ref_file = PathBuf::from(get_output("git", &["rev-parse", "--git-path", &git_head_ref])?);
+    // If this ref is packed, the file does not exist. However, the checked-out branch is never (?)
+    // packed, so we should always be able to find this file.
+    if git_head_ref_file.exists() {
+        println!("cargo::rerun-if-changed={}", git_head_ref_file.display());
+    }
+
+    Some(())
+}
+
+#[must_use]
+pub fn get_commit_hash() -> Option<String> {
+    let mut stdout = get_output("git", &["rev-parse", "HEAD"])?;
+    stdout.truncate(10);
+    Some(stdout)
+}
+
+#[must_use]
 pub fn get_commit_date() -> Option<String> {
-    let output = std::process::Command::new("git")
-        .args(["log", "-1", "--date=short", "--pretty=format:%cd"])
-        .output()
-        .ok()?;
-    let stdout = output.status.success().then_some(output.stdout)?;
-    String::from_utf8(stdout).ok()
+    get_output("git", &["log", "-1", "--date=short", "--pretty=format:%cd"])
 }
 
 #[must_use]
@@ -127,15 +158,11 @@ pub fn get_channel() -> String {
     }
 
     // if that failed, try to ask rustc -V, do some parsing and find out
-    if let Ok(output) = std::process::Command::new("rustc").arg("-V").output() {
-        if output.status.success() {
-            if let Ok(rustc_output) = str::from_utf8(&output.stdout) {
-                if rustc_output.contains("beta") {
-                    return String::from("beta");
-                } else if rustc_output.contains("stable") {
-                    return String::from("stable");
-                }
-            }
+    if let Some(rustc_output) = get_output("rustc", &["-V"]) {
+        if rustc_output.contains("beta") {
+            return String::from("beta");
+        } else if rustc_output.contains("stable") {
+            return String::from("stable");
         }
     }
 
@@ -151,7 +178,7 @@ mod test {
     fn test_struct_local() {
         let vi = get_version_info!();
         assert_eq!(vi.major, 0);
-        assert_eq!(vi.minor, 3);
+        assert_eq!(vi.minor, 4);
         assert_eq!(vi.patch, 0);
         assert_eq!(vi.crate_name, "rustc_tools_util");
         // hard to make positive tests for these since they will always change
@@ -162,7 +189,7 @@ mod test {
     #[test]
     fn test_display_local() {
         let vi = get_version_info!();
-        assert_eq!(vi.to_string(), "rustc_tools_util 0.3.0");
+        assert_eq!(vi.to_string(), "rustc_tools_util 0.4.0");
     }
 
     #[test]
@@ -171,7 +198,7 @@ mod test {
         let s = format!("{vi:?}");
         assert_eq!(
             s,
-            "VersionInfo { crate_name: \"rustc_tools_util\", major: 0, minor: 3, patch: 0 }"
+            "VersionInfo { crate_name: \"rustc_tools_util\", major: 0, minor: 4, patch: 0 }"
         );
     }
 }
diff --git a/src/tools/clippy/tests/compile-test.rs b/src/tools/clippy/tests/compile-test.rs
index af2aa519257..5774e20e0be 100644
--- a/src/tools/clippy/tests/compile-test.rs
+++ b/src/tools/clippy/tests/compile-test.rs
@@ -8,7 +8,10 @@ use clippy_config::ClippyConfiguration;
 use clippy_lints::LintInfo;
 use clippy_lints::declared_lints::LINTS;
 use clippy_lints::deprecated_lints::{DEPRECATED, DEPRECATED_VERSION, RENAMED};
-use serde::{Deserialize, Serialize};
+use pulldown_cmark::{Options, Parser, html};
+use rinja::Template;
+use rinja::filters::Safe;
+use serde::Deserialize;
 use test_utils::IS_RUSTC_TEST_SUITE;
 use ui_test::custom_flags::Flag;
 use ui_test::custom_flags::rustfix::RustfixMode;
@@ -385,6 +388,22 @@ fn ui_cargo_toml_metadata() {
     }
 }
 
+#[derive(Template)]
+#[template(path = "index_template.html")]
+struct Renderer<'a> {
+    lints: &'a Vec<LintMetadata>,
+}
+
+impl Renderer<'_> {
+    fn markdown(input: &str) -> Safe<String> {
+        let parser = Parser::new_ext(input, Options::all());
+        let mut html_output = String::new();
+        html::push_html(&mut html_output, parser);
+        // Oh deer, what a hack :O
+        Safe(html_output.replace("<table", "<table class=\"table\""))
+    }
+}
+
 #[derive(Deserialize)]
 #[serde(untagged)]
 enum DiagnosticOrMessage {
@@ -445,10 +464,14 @@ impl DiagnosticCollector {
                         .map(|((lint, reason), version)| LintMetadata::new_deprecated(lint, reason, version)),
                 )
                 .collect();
+
             metadata.sort_unstable_by(|a, b| a.id.cmp(&b.id));
 
-            let json = serde_json::to_string_pretty(&metadata).unwrap();
-            fs::write("util/gh-pages/lints.json", json).unwrap();
+            fs::write(
+                "util/gh-pages/index.html",
+                Renderer { lints: &metadata }.render().unwrap(),
+            )
+            .unwrap();
         });
 
         (Self { sender }, handle)
@@ -487,7 +510,7 @@ impl Flag for DiagnosticCollector {
     }
 }
 
-#[derive(Debug, Serialize)]
+#[derive(Debug)]
 struct LintMetadata {
     id: String,
     id_location: Option<&'static str>,
@@ -559,4 +582,14 @@ impl LintMetadata {
             applicability: Applicability::Unspecified,
         }
     }
+
+    fn applicability_str(&self) -> &str {
+        match self.applicability {
+            Applicability::MachineApplicable => "MachineApplicable",
+            Applicability::HasPlaceholders => "HasPlaceholders",
+            Applicability::MaybeIncorrect => "MaybeIncorrect",
+            Applicability::Unspecified => "Unspecified",
+            _ => panic!("needs to update this code"),
+        }
+    }
 }
diff --git a/src/tools/clippy/tests/config-metadata.rs b/src/tools/clippy/tests/config-metadata.rs
index 628dfc8f758..af9fe064dc7 100644
--- a/src/tools/clippy/tests/config-metadata.rs
+++ b/src/tools/clippy/tests/config-metadata.rs
@@ -20,7 +20,7 @@ fn book() {
 
     let configs = metadata().map(|conf| conf.to_markdown_paragraph()).join("\n");
     let expected = format!(
-        r#"<!--
+        r"<!--
 This file is generated by `cargo bless --test config-metadata`.
 Please use that command to update the file and do not edit it by hand.
 -->
@@ -33,7 +33,7 @@ and lints affected.
 ---
 
 {}
-"#,
+",
         configs.trim(),
     );
 
diff --git a/src/tools/clippy/tests/ui-toml/array_size_threshold/array_size_threshold.stderr b/src/tools/clippy/tests/ui-toml/array_size_threshold/array_size_threshold.stderr
index 009153bc4a1..41cb85b67df 100644
--- a/src/tools/clippy/tests/ui-toml/array_size_threshold/array_size_threshold.stderr
+++ b/src/tools/clippy/tests/ui-toml/array_size_threshold/array_size_threshold.stderr
@@ -10,22 +10,14 @@ LL | const ABOVE: [u8; 11] = [0; 11];
    = help: to override `-D warnings` add `#[allow(clippy::large_const_arrays)]`
 
 error: allocating a local array larger than 10 bytes
-  --> tests/ui-toml/array_size_threshold/array_size_threshold.rs:4:25
-   |
-LL | const ABOVE: [u8; 11] = [0; 11];
-   |                         ^^^^^^^
-   |
-   = help: consider allocating on the heap with `vec![0; 11].into_boxed_slice()`
-   = note: `-D clippy::large-stack-arrays` implied by `-D warnings`
-   = help: to override `-D warnings` add `#[allow(clippy::large_stack_arrays)]`
-
-error: allocating a local array larger than 10 bytes
   --> tests/ui-toml/array_size_threshold/array_size_threshold.rs:8:17
    |
 LL |     let above = [0u8; 11];
    |                 ^^^^^^^^^
    |
    = help: consider allocating on the heap with `vec![0u8; 11].into_boxed_slice()`
+   = note: `-D clippy::large-stack-arrays` implied by `-D warnings`
+   = help: to override `-D warnings` add `#[allow(clippy::large_stack_arrays)]`
 
-error: aborting due to 3 previous errors
+error: aborting due to 2 previous errors
 
diff --git a/src/tools/clippy/tests/ui-toml/item_name_repetitions/allowed_prefixes/item_name_repetitions.rs b/src/tools/clippy/tests/ui-toml/item_name_repetitions/allowed_prefixes/item_name_repetitions.rs
index 4142ced5f6b..2ae673a6def 100644
--- a/src/tools/clippy/tests/ui-toml/item_name_repetitions/allowed_prefixes/item_name_repetitions.rs
+++ b/src/tools/clippy/tests/ui-toml/item_name_repetitions/allowed_prefixes/item_name_repetitions.rs
@@ -1,7 +1,7 @@
 #![warn(clippy::module_name_repetitions)]
 #![allow(dead_code)]
 
-mod foo {
+pub mod foo {
     // #12544 - shouldn't warn if item name consists only of an allowed prefix and a module name.
     // In this test, allowed prefixes are configured to be ["bar"].
 
diff --git a/src/tools/clippy/tests/ui-toml/item_name_repetitions/allowed_prefixes_extend/item_name_repetitions.rs b/src/tools/clippy/tests/ui-toml/item_name_repetitions/allowed_prefixes_extend/item_name_repetitions.rs
index b132305d01c..dbd61992c0d 100644
--- a/src/tools/clippy/tests/ui-toml/item_name_repetitions/allowed_prefixes_extend/item_name_repetitions.rs
+++ b/src/tools/clippy/tests/ui-toml/item_name_repetitions/allowed_prefixes_extend/item_name_repetitions.rs
@@ -1,7 +1,7 @@
 #![warn(clippy::module_name_repetitions)]
 #![allow(dead_code)]
 
-mod foo {
+pub mod foo {
     // #12544 - shouldn't warn if item name consists only of an allowed prefix and a module name.
     // In this test, allowed prefixes are configured to be all of the default prefixes and ["bar"].
 
diff --git a/src/tools/clippy/tests/ui/expect_fun_call.stderr b/src/tools/clippy/tests/ui/expect_fun_call.stderr
index bae853ac5c1..050c039f834 100644
--- a/src/tools/clippy/tests/ui/expect_fun_call.stderr
+++ b/src/tools/clippy/tests/ui/expect_fun_call.stderr
@@ -1,4 +1,4 @@
-error: use of `expect` followed by a function call
+error: function call inside of `expect`
   --> tests/ui/expect_fun_call.rs:35:26
    |
 LL |     with_none_and_format.expect(&format!("Error {}: fake error", error_code));
@@ -7,85 +7,85 @@ LL |     with_none_and_format.expect(&format!("Error {}: fake error", error_code
    = note: `-D clippy::expect-fun-call` implied by `-D warnings`
    = help: to override `-D warnings` add `#[allow(clippy::expect_fun_call)]`
 
-error: use of `expect` followed by a function call
+error: function call inside of `expect`
   --> tests/ui/expect_fun_call.rs:38:26
    |
 LL |     with_none_and_as_str.expect(format!("Error {}: fake error", error_code).as_str());
    |                          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(|| panic!("Error {}: fake error", error_code))`
 
-error: use of `expect` followed by a function call
+error: function call inside of `expect`
   --> tests/ui/expect_fun_call.rs:41:37
    |
 LL |     with_none_and_format_with_macro.expect(format!("Error {}: fake error", one!()).as_str());
    |                                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(|| panic!("Error {}: fake error", one!()))`
 
-error: use of `expect` followed by a function call
+error: function call inside of `expect`
   --> tests/ui/expect_fun_call.rs:51:25
    |
 LL |     with_err_and_format.expect(&format!("Error {}: fake error", error_code));
    |                         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(|_| panic!("Error {}: fake error", error_code))`
 
-error: use of `expect` followed by a function call
+error: function call inside of `expect`
   --> tests/ui/expect_fun_call.rs:54:25
    |
 LL |     with_err_and_as_str.expect(format!("Error {}: fake error", error_code).as_str());
    |                         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(|_| panic!("Error {}: fake error", error_code))`
 
-error: use of `expect` followed by a function call
+error: function call inside of `expect`
   --> tests/ui/expect_fun_call.rs:66:17
    |
 LL |     Some("foo").expect(format!("{} {}", 1, 2).as_ref());
    |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(|| panic!("{} {}", 1, 2))`
 
-error: use of `expect` followed by a function call
+error: function call inside of `expect`
   --> tests/ui/expect_fun_call.rs:87:21
    |
 LL |         Some("foo").expect(&get_string());
    |                     ^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(|| { panic!("{}", get_string()) })`
 
-error: use of `expect` followed by a function call
+error: function call inside of `expect`
   --> tests/ui/expect_fun_call.rs:88:21
    |
 LL |         Some("foo").expect(get_string().as_ref());
    |                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(|| { panic!("{}", get_string()) })`
 
-error: use of `expect` followed by a function call
+error: function call inside of `expect`
   --> tests/ui/expect_fun_call.rs:89:21
    |
 LL |         Some("foo").expect(get_string().as_str());
    |                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(|| { panic!("{}", get_string()) })`
 
-error: use of `expect` followed by a function call
+error: function call inside of `expect`
   --> tests/ui/expect_fun_call.rs:91:21
    |
 LL |         Some("foo").expect(get_static_str());
    |                     ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(|| { panic!("{}", get_static_str()) })`
 
-error: use of `expect` followed by a function call
+error: function call inside of `expect`
   --> tests/ui/expect_fun_call.rs:92:21
    |
 LL |         Some("foo").expect(get_non_static_str(&0));
    |                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(|| { panic!("{}", get_non_static_str(&0).to_string()) })`
 
-error: use of `expect` followed by a function call
+error: function call inside of `expect`
   --> tests/ui/expect_fun_call.rs:96:16
    |
 LL |     Some(true).expect(&format!("key {}, {}", 1, 2));
    |                ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(|| panic!("key {}, {}", 1, 2))`
 
-error: use of `expect` followed by a function call
+error: function call inside of `expect`
   --> tests/ui/expect_fun_call.rs:102:17
    |
 LL |         opt_ref.expect(&format!("{:?}", opt_ref));
    |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(|| panic!("{:?}", opt_ref))`
 
-error: use of `expect` followed by a function call
+error: function call inside of `expect`
   --> tests/ui/expect_fun_call.rs:106:20
    |
 LL |     format_capture.expect(&format!("{error_code}"));
    |                    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(|| panic!("{error_code}"))`
 
-error: use of `expect` followed by a function call
+error: function call inside of `expect`
   --> tests/ui/expect_fun_call.rs:109:30
    |
 LL |     format_capture_and_value.expect(&format!("{error_code}, {}", 1));
diff --git a/src/tools/clippy/tests/ui/implicit_saturating_sub.fixed b/src/tools/clippy/tests/ui/implicit_saturating_sub.fixed
index 81cc1494914..136238f9eca 100644
--- a/src/tools/clippy/tests/ui/implicit_saturating_sub.fixed
+++ b/src/tools/clippy/tests/ui/implicit_saturating_sub.fixed
@@ -222,3 +222,9 @@ fn main() {
         a - b
     };
 }
+
+fn regression_13524(a: usize, b: usize, c: bool) -> usize {
+    if c {
+        123
+    } else { b.saturating_sub(a) }
+}
diff --git a/src/tools/clippy/tests/ui/implicit_saturating_sub.rs b/src/tools/clippy/tests/ui/implicit_saturating_sub.rs
index f73396ebd27..e371e37fb2f 100644
--- a/src/tools/clippy/tests/ui/implicit_saturating_sub.rs
+++ b/src/tools/clippy/tests/ui/implicit_saturating_sub.rs
@@ -268,3 +268,13 @@ fn main() {
         a - b
     };
 }
+
+fn regression_13524(a: usize, b: usize, c: bool) -> usize {
+    if c {
+        123
+    } else if a >= b {
+        0
+    } else {
+        b - a
+    }
+}
diff --git a/src/tools/clippy/tests/ui/implicit_saturating_sub.stderr b/src/tools/clippy/tests/ui/implicit_saturating_sub.stderr
index 59a9ddbff2d..61319851228 100644
--- a/src/tools/clippy/tests/ui/implicit_saturating_sub.stderr
+++ b/src/tools/clippy/tests/ui/implicit_saturating_sub.stderr
@@ -185,5 +185,16 @@ LL | |         i_64 -= 1;
 LL | |     }
    | |_____^ help: try: `i_64 = i_64.saturating_sub(1);`
 
-error: aborting due to 23 previous errors
+error: manual arithmetic check found
+  --> tests/ui/implicit_saturating_sub.rs:275:12
+   |
+LL |       } else if a >= b {
+   |  ____________^
+LL | |         0
+LL | |     } else {
+LL | |         b - a
+LL | |     }
+   | |_____^ help: replace it with: `{ b.saturating_sub(a) }`
+
+error: aborting due to 24 previous errors
 
diff --git a/src/tools/clippy/tests/ui/infinite_loops.rs b/src/tools/clippy/tests/ui/infinite_loops.rs
index b2d522fa011..b6cb7ff49b0 100644
--- a/src/tools/clippy/tests/ui/infinite_loops.rs
+++ b/src/tools/clippy/tests/ui/infinite_loops.rs
@@ -390,4 +390,42 @@ fn span_inside_fn() {
     }
 }
 
+fn continue_outer() {
+    // Should not lint (issue #13511)
+    let mut count = 0;
+    'outer: loop {
+        if count != 0 {
+            break;
+        }
+
+        loop {
+            count += 1;
+            continue 'outer;
+        }
+    }
+
+    // This should lint as we continue the loop itself
+    'infinite: loop {
+        //~^ ERROR: infinite loop detected
+        loop {
+            continue 'infinite;
+        }
+    }
+    // This should lint as we continue an inner loop
+    loop {
+        //~^ ERROR: infinite loop detected
+        'inner: loop {
+            loop {
+                continue 'inner;
+            }
+        }
+    }
+
+    // This should lint as we continue the loop itself
+    loop {
+        //~^ ERROR: infinite loop detected
+        continue;
+    }
+}
+
 fn main() {}
diff --git a/src/tools/clippy/tests/ui/infinite_loops.stderr b/src/tools/clippy/tests/ui/infinite_loops.stderr
index ec6bd81dc17..7635a7442f4 100644
--- a/src/tools/clippy/tests/ui/infinite_loops.stderr
+++ b/src/tools/clippy/tests/ui/infinite_loops.stderr
@@ -255,5 +255,67 @@ LL | |     })
    |
    = help: if this is not intended, try adding a `break` or `return` condition in the loop
 
-error: aborting due to 17 previous errors
+error: infinite loop detected
+  --> tests/ui/infinite_loops.rs:408:5
+   |
+LL | /     'infinite: loop {
+LL | |
+LL | |         loop {
+LL | |             continue 'infinite;
+LL | |         }
+LL | |     }
+   | |_____^
+   |
+help: if this is intentional, consider specifying `!` as function return
+   |
+LL | fn continue_outer() -> ! {
+   |                     ++++
+
+error: infinite loop detected
+  --> tests/ui/infinite_loops.rs:415:5
+   |
+LL | /     loop {
+LL | |
+LL | |         'inner: loop {
+LL | |             loop {
+...  |
+LL | |         }
+LL | |     }
+   | |_____^
+   |
+help: if this is intentional, consider specifying `!` as function return
+   |
+LL | fn continue_outer() -> ! {
+   |                     ++++
+
+error: infinite loop detected
+  --> tests/ui/infinite_loops.rs:417:9
+   |
+LL | /         'inner: loop {
+LL | |             loop {
+LL | |                 continue 'inner;
+LL | |             }
+LL | |         }
+   | |_________^
+   |
+help: if this is intentional, consider specifying `!` as function return
+   |
+LL | fn continue_outer() -> ! {
+   |                     ++++
+
+error: infinite loop detected
+  --> tests/ui/infinite_loops.rs:425:5
+   |
+LL | /     loop {
+LL | |
+LL | |         continue;
+LL | |     }
+   | |_____^
+   |
+help: if this is intentional, consider specifying `!` as function return
+   |
+LL | fn continue_outer() -> ! {
+   |                     ++++
+
+error: aborting due to 21 previous errors
 
diff --git a/src/tools/clippy/tests/ui/invalid_null_ptr_usage.fixed b/src/tools/clippy/tests/ui/invalid_null_ptr_usage.fixed
index 092e875a255..ba225102c98 100644
--- a/src/tools/clippy/tests/ui/invalid_null_ptr_usage.fixed
+++ b/src/tools/clippy/tests/ui/invalid_null_ptr_usage.fixed
@@ -1,44 +1,44 @@
 fn main() {
     unsafe {
-        let _slice: &[usize] = std::slice::from_raw_parts(core::ptr::NonNull::dangling().as_ptr(), 0);
-        let _slice: &[usize] = std::slice::from_raw_parts(core::ptr::NonNull::dangling().as_ptr(), 0);
+        let _slice: &[usize] = std::slice::from_raw_parts(std::ptr::NonNull::dangling().as_ptr(), 0);
+        let _slice: &[usize] = std::slice::from_raw_parts(std::ptr::NonNull::dangling().as_ptr(), 0);
 
-        let _slice: &[usize] = std::slice::from_raw_parts_mut(core::ptr::NonNull::dangling().as_ptr(), 0);
+        let _slice: &[usize] = std::slice::from_raw_parts_mut(std::ptr::NonNull::dangling().as_ptr(), 0);
 
-        std::ptr::copy::<usize>(core::ptr::NonNull::dangling().as_ptr(), std::ptr::NonNull::dangling().as_ptr(), 0);
-        std::ptr::copy::<usize>(std::ptr::NonNull::dangling().as_ptr(), core::ptr::NonNull::dangling().as_ptr(), 0);
+        std::ptr::copy::<usize>(std::ptr::NonNull::dangling().as_ptr(), std::ptr::NonNull::dangling().as_ptr(), 0);
+        std::ptr::copy::<usize>(std::ptr::NonNull::dangling().as_ptr(), std::ptr::NonNull::dangling().as_ptr(), 0);
 
-        std::ptr::copy_nonoverlapping::<usize>(core::ptr::NonNull::dangling().as_ptr(), std::ptr::NonNull::dangling().as_ptr(), 0);
-        std::ptr::copy_nonoverlapping::<usize>(std::ptr::NonNull::dangling().as_ptr(), core::ptr::NonNull::dangling().as_ptr(), 0);
+        std::ptr::copy_nonoverlapping::<usize>(std::ptr::NonNull::dangling().as_ptr(), std::ptr::NonNull::dangling().as_ptr(), 0);
+        std::ptr::copy_nonoverlapping::<usize>(std::ptr::NonNull::dangling().as_ptr(), std::ptr::NonNull::dangling().as_ptr(), 0);
 
         struct A; // zero sized struct
         assert_eq!(std::mem::size_of::<A>(), 0);
 
-        let _a: A = std::ptr::read(core::ptr::NonNull::dangling().as_ptr());
-        let _a: A = std::ptr::read(core::ptr::NonNull::dangling().as_ptr());
+        let _a: A = std::ptr::read(std::ptr::NonNull::dangling().as_ptr());
+        let _a: A = std::ptr::read(std::ptr::NonNull::dangling().as_ptr());
 
-        let _a: A = std::ptr::read_unaligned(core::ptr::NonNull::dangling().as_ptr());
-        let _a: A = std::ptr::read_unaligned(core::ptr::NonNull::dangling().as_ptr());
+        let _a: A = std::ptr::read_unaligned(std::ptr::NonNull::dangling().as_ptr());
+        let _a: A = std::ptr::read_unaligned(std::ptr::NonNull::dangling().as_ptr());
 
-        let _a: A = std::ptr::read_volatile(core::ptr::NonNull::dangling().as_ptr());
-        let _a: A = std::ptr::read_volatile(core::ptr::NonNull::dangling().as_ptr());
+        let _a: A = std::ptr::read_volatile(std::ptr::NonNull::dangling().as_ptr());
+        let _a: A = std::ptr::read_volatile(std::ptr::NonNull::dangling().as_ptr());
 
-        let _a: A = std::ptr::replace(core::ptr::NonNull::dangling().as_ptr(), A);
+        let _a: A = std::ptr::replace(std::ptr::NonNull::dangling().as_ptr(), A);
         let _slice: *const [usize] = std::ptr::slice_from_raw_parts(std::ptr::null_mut(), 0); // shouldn't lint
         let _slice: *const [usize] = std::ptr::slice_from_raw_parts_mut(std::ptr::null_mut(), 0);
 
-        std::ptr::swap::<A>(core::ptr::NonNull::dangling().as_ptr(), &mut A);
-        std::ptr::swap::<A>(&mut A, core::ptr::NonNull::dangling().as_ptr());
+        std::ptr::swap::<A>(std::ptr::NonNull::dangling().as_ptr(), &mut A);
+        std::ptr::swap::<A>(&mut A, std::ptr::NonNull::dangling().as_ptr());
 
-        std::ptr::swap_nonoverlapping::<A>(core::ptr::NonNull::dangling().as_ptr(), &mut A, 0);
-        std::ptr::swap_nonoverlapping::<A>(&mut A, core::ptr::NonNull::dangling().as_ptr(), 0);
+        std::ptr::swap_nonoverlapping::<A>(std::ptr::NonNull::dangling().as_ptr(), &mut A, 0);
+        std::ptr::swap_nonoverlapping::<A>(&mut A, std::ptr::NonNull::dangling().as_ptr(), 0);
 
-        std::ptr::write(core::ptr::NonNull::dangling().as_ptr(), A);
+        std::ptr::write(std::ptr::NonNull::dangling().as_ptr(), A);
 
-        std::ptr::write_unaligned(core::ptr::NonNull::dangling().as_ptr(), A);
+        std::ptr::write_unaligned(std::ptr::NonNull::dangling().as_ptr(), A);
 
-        std::ptr::write_volatile(core::ptr::NonNull::dangling().as_ptr(), A);
+        std::ptr::write_volatile(std::ptr::NonNull::dangling().as_ptr(), A);
 
-        std::ptr::write_bytes::<usize>(core::ptr::NonNull::dangling().as_ptr(), 42, 0);
+        std::ptr::write_bytes::<usize>(std::ptr::NonNull::dangling().as_ptr(), 42, 0);
     }
 }
diff --git a/src/tools/clippy/tests/ui/invalid_null_ptr_usage.stderr b/src/tools/clippy/tests/ui/invalid_null_ptr_usage.stderr
index a0be2c0ad75..613a2cc3688 100644
--- a/src/tools/clippy/tests/ui/invalid_null_ptr_usage.stderr
+++ b/src/tools/clippy/tests/ui/invalid_null_ptr_usage.stderr
@@ -2,7 +2,7 @@ error: pointer must be non-null
   --> tests/ui/invalid_null_ptr_usage.rs:3:59
    |
 LL |         let _slice: &[usize] = std::slice::from_raw_parts(std::ptr::null(), 0);
-   |                                                           ^^^^^^^^^^^^^^^^ help: change this to: `core::ptr::NonNull::dangling().as_ptr()`
+   |                                                           ^^^^^^^^^^^^^^^^ help: change this to: `std::ptr::NonNull::dangling().as_ptr()`
    |
    = note: `#[deny(clippy::invalid_null_ptr_usage)]` on by default
 
@@ -10,127 +10,127 @@ error: pointer must be non-null
   --> tests/ui/invalid_null_ptr_usage.rs:4:59
    |
 LL |         let _slice: &[usize] = std::slice::from_raw_parts(std::ptr::null_mut(), 0);
-   |                                                           ^^^^^^^^^^^^^^^^^^^^ help: change this to: `core::ptr::NonNull::dangling().as_ptr()`
+   |                                                           ^^^^^^^^^^^^^^^^^^^^ help: change this to: `std::ptr::NonNull::dangling().as_ptr()`
 
 error: pointer must be non-null
   --> tests/ui/invalid_null_ptr_usage.rs:6:63
    |
 LL |         let _slice: &[usize] = std::slice::from_raw_parts_mut(std::ptr::null_mut(), 0);
-   |                                                               ^^^^^^^^^^^^^^^^^^^^ help: change this to: `core::ptr::NonNull::dangling().as_ptr()`
+   |                                                               ^^^^^^^^^^^^^^^^^^^^ help: change this to: `std::ptr::NonNull::dangling().as_ptr()`
 
 error: pointer must be non-null
   --> tests/ui/invalid_null_ptr_usage.rs:8:33
    |
 LL |         std::ptr::copy::<usize>(std::ptr::null(), std::ptr::NonNull::dangling().as_ptr(), 0);
-   |                                 ^^^^^^^^^^^^^^^^ help: change this to: `core::ptr::NonNull::dangling().as_ptr()`
+   |                                 ^^^^^^^^^^^^^^^^ help: change this to: `std::ptr::NonNull::dangling().as_ptr()`
 
 error: pointer must be non-null
   --> tests/ui/invalid_null_ptr_usage.rs:9:73
    |
 LL |         std::ptr::copy::<usize>(std::ptr::NonNull::dangling().as_ptr(), std::ptr::null_mut(), 0);
-   |                                                                         ^^^^^^^^^^^^^^^^^^^^ help: change this to: `core::ptr::NonNull::dangling().as_ptr()`
+   |                                                                         ^^^^^^^^^^^^^^^^^^^^ help: change this to: `std::ptr::NonNull::dangling().as_ptr()`
 
 error: pointer must be non-null
   --> tests/ui/invalid_null_ptr_usage.rs:11:48
    |
 LL |         std::ptr::copy_nonoverlapping::<usize>(std::ptr::null(), std::ptr::NonNull::dangling().as_ptr(), 0);
-   |                                                ^^^^^^^^^^^^^^^^ help: change this to: `core::ptr::NonNull::dangling().as_ptr()`
+   |                                                ^^^^^^^^^^^^^^^^ help: change this to: `std::ptr::NonNull::dangling().as_ptr()`
 
 error: pointer must be non-null
   --> tests/ui/invalid_null_ptr_usage.rs:12:88
    |
 LL |         std::ptr::copy_nonoverlapping::<usize>(std::ptr::NonNull::dangling().as_ptr(), std::ptr::null_mut(), 0);
-   |                                                                                        ^^^^^^^^^^^^^^^^^^^^ help: change this to: `core::ptr::NonNull::dangling().as_ptr()`
+   |                                                                                        ^^^^^^^^^^^^^^^^^^^^ help: change this to: `std::ptr::NonNull::dangling().as_ptr()`
 
 error: pointer must be non-null
   --> tests/ui/invalid_null_ptr_usage.rs:17:36
    |
 LL |         let _a: A = std::ptr::read(std::ptr::null());
-   |                                    ^^^^^^^^^^^^^^^^ help: change this to: `core::ptr::NonNull::dangling().as_ptr()`
+   |                                    ^^^^^^^^^^^^^^^^ help: change this to: `std::ptr::NonNull::dangling().as_ptr()`
 
 error: pointer must be non-null
   --> tests/ui/invalid_null_ptr_usage.rs:18:36
    |
 LL |         let _a: A = std::ptr::read(std::ptr::null_mut());
-   |                                    ^^^^^^^^^^^^^^^^^^^^ help: change this to: `core::ptr::NonNull::dangling().as_ptr()`
+   |                                    ^^^^^^^^^^^^^^^^^^^^ help: change this to: `std::ptr::NonNull::dangling().as_ptr()`
 
 error: pointer must be non-null
   --> tests/ui/invalid_null_ptr_usage.rs:20:46
    |
 LL |         let _a: A = std::ptr::read_unaligned(std::ptr::null());
-   |                                              ^^^^^^^^^^^^^^^^ help: change this to: `core::ptr::NonNull::dangling().as_ptr()`
+   |                                              ^^^^^^^^^^^^^^^^ help: change this to: `std::ptr::NonNull::dangling().as_ptr()`
 
 error: pointer must be non-null
   --> tests/ui/invalid_null_ptr_usage.rs:21:46
    |
 LL |         let _a: A = std::ptr::read_unaligned(std::ptr::null_mut());
-   |                                              ^^^^^^^^^^^^^^^^^^^^ help: change this to: `core::ptr::NonNull::dangling().as_ptr()`
+   |                                              ^^^^^^^^^^^^^^^^^^^^ help: change this to: `std::ptr::NonNull::dangling().as_ptr()`
 
 error: pointer must be non-null
   --> tests/ui/invalid_null_ptr_usage.rs:23:45
    |
 LL |         let _a: A = std::ptr::read_volatile(std::ptr::null());
-   |                                             ^^^^^^^^^^^^^^^^ help: change this to: `core::ptr::NonNull::dangling().as_ptr()`
+   |                                             ^^^^^^^^^^^^^^^^ help: change this to: `std::ptr::NonNull::dangling().as_ptr()`
 
 error: pointer must be non-null
   --> tests/ui/invalid_null_ptr_usage.rs:24:45
    |
 LL |         let _a: A = std::ptr::read_volatile(std::ptr::null_mut());
-   |                                             ^^^^^^^^^^^^^^^^^^^^ help: change this to: `core::ptr::NonNull::dangling().as_ptr()`
+   |                                             ^^^^^^^^^^^^^^^^^^^^ help: change this to: `std::ptr::NonNull::dangling().as_ptr()`
 
 error: pointer must be non-null
   --> tests/ui/invalid_null_ptr_usage.rs:26:39
    |
 LL |         let _a: A = std::ptr::replace(std::ptr::null_mut(), A);
-   |                                       ^^^^^^^^^^^^^^^^^^^^ help: change this to: `core::ptr::NonNull::dangling().as_ptr()`
+   |                                       ^^^^^^^^^^^^^^^^^^^^ help: change this to: `std::ptr::NonNull::dangling().as_ptr()`
 
 error: pointer must be non-null
   --> tests/ui/invalid_null_ptr_usage.rs:30:29
    |
 LL |         std::ptr::swap::<A>(std::ptr::null_mut(), &mut A);
-   |                             ^^^^^^^^^^^^^^^^^^^^ help: change this to: `core::ptr::NonNull::dangling().as_ptr()`
+   |                             ^^^^^^^^^^^^^^^^^^^^ help: change this to: `std::ptr::NonNull::dangling().as_ptr()`
 
 error: pointer must be non-null
   --> tests/ui/invalid_null_ptr_usage.rs:31:37
    |
 LL |         std::ptr::swap::<A>(&mut A, std::ptr::null_mut());
-   |                                     ^^^^^^^^^^^^^^^^^^^^ help: change this to: `core::ptr::NonNull::dangling().as_ptr()`
+   |                                     ^^^^^^^^^^^^^^^^^^^^ help: change this to: `std::ptr::NonNull::dangling().as_ptr()`
 
 error: pointer must be non-null
   --> tests/ui/invalid_null_ptr_usage.rs:33:44
    |
 LL |         std::ptr::swap_nonoverlapping::<A>(std::ptr::null_mut(), &mut A, 0);
-   |                                            ^^^^^^^^^^^^^^^^^^^^ help: change this to: `core::ptr::NonNull::dangling().as_ptr()`
+   |                                            ^^^^^^^^^^^^^^^^^^^^ help: change this to: `std::ptr::NonNull::dangling().as_ptr()`
 
 error: pointer must be non-null
   --> tests/ui/invalid_null_ptr_usage.rs:34:52
    |
 LL |         std::ptr::swap_nonoverlapping::<A>(&mut A, std::ptr::null_mut(), 0);
-   |                                                    ^^^^^^^^^^^^^^^^^^^^ help: change this to: `core::ptr::NonNull::dangling().as_ptr()`
+   |                                                    ^^^^^^^^^^^^^^^^^^^^ help: change this to: `std::ptr::NonNull::dangling().as_ptr()`
 
 error: pointer must be non-null
   --> tests/ui/invalid_null_ptr_usage.rs:36:25
    |
 LL |         std::ptr::write(std::ptr::null_mut(), A);
-   |                         ^^^^^^^^^^^^^^^^^^^^ help: change this to: `core::ptr::NonNull::dangling().as_ptr()`
+   |                         ^^^^^^^^^^^^^^^^^^^^ help: change this to: `std::ptr::NonNull::dangling().as_ptr()`
 
 error: pointer must be non-null
   --> tests/ui/invalid_null_ptr_usage.rs:38:35
    |
 LL |         std::ptr::write_unaligned(std::ptr::null_mut(), A);
-   |                                   ^^^^^^^^^^^^^^^^^^^^ help: change this to: `core::ptr::NonNull::dangling().as_ptr()`
+   |                                   ^^^^^^^^^^^^^^^^^^^^ help: change this to: `std::ptr::NonNull::dangling().as_ptr()`
 
 error: pointer must be non-null
   --> tests/ui/invalid_null_ptr_usage.rs:40:34
    |
 LL |         std::ptr::write_volatile(std::ptr::null_mut(), A);
-   |                                  ^^^^^^^^^^^^^^^^^^^^ help: change this to: `core::ptr::NonNull::dangling().as_ptr()`
+   |                                  ^^^^^^^^^^^^^^^^^^^^ help: change this to: `std::ptr::NonNull::dangling().as_ptr()`
 
 error: pointer must be non-null
   --> tests/ui/invalid_null_ptr_usage.rs:42:40
    |
 LL |         std::ptr::write_bytes::<usize>(std::ptr::null_mut(), 42, 0);
-   |                                        ^^^^^^^^^^^^^^^^^^^^ help: change this to: `core::ptr::NonNull::dangling().as_ptr()`
+   |                                        ^^^^^^^^^^^^^^^^^^^^ help: change this to: `std::ptr::NonNull::dangling().as_ptr()`
 
 error: aborting due to 22 previous errors
 
diff --git a/src/tools/clippy/tests/ui/invalid_null_ptr_usage_no_std.fixed b/src/tools/clippy/tests/ui/invalid_null_ptr_usage_no_std.fixed
new file mode 100644
index 00000000000..2bbfe727424
--- /dev/null
+++ b/src/tools/clippy/tests/ui/invalid_null_ptr_usage_no_std.fixed
@@ -0,0 +1,57 @@
+#![no_std]
+#![feature(lang_items)]
+
+use core::panic::PanicInfo;
+
+#[lang = "eh_personality"]
+extern "C" fn eh_personality() {}
+
+#[panic_handler]
+fn panic(info: &PanicInfo) -> ! {
+    loop {}
+}
+
+fn main() {
+    unsafe {
+        let _slice: &[usize] = core::slice::from_raw_parts(core::ptr::NonNull::dangling().as_ptr(), 0);
+        let _slice: &[usize] = core::slice::from_raw_parts(core::ptr::NonNull::dangling().as_ptr(), 0);
+
+        let _slice: &[usize] = core::slice::from_raw_parts_mut(core::ptr::NonNull::dangling().as_ptr(), 0);
+
+        core::ptr::copy::<usize>(core::ptr::NonNull::dangling().as_ptr(), core::ptr::NonNull::dangling().as_ptr(), 0);
+        core::ptr::copy::<usize>(core::ptr::NonNull::dangling().as_ptr(), core::ptr::NonNull::dangling().as_ptr(), 0);
+
+        core::ptr::copy_nonoverlapping::<usize>(core::ptr::NonNull::dangling().as_ptr(), core::ptr::NonNull::dangling().as_ptr(), 0);
+        core::ptr::copy_nonoverlapping::<usize>(core::ptr::NonNull::dangling().as_ptr(), core::ptr::NonNull::dangling().as_ptr(), 0);
+
+        struct A; // zero sized struct
+        assert_eq!(core::mem::size_of::<A>(), 0);
+
+        let _a: A = core::ptr::read(core::ptr::NonNull::dangling().as_ptr());
+        let _a: A = core::ptr::read(core::ptr::NonNull::dangling().as_ptr());
+
+        let _a: A = core::ptr::read_unaligned(core::ptr::NonNull::dangling().as_ptr());
+        let _a: A = core::ptr::read_unaligned(core::ptr::NonNull::dangling().as_ptr());
+
+        let _a: A = core::ptr::read_volatile(core::ptr::NonNull::dangling().as_ptr());
+        let _a: A = core::ptr::read_volatile(core::ptr::NonNull::dangling().as_ptr());
+
+        let _a: A = core::ptr::replace(core::ptr::NonNull::dangling().as_ptr(), A);
+        let _slice: *const [usize] = core::ptr::slice_from_raw_parts(core::ptr::null_mut(), 0); // shouldn't lint
+        let _slice: *const [usize] = core::ptr::slice_from_raw_parts_mut(core::ptr::null_mut(), 0);
+
+        core::ptr::swap::<A>(core::ptr::NonNull::dangling().as_ptr(), &mut A);
+        core::ptr::swap::<A>(&mut A, core::ptr::NonNull::dangling().as_ptr());
+
+        core::ptr::swap_nonoverlapping::<A>(core::ptr::NonNull::dangling().as_ptr(), &mut A, 0);
+        core::ptr::swap_nonoverlapping::<A>(&mut A, core::ptr::NonNull::dangling().as_ptr(), 0);
+
+        core::ptr::write(core::ptr::NonNull::dangling().as_ptr(), A);
+
+        core::ptr::write_unaligned(core::ptr::NonNull::dangling().as_ptr(), A);
+
+        core::ptr::write_volatile(core::ptr::NonNull::dangling().as_ptr(), A);
+
+        core::ptr::write_bytes::<usize>(core::ptr::NonNull::dangling().as_ptr(), 42, 0);
+    }
+}
diff --git a/src/tools/clippy/tests/ui/invalid_null_ptr_usage_no_std.rs b/src/tools/clippy/tests/ui/invalid_null_ptr_usage_no_std.rs
new file mode 100644
index 00000000000..cbce44f7c0d
--- /dev/null
+++ b/src/tools/clippy/tests/ui/invalid_null_ptr_usage_no_std.rs
@@ -0,0 +1,57 @@
+#![no_std]
+#![feature(lang_items)]
+
+use core::panic::PanicInfo;
+
+#[lang = "eh_personality"]
+extern "C" fn eh_personality() {}
+
+#[panic_handler]
+fn panic(info: &PanicInfo) -> ! {
+    loop {}
+}
+
+fn main() {
+    unsafe {
+        let _slice: &[usize] = core::slice::from_raw_parts(core::ptr::null(), 0);
+        let _slice: &[usize] = core::slice::from_raw_parts(core::ptr::null_mut(), 0);
+
+        let _slice: &[usize] = core::slice::from_raw_parts_mut(core::ptr::null_mut(), 0);
+
+        core::ptr::copy::<usize>(core::ptr::null(), core::ptr::NonNull::dangling().as_ptr(), 0);
+        core::ptr::copy::<usize>(core::ptr::NonNull::dangling().as_ptr(), core::ptr::null_mut(), 0);
+
+        core::ptr::copy_nonoverlapping::<usize>(core::ptr::null(), core::ptr::NonNull::dangling().as_ptr(), 0);
+        core::ptr::copy_nonoverlapping::<usize>(core::ptr::NonNull::dangling().as_ptr(), core::ptr::null_mut(), 0);
+
+        struct A; // zero sized struct
+        assert_eq!(core::mem::size_of::<A>(), 0);
+
+        let _a: A = core::ptr::read(core::ptr::null());
+        let _a: A = core::ptr::read(core::ptr::null_mut());
+
+        let _a: A = core::ptr::read_unaligned(core::ptr::null());
+        let _a: A = core::ptr::read_unaligned(core::ptr::null_mut());
+
+        let _a: A = core::ptr::read_volatile(core::ptr::null());
+        let _a: A = core::ptr::read_volatile(core::ptr::null_mut());
+
+        let _a: A = core::ptr::replace(core::ptr::null_mut(), A);
+        let _slice: *const [usize] = core::ptr::slice_from_raw_parts(core::ptr::null_mut(), 0); // shouldn't lint
+        let _slice: *const [usize] = core::ptr::slice_from_raw_parts_mut(core::ptr::null_mut(), 0);
+
+        core::ptr::swap::<A>(core::ptr::null_mut(), &mut A);
+        core::ptr::swap::<A>(&mut A, core::ptr::null_mut());
+
+        core::ptr::swap_nonoverlapping::<A>(core::ptr::null_mut(), &mut A, 0);
+        core::ptr::swap_nonoverlapping::<A>(&mut A, core::ptr::null_mut(), 0);
+
+        core::ptr::write(core::ptr::null_mut(), A);
+
+        core::ptr::write_unaligned(core::ptr::null_mut(), A);
+
+        core::ptr::write_volatile(core::ptr::null_mut(), A);
+
+        core::ptr::write_bytes::<usize>(core::ptr::null_mut(), 42, 0);
+    }
+}
diff --git a/src/tools/clippy/tests/ui/invalid_null_ptr_usage_no_std.stderr b/src/tools/clippy/tests/ui/invalid_null_ptr_usage_no_std.stderr
new file mode 100644
index 00000000000..df0d40e9e07
--- /dev/null
+++ b/src/tools/clippy/tests/ui/invalid_null_ptr_usage_no_std.stderr
@@ -0,0 +1,136 @@
+error: pointer must be non-null
+  --> tests/ui/invalid_null_ptr_usage_no_std.rs:16:60
+   |
+LL |         let _slice: &[usize] = core::slice::from_raw_parts(core::ptr::null(), 0);
+   |                                                            ^^^^^^^^^^^^^^^^^ help: change this to: `core::ptr::NonNull::dangling().as_ptr()`
+   |
+   = note: `#[deny(clippy::invalid_null_ptr_usage)]` on by default
+
+error: pointer must be non-null
+  --> tests/ui/invalid_null_ptr_usage_no_std.rs:17:60
+   |
+LL |         let _slice: &[usize] = core::slice::from_raw_parts(core::ptr::null_mut(), 0);
+   |                                                            ^^^^^^^^^^^^^^^^^^^^^ help: change this to: `core::ptr::NonNull::dangling().as_ptr()`
+
+error: pointer must be non-null
+  --> tests/ui/invalid_null_ptr_usage_no_std.rs:19:64
+   |
+LL |         let _slice: &[usize] = core::slice::from_raw_parts_mut(core::ptr::null_mut(), 0);
+   |                                                                ^^^^^^^^^^^^^^^^^^^^^ help: change this to: `core::ptr::NonNull::dangling().as_ptr()`
+
+error: pointer must be non-null
+  --> tests/ui/invalid_null_ptr_usage_no_std.rs:21:34
+   |
+LL |         core::ptr::copy::<usize>(core::ptr::null(), core::ptr::NonNull::dangling().as_ptr(), 0);
+   |                                  ^^^^^^^^^^^^^^^^^ help: change this to: `core::ptr::NonNull::dangling().as_ptr()`
+
+error: pointer must be non-null
+  --> tests/ui/invalid_null_ptr_usage_no_std.rs:22:75
+   |
+LL |         core::ptr::copy::<usize>(core::ptr::NonNull::dangling().as_ptr(), core::ptr::null_mut(), 0);
+   |                                                                           ^^^^^^^^^^^^^^^^^^^^^ help: change this to: `core::ptr::NonNull::dangling().as_ptr()`
+
+error: pointer must be non-null
+  --> tests/ui/invalid_null_ptr_usage_no_std.rs:24:49
+   |
+LL |         core::ptr::copy_nonoverlapping::<usize>(core::ptr::null(), core::ptr::NonNull::dangling().as_ptr(), 0);
+   |                                                 ^^^^^^^^^^^^^^^^^ help: change this to: `core::ptr::NonNull::dangling().as_ptr()`
+
+error: pointer must be non-null
+  --> tests/ui/invalid_null_ptr_usage_no_std.rs:25:90
+   |
+LL |         core::ptr::copy_nonoverlapping::<usize>(core::ptr::NonNull::dangling().as_ptr(), core::ptr::null_mut(), 0);
+   |                                                                                          ^^^^^^^^^^^^^^^^^^^^^ help: change this to: `core::ptr::NonNull::dangling().as_ptr()`
+
+error: pointer must be non-null
+  --> tests/ui/invalid_null_ptr_usage_no_std.rs:30:37
+   |
+LL |         let _a: A = core::ptr::read(core::ptr::null());
+   |                                     ^^^^^^^^^^^^^^^^^ help: change this to: `core::ptr::NonNull::dangling().as_ptr()`
+
+error: pointer must be non-null
+  --> tests/ui/invalid_null_ptr_usage_no_std.rs:31:37
+   |
+LL |         let _a: A = core::ptr::read(core::ptr::null_mut());
+   |                                     ^^^^^^^^^^^^^^^^^^^^^ help: change this to: `core::ptr::NonNull::dangling().as_ptr()`
+
+error: pointer must be non-null
+  --> tests/ui/invalid_null_ptr_usage_no_std.rs:33:47
+   |
+LL |         let _a: A = core::ptr::read_unaligned(core::ptr::null());
+   |                                               ^^^^^^^^^^^^^^^^^ help: change this to: `core::ptr::NonNull::dangling().as_ptr()`
+
+error: pointer must be non-null
+  --> tests/ui/invalid_null_ptr_usage_no_std.rs:34:47
+   |
+LL |         let _a: A = core::ptr::read_unaligned(core::ptr::null_mut());
+   |                                               ^^^^^^^^^^^^^^^^^^^^^ help: change this to: `core::ptr::NonNull::dangling().as_ptr()`
+
+error: pointer must be non-null
+  --> tests/ui/invalid_null_ptr_usage_no_std.rs:36:46
+   |
+LL |         let _a: A = core::ptr::read_volatile(core::ptr::null());
+   |                                              ^^^^^^^^^^^^^^^^^ help: change this to: `core::ptr::NonNull::dangling().as_ptr()`
+
+error: pointer must be non-null
+  --> tests/ui/invalid_null_ptr_usage_no_std.rs:37:46
+   |
+LL |         let _a: A = core::ptr::read_volatile(core::ptr::null_mut());
+   |                                              ^^^^^^^^^^^^^^^^^^^^^ help: change this to: `core::ptr::NonNull::dangling().as_ptr()`
+
+error: pointer must be non-null
+  --> tests/ui/invalid_null_ptr_usage_no_std.rs:39:40
+   |
+LL |         let _a: A = core::ptr::replace(core::ptr::null_mut(), A);
+   |                                        ^^^^^^^^^^^^^^^^^^^^^ help: change this to: `core::ptr::NonNull::dangling().as_ptr()`
+
+error: pointer must be non-null
+  --> tests/ui/invalid_null_ptr_usage_no_std.rs:43:30
+   |
+LL |         core::ptr::swap::<A>(core::ptr::null_mut(), &mut A);
+   |                              ^^^^^^^^^^^^^^^^^^^^^ help: change this to: `core::ptr::NonNull::dangling().as_ptr()`
+
+error: pointer must be non-null
+  --> tests/ui/invalid_null_ptr_usage_no_std.rs:44:38
+   |
+LL |         core::ptr::swap::<A>(&mut A, core::ptr::null_mut());
+   |                                      ^^^^^^^^^^^^^^^^^^^^^ help: change this to: `core::ptr::NonNull::dangling().as_ptr()`
+
+error: pointer must be non-null
+  --> tests/ui/invalid_null_ptr_usage_no_std.rs:46:45
+   |
+LL |         core::ptr::swap_nonoverlapping::<A>(core::ptr::null_mut(), &mut A, 0);
+   |                                             ^^^^^^^^^^^^^^^^^^^^^ help: change this to: `core::ptr::NonNull::dangling().as_ptr()`
+
+error: pointer must be non-null
+  --> tests/ui/invalid_null_ptr_usage_no_std.rs:47:53
+   |
+LL |         core::ptr::swap_nonoverlapping::<A>(&mut A, core::ptr::null_mut(), 0);
+   |                                                     ^^^^^^^^^^^^^^^^^^^^^ help: change this to: `core::ptr::NonNull::dangling().as_ptr()`
+
+error: pointer must be non-null
+  --> tests/ui/invalid_null_ptr_usage_no_std.rs:49:26
+   |
+LL |         core::ptr::write(core::ptr::null_mut(), A);
+   |                          ^^^^^^^^^^^^^^^^^^^^^ help: change this to: `core::ptr::NonNull::dangling().as_ptr()`
+
+error: pointer must be non-null
+  --> tests/ui/invalid_null_ptr_usage_no_std.rs:51:36
+   |
+LL |         core::ptr::write_unaligned(core::ptr::null_mut(), A);
+   |                                    ^^^^^^^^^^^^^^^^^^^^^ help: change this to: `core::ptr::NonNull::dangling().as_ptr()`
+
+error: pointer must be non-null
+  --> tests/ui/invalid_null_ptr_usage_no_std.rs:53:35
+   |
+LL |         core::ptr::write_volatile(core::ptr::null_mut(), A);
+   |                                   ^^^^^^^^^^^^^^^^^^^^^ help: change this to: `core::ptr::NonNull::dangling().as_ptr()`
+
+error: pointer must be non-null
+  --> tests/ui/invalid_null_ptr_usage_no_std.rs:55:41
+   |
+LL |         core::ptr::write_bytes::<usize>(core::ptr::null_mut(), 42, 0);
+   |                                         ^^^^^^^^^^^^^^^^^^^^^ help: change this to: `core::ptr::NonNull::dangling().as_ptr()`
+
+error: aborting due to 22 previous errors
+
diff --git a/src/tools/clippy/tests/ui/large_const_arrays.fixed b/src/tools/clippy/tests/ui/large_const_arrays.fixed
index 6011bb99dec..543ce460e7b 100644
--- a/src/tools/clippy/tests/ui/large_const_arrays.fixed
+++ b/src/tools/clippy/tests/ui/large_const_arrays.fixed
@@ -12,9 +12,9 @@ pub static FOO_PUB: [u32; 1_000_000] = [0u32; 1_000_000];
 static FOO: [u32; 1_000_000] = [0u32; 1_000_000];
 
 // Good
-pub(crate) const G_FOO_PUB_CRATE: [u32; 1_000] = [0u32; 1_000];
-pub const G_FOO_PUB: [u32; 1_000] = [0u32; 1_000];
-const G_FOO: [u32; 1_000] = [0u32; 1_000];
+pub(crate) const G_FOO_PUB_CRATE: [u32; 250] = [0u32; 250];
+pub const G_FOO_PUB: [u32; 250] = [0u32; 250];
+const G_FOO: [u32; 250] = [0u32; 250];
 
 fn main() {
     // Should lint
@@ -26,10 +26,10 @@ fn main() {
     static BAR_S: [Option<&str>; 200_000] = [Some("str"); 200_000];
 
     // Good
-    pub const G_BAR_PUB: [u32; 1_000] = [0u32; 1_000];
-    const G_BAR: [u32; 1_000] = [0u32; 1_000];
-    pub const G_BAR_STRUCT_PUB: [S; 500] = [S { data: [0; 32] }; 500];
-    const G_BAR_STRUCT: [S; 500] = [S { data: [0; 32] }; 500];
-    pub const G_BAR_S_PUB: [Option<&str>; 200] = [Some("str"); 200];
-    const G_BAR_S: [Option<&str>; 200] = [Some("str"); 200];
+    pub const G_BAR_PUB: [u32; 250] = [0u32; 250];
+    const G_BAR: [u32; 250] = [0u32; 250];
+    pub const G_BAR_STRUCT_PUB: [S; 4] = [S { data: [0; 32] }; 4];
+    const G_BAR_STRUCT: [S; 4] = [S { data: [0; 32] }; 4];
+    pub const G_BAR_S_PUB: [Option<&str>; 50] = [Some("str"); 50];
+    const G_BAR_S: [Option<&str>; 50] = [Some("str"); 50];
 }
diff --git a/src/tools/clippy/tests/ui/large_const_arrays.rs b/src/tools/clippy/tests/ui/large_const_arrays.rs
index a78425d7bc6..e23a8081171 100644
--- a/src/tools/clippy/tests/ui/large_const_arrays.rs
+++ b/src/tools/clippy/tests/ui/large_const_arrays.rs
@@ -12,9 +12,9 @@ pub const FOO_PUB: [u32; 1_000_000] = [0u32; 1_000_000];
 const FOO: [u32; 1_000_000] = [0u32; 1_000_000];
 
 // Good
-pub(crate) const G_FOO_PUB_CRATE: [u32; 1_000] = [0u32; 1_000];
-pub const G_FOO_PUB: [u32; 1_000] = [0u32; 1_000];
-const G_FOO: [u32; 1_000] = [0u32; 1_000];
+pub(crate) const G_FOO_PUB_CRATE: [u32; 250] = [0u32; 250];
+pub const G_FOO_PUB: [u32; 250] = [0u32; 250];
+const G_FOO: [u32; 250] = [0u32; 250];
 
 fn main() {
     // Should lint
@@ -26,10 +26,10 @@ fn main() {
     const BAR_S: [Option<&str>; 200_000] = [Some("str"); 200_000];
 
     // Good
-    pub const G_BAR_PUB: [u32; 1_000] = [0u32; 1_000];
-    const G_BAR: [u32; 1_000] = [0u32; 1_000];
-    pub const G_BAR_STRUCT_PUB: [S; 500] = [S { data: [0; 32] }; 500];
-    const G_BAR_STRUCT: [S; 500] = [S { data: [0; 32] }; 500];
-    pub const G_BAR_S_PUB: [Option<&str>; 200] = [Some("str"); 200];
-    const G_BAR_S: [Option<&str>; 200] = [Some("str"); 200];
+    pub const G_BAR_PUB: [u32; 250] = [0u32; 250];
+    const G_BAR: [u32; 250] = [0u32; 250];
+    pub const G_BAR_STRUCT_PUB: [S; 4] = [S { data: [0; 32] }; 4];
+    const G_BAR_STRUCT: [S; 4] = [S { data: [0; 32] }; 4];
+    pub const G_BAR_S_PUB: [Option<&str>; 50] = [Some("str"); 50];
+    const G_BAR_S: [Option<&str>; 50] = [Some("str"); 50];
 }
diff --git a/src/tools/clippy/tests/ui/large_stack_arrays.rs b/src/tools/clippy/tests/ui/large_stack_arrays.rs
index 6bcaf481c9f..cd72b9bfa47 100644
--- a/src/tools/clippy/tests/ui/large_stack_arrays.rs
+++ b/src/tools/clippy/tests/ui/large_stack_arrays.rs
@@ -15,6 +15,12 @@ enum E {
     T(u32),
 }
 
+const STATIC_PROMOTED_LARGE_ARRAY: &[u8; 512001] = &[0; 512001];
+const STATIC_PROMOTED_LARGE_ARRAY_WITH_NESTED: &[u8; 512001] = {
+    const NESTED: () = ();
+    &[0; 512001]
+};
+
 pub static DOESNOTLINT: [u8; 512_001] = [0; 512_001];
 pub static DOESNOTLINT2: [u8; 512_001] = {
     let x = 0;
@@ -23,38 +29,38 @@ pub static DOESNOTLINT2: [u8; 512_001] = {
 
 fn issue_10741() {
     #[derive(Copy, Clone)]
-    struct Large([u32; 100_000]);
+    struct Large([u32; 2048]);
 
     fn build() -> Large {
-        Large([0; 100_000])
+        Large([0; 2048])
     }
 
     let _x = [build(); 3];
-    //~^ ERROR: allocating a local array larger than 512000 bytes
+    //~^ ERROR: allocating a local array larger than 16384 bytes
 
     let _y = [build(), build(), build()];
-    //~^ ERROR: allocating a local array larger than 512000 bytes
+    //~^ ERROR: allocating a local array larger than 16384 bytes
 }
 
 fn main() {
     let bad = (
         [0u32; 20_000_000],
-        //~^ ERROR: allocating a local array larger than 512000 bytes
+        //~^ ERROR: allocating a local array larger than 16384 bytes
         [S { data: [0; 32] }; 5000],
-        //~^ ERROR: allocating a local array larger than 512000 bytes
+        //~^ ERROR: allocating a local array larger than 16384 bytes
         [Some(""); 20_000_000],
-        //~^ ERROR: allocating a local array larger than 512000 bytes
+        //~^ ERROR: allocating a local array larger than 16384 bytes
         [E::T(0); 5000],
-        //~^ ERROR: allocating a local array larger than 512000 bytes
+        //~^ ERROR: allocating a local array larger than 16384 bytes
         [0u8; usize::MAX],
-        //~^ ERROR: allocating a local array larger than 512000 bytes
+        //~^ ERROR: allocating a local array larger than 16384 bytes
     );
 
     let good = (
-        [0u32; 1000],
-        [S { data: [0; 32] }; 1000],
-        [Some(""); 1000],
-        [E::T(0); 1000],
+        [0u32; 50],
+        [S { data: [0; 32] }; 4],
+        [Some(""); 50],
+        [E::T(0); 2],
         [(); 20_000_000],
     );
 }
@@ -68,7 +74,7 @@ fn issue_12586() {
         // Weird rule to test help messages.
         ($a:expr => $b:expr) => {
             [$a, $b, $a, $b]
-            //~^ ERROR: allocating a local array larger than 512000 bytes
+            //~^ ERROR: allocating a local array larger than 16384 bytes
         };
         ($id:ident; $n:literal) => {
             dummy!(::std::vec![$id;$n])
@@ -80,26 +86,26 @@ fn issue_12586() {
     macro_rules! create_then_move {
         ($id:ident; $n:literal) => {{
             let _x_ = [$id; $n];
-            //~^ ERROR: allocating a local array larger than 512000 bytes
+            //~^ ERROR: allocating a local array larger than 16384 bytes
             _x_
         }};
     }
 
-    let x = [0u32; 50_000];
+    let x = [0u32; 4096];
     let y = vec![x, x, x, x, x];
     let y = vec![dummy![x, x, x, x, x]];
     let y = vec![dummy![[x, x, x, x, x]]];
     let y = dummy![x, x, x, x, x];
     let y = [x, x, dummy!(x), x, x];
-    //~^ ERROR: allocating a local array larger than 512000 bytes
+    //~^ ERROR: allocating a local array larger than 16384 bytes
     let y = dummy![x => x];
     let y = dummy![x;5];
     let y = dummy!(vec![dummy![x, x, x, x, x]]);
     let y = dummy![[x, x, x, x, x]];
-    //~^ ERROR: allocating a local array larger than 512000 bytes
+    //~^ ERROR: allocating a local array larger than 16384 bytes
 
     let y = proc_macros::make_it_big!([x; 1]);
-    //~^ ERROR: allocating a local array larger than 512000 bytes
+    //~^ ERROR: allocating a local array larger than 16384 bytes
     let y = vec![proc_macros::make_it_big!([x; 10])];
     let y = vec![create_then_move![x; 5]; 5];
 }
diff --git a/src/tools/clippy/tests/ui/large_stack_arrays.stderr b/src/tools/clippy/tests/ui/large_stack_arrays.stderr
index 06294ee8b8c..f48706415e6 100644
--- a/src/tools/clippy/tests/ui/large_stack_arrays.stderr
+++ b/src/tools/clippy/tests/ui/large_stack_arrays.stderr
@@ -1,5 +1,5 @@
-error: allocating a local array larger than 512000 bytes
-  --> tests/ui/large_stack_arrays.rs:32:14
+error: allocating a local array larger than 16384 bytes
+  --> tests/ui/large_stack_arrays.rs:38:14
    |
 LL |     let _x = [build(); 3];
    |              ^^^^^^^^^^^^
@@ -8,64 +8,64 @@ LL |     let _x = [build(); 3];
    = note: `-D clippy::large-stack-arrays` implied by `-D warnings`
    = help: to override `-D warnings` add `#[allow(clippy::large_stack_arrays)]`
 
-error: allocating a local array larger than 512000 bytes
-  --> tests/ui/large_stack_arrays.rs:35:14
+error: allocating a local array larger than 16384 bytes
+  --> tests/ui/large_stack_arrays.rs:41:14
    |
 LL |     let _y = [build(), build(), build()];
    |              ^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |
    = help: consider allocating on the heap with `vec![build(), build(), build()].into_boxed_slice()`
 
-error: allocating a local array larger than 512000 bytes
-  --> tests/ui/large_stack_arrays.rs:41:9
+error: allocating a local array larger than 16384 bytes
+  --> tests/ui/large_stack_arrays.rs:47:9
    |
 LL |         [0u32; 20_000_000],
    |         ^^^^^^^^^^^^^^^^^^
    |
    = help: consider allocating on the heap with `vec![0u32; 20_000_000].into_boxed_slice()`
 
-error: allocating a local array larger than 512000 bytes
-  --> tests/ui/large_stack_arrays.rs:43:9
+error: allocating a local array larger than 16384 bytes
+  --> tests/ui/large_stack_arrays.rs:49:9
    |
 LL |         [S { data: [0; 32] }; 5000],
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |
    = help: consider allocating on the heap with `vec![S { data: [0; 32] }; 5000].into_boxed_slice()`
 
-error: allocating a local array larger than 512000 bytes
-  --> tests/ui/large_stack_arrays.rs:45:9
+error: allocating a local array larger than 16384 bytes
+  --> tests/ui/large_stack_arrays.rs:51:9
    |
 LL |         [Some(""); 20_000_000],
    |         ^^^^^^^^^^^^^^^^^^^^^^
    |
    = help: consider allocating on the heap with `vec![Some(""); 20_000_000].into_boxed_slice()`
 
-error: allocating a local array larger than 512000 bytes
-  --> tests/ui/large_stack_arrays.rs:47:9
+error: allocating a local array larger than 16384 bytes
+  --> tests/ui/large_stack_arrays.rs:53:9
    |
 LL |         [E::T(0); 5000],
    |         ^^^^^^^^^^^^^^^
    |
    = help: consider allocating on the heap with `vec![E::T(0); 5000].into_boxed_slice()`
 
-error: allocating a local array larger than 512000 bytes
-  --> tests/ui/large_stack_arrays.rs:49:9
+error: allocating a local array larger than 16384 bytes
+  --> tests/ui/large_stack_arrays.rs:55:9
    |
 LL |         [0u8; usize::MAX],
    |         ^^^^^^^^^^^^^^^^^
    |
    = help: consider allocating on the heap with `vec![0u8; usize::MAX].into_boxed_slice()`
 
-error: allocating a local array larger than 512000 bytes
-  --> tests/ui/large_stack_arrays.rs:93:13
+error: allocating a local array larger than 16384 bytes
+  --> tests/ui/large_stack_arrays.rs:99:13
    |
 LL |     let y = [x, x, dummy!(x), x, x];
    |             ^^^^^^^^^^^^^^^^^^^^^^^
    |
    = help: consider allocating on the heap with `vec![x, x, dummy!(x), x, x].into_boxed_slice()`
 
-error: allocating a local array larger than 512000 bytes
-  --> tests/ui/large_stack_arrays.rs:70:13
+error: allocating a local array larger than 16384 bytes
+  --> tests/ui/large_stack_arrays.rs:76:13
    |
 LL |             [$a, $b, $a, $b]
    |             ^^^^^^^^^^^^^^^^
@@ -75,22 +75,22 @@ LL |     let y = dummy![x => x];
    |
    = note: this error originates in the macro `dummy` (in Nightly builds, run with -Z macro-backtrace for more info)
 
-error: allocating a local array larger than 512000 bytes
-  --> tests/ui/large_stack_arrays.rs:98:20
+error: allocating a local array larger than 16384 bytes
+  --> tests/ui/large_stack_arrays.rs:104:20
    |
 LL |     let y = dummy![[x, x, x, x, x]];
    |                    ^^^^^^^^^^^^^^^
    |
    = help: consider allocating on the heap with `vec![x, x, x, x, x].into_boxed_slice()`
 
-error: allocating a local array larger than 512000 bytes
-  --> tests/ui/large_stack_arrays.rs:101:39
+error: allocating a local array larger than 16384 bytes
+  --> tests/ui/large_stack_arrays.rs:107:39
    |
 LL |     let y = proc_macros::make_it_big!([x; 1]);
    |                                       ^^^^^^
 
-error: allocating a local array larger than 512000 bytes
-  --> tests/ui/large_stack_arrays.rs:82:23
+error: allocating a local array larger than 16384 bytes
+  --> tests/ui/large_stack_arrays.rs:88:23
    |
 LL |             let _x_ = [$id; $n];
    |                       ^^^^^^^^^
diff --git a/src/tools/clippy/tests/ui/manual_c_str_literals.fixed b/src/tools/clippy/tests/ui/manual_c_str_literals.edition2021.fixed
index a24d7088c88..391c63bb4b8 100644
--- a/src/tools/clippy/tests/ui/manual_c_str_literals.fixed
+++ b/src/tools/clippy/tests/ui/manual_c_str_literals.edition2021.fixed
@@ -1,3 +1,6 @@
+//@revisions: edition2018 edition2021
+//@[edition2018] edition:2018
+//@[edition2021] edition:2021
 #![warn(clippy::manual_c_str_literals)]
 #![allow(clippy::no_effect)]
 
diff --git a/src/tools/clippy/tests/ui/manual_c_str_literals.stderr b/src/tools/clippy/tests/ui/manual_c_str_literals.edition2021.stderr
index 9c70bddb81c..beab29ccdda 100644
--- a/src/tools/clippy/tests/ui/manual_c_str_literals.stderr
+++ b/src/tools/clippy/tests/ui/manual_c_str_literals.edition2021.stderr
@@ -1,5 +1,5 @@
 error: calling `CStr::new` with a byte string literal
-  --> tests/ui/manual_c_str_literals.rs:31:5
+  --> tests/ui/manual_c_str_literals.rs:34:5
    |
 LL |     CStr::from_bytes_with_nul(b"foo\0");
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use a `c""` literal: `c"foo"`
@@ -8,73 +8,73 @@ LL |     CStr::from_bytes_with_nul(b"foo\0");
    = help: to override `-D warnings` add `#[allow(clippy::manual_c_str_literals)]`
 
 error: calling `CStr::new` with a byte string literal
-  --> tests/ui/manual_c_str_literals.rs:35:5
+  --> tests/ui/manual_c_str_literals.rs:38:5
    |
 LL |     CStr::from_bytes_with_nul(b"foo\0");
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use a `c""` literal: `c"foo"`
 
 error: calling `CStr::new` with a byte string literal
-  --> tests/ui/manual_c_str_literals.rs:36:5
+  --> tests/ui/manual_c_str_literals.rs:39:5
    |
 LL |     CStr::from_bytes_with_nul(b"foo\x00");
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use a `c""` literal: `c"foo"`
 
 error: calling `CStr::new` with a byte string literal
-  --> tests/ui/manual_c_str_literals.rs:37:5
+  --> tests/ui/manual_c_str_literals.rs:40:5
    |
 LL |     CStr::from_bytes_with_nul(b"foo\0").unwrap();
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use a `c""` literal: `c"foo"`
 
 error: calling `CStr::new` with a byte string literal
-  --> tests/ui/manual_c_str_literals.rs:38:5
+  --> tests/ui/manual_c_str_literals.rs:41:5
    |
 LL |     CStr::from_bytes_with_nul(b"foo\\0sdsd\0").unwrap();
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use a `c""` literal: `c"foo\\0sdsd"`
 
 error: calling `CStr::from_ptr` with a byte string literal
-  --> tests/ui/manual_c_str_literals.rs:43:14
+  --> tests/ui/manual_c_str_literals.rs:46:14
    |
 LL |     unsafe { CStr::from_ptr(b"foo\0".as_ptr().cast()) };
    |              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use a `c""` literal: `c"foo"`
 
 error: calling `CStr::from_ptr` with a byte string literal
-  --> tests/ui/manual_c_str_literals.rs:44:14
+  --> tests/ui/manual_c_str_literals.rs:47:14
    |
 LL |     unsafe { CStr::from_ptr(b"foo\0".as_ptr() as *const _) };
    |              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use a `c""` literal: `c"foo"`
 
 error: manually constructing a nul-terminated string
-  --> tests/ui/manual_c_str_literals.rs:45:23
+  --> tests/ui/manual_c_str_literals.rs:48:23
    |
 LL |     let _: *const _ = b"foo\0".as_ptr();
    |                       ^^^^^^^^ help: use a `c""` literal: `c"foo"`
 
 error: manually constructing a nul-terminated string
-  --> tests/ui/manual_c_str_literals.rs:46:23
+  --> tests/ui/manual_c_str_literals.rs:49:23
    |
 LL |     let _: *const _ = "foo\0".as_ptr();
    |                       ^^^^^^^ help: use a `c""` literal: `c"foo"`
 
 error: manually constructing a nul-terminated string
-  --> tests/ui/manual_c_str_literals.rs:49:23
+  --> tests/ui/manual_c_str_literals.rs:52:23
    |
 LL |     let _: *const _ = b"foo\0".as_ptr().cast::<i8>();
    |                       ^^^^^^^^ help: use a `c""` literal: `c"foo"`
 
 error: manually constructing a nul-terminated string
-  --> tests/ui/manual_c_str_literals.rs:52:13
+  --> tests/ui/manual_c_str_literals.rs:55:13
    |
 LL |     let _ = "电脑\\\0".as_ptr();
    |             ^^^^^^^^^^ help: use a `c""` literal: `c"电脑\\"`
 
 error: manually constructing a nul-terminated string
-  --> tests/ui/manual_c_str_literals.rs:53:13
+  --> tests/ui/manual_c_str_literals.rs:56:13
    |
 LL |     let _ = "电脑\0".as_ptr();
    |             ^^^^^^^^ help: use a `c""` literal: `c"电脑"`
 
 error: manually constructing a nul-terminated string
-  --> tests/ui/manual_c_str_literals.rs:54:13
+  --> tests/ui/manual_c_str_literals.rs:57:13
    |
 LL |     let _ = "电脑\x00".as_ptr();
    |             ^^^^^^^^^^ help: use a `c""` literal: `c"电脑"`
diff --git a/src/tools/clippy/tests/ui/manual_c_str_literals.rs b/src/tools/clippy/tests/ui/manual_c_str_literals.rs
index 0a007786720..39b62258077 100644
--- a/src/tools/clippy/tests/ui/manual_c_str_literals.rs
+++ b/src/tools/clippy/tests/ui/manual_c_str_literals.rs
@@ -1,3 +1,6 @@
+//@revisions: edition2018 edition2021
+//@[edition2018] edition:2018
+//@[edition2021] edition:2021
 #![warn(clippy::manual_c_str_literals)]
 #![allow(clippy::no_effect)]
 
diff --git a/src/tools/clippy/tests/ui/manual_float_methods.rs b/src/tools/clippy/tests/ui/manual_float_methods.rs
index ee3daa12834..66545d180ef 100644
--- a/src/tools/clippy/tests/ui/manual_float_methods.rs
+++ b/src/tools/clippy/tests/ui/manual_float_methods.rs
@@ -39,8 +39,11 @@ fn main() {
     if x != f64::INFINITY && x != fn_test() {}
     // Not -inf
     if x != f64::INFINITY && x != fn_test_not_inf() {}
+    const {
+        let x = 1.0f64;
+        if x == f64::INFINITY || x == f64::NEG_INFINITY {}
+    }
     const X: f64 = 1.0f64;
-    // Will be linted if `const_float_classify` is enabled
     if const { X == f64::INFINITY || X == f64::NEG_INFINITY } {}
     if const { X != f64::INFINITY && X != f64::NEG_INFINITY } {}
     external! {
diff --git a/src/tools/clippy/tests/ui/manual_float_methods.stderr b/src/tools/clippy/tests/ui/manual_float_methods.stderr
index 70057620a4a..676a4485ab4 100644
--- a/src/tools/clippy/tests/ui/manual_float_methods.stderr
+++ b/src/tools/clippy/tests/ui/manual_float_methods.stderr
@@ -78,5 +78,11 @@ help: or, for conciseness
 LL |     if !x.is_infinite() {}
    |        ~~~~~~~~~~~~~~~~
 
-error: aborting due to 6 previous errors
+error: manually checking if a float is infinite
+  --> tests/ui/manual_float_methods.rs:44:12
+   |
+LL |         if x == f64::INFINITY || x == f64::NEG_INFINITY {}
+   |            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the dedicated method instead: `x.is_infinite()`
+
+error: aborting due to 7 previous errors
 
diff --git a/src/tools/clippy/tests/ui/manual_ignore_case_cmp.fixed b/src/tools/clippy/tests/ui/manual_ignore_case_cmp.fixed
new file mode 100644
index 00000000000..53a124f59c8
--- /dev/null
+++ b/src/tools/clippy/tests/ui/manual_ignore_case_cmp.fixed
@@ -0,0 +1,107 @@
+#![allow(clippy::all)]
+#![deny(clippy::manual_ignore_case_cmp)]
+
+use std::ffi::{OsStr, OsString};
+
+fn main() {}
+
+fn variants(a: &str, b: &str) {
+    if a.eq_ignore_ascii_case(b) {
+        return;
+    }
+    if a.eq_ignore_ascii_case(b) {
+        return;
+    }
+    let r = a.eq_ignore_ascii_case(b);
+    let r = r || a.eq_ignore_ascii_case(b);
+    r && a.eq_ignore_ascii_case(&b.to_uppercase());
+    // !=
+    if !a.eq_ignore_ascii_case(b) {
+        return;
+    }
+    if !a.eq_ignore_ascii_case(b) {
+        return;
+    }
+    let r = !a.eq_ignore_ascii_case(b);
+    let r = r || !a.eq_ignore_ascii_case(b);
+    r && !a.eq_ignore_ascii_case(&b.to_uppercase());
+}
+
+fn unsupported(a: char, b: char) {
+    // TODO:: these are rare, and might not be worth supporting
+    a.to_ascii_lowercase() == char::to_ascii_lowercase(&b);
+    char::to_ascii_lowercase(&a) == b.to_ascii_lowercase();
+    char::to_ascii_lowercase(&a) == char::to_ascii_lowercase(&b);
+}
+
+fn char(a: char, b: char) {
+    a.eq_ignore_ascii_case(&b);
+    a.to_ascii_lowercase() == *&b.to_ascii_lowercase();
+    *&a.to_ascii_lowercase() == b.to_ascii_lowercase();
+    a.eq_ignore_ascii_case(&'a');
+    'a'.eq_ignore_ascii_case(&b);
+}
+fn u8(a: u8, b: u8) {
+    a.eq_ignore_ascii_case(&b);
+    a.eq_ignore_ascii_case(&b'a');
+    b'a'.eq_ignore_ascii_case(&b);
+}
+fn ref_str(a: &str, b: &str) {
+    a.eq_ignore_ascii_case(b);
+    a.to_uppercase().eq_ignore_ascii_case(b);
+    a.eq_ignore_ascii_case("a");
+    "a".eq_ignore_ascii_case(b);
+}
+fn ref_ref_str(a: &&str, b: &&str) {
+    a.eq_ignore_ascii_case(b);
+    a.to_uppercase().eq_ignore_ascii_case(b);
+    a.eq_ignore_ascii_case("a");
+    "a".eq_ignore_ascii_case(b);
+}
+fn string(a: String, b: String) {
+    a.eq_ignore_ascii_case(&b);
+    a.eq_ignore_ascii_case("a");
+    "a".eq_ignore_ascii_case(&b);
+    &a.to_ascii_lowercase() == &b.to_ascii_lowercase();
+    &&a.to_ascii_lowercase() == &&b.to_ascii_lowercase();
+    a.eq_ignore_ascii_case("a");
+    "a".eq_ignore_ascii_case(&b);
+}
+fn ref_string(a: String, b: &String) {
+    a.eq_ignore_ascii_case(b);
+    a.eq_ignore_ascii_case("a");
+    "a".eq_ignore_ascii_case(b);
+
+    b.eq_ignore_ascii_case(&a);
+    b.eq_ignore_ascii_case("a");
+    "a".eq_ignore_ascii_case(&a);
+}
+fn string_ref_str(a: String, b: &str) {
+    a.eq_ignore_ascii_case(b);
+    a.eq_ignore_ascii_case("a");
+    "a".eq_ignore_ascii_case(b);
+
+    b.eq_ignore_ascii_case(&a);
+    b.eq_ignore_ascii_case("a");
+    "a".eq_ignore_ascii_case(&a);
+}
+fn ref_u8slice(a: &[u8], b: &[u8]) {
+    a.eq_ignore_ascii_case(b);
+}
+fn u8vec(a: Vec<u8>, b: Vec<u8>) {
+    a.eq_ignore_ascii_case(&b);
+}
+fn ref_u8vec(a: Vec<u8>, b: &Vec<u8>) {
+    a.eq_ignore_ascii_case(b);
+    b.eq_ignore_ascii_case(&a);
+}
+fn ref_osstr(a: &OsStr, b: &OsStr) {
+    a.eq_ignore_ascii_case(b);
+}
+fn osstring(a: OsString, b: OsString) {
+    a.eq_ignore_ascii_case(b);
+}
+fn ref_osstring(a: OsString, b: &OsString) {
+    a.eq_ignore_ascii_case(b);
+    b.eq_ignore_ascii_case(a);
+}
diff --git a/src/tools/clippy/tests/ui/manual_ignore_case_cmp.rs b/src/tools/clippy/tests/ui/manual_ignore_case_cmp.rs
new file mode 100644
index 00000000000..2a4d84b30ac
--- /dev/null
+++ b/src/tools/clippy/tests/ui/manual_ignore_case_cmp.rs
@@ -0,0 +1,107 @@
+#![allow(clippy::all)]
+#![deny(clippy::manual_ignore_case_cmp)]
+
+use std::ffi::{OsStr, OsString};
+
+fn main() {}
+
+fn variants(a: &str, b: &str) {
+    if a.to_ascii_lowercase() == b.to_ascii_lowercase() {
+        return;
+    }
+    if a.to_ascii_uppercase() == b.to_ascii_uppercase() {
+        return;
+    }
+    let r = a.to_ascii_lowercase() == b.to_ascii_lowercase();
+    let r = r || a.to_ascii_uppercase() == b.to_ascii_uppercase();
+    r && a.to_ascii_lowercase() == b.to_uppercase().to_ascii_lowercase();
+    // !=
+    if a.to_ascii_lowercase() != b.to_ascii_lowercase() {
+        return;
+    }
+    if a.to_ascii_uppercase() != b.to_ascii_uppercase() {
+        return;
+    }
+    let r = a.to_ascii_lowercase() != b.to_ascii_lowercase();
+    let r = r || a.to_ascii_uppercase() != b.to_ascii_uppercase();
+    r && a.to_ascii_lowercase() != b.to_uppercase().to_ascii_lowercase();
+}
+
+fn unsupported(a: char, b: char) {
+    // TODO:: these are rare, and might not be worth supporting
+    a.to_ascii_lowercase() == char::to_ascii_lowercase(&b);
+    char::to_ascii_lowercase(&a) == b.to_ascii_lowercase();
+    char::to_ascii_lowercase(&a) == char::to_ascii_lowercase(&b);
+}
+
+fn char(a: char, b: char) {
+    a.to_ascii_lowercase() == b.to_ascii_lowercase();
+    a.to_ascii_lowercase() == *&b.to_ascii_lowercase();
+    *&a.to_ascii_lowercase() == b.to_ascii_lowercase();
+    a.to_ascii_lowercase() == 'a';
+    'a' == b.to_ascii_lowercase();
+}
+fn u8(a: u8, b: u8) {
+    a.to_ascii_lowercase() == b.to_ascii_lowercase();
+    a.to_ascii_lowercase() == b'a';
+    b'a' == b.to_ascii_lowercase();
+}
+fn ref_str(a: &str, b: &str) {
+    a.to_ascii_lowercase() == b.to_ascii_lowercase();
+    a.to_uppercase().to_ascii_lowercase() == b.to_ascii_lowercase();
+    a.to_ascii_lowercase() == "a";
+    "a" == b.to_ascii_lowercase();
+}
+fn ref_ref_str(a: &&str, b: &&str) {
+    a.to_ascii_lowercase() == b.to_ascii_lowercase();
+    a.to_uppercase().to_ascii_lowercase() == b.to_ascii_lowercase();
+    a.to_ascii_lowercase() == "a";
+    "a" == b.to_ascii_lowercase();
+}
+fn string(a: String, b: String) {
+    a.to_ascii_lowercase() == b.to_ascii_lowercase();
+    a.to_ascii_lowercase() == "a";
+    "a" == b.to_ascii_lowercase();
+    &a.to_ascii_lowercase() == &b.to_ascii_lowercase();
+    &&a.to_ascii_lowercase() == &&b.to_ascii_lowercase();
+    a.to_ascii_lowercase() == "a";
+    "a" == b.to_ascii_lowercase();
+}
+fn ref_string(a: String, b: &String) {
+    a.to_ascii_lowercase() == b.to_ascii_lowercase();
+    a.to_ascii_lowercase() == "a";
+    "a" == b.to_ascii_lowercase();
+
+    b.to_ascii_lowercase() == a.to_ascii_lowercase();
+    b.to_ascii_lowercase() == "a";
+    "a" == a.to_ascii_lowercase();
+}
+fn string_ref_str(a: String, b: &str) {
+    a.to_ascii_lowercase() == b.to_ascii_lowercase();
+    a.to_ascii_lowercase() == "a";
+    "a" == b.to_ascii_lowercase();
+
+    b.to_ascii_lowercase() == a.to_ascii_lowercase();
+    b.to_ascii_lowercase() == "a";
+    "a" == a.to_ascii_lowercase();
+}
+fn ref_u8slice(a: &[u8], b: &[u8]) {
+    a.to_ascii_lowercase() == b.to_ascii_lowercase();
+}
+fn u8vec(a: Vec<u8>, b: Vec<u8>) {
+    a.to_ascii_lowercase() == b.to_ascii_lowercase();
+}
+fn ref_u8vec(a: Vec<u8>, b: &Vec<u8>) {
+    a.to_ascii_lowercase() == b.to_ascii_lowercase();
+    b.to_ascii_lowercase() == a.to_ascii_lowercase();
+}
+fn ref_osstr(a: &OsStr, b: &OsStr) {
+    a.to_ascii_lowercase() == b.to_ascii_lowercase();
+}
+fn osstring(a: OsString, b: OsString) {
+    a.to_ascii_lowercase() == b.to_ascii_lowercase();
+}
+fn ref_osstring(a: OsString, b: &OsString) {
+    a.to_ascii_lowercase() == b.to_ascii_lowercase();
+    b.to_ascii_lowercase() == a.to_ascii_lowercase();
+}
diff --git a/src/tools/clippy/tests/ui/manual_ignore_case_cmp.stderr b/src/tools/clippy/tests/ui/manual_ignore_case_cmp.stderr
new file mode 100644
index 00000000000..11e8b8aebb5
--- /dev/null
+++ b/src/tools/clippy/tests/ui/manual_ignore_case_cmp.stderr
@@ -0,0 +1,546 @@
+error: manual case-insensitive ASCII comparison
+  --> tests/ui/manual_ignore_case_cmp.rs:9:8
+   |
+LL |     if a.to_ascii_lowercase() == b.to_ascii_lowercase() {
+   |        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+note: the lint level is defined here
+  --> tests/ui/manual_ignore_case_cmp.rs:2:9
+   |
+LL | #![deny(clippy::manual_ignore_case_cmp)]
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+help: consider using `.eq_ignore_ascii_case()` instead
+   |
+LL |     if a.eq_ignore_ascii_case(b) {
+   |        ~~~~~~~~~~~~~~~~~~~~~~~~~
+
+error: manual case-insensitive ASCII comparison
+  --> tests/ui/manual_ignore_case_cmp.rs:12:8
+   |
+LL |     if a.to_ascii_uppercase() == b.to_ascii_uppercase() {
+   |        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: consider using `.eq_ignore_ascii_case()` instead
+   |
+LL |     if a.eq_ignore_ascii_case(b) {
+   |        ~~~~~~~~~~~~~~~~~~~~~~~~~
+
+error: manual case-insensitive ASCII comparison
+  --> tests/ui/manual_ignore_case_cmp.rs:15:13
+   |
+LL |     let r = a.to_ascii_lowercase() == b.to_ascii_lowercase();
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: consider using `.eq_ignore_ascii_case()` instead
+   |
+LL |     let r = a.eq_ignore_ascii_case(b);
+   |             ~~~~~~~~~~~~~~~~~~~~~~~~~
+
+error: manual case-insensitive ASCII comparison
+  --> tests/ui/manual_ignore_case_cmp.rs:16:18
+   |
+LL |     let r = r || a.to_ascii_uppercase() == b.to_ascii_uppercase();
+   |                  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: consider using `.eq_ignore_ascii_case()` instead
+   |
+LL |     let r = r || a.eq_ignore_ascii_case(b);
+   |                  ~~~~~~~~~~~~~~~~~~~~~~~~~
+
+error: manual case-insensitive ASCII comparison
+  --> tests/ui/manual_ignore_case_cmp.rs:17:10
+   |
+LL |     r && a.to_ascii_lowercase() == b.to_uppercase().to_ascii_lowercase();
+   |          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: consider using `.eq_ignore_ascii_case()` instead
+   |
+LL |     r && a.eq_ignore_ascii_case(&b.to_uppercase());
+   |          ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+error: manual case-insensitive ASCII comparison
+  --> tests/ui/manual_ignore_case_cmp.rs:19:8
+   |
+LL |     if a.to_ascii_lowercase() != b.to_ascii_lowercase() {
+   |        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: consider using `.eq_ignore_ascii_case()` instead
+   |
+LL |     if !a.eq_ignore_ascii_case(b) {
+   |        ~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+error: manual case-insensitive ASCII comparison
+  --> tests/ui/manual_ignore_case_cmp.rs:22:8
+   |
+LL |     if a.to_ascii_uppercase() != b.to_ascii_uppercase() {
+   |        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: consider using `.eq_ignore_ascii_case()` instead
+   |
+LL |     if !a.eq_ignore_ascii_case(b) {
+   |        ~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+error: manual case-insensitive ASCII comparison
+  --> tests/ui/manual_ignore_case_cmp.rs:25:13
+   |
+LL |     let r = a.to_ascii_lowercase() != b.to_ascii_lowercase();
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: consider using `.eq_ignore_ascii_case()` instead
+   |
+LL |     let r = !a.eq_ignore_ascii_case(b);
+   |             ~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+error: manual case-insensitive ASCII comparison
+  --> tests/ui/manual_ignore_case_cmp.rs:26:18
+   |
+LL |     let r = r || a.to_ascii_uppercase() != b.to_ascii_uppercase();
+   |                  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: consider using `.eq_ignore_ascii_case()` instead
+   |
+LL |     let r = r || !a.eq_ignore_ascii_case(b);
+   |                  ~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+error: manual case-insensitive ASCII comparison
+  --> tests/ui/manual_ignore_case_cmp.rs:27:10
+   |
+LL |     r && a.to_ascii_lowercase() != b.to_uppercase().to_ascii_lowercase();
+   |          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: consider using `.eq_ignore_ascii_case()` instead
+   |
+LL |     r && !a.eq_ignore_ascii_case(&b.to_uppercase());
+   |          ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+error: manual case-insensitive ASCII comparison
+  --> tests/ui/manual_ignore_case_cmp.rs:38:5
+   |
+LL |     a.to_ascii_lowercase() == b.to_ascii_lowercase();
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: consider using `.eq_ignore_ascii_case()` instead
+   |
+LL |     a.eq_ignore_ascii_case(&b);
+   |     ~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+error: manual case-insensitive ASCII comparison
+  --> tests/ui/manual_ignore_case_cmp.rs:41:5
+   |
+LL |     a.to_ascii_lowercase() == 'a';
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: consider using `.eq_ignore_ascii_case()` instead
+   |
+LL |     a.eq_ignore_ascii_case(&'a');
+   |     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+error: manual case-insensitive ASCII comparison
+  --> tests/ui/manual_ignore_case_cmp.rs:42:5
+   |
+LL |     'a' == b.to_ascii_lowercase();
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: consider using `.eq_ignore_ascii_case()` instead
+   |
+LL |     'a'.eq_ignore_ascii_case(&b);
+   |     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+error: manual case-insensitive ASCII comparison
+  --> tests/ui/manual_ignore_case_cmp.rs:45:5
+   |
+LL |     a.to_ascii_lowercase() == b.to_ascii_lowercase();
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: consider using `.eq_ignore_ascii_case()` instead
+   |
+LL |     a.eq_ignore_ascii_case(&b);
+   |     ~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+error: manual case-insensitive ASCII comparison
+  --> tests/ui/manual_ignore_case_cmp.rs:46:5
+   |
+LL |     a.to_ascii_lowercase() == b'a';
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: consider using `.eq_ignore_ascii_case()` instead
+   |
+LL |     a.eq_ignore_ascii_case(&b'a');
+   |     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+error: manual case-insensitive ASCII comparison
+  --> tests/ui/manual_ignore_case_cmp.rs:47:5
+   |
+LL |     b'a' == b.to_ascii_lowercase();
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: consider using `.eq_ignore_ascii_case()` instead
+   |
+LL |     b'a'.eq_ignore_ascii_case(&b);
+   |     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+error: manual case-insensitive ASCII comparison
+  --> tests/ui/manual_ignore_case_cmp.rs:50:5
+   |
+LL |     a.to_ascii_lowercase() == b.to_ascii_lowercase();
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: consider using `.eq_ignore_ascii_case()` instead
+   |
+LL |     a.eq_ignore_ascii_case(b);
+   |     ~~~~~~~~~~~~~~~~~~~~~~~~~
+
+error: manual case-insensitive ASCII comparison
+  --> tests/ui/manual_ignore_case_cmp.rs:51:5
+   |
+LL |     a.to_uppercase().to_ascii_lowercase() == b.to_ascii_lowercase();
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: consider using `.eq_ignore_ascii_case()` instead
+   |
+LL |     a.to_uppercase().eq_ignore_ascii_case(b);
+   |     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+error: manual case-insensitive ASCII comparison
+  --> tests/ui/manual_ignore_case_cmp.rs:52:5
+   |
+LL |     a.to_ascii_lowercase() == "a";
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: consider using `.eq_ignore_ascii_case()` instead
+   |
+LL |     a.eq_ignore_ascii_case("a");
+   |     ~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+error: manual case-insensitive ASCII comparison
+  --> tests/ui/manual_ignore_case_cmp.rs:53:5
+   |
+LL |     "a" == b.to_ascii_lowercase();
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: consider using `.eq_ignore_ascii_case()` instead
+   |
+LL |     "a".eq_ignore_ascii_case(b);
+   |     ~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+error: manual case-insensitive ASCII comparison
+  --> tests/ui/manual_ignore_case_cmp.rs:56:5
+   |
+LL |     a.to_ascii_lowercase() == b.to_ascii_lowercase();
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: consider using `.eq_ignore_ascii_case()` instead
+   |
+LL |     a.eq_ignore_ascii_case(b);
+   |     ~~~~~~~~~~~~~~~~~~~~~~~~~
+
+error: manual case-insensitive ASCII comparison
+  --> tests/ui/manual_ignore_case_cmp.rs:57:5
+   |
+LL |     a.to_uppercase().to_ascii_lowercase() == b.to_ascii_lowercase();
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: consider using `.eq_ignore_ascii_case()` instead
+   |
+LL |     a.to_uppercase().eq_ignore_ascii_case(b);
+   |     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+error: manual case-insensitive ASCII comparison
+  --> tests/ui/manual_ignore_case_cmp.rs:58:5
+   |
+LL |     a.to_ascii_lowercase() == "a";
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: consider using `.eq_ignore_ascii_case()` instead
+   |
+LL |     a.eq_ignore_ascii_case("a");
+   |     ~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+error: manual case-insensitive ASCII comparison
+  --> tests/ui/manual_ignore_case_cmp.rs:59:5
+   |
+LL |     "a" == b.to_ascii_lowercase();
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: consider using `.eq_ignore_ascii_case()` instead
+   |
+LL |     "a".eq_ignore_ascii_case(b);
+   |     ~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+error: manual case-insensitive ASCII comparison
+  --> tests/ui/manual_ignore_case_cmp.rs:62:5
+   |
+LL |     a.to_ascii_lowercase() == b.to_ascii_lowercase();
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: consider using `.eq_ignore_ascii_case()` instead
+   |
+LL |     a.eq_ignore_ascii_case(&b);
+   |     ~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+error: manual case-insensitive ASCII comparison
+  --> tests/ui/manual_ignore_case_cmp.rs:63:5
+   |
+LL |     a.to_ascii_lowercase() == "a";
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: consider using `.eq_ignore_ascii_case()` instead
+   |
+LL |     a.eq_ignore_ascii_case("a");
+   |     ~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+error: manual case-insensitive ASCII comparison
+  --> tests/ui/manual_ignore_case_cmp.rs:64:5
+   |
+LL |     "a" == b.to_ascii_lowercase();
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: consider using `.eq_ignore_ascii_case()` instead
+   |
+LL |     "a".eq_ignore_ascii_case(&b);
+   |     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+error: manual case-insensitive ASCII comparison
+  --> tests/ui/manual_ignore_case_cmp.rs:67:5
+   |
+LL |     a.to_ascii_lowercase() == "a";
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: consider using `.eq_ignore_ascii_case()` instead
+   |
+LL |     a.eq_ignore_ascii_case("a");
+   |     ~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+error: manual case-insensitive ASCII comparison
+  --> tests/ui/manual_ignore_case_cmp.rs:68:5
+   |
+LL |     "a" == b.to_ascii_lowercase();
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: consider using `.eq_ignore_ascii_case()` instead
+   |
+LL |     "a".eq_ignore_ascii_case(&b);
+   |     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+error: manual case-insensitive ASCII comparison
+  --> tests/ui/manual_ignore_case_cmp.rs:71:5
+   |
+LL |     a.to_ascii_lowercase() == b.to_ascii_lowercase();
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: consider using `.eq_ignore_ascii_case()` instead
+   |
+LL |     a.eq_ignore_ascii_case(b);
+   |     ~~~~~~~~~~~~~~~~~~~~~~~~~
+
+error: manual case-insensitive ASCII comparison
+  --> tests/ui/manual_ignore_case_cmp.rs:72:5
+   |
+LL |     a.to_ascii_lowercase() == "a";
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: consider using `.eq_ignore_ascii_case()` instead
+   |
+LL |     a.eq_ignore_ascii_case("a");
+   |     ~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+error: manual case-insensitive ASCII comparison
+  --> tests/ui/manual_ignore_case_cmp.rs:73:5
+   |
+LL |     "a" == b.to_ascii_lowercase();
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: consider using `.eq_ignore_ascii_case()` instead
+   |
+LL |     "a".eq_ignore_ascii_case(b);
+   |     ~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+error: manual case-insensitive ASCII comparison
+  --> tests/ui/manual_ignore_case_cmp.rs:75:5
+   |
+LL |     b.to_ascii_lowercase() == a.to_ascii_lowercase();
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: consider using `.eq_ignore_ascii_case()` instead
+   |
+LL |     b.eq_ignore_ascii_case(&a);
+   |     ~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+error: manual case-insensitive ASCII comparison
+  --> tests/ui/manual_ignore_case_cmp.rs:76:5
+   |
+LL |     b.to_ascii_lowercase() == "a";
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: consider using `.eq_ignore_ascii_case()` instead
+   |
+LL |     b.eq_ignore_ascii_case("a");
+   |     ~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+error: manual case-insensitive ASCII comparison
+  --> tests/ui/manual_ignore_case_cmp.rs:77:5
+   |
+LL |     "a" == a.to_ascii_lowercase();
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: consider using `.eq_ignore_ascii_case()` instead
+   |
+LL |     "a".eq_ignore_ascii_case(&a);
+   |     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+error: manual case-insensitive ASCII comparison
+  --> tests/ui/manual_ignore_case_cmp.rs:80:5
+   |
+LL |     a.to_ascii_lowercase() == b.to_ascii_lowercase();
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: consider using `.eq_ignore_ascii_case()` instead
+   |
+LL |     a.eq_ignore_ascii_case(b);
+   |     ~~~~~~~~~~~~~~~~~~~~~~~~~
+
+error: manual case-insensitive ASCII comparison
+  --> tests/ui/manual_ignore_case_cmp.rs:81:5
+   |
+LL |     a.to_ascii_lowercase() == "a";
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: consider using `.eq_ignore_ascii_case()` instead
+   |
+LL |     a.eq_ignore_ascii_case("a");
+   |     ~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+error: manual case-insensitive ASCII comparison
+  --> tests/ui/manual_ignore_case_cmp.rs:82:5
+   |
+LL |     "a" == b.to_ascii_lowercase();
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: consider using `.eq_ignore_ascii_case()` instead
+   |
+LL |     "a".eq_ignore_ascii_case(b);
+   |     ~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+error: manual case-insensitive ASCII comparison
+  --> tests/ui/manual_ignore_case_cmp.rs:84:5
+   |
+LL |     b.to_ascii_lowercase() == a.to_ascii_lowercase();
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: consider using `.eq_ignore_ascii_case()` instead
+   |
+LL |     b.eq_ignore_ascii_case(&a);
+   |     ~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+error: manual case-insensitive ASCII comparison
+  --> tests/ui/manual_ignore_case_cmp.rs:85:5
+   |
+LL |     b.to_ascii_lowercase() == "a";
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: consider using `.eq_ignore_ascii_case()` instead
+   |
+LL |     b.eq_ignore_ascii_case("a");
+   |     ~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+error: manual case-insensitive ASCII comparison
+  --> tests/ui/manual_ignore_case_cmp.rs:86:5
+   |
+LL |     "a" == a.to_ascii_lowercase();
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: consider using `.eq_ignore_ascii_case()` instead
+   |
+LL |     "a".eq_ignore_ascii_case(&a);
+   |     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+error: manual case-insensitive ASCII comparison
+  --> tests/ui/manual_ignore_case_cmp.rs:89:5
+   |
+LL |     a.to_ascii_lowercase() == b.to_ascii_lowercase();
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: consider using `.eq_ignore_ascii_case()` instead
+   |
+LL |     a.eq_ignore_ascii_case(b);
+   |     ~~~~~~~~~~~~~~~~~~~~~~~~~
+
+error: manual case-insensitive ASCII comparison
+  --> tests/ui/manual_ignore_case_cmp.rs:92:5
+   |
+LL |     a.to_ascii_lowercase() == b.to_ascii_lowercase();
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: consider using `.eq_ignore_ascii_case()` instead
+   |
+LL |     a.eq_ignore_ascii_case(&b);
+   |     ~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+error: manual case-insensitive ASCII comparison
+  --> tests/ui/manual_ignore_case_cmp.rs:95:5
+   |
+LL |     a.to_ascii_lowercase() == b.to_ascii_lowercase();
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: consider using `.eq_ignore_ascii_case()` instead
+   |
+LL |     a.eq_ignore_ascii_case(b);
+   |     ~~~~~~~~~~~~~~~~~~~~~~~~~
+
+error: manual case-insensitive ASCII comparison
+  --> tests/ui/manual_ignore_case_cmp.rs:96:5
+   |
+LL |     b.to_ascii_lowercase() == a.to_ascii_lowercase();
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: consider using `.eq_ignore_ascii_case()` instead
+   |
+LL |     b.eq_ignore_ascii_case(&a);
+   |     ~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+error: manual case-insensitive ASCII comparison
+  --> tests/ui/manual_ignore_case_cmp.rs:99:5
+   |
+LL |     a.to_ascii_lowercase() == b.to_ascii_lowercase();
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: consider using `.eq_ignore_ascii_case()` instead
+   |
+LL |     a.eq_ignore_ascii_case(b);
+   |     ~~~~~~~~~~~~~~~~~~~~~~~~~
+
+error: manual case-insensitive ASCII comparison
+  --> tests/ui/manual_ignore_case_cmp.rs:102:5
+   |
+LL |     a.to_ascii_lowercase() == b.to_ascii_lowercase();
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: consider using `.eq_ignore_ascii_case()` instead
+   |
+LL |     a.eq_ignore_ascii_case(b);
+   |     ~~~~~~~~~~~~~~~~~~~~~~~~~
+
+error: manual case-insensitive ASCII comparison
+  --> tests/ui/manual_ignore_case_cmp.rs:105:5
+   |
+LL |     a.to_ascii_lowercase() == b.to_ascii_lowercase();
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: consider using `.eq_ignore_ascii_case()` instead
+   |
+LL |     a.eq_ignore_ascii_case(b);
+   |     ~~~~~~~~~~~~~~~~~~~~~~~~~
+
+error: manual case-insensitive ASCII comparison
+  --> tests/ui/manual_ignore_case_cmp.rs:106:5
+   |
+LL |     b.to_ascii_lowercase() == a.to_ascii_lowercase();
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: consider using `.eq_ignore_ascii_case()` instead
+   |
+LL |     b.eq_ignore_ascii_case(a);
+   |     ~~~~~~~~~~~~~~~~~~~~~~~~~
+
+error: aborting due to 49 previous errors
+
diff --git a/src/tools/clippy/tests/ui/manual_slice_size_calculation.fixed b/src/tools/clippy/tests/ui/manual_slice_size_calculation.fixed
index 62b372f4b8d..0603b30e346 100644
--- a/src/tools/clippy/tests/ui/manual_slice_size_calculation.fixed
+++ b/src/tools/clippy/tests/ui/manual_slice_size_calculation.fixed
@@ -10,11 +10,15 @@ use proc_macros::external;
 fn main() {
     let v_i32 = Vec::<i32>::new();
     let s_i32 = v_i32.as_slice();
+    let s_i32_ref = &s_i32;
+    let s_i32_ref_ref = &s_i32_ref;
 
     // True positives:
     let _ = std::mem::size_of_val(s_i32); // WARNING
     let _ = std::mem::size_of_val(s_i32); // WARNING
     let _ = std::mem::size_of_val(s_i32) * 5; // WARNING
+    let _ = std::mem::size_of_val(*s_i32_ref); // WARNING
+    let _ = std::mem::size_of_val(**s_i32_ref_ref); // WARNING
 
     let len = s_i32.len();
     let size = size_of::<i32>();
diff --git a/src/tools/clippy/tests/ui/manual_slice_size_calculation.rs b/src/tools/clippy/tests/ui/manual_slice_size_calculation.rs
index d59f5fd8b94..14093e653c0 100644
--- a/src/tools/clippy/tests/ui/manual_slice_size_calculation.rs
+++ b/src/tools/clippy/tests/ui/manual_slice_size_calculation.rs
@@ -10,11 +10,15 @@ use proc_macros::external;
 fn main() {
     let v_i32 = Vec::<i32>::new();
     let s_i32 = v_i32.as_slice();
+    let s_i32_ref = &s_i32;
+    let s_i32_ref_ref = &s_i32_ref;
 
     // True positives:
     let _ = s_i32.len() * size_of::<i32>(); // WARNING
     let _ = size_of::<i32>() * s_i32.len(); // WARNING
     let _ = size_of::<i32>() * s_i32.len() * 5; // WARNING
+    let _ = size_of::<i32>() * s_i32_ref.len(); // WARNING
+    let _ = size_of::<i32>() * s_i32_ref_ref.len(); // WARNING
 
     let len = s_i32.len();
     let size = size_of::<i32>();
diff --git a/src/tools/clippy/tests/ui/manual_slice_size_calculation.stderr b/src/tools/clippy/tests/ui/manual_slice_size_calculation.stderr
index 4bd8a4fdf17..0397f3a4969 100644
--- a/src/tools/clippy/tests/ui/manual_slice_size_calculation.stderr
+++ b/src/tools/clippy/tests/ui/manual_slice_size_calculation.stderr
@@ -1,5 +1,5 @@
 error: manual slice size calculation
-  --> tests/ui/manual_slice_size_calculation.rs:15:13
+  --> tests/ui/manual_slice_size_calculation.rs:17:13
    |
 LL |     let _ = s_i32.len() * size_of::<i32>(); // WARNING
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `std::mem::size_of_val(s_i32)`
@@ -8,40 +8,52 @@ LL |     let _ = s_i32.len() * size_of::<i32>(); // WARNING
    = help: to override `-D warnings` add `#[allow(clippy::manual_slice_size_calculation)]`
 
 error: manual slice size calculation
-  --> tests/ui/manual_slice_size_calculation.rs:16:13
+  --> tests/ui/manual_slice_size_calculation.rs:18:13
    |
 LL |     let _ = size_of::<i32>() * s_i32.len(); // WARNING
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `std::mem::size_of_val(s_i32)`
 
 error: manual slice size calculation
-  --> tests/ui/manual_slice_size_calculation.rs:17:13
+  --> tests/ui/manual_slice_size_calculation.rs:19:13
    |
 LL |     let _ = size_of::<i32>() * s_i32.len() * 5; // WARNING
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `std::mem::size_of_val(s_i32)`
 
 error: manual slice size calculation
+  --> tests/ui/manual_slice_size_calculation.rs:20:13
+   |
+LL |     let _ = size_of::<i32>() * s_i32_ref.len(); // WARNING
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `std::mem::size_of_val(*s_i32_ref)`
+
+error: manual slice size calculation
   --> tests/ui/manual_slice_size_calculation.rs:21:13
    |
+LL |     let _ = size_of::<i32>() * s_i32_ref_ref.len(); // WARNING
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `std::mem::size_of_val(**s_i32_ref_ref)`
+
+error: manual slice size calculation
+  --> tests/ui/manual_slice_size_calculation.rs:25:13
+   |
 LL |     let _ = len * size_of::<i32>(); // WARNING
    |             ^^^^^^^^^^^^^^^^^^^^^^ help: try: `std::mem::size_of_val(s_i32)`
 
 error: manual slice size calculation
-  --> tests/ui/manual_slice_size_calculation.rs:22:13
+  --> tests/ui/manual_slice_size_calculation.rs:26:13
    |
 LL |     let _ = s_i32.len() * size; // WARNING
    |             ^^^^^^^^^^^^^^^^^^ help: try: `std::mem::size_of_val(s_i32)`
 
 error: manual slice size calculation
-  --> tests/ui/manual_slice_size_calculation.rs:23:13
+  --> tests/ui/manual_slice_size_calculation.rs:27:13
    |
 LL |     let _ = len * size; // WARNING
    |             ^^^^^^^^^^ help: try: `std::mem::size_of_val(s_i32)`
 
 error: manual slice size calculation
-  --> tests/ui/manual_slice_size_calculation.rs:25:13
+  --> tests/ui/manual_slice_size_calculation.rs:29:13
    |
 LL |     let _ = external!(&[1u64][..]).len() * size_of::<u64>();
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `std::mem::size_of_val(external!(&[1u64][..]))`
 
-error: aborting due to 7 previous errors
+error: aborting due to 9 previous errors
 
diff --git a/src/tools/clippy/tests/ui/missing_const_for_fn/could_be_const.fixed b/src/tools/clippy/tests/ui/missing_const_for_fn/could_be_const.fixed
index 41b424a8e5d..f7b6e1a186b 100644
--- a/src/tools/clippy/tests/ui/missing_const_for_fn/could_be_const.fixed
+++ b/src/tools/clippy/tests/ui/missing_const_for_fn/could_be_const.fixed
@@ -1,7 +1,7 @@
 #![warn(clippy::missing_const_for_fn)]
 #![allow(incomplete_features, clippy::let_and_return, clippy::missing_transmute_annotations)]
-#![allow(unsupported_calling_conventions)]
-#![feature(const_trait_impl)]
+#![feature(const_trait_impl, abi_vectorcall)]
+
 
 use std::mem::transmute;
 
@@ -212,8 +212,8 @@ mod extern_fn {
     //~^ ERROR: this could be a `const fn`
     const extern "system-unwind" fn system_unwind() {}
     //~^ ERROR: this could be a `const fn`
-    pub const extern "stdcall" fn std_call() {}
+    pub const extern "vectorcall" fn std_call() {}
     //~^ ERROR: this could be a `const fn`
-    pub const extern "stdcall-unwind" fn std_call_unwind() {}
+    pub const extern "vectorcall-unwind" fn std_call_unwind() {}
     //~^ ERROR: this could be a `const fn`
 }
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 27593575a01..4866e321024 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,7 +1,7 @@
 #![warn(clippy::missing_const_for_fn)]
 #![allow(incomplete_features, clippy::let_and_return, clippy::missing_transmute_annotations)]
-#![allow(unsupported_calling_conventions)]
-#![feature(const_trait_impl)]
+#![feature(const_trait_impl, abi_vectorcall)]
+
 
 use std::mem::transmute;
 
@@ -212,8 +212,8 @@ mod extern_fn {
     //~^ ERROR: this could be a `const fn`
     extern "system-unwind" fn system_unwind() {}
     //~^ ERROR: this could be a `const fn`
-    pub extern "stdcall" fn std_call() {}
+    pub extern "vectorcall" fn std_call() {}
     //~^ ERROR: this could be a `const fn`
-    pub extern "stdcall-unwind" fn std_call_unwind() {}
+    pub extern "vectorcall-unwind" fn std_call_unwind() {}
     //~^ ERROR: this could be a `const fn`
 }
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 12d97b17119..f28dec5c7f1 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
@@ -319,23 +319,23 @@ LL |     const extern "system-unwind" fn system_unwind() {}
 error: this could be a `const fn`
   --> tests/ui/missing_const_for_fn/could_be_const.rs:215:5
    |
-LL |     pub extern "stdcall" fn std_call() {}
-   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+LL |     pub extern "vectorcall" fn std_call() {}
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |
 help: make the function `const`
    |
-LL |     pub const extern "stdcall" fn std_call() {}
+LL |     pub const extern "vectorcall" fn std_call() {}
    |         +++++
 
 error: this could be a `const fn`
   --> tests/ui/missing_const_for_fn/could_be_const.rs:217:5
    |
-LL |     pub extern "stdcall-unwind" fn std_call_unwind() {}
-   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+LL |     pub extern "vectorcall-unwind" fn std_call_unwind() {}
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |
 help: make the function `const`
    |
-LL |     pub const extern "stdcall-unwind" fn std_call_unwind() {}
+LL |     pub const extern "vectorcall-unwind" fn std_call_unwind() {}
    |         +++++
 
 error: aborting due to 26 previous errors
diff --git a/src/tools/clippy/tests/ui/module_name_repetitions.rs b/src/tools/clippy/tests/ui/module_name_repetitions.rs
index b75ef87ab36..71d8ac7a1f0 100644
--- a/src/tools/clippy/tests/ui/module_name_repetitions.rs
+++ b/src/tools/clippy/tests/ui/module_name_repetitions.rs
@@ -3,7 +3,7 @@
 #![warn(clippy::module_name_repetitions)]
 #![allow(dead_code)]
 
-mod foo {
+pub mod foo {
     pub fn foo() {}
     pub fn foo_bar() {}
     //~^ ERROR: item name starts with its containing module's name
@@ -20,6 +20,22 @@ mod foo {
     // Should not warn
     pub struct Foobar;
 
+    // #8524 - shouldn't warn when item is declared in a private module...
+    mod error {
+        pub struct Error;
+        pub struct FooError;
+    }
+    pub use error::Error;
+    // ... but should still warn when the item is reexported to create a *public* path with repetition.
+    pub use error::FooError;
+    //~^ ERROR: item name starts with its containing module's name
+
+    // FIXME: This should also warn because it creates the public path `foo::FooIter`.
+    mod iter {
+        pub struct FooIter;
+    }
+    pub use iter::*;
+
     // #12544 - shouldn't warn if item name consists only of an allowed prefix and a module name.
     pub fn to_foo() {}
     pub fn into_foo() {}
diff --git a/src/tools/clippy/tests/ui/module_name_repetitions.stderr b/src/tools/clippy/tests/ui/module_name_repetitions.stderr
index bffb08f6f87..8fd8b394875 100644
--- a/src/tools/clippy/tests/ui/module_name_repetitions.stderr
+++ b/src/tools/clippy/tests/ui/module_name_repetitions.stderr
@@ -31,5 +31,11 @@ error: item name starts with its containing module's name
 LL |     pub struct Foo7Bar;
    |                ^^^^^^^
 
-error: aborting due to 5 previous errors
+error: item name starts with its containing module's name
+  --> tests/ui/module_name_repetitions.rs:30:20
+   |
+LL |     pub use error::FooError;
+   |                    ^^^^^^^^
+
+error: aborting due to 6 previous errors
 
diff --git a/src/tools/clippy/tests/ui/mut_key.stderr b/src/tools/clippy/tests/ui/mut_key.stderr
index 5ad9aad2d0a..8698ed4fd67 100644
--- a/src/tools/clippy/tests/ui/mut_key.stderr
+++ b/src/tools/clippy/tests/ui/mut_key.stderr
@@ -4,6 +4,9 @@ error: mutable key type
 LL | fn should_not_take_this_arg(m: &mut HashMap<Key, usize>, _n: usize) -> HashSet<Key> {
    |                                ^^^^^^^^^^^^^^^^^^^^^^^^
    |
+   = note: ... because it contains `Key`, which has interior mutability
+   = note: ... because it contains `AtomicUsize`, which has interior mutability
+   = note: ... because it contains `UnsafeCell<usize>`, which has interior mutability
    = note: `-D clippy::mutable-key-type` implied by `-D warnings`
    = help: to override `-D warnings` add `#[allow(clippy::mutable_key_type)]`
 
@@ -12,84 +15,141 @@ error: mutable key type
    |
 LL | fn should_not_take_this_arg(m: &mut HashMap<Key, usize>, _n: usize) -> HashSet<Key> {
    |                                                                        ^^^^^^^^^^^^
+   |
+   = note: ... because it contains `Key`, which has interior mutability
+   = note: ... because it contains `AtomicUsize`, which has interior mutability
+   = note: ... because it contains `UnsafeCell<usize>`, which has interior mutability
 
 error: mutable key type
   --> tests/ui/mut_key.rs:35:5
    |
 LL |     let _other: HashMap<Key, bool> = HashMap::new();
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: ... because it contains `Key`, which has interior mutability
+   = note: ... because it contains `AtomicUsize`, which has interior mutability
+   = note: ... because it contains `UnsafeCell<usize>`, which has interior mutability
 
 error: mutable key type
   --> tests/ui/mut_key.rs:63:22
    |
 LL | fn tuples_bad<U>(_m: &mut HashMap<(Key, U), bool>) {}
    |                      ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: ... because it contains `(Key, U)`, which has interior mutability
+   = note: ... because it contains `Key`, which has interior mutability
+   = note: ... because it contains `AtomicUsize`, which has interior mutability
+   = note: ... because it contains `UnsafeCell<usize>`, which has interior mutability
 
 error: mutable key type
   --> tests/ui/mut_key.rs:76:5
    |
 LL |     let _map = HashMap::<Cell<usize>, usize>::new();
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: ... because it contains `Cell<usize>`, which has interior mutability
+   = note: ... because it contains `UnsafeCell<usize>`, which has interior mutability
 
 error: mutable key type
   --> tests/ui/mut_key.rs:78:5
    |
 LL |     let _map = HashMap::<&mut Cell<usize>, usize>::new();
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: ... because it contains `&mut Cell<usize>`, which has interior mutability
+   = note: ... because it contains `Cell<usize>`, which has interior mutability
+   = note: ... because it contains `UnsafeCell<usize>`, which has interior mutability
 
 error: mutable key type
   --> tests/ui/mut_key.rs:81:5
    |
 LL |     let _map = HashMap::<Vec<Cell<usize>>, usize>::new();
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: ... because it contains `Vec<Cell<usize>>`, which has interior mutability
+   = note: ... because it contains `Cell<usize>`, which has interior mutability
+   = note: ... because it contains `UnsafeCell<usize>`, which has interior mutability
 
 error: mutable key type
   --> tests/ui/mut_key.rs:83:5
    |
 LL |     let _map = HashMap::<BTreeMap<Cell<usize>, ()>, usize>::new();
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: ... because it contains `BTreeMap<Cell<usize>, ()>`, which has interior mutability
+   = note: ... because it contains `Cell<usize>`, which has interior mutability
+   = note: ... because it contains `UnsafeCell<usize>`, which has interior mutability
 
 error: mutable key type
   --> tests/ui/mut_key.rs:85:5
    |
 LL |     let _map = HashMap::<BTreeMap<(), Cell<usize>>, usize>::new();
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: ... because it contains `BTreeMap<(), Cell<usize>>`, which has interior mutability
+   = note: ... because it contains `Cell<usize>`, which has interior mutability
+   = note: ... because it contains `UnsafeCell<usize>`, which has interior mutability
 
 error: mutable key type
   --> tests/ui/mut_key.rs:87:5
    |
 LL |     let _map = HashMap::<BTreeSet<Cell<usize>>, usize>::new();
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: ... because it contains `BTreeSet<Cell<usize>>`, which has interior mutability
+   = note: ... because it contains `Cell<usize>`, which has interior mutability
+   = note: ... because it contains `UnsafeCell<usize>`, which has interior mutability
 
 error: mutable key type
   --> tests/ui/mut_key.rs:89:5
    |
 LL |     let _map = HashMap::<Option<Cell<usize>>, usize>::new();
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: ... because it contains `Option<Cell<usize>>`, which has interior mutability
+   = note: ... because it contains `Cell<usize>`, which has interior mutability
+   = note: ... because it contains `UnsafeCell<usize>`, which has interior mutability
 
 error: mutable key type
   --> tests/ui/mut_key.rs:91:5
    |
 LL |     let _map = HashMap::<Option<Vec<Cell<usize>>>, usize>::new();
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: ... because it contains `Option<Vec<Cell<usize>>>`, which has interior mutability
+   = note: ... because it contains `Vec<Cell<usize>>`, which has interior mutability
+   = note: ... because it contains `Cell<usize>`, which has interior mutability
+   = note: ... because it contains `UnsafeCell<usize>`, which has interior mutability
 
 error: mutable key type
   --> tests/ui/mut_key.rs:94:5
    |
 LL |     let _map = HashMap::<Box<Cell<usize>>, usize>::new();
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: ... because it contains `Box<Cell<usize>>`, which has interior mutability
+   = note: ... because it contains `Cell<usize>`, which has interior mutability
+   = note: ... because it contains `UnsafeCell<usize>`, which has interior mutability
 
 error: mutable key type
   --> tests/ui/mut_key.rs:96:5
    |
 LL |     let _map = HashMap::<Rc<Cell<usize>>, usize>::new();
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: ... because it contains `Rc<Cell<usize>>`, which has interior mutability
+   = note: ... because it contains `Cell<usize>`, which has interior mutability
+   = note: ... because it contains `UnsafeCell<usize>`, which has interior mutability
 
 error: mutable key type
   --> tests/ui/mut_key.rs:98:5
    |
 LL |     let _map = HashMap::<Arc<Cell<usize>>, usize>::new();
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: ... because it contains `Arc<Cell<usize>>`, which has interior mutability
+   = note: ... because it contains `Cell<usize>`, which has interior mutability
+   = note: ... because it contains `UnsafeCell<usize>`, which has interior mutability
 
 error: aborting due to 15 previous errors
 
diff --git a/src/tools/clippy/tests/ui/needless_raw_string.fixed b/src/tools/clippy/tests/ui/needless_raw_string.fixed
index 1a9c601c462..ab061467488 100644
--- a/src/tools/clippy/tests/ui/needless_raw_string.fixed
+++ b/src/tools/clippy/tests/ui/needless_raw_string.fixed
@@ -22,3 +22,12 @@ fn main() {
     b"no hashes";
     c"no hashes";
 }
+
+fn issue_13503() {
+    println!("SELECT * FROM posts");
+    println!("SELECT * FROM posts");
+    println!(r##"SELECT * FROM "posts""##);
+
+    // Test arguments as well
+    println!("{}", "foobar".len());
+}
diff --git a/src/tools/clippy/tests/ui/needless_raw_string.rs b/src/tools/clippy/tests/ui/needless_raw_string.rs
index 1126ea5aa30..5be8bdeb4ad 100644
--- a/src/tools/clippy/tests/ui/needless_raw_string.rs
+++ b/src/tools/clippy/tests/ui/needless_raw_string.rs
@@ -22,3 +22,12 @@ fn main() {
     br"no hashes";
     cr"no hashes";
 }
+
+fn issue_13503() {
+    println!(r"SELECT * FROM posts");
+    println!(r#"SELECT * FROM posts"#);
+    println!(r##"SELECT * FROM "posts""##);
+
+    // Test arguments as well
+    println!("{}", r"foobar".len());
+}
diff --git a/src/tools/clippy/tests/ui/needless_raw_string.stderr b/src/tools/clippy/tests/ui/needless_raw_string.stderr
index 7d3451a03c7..5169f085573 100644
--- a/src/tools/clippy/tests/ui/needless_raw_string.stderr
+++ b/src/tools/clippy/tests/ui/needless_raw_string.stderr
@@ -91,5 +91,41 @@ LL -     cr"no hashes";
 LL +     c"no hashes";
    |
 
-error: aborting due to 7 previous errors
+error: unnecessary raw string literal
+  --> tests/ui/needless_raw_string.rs:27:14
+   |
+LL |     println!(r"SELECT * FROM posts");
+   |              ^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: use a plain string literal instead
+   |
+LL -     println!(r"SELECT * FROM posts");
+LL +     println!("SELECT * FROM posts");
+   |
+
+error: unnecessary raw string literal
+  --> tests/ui/needless_raw_string.rs:28:14
+   |
+LL |     println!(r#"SELECT * FROM posts"#);
+   |              ^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: use a plain string literal instead
+   |
+LL -     println!(r#"SELECT * FROM posts"#);
+LL +     println!("SELECT * FROM posts");
+   |
+
+error: unnecessary raw string literal
+  --> tests/ui/needless_raw_string.rs:32:20
+   |
+LL |     println!("{}", r"foobar".len());
+   |                    ^^^^^^^^^
+   |
+help: use a plain string literal instead
+   |
+LL -     println!("{}", r"foobar".len());
+LL +     println!("{}", "foobar".len());
+   |
+
+error: aborting due to 10 previous errors
 
diff --git a/src/tools/clippy/tests/ui/needless_raw_string_hashes.fixed b/src/tools/clippy/tests/ui/needless_raw_string_hashes.fixed
index b2ad657d6b2..4c113709107 100644
--- a/src/tools/clippy/tests/ui/needless_raw_string_hashes.fixed
+++ b/src/tools/clippy/tests/ui/needless_raw_string_hashes.fixed
@@ -24,3 +24,13 @@ fn main() {
     r"rust";
     r"hello world";
 }
+
+fn issue_13503() {
+    println!(r"SELECT * FROM posts");
+    println!(r"SELECT * FROM posts");
+    println!(r#"SELECT * FROM "posts""#);
+    println!(r#"SELECT * FROM "posts""#);
+
+    // Test arguments as well
+    println!("{}", r"foobar".len());
+}
diff --git a/src/tools/clippy/tests/ui/needless_raw_string_hashes.rs b/src/tools/clippy/tests/ui/needless_raw_string_hashes.rs
index 54d8ed76d47..7b6b4e784ee 100644
--- a/src/tools/clippy/tests/ui/needless_raw_string_hashes.rs
+++ b/src/tools/clippy/tests/ui/needless_raw_string_hashes.rs
@@ -24,3 +24,13 @@ fn main() {
     r###"rust"###;
     r#"hello world"#;
 }
+
+fn issue_13503() {
+    println!(r"SELECT * FROM posts");
+    println!(r#"SELECT * FROM posts"#);
+    println!(r##"SELECT * FROM "posts""##);
+    println!(r##"SELECT * FROM "posts""##);
+
+    // Test arguments as well
+    println!("{}", r"foobar".len());
+}
diff --git a/src/tools/clippy/tests/ui/needless_raw_string_hashes.stderr b/src/tools/clippy/tests/ui/needless_raw_string_hashes.stderr
index 96864f612c0..a213ba3e743 100644
--- a/src/tools/clippy/tests/ui/needless_raw_string_hashes.stderr
+++ b/src/tools/clippy/tests/ui/needless_raw_string_hashes.stderr
@@ -187,5 +187,41 @@ LL -     r#"hello world"#;
 LL +     r"hello world";
    |
 
-error: aborting due to 15 previous errors
+error: unnecessary hashes around raw string literal
+  --> tests/ui/needless_raw_string_hashes.rs:30:14
+   |
+LL |     println!(r#"SELECT * FROM posts"#);
+   |              ^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: remove all the hashes around the string literal
+   |
+LL -     println!(r#"SELECT * FROM posts"#);
+LL +     println!(r"SELECT * FROM posts");
+   |
+
+error: unnecessary hashes around raw string literal
+  --> tests/ui/needless_raw_string_hashes.rs:31:14
+   |
+LL |     println!(r##"SELECT * FROM "posts""##);
+   |              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: remove one hash from both sides of the string literal
+   |
+LL -     println!(r##"SELECT * FROM "posts""##);
+LL +     println!(r#"SELECT * FROM "posts""#);
+   |
+
+error: unnecessary hashes around raw string literal
+  --> tests/ui/needless_raw_string_hashes.rs:32:14
+   |
+LL |     println!(r##"SELECT * FROM "posts""##);
+   |              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: remove one hash from both sides of the string literal
+   |
+LL -     println!(r##"SELECT * FROM "posts""##);
+LL +     println!(r#"SELECT * FROM "posts""#);
+   |
+
+error: aborting due to 18 previous errors
 
diff --git a/src/tools/clippy/tests/ui/or_fun_call.fixed b/src/tools/clippy/tests/ui/or_fun_call.fixed
index 7452eb77688..625d654dd39 100644
--- a/src/tools/clippy/tests/ui/or_fun_call.fixed
+++ b/src/tools/clippy/tests/ui/or_fun_call.fixed
@@ -341,18 +341,18 @@ fn fn_call_in_nested_expr() {
     }
     let opt: Option<i32> = Some(1);
 
-    //~v ERROR: use of `unwrap_or` followed by a function call
+    //~v ERROR: function call inside of `unwrap_or`
     let _ = opt.unwrap_or_else(f); // suggest `.unwrap_or_else(f)`
     //
-    //~v ERROR: use of `unwrap_or` followed by a function call
+    //~v ERROR: function call inside of `unwrap_or`
     let _ = opt.unwrap_or_else(|| f() + 1); // suggest `.unwrap_or_else(|| f() + 1)`
     //
-    //~v ERROR: use of `unwrap_or` followed by a function call
+    //~v ERROR: function call inside of `unwrap_or`
     let _ = opt.unwrap_or_else(|| {
         let x = f();
         x + 1
     });
-    //~v ERROR: use of `map_or` followed by a function call
+    //~v ERROR: function call inside of `map_or`
     let _ = opt.map_or_else(|| f() + 1, |v| v); // suggest `.map_or_else(|| f() + 1, |v| v)`
     //
     //~v ERROR: use of `unwrap_or` to construct default value
@@ -361,7 +361,7 @@ fn fn_call_in_nested_expr() {
     let opt_foo = Some(Foo {
         val: String::from("123"),
     });
-    //~v ERROR: use of `unwrap_or` followed by a function call
+    //~v ERROR: function call inside of `unwrap_or`
     let _ = opt_foo.unwrap_or_else(|| Foo { val: String::default() });
 }
 
diff --git a/src/tools/clippy/tests/ui/or_fun_call.rs b/src/tools/clippy/tests/ui/or_fun_call.rs
index cd6f7bb2070..5b7d8faec7b 100644
--- a/src/tools/clippy/tests/ui/or_fun_call.rs
+++ b/src/tools/clippy/tests/ui/or_fun_call.rs
@@ -341,18 +341,18 @@ fn fn_call_in_nested_expr() {
     }
     let opt: Option<i32> = Some(1);
 
-    //~v ERROR: use of `unwrap_or` followed by a function call
+    //~v ERROR: function call inside of `unwrap_or`
     let _ = opt.unwrap_or({ f() }); // suggest `.unwrap_or_else(f)`
     //
-    //~v ERROR: use of `unwrap_or` followed by a function call
+    //~v ERROR: function call inside of `unwrap_or`
     let _ = opt.unwrap_or(f() + 1); // suggest `.unwrap_or_else(|| f() + 1)`
     //
-    //~v ERROR: use of `unwrap_or` followed by a function call
+    //~v ERROR: function call inside of `unwrap_or`
     let _ = opt.unwrap_or({
         let x = f();
         x + 1
     });
-    //~v ERROR: use of `map_or` followed by a function call
+    //~v ERROR: function call inside of `map_or`
     let _ = opt.map_or(f() + 1, |v| v); // suggest `.map_or_else(|| f() + 1, |v| v)`
     //
     //~v ERROR: use of `unwrap_or` to construct default value
@@ -361,7 +361,7 @@ fn fn_call_in_nested_expr() {
     let opt_foo = Some(Foo {
         val: String::from("123"),
     });
-    //~v ERROR: use of `unwrap_or` followed by a function call
+    //~v ERROR: function call inside of `unwrap_or`
     let _ = opt_foo.unwrap_or(Foo { val: String::default() });
 }
 
diff --git a/src/tools/clippy/tests/ui/or_fun_call.stderr b/src/tools/clippy/tests/ui/or_fun_call.stderr
index 06f804fb41e..9f90a830a21 100644
--- a/src/tools/clippy/tests/ui/or_fun_call.stderr
+++ b/src/tools/clippy/tests/ui/or_fun_call.stderr
@@ -1,4 +1,4 @@
-error: use of `unwrap_or` followed by a function call
+error: function call inside of `unwrap_or`
   --> tests/ui/or_fun_call.rs:52:22
    |
 LL |     with_constructor.unwrap_or(make());
@@ -16,19 +16,19 @@ LL |     with_new.unwrap_or(Vec::new());
    = note: `-D clippy::unwrap-or-default` implied by `-D warnings`
    = help: to override `-D warnings` add `#[allow(clippy::unwrap_or_default)]`
 
-error: use of `unwrap_or` followed by a function call
+error: function call inside of `unwrap_or`
   --> tests/ui/or_fun_call.rs:58:21
    |
 LL |     with_const_args.unwrap_or(Vec::with_capacity(12));
    |                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(|| Vec::with_capacity(12))`
 
-error: use of `unwrap_or` followed by a function call
+error: function call inside of `unwrap_or`
   --> tests/ui/or_fun_call.rs:61:14
    |
 LL |     with_err.unwrap_or(make());
    |              ^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(|_| make())`
 
-error: use of `unwrap_or` followed by a function call
+error: function call inside of `unwrap_or`
   --> tests/ui/or_fun_call.rs:64:19
    |
 LL |     with_err_args.unwrap_or(Vec::with_capacity(12));
@@ -46,7 +46,7 @@ error: use of `unwrap_or` to construct default value
 LL |     with_default_type.unwrap_or(u64::default());
    |                       ^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_default()`
 
-error: use of `unwrap_or` followed by a function call
+error: function call inside of `unwrap_or`
   --> tests/ui/or_fun_call.rs:73:18
    |
 LL |     self_default.unwrap_or(<FakeDefault>::default());
@@ -64,7 +64,7 @@ error: use of `unwrap_or` to construct default value
 LL |     with_vec.unwrap_or(vec![]);
    |              ^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_default()`
 
-error: use of `unwrap_or` followed by a function call
+error: function call inside of `unwrap_or`
   --> tests/ui/or_fun_call.rs:82:21
    |
 LL |     without_default.unwrap_or(Foo::new());
@@ -100,55 +100,55 @@ error: use of `unwrap_or` to construct default value
 LL |     let _ = stringy.unwrap_or(String::new());
    |                     ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_default()`
 
-error: use of `ok_or` followed by a function call
+error: function call inside of `ok_or`
   --> tests/ui/or_fun_call.rs:101:17
    |
 LL |     let _ = opt.ok_or(format!("{} world.", hello));
    |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `ok_or_else(|| format!("{} world.", hello))`
 
-error: use of `unwrap_or` followed by a function call
+error: function call inside of `unwrap_or`
   --> tests/ui/or_fun_call.rs:105:21
    |
 LL |     let _ = Some(1).unwrap_or(map[&1]);
    |                     ^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(|| map[&1])`
 
-error: use of `unwrap_or` followed by a function call
+error: function call inside of `unwrap_or`
   --> tests/ui/or_fun_call.rs:107:21
    |
 LL |     let _ = Some(1).unwrap_or(map[&1]);
    |                     ^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(|| map[&1])`
 
-error: use of `or` followed by a function call
+error: function call inside of `or`
   --> tests/ui/or_fun_call.rs:131:35
    |
 LL |     let _ = Some("a".to_string()).or(Some("b".to_string()));
    |                                   ^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `or_else(|| Some("b".to_string()))`
 
-error: use of `unwrap_or` followed by a function call
+error: function call inside of `unwrap_or`
   --> tests/ui/or_fun_call.rs:170:14
    |
 LL |         None.unwrap_or(ptr_to_ref(s));
    |              ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(|| ptr_to_ref(s))`
 
-error: use of `unwrap_or` followed by a function call
+error: function call inside of `unwrap_or`
   --> tests/ui/or_fun_call.rs:176:14
    |
 LL |         None.unwrap_or(unsafe { ptr_to_ref(s) });
    |              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(|| unsafe { ptr_to_ref(s) })`
 
-error: use of `unwrap_or` followed by a function call
+error: function call inside of `unwrap_or`
   --> tests/ui/or_fun_call.rs:178:14
    |
 LL |         None.unwrap_or( unsafe { ptr_to_ref(s) }    );
    |              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(|| unsafe { ptr_to_ref(s) })`
 
-error: use of `map_or` followed by a function call
+error: function call inside of `map_or`
   --> tests/ui/or_fun_call.rs:253:25
    |
 LL |         let _ = Some(4).map_or(g(), |v| v);
    |                         ^^^^^^^^^^^^^^^^^^ help: try: `map_or_else(g, |v| v)`
 
-error: use of `map_or` followed by a function call
+error: function call inside of `map_or`
   --> tests/ui/or_fun_call.rs:254:25
    |
 LL |         let _ = Some(4).map_or(g(), f);
@@ -196,19 +196,19 @@ error: use of `unwrap_or_else` to construct default value
 LL |         let _ = stringy.unwrap_or_else(String::new);
    |                         ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_default()`
 
-error: use of `unwrap_or` followed by a function call
+error: function call inside of `unwrap_or`
   --> tests/ui/or_fun_call.rs:345:17
    |
 LL |     let _ = opt.unwrap_or({ f() }); // suggest `.unwrap_or_else(f)`
    |                 ^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(f)`
 
-error: use of `unwrap_or` followed by a function call
+error: function call inside of `unwrap_or`
   --> tests/ui/or_fun_call.rs:348:17
    |
 LL |     let _ = opt.unwrap_or(f() + 1); // suggest `.unwrap_or_else(|| f() + 1)`
    |                 ^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(|| f() + 1)`
 
-error: use of `unwrap_or` followed by a function call
+error: function call inside of `unwrap_or`
   --> tests/ui/or_fun_call.rs:351:17
    |
 LL |       let _ = opt.unwrap_or({
@@ -226,7 +226,7 @@ LL +         x + 1
 LL ~     });
    |
 
-error: use of `map_or` followed by a function call
+error: function call inside of `map_or`
   --> tests/ui/or_fun_call.rs:356:17
    |
 LL |     let _ = opt.map_or(f() + 1, |v| v); // suggest `.map_or_else(|| f() + 1, |v| v)`
@@ -238,7 +238,7 @@ error: use of `unwrap_or` to construct default value
 LL |     let _ = opt.unwrap_or({ i32::default() });
    |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_default()`
 
-error: use of `unwrap_or` followed by a function call
+error: function call inside of `unwrap_or`
   --> tests/ui/or_fun_call.rs:365:21
    |
 LL |     let _ = opt_foo.unwrap_or(Foo { val: String::default() });
diff --git a/src/tools/clippy/tests/ui/regex.rs b/src/tools/clippy/tests/ui/regex.rs
index 4fb6c08bb44..f607a2d50c6 100644
--- a/src/tools/clippy/tests/ui/regex.rs
+++ b/src/tools/clippy/tests/ui/regex.rs
@@ -5,7 +5,7 @@
     clippy::needless_borrow,
     clippy::needless_borrows_for_generic_args
 )]
-#![warn(clippy::invalid_regex, clippy::trivial_regex)]
+#![warn(clippy::invalid_regex, clippy::trivial_regex, clippy::regex_creation_in_loops)]
 
 extern crate regex;
 
@@ -118,7 +118,35 @@ fn trivial_regex() {
     let _ = BRegex::new(r"\b{start}word\b{end}");
 }
 
+fn regex_creation_in_loops() {
+    loop {
+        static STATIC_REGEX: std::sync::LazyLock<Regex> = std::sync::LazyLock::new(|| Regex::new("a.b").unwrap());
+
+        let regex = Regex::new("a.b");
+        //~^ ERROR: compiling a regex in a loop
+        let regex = BRegex::new("a.b");
+        //~^ ERROR: compiling a regex in a loop
+        #[allow(clippy::regex_creation_in_loops)]
+        let allowed_regex = Regex::new("a.b");
+
+        if true {
+            let regex = Regex::new("a.b");
+            //~^ ERROR: compiling a regex in a loop
+        }
+
+        for _ in 0..10 {
+            let nested_regex = Regex::new("a.b");
+            //~^ ERROR: compiling a regex in a loop
+        }
+    }
+
+    for i in 0..10 {
+        let dependant_regex = Regex::new(&format!("{i}"));
+    }
+}
+
 fn main() {
     syntax_error();
     trivial_regex();
+    regex_creation_in_loops();
 }
diff --git a/src/tools/clippy/tests/ui/regex.stderr b/src/tools/clippy/tests/ui/regex.stderr
index e936208d8d7..18dd538c68b 100644
--- a/src/tools/clippy/tests/ui/regex.stderr
+++ b/src/tools/clippy/tests/ui/regex.stderr
@@ -195,5 +195,55 @@ LL |     let binary_trivial_empty = BRegex::new("^$");
    |
    = help: consider using `str::is_empty`
 
-error: aborting due to 24 previous errors
+error: compiling a regex in a loop
+  --> tests/ui/regex.rs:125:21
+   |
+LL |         let regex = Regex::new("a.b");
+   |                     ^^^^^^^^^^
+   |
+help: move the regex construction outside this loop
+  --> tests/ui/regex.rs:122:5
+   |
+LL |     loop {
+   |     ^^^^
+   = note: `-D clippy::regex-creation-in-loops` implied by `-D warnings`
+   = help: to override `-D warnings` add `#[allow(clippy::regex_creation_in_loops)]`
+
+error: compiling a regex in a loop
+  --> tests/ui/regex.rs:127:21
+   |
+LL |         let regex = BRegex::new("a.b");
+   |                     ^^^^^^^^^^^
+   |
+help: move the regex construction outside this loop
+  --> tests/ui/regex.rs:122:5
+   |
+LL |     loop {
+   |     ^^^^
+
+error: compiling a regex in a loop
+  --> tests/ui/regex.rs:133:25
+   |
+LL |             let regex = Regex::new("a.b");
+   |                         ^^^^^^^^^^
+   |
+help: move the regex construction outside this loop
+  --> tests/ui/regex.rs:122:5
+   |
+LL |     loop {
+   |     ^^^^
+
+error: compiling a regex in a loop
+  --> tests/ui/regex.rs:138:32
+   |
+LL |             let nested_regex = Regex::new("a.b");
+   |                                ^^^^^^^^^^
+   |
+help: move the regex construction outside this loop
+  --> tests/ui/regex.rs:137:9
+   |
+LL |         for _ in 0..10 {
+   |         ^^^^^^^^^^^^^^
+
+error: aborting due to 28 previous errors
 
diff --git a/src/tools/clippy/tests/ui/trait_duplication_in_bounds.fixed b/src/tools/clippy/tests/ui/trait_duplication_in_bounds.fixed
index 7e2663d734f..779431303ae 100644
--- a/src/tools/clippy/tests/ui/trait_duplication_in_bounds.fixed
+++ b/src/tools/clippy/tests/ui/trait_duplication_in_bounds.fixed
@@ -1,5 +1,6 @@
 #![deny(clippy::trait_duplication_in_bounds)]
 #![allow(unused)]
+#![feature(const_trait_impl)]
 
 use std::any::Any;
 
@@ -144,6 +145,36 @@ fn f<P: Proj>(obj: &dyn Derived<P>) {
     Base::<()>::is_base(obj);
 }
 
+// #13476
+trait Value<const N: usize> {}
+fn const_generic<T: Value<0> + Value<1>>() {}
+
+// #11067 and #9626
+fn assoc_tys_generics<'a, 'b, T, U>()
+where
+    T: IntoIterator<Item = ()> + IntoIterator<Item = i32>,
+    U: From<&'a str> + From<&'b [u16]>,
+{
+}
+
+// #13476
+#[const_trait]
+trait ConstTrait {}
+const fn const_trait_bounds_good<T: ConstTrait + ~const ConstTrait>() {}
+
+const fn const_trait_bounds_bad<T: ~const ConstTrait>() {}
+//~^ trait_duplication_in_bounds
+
+fn projections<T, U, V>()
+where
+    U: ToOwned,
+    V: ToOwned,
+    T: IntoIterator<Item = U::Owned>,
+    //~^ trait_duplication_in_bounds
+    V: IntoIterator<Item = U::Owned> + IntoIterator<Item = V::Owned>,
+{
+}
+
 fn main() {
     let _x: fn(_) = f::<()>;
     let _x: fn(_) = f::<i32>;
diff --git a/src/tools/clippy/tests/ui/trait_duplication_in_bounds.rs b/src/tools/clippy/tests/ui/trait_duplication_in_bounds.rs
index fede1671a43..3e974dc0a8f 100644
--- a/src/tools/clippy/tests/ui/trait_duplication_in_bounds.rs
+++ b/src/tools/clippy/tests/ui/trait_duplication_in_bounds.rs
@@ -1,5 +1,6 @@
 #![deny(clippy::trait_duplication_in_bounds)]
 #![allow(unused)]
+#![feature(const_trait_impl)]
 
 use std::any::Any;
 
@@ -144,6 +145,36 @@ fn f<P: Proj>(obj: &dyn Derived<P>) {
     Base::<()>::is_base(obj);
 }
 
+// #13476
+trait Value<const N: usize> {}
+fn const_generic<T: Value<0> + Value<1>>() {}
+
+// #11067 and #9626
+fn assoc_tys_generics<'a, 'b, T, U>()
+where
+    T: IntoIterator<Item = ()> + IntoIterator<Item = i32>,
+    U: From<&'a str> + From<&'b [u16]>,
+{
+}
+
+// #13476
+#[const_trait]
+trait ConstTrait {}
+const fn const_trait_bounds_good<T: ConstTrait + ~const ConstTrait>() {}
+
+const fn const_trait_bounds_bad<T: ~const ConstTrait + ~const ConstTrait>() {}
+//~^ trait_duplication_in_bounds
+
+fn projections<T, U, V>()
+where
+    U: ToOwned,
+    V: ToOwned,
+    T: IntoIterator<Item = U::Owned> + IntoIterator<Item = U::Owned>,
+    //~^ trait_duplication_in_bounds
+    V: IntoIterator<Item = U::Owned> + IntoIterator<Item = V::Owned>,
+{
+}
+
 fn main() {
     let _x: fn(_) = f::<()>;
     let _x: fn(_) = f::<i32>;
diff --git a/src/tools/clippy/tests/ui/trait_duplication_in_bounds.stderr b/src/tools/clippy/tests/ui/trait_duplication_in_bounds.stderr
index 78861fc16e8..0dd508e4745 100644
--- a/src/tools/clippy/tests/ui/trait_duplication_in_bounds.stderr
+++ b/src/tools/clippy/tests/ui/trait_duplication_in_bounds.stderr
@@ -1,5 +1,5 @@
 error: these bounds contain repeated elements
-  --> tests/ui/trait_duplication_in_bounds.rs:6:15
+  --> tests/ui/trait_duplication_in_bounds.rs:7:15
    |
 LL | fn bad_foo<T: Clone + Clone + Clone + Copy, U: Clone + Copy>(arg0: T, argo1: U) {
    |               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Clone + Copy`
@@ -11,52 +11,64 @@ LL | #![deny(clippy::trait_duplication_in_bounds)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 error: these where clauses contain repeated elements
-  --> tests/ui/trait_duplication_in_bounds.rs:12:8
+  --> tests/ui/trait_duplication_in_bounds.rs:13:8
    |
 LL |     T: Clone + Clone + Clone + Copy,
    |        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Clone + Copy`
 
 error: these bounds contain repeated elements
-  --> tests/ui/trait_duplication_in_bounds.rs:40:26
+  --> tests/ui/trait_duplication_in_bounds.rs:41:26
    |
 LL | trait BadSelfTraitBound: Clone + Clone + Clone {
    |                          ^^^^^^^^^^^^^^^^^^^^^ help: try: `Clone`
 
 error: these where clauses contain repeated elements
-  --> tests/ui/trait_duplication_in_bounds.rs:47:15
+  --> tests/ui/trait_duplication_in_bounds.rs:48:15
    |
 LL |         Self: Clone + Clone + Clone;
    |               ^^^^^^^^^^^^^^^^^^^^^ help: try: `Clone`
 
 error: these bounds contain repeated elements
-  --> tests/ui/trait_duplication_in_bounds.rs:61:24
+  --> tests/ui/trait_duplication_in_bounds.rs:62:24
    |
 LL | trait BadTraitBound<T: Clone + Clone + Clone + Copy, U: Clone + Copy> {
    |                        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Clone + Copy`
 
 error: these where clauses contain repeated elements
-  --> tests/ui/trait_duplication_in_bounds.rs:68:12
+  --> tests/ui/trait_duplication_in_bounds.rs:69:12
    |
 LL |         T: Clone + Clone + Clone + Copy,
    |            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Clone + Copy`
 
 error: these bounds contain repeated elements
-  --> tests/ui/trait_duplication_in_bounds.rs:101:19
+  --> tests/ui/trait_duplication_in_bounds.rs:102:19
    |
 LL | fn bad_generic<T: GenericTrait<u64> + GenericTrait<u32> + GenericTrait<u64>>(arg0: T) {
    |                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `GenericTrait<u64> + GenericTrait<u32>`
 
 error: these bounds contain repeated elements
-  --> tests/ui/trait_duplication_in_bounds.rs:109:22
+  --> tests/ui/trait_duplication_in_bounds.rs:110:22
    |
 LL | fn qualified_path<T: std::clone::Clone + Clone + foo::Clone>(arg0: T) {
    |                      ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `std::clone::Clone + foo::Clone`
 
 error: this trait bound is already specified in trait declaration
-  --> tests/ui/trait_duplication_in_bounds.rs:117:33
+  --> tests/ui/trait_duplication_in_bounds.rs:118:33
    |
 LL | fn bad_trait_object(arg0: &(dyn Any + Send + Send)) {
    |                                 ^^^^^^^^^^^^^^^^^ help: try: `Any + Send`
 
-error: aborting due to 9 previous errors
+error: these bounds contain repeated elements
+  --> tests/ui/trait_duplication_in_bounds.rs:165:36
+   |
+LL | const fn const_trait_bounds_bad<T: ~const ConstTrait + ~const ConstTrait>() {}
+   |                                    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `~const ConstTrait`
+
+error: these where clauses contain repeated elements
+  --> tests/ui/trait_duplication_in_bounds.rs:172:8
+   |
+LL |     T: IntoIterator<Item = U::Owned> + IntoIterator<Item = U::Owned>,
+   |        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `IntoIterator<Item = U::Owned>`
+
+error: aborting due to 11 previous errors
 
diff --git a/src/tools/clippy/tests/ui/transmutes_expressible_as_ptr_casts.fixed b/src/tools/clippy/tests/ui/transmutes_expressible_as_ptr_casts.fixed
index 617d32d1fa7..a4a3ca82e76 100644
--- a/src/tools/clippy/tests/ui/transmutes_expressible_as_ptr_casts.fixed
+++ b/src/tools/clippy/tests/ui/transmutes_expressible_as_ptr_casts.fixed
@@ -84,7 +84,10 @@ fn issue_10449() {
 }
 
 // Pointers cannot be cast to integers in const contexts
-#[allow(ptr_to_integer_transmute_in_consts, reason = "This is tested in the compiler test suite")]
+#[allow(
+    ptr_to_integer_transmute_in_consts,
+    reason = "This is tested in the compiler test suite"
+)]
 const fn issue_12402<P>(ptr: *const P) {
     // This test exists even though the compiler lints against it
     // to test that clippy's transmute lints do not trigger on this.
diff --git a/src/tools/clippy/tests/ui/transmutes_expressible_as_ptr_casts.rs b/src/tools/clippy/tests/ui/transmutes_expressible_as_ptr_casts.rs
index d68db3c2deb..6aa8e384e26 100644
--- a/src/tools/clippy/tests/ui/transmutes_expressible_as_ptr_casts.rs
+++ b/src/tools/clippy/tests/ui/transmutes_expressible_as_ptr_casts.rs
@@ -84,7 +84,10 @@ fn issue_10449() {
 }
 
 // Pointers cannot be cast to integers in const contexts
-#[allow(ptr_to_integer_transmute_in_consts, reason = "This is tested in the compiler test suite")]
+#[allow(
+    ptr_to_integer_transmute_in_consts,
+    reason = "This is tested in the compiler test suite"
+)]
 const fn issue_12402<P>(ptr: *const P) {
     // This test exists even though the compiler lints against it
     // to test that clippy's transmute lints do not trigger on this.
diff --git a/src/tools/clippy/tests/ui/unnecessary_literal_bound.fixed b/src/tools/clippy/tests/ui/unnecessary_literal_bound.fixed
new file mode 100644
index 00000000000..107e397466d
--- /dev/null
+++ b/src/tools/clippy/tests/ui/unnecessary_literal_bound.fixed
@@ -0,0 +1,65 @@
+#![warn(clippy::unnecessary_literal_bound)]
+
+struct Struct<'a> {
+    not_literal: &'a str,
+}
+
+impl Struct<'_> {
+    // Should warn
+    fn returns_lit(&self) -> &'static str {
+        "Hello"
+    }
+
+    // Should NOT warn
+    fn returns_non_lit(&self) -> &str {
+        self.not_literal
+    }
+
+    // Should warn, does not currently
+    fn conditionally_returns_lit(&self, cond: bool) -> &str {
+        if cond { "Literal" } else { "also a literal" }
+    }
+
+    // Should NOT warn
+    fn conditionally_returns_non_lit(&self, cond: bool) -> &str {
+        if cond { "Literal" } else { self.not_literal }
+    }
+
+    // Should warn
+    fn contionally_returns_literals_explicit(&self, cond: bool) -> &'static str {
+        if cond {
+            return "Literal";
+        }
+
+        "also a literal"
+    }
+
+    // Should NOT warn
+    fn conditionally_returns_non_lit_explicit(&self, cond: bool) -> &str {
+        if cond {
+            return self.not_literal;
+        }
+
+        "Literal"
+    }
+}
+
+trait ReturnsStr {
+    fn trait_method(&self) -> &str;
+}
+
+impl ReturnsStr for u8 {
+    // Should warn, even though not useful without trait refinement
+    fn trait_method(&self) -> &'static str {
+        "Literal"
+    }
+}
+
+impl ReturnsStr for Struct<'_> {
+    // Should NOT warn
+    fn trait_method(&self) -> &str {
+        self.not_literal
+    }
+}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/unnecessary_literal_bound.rs b/src/tools/clippy/tests/ui/unnecessary_literal_bound.rs
new file mode 100644
index 00000000000..b371ff9d3a2
--- /dev/null
+++ b/src/tools/clippy/tests/ui/unnecessary_literal_bound.rs
@@ -0,0 +1,65 @@
+#![warn(clippy::unnecessary_literal_bound)]
+
+struct Struct<'a> {
+    not_literal: &'a str,
+}
+
+impl Struct<'_> {
+    // Should warn
+    fn returns_lit(&self) -> &str {
+        "Hello"
+    }
+
+    // Should NOT warn
+    fn returns_non_lit(&self) -> &str {
+        self.not_literal
+    }
+
+    // Should warn, does not currently
+    fn conditionally_returns_lit(&self, cond: bool) -> &str {
+        if cond { "Literal" } else { "also a literal" }
+    }
+
+    // Should NOT warn
+    fn conditionally_returns_non_lit(&self, cond: bool) -> &str {
+        if cond { "Literal" } else { self.not_literal }
+    }
+
+    // Should warn
+    fn contionally_returns_literals_explicit(&self, cond: bool) -> &str {
+        if cond {
+            return "Literal";
+        }
+
+        "also a literal"
+    }
+
+    // Should NOT warn
+    fn conditionally_returns_non_lit_explicit(&self, cond: bool) -> &str {
+        if cond {
+            return self.not_literal;
+        }
+
+        "Literal"
+    }
+}
+
+trait ReturnsStr {
+    fn trait_method(&self) -> &str;
+}
+
+impl ReturnsStr for u8 {
+    // Should warn, even though not useful without trait refinement
+    fn trait_method(&self) -> &str {
+        "Literal"
+    }
+}
+
+impl ReturnsStr for Struct<'_> {
+    // Should NOT warn
+    fn trait_method(&self) -> &str {
+        self.not_literal
+    }
+}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/unnecessary_literal_bound.stderr b/src/tools/clippy/tests/ui/unnecessary_literal_bound.stderr
new file mode 100644
index 00000000000..512b2f9a0af
--- /dev/null
+++ b/src/tools/clippy/tests/ui/unnecessary_literal_bound.stderr
@@ -0,0 +1,23 @@
+error: returning a `str` unnecessarily tied to the lifetime of arguments
+  --> tests/ui/unnecessary_literal_bound.rs:9:30
+   |
+LL |     fn returns_lit(&self) -> &str {
+   |                              ^^^^ help: try: `&'static str`
+   |
+   = note: `-D clippy::unnecessary-literal-bound` implied by `-D warnings`
+   = help: to override `-D warnings` add `#[allow(clippy::unnecessary_literal_bound)]`
+
+error: returning a `str` unnecessarily tied to the lifetime of arguments
+  --> tests/ui/unnecessary_literal_bound.rs:29:68
+   |
+LL |     fn contionally_returns_literals_explicit(&self, cond: bool) -> &str {
+   |                                                                    ^^^^ help: try: `&'static str`
+
+error: returning a `str` unnecessarily tied to the lifetime of arguments
+  --> tests/ui/unnecessary_literal_bound.rs:53:31
+   |
+LL |     fn trait_method(&self) -> &str {
+   |                               ^^^^ help: try: `&'static str`
+
+error: aborting due to 3 previous errors
+
diff --git a/src/tools/clippy/tests/ui/unwrap_or.fixed b/src/tools/clippy/tests/ui/unwrap_or.fixed
index e1a47fc7bd9..62bc1966da6 100644
--- a/src/tools/clippy/tests/ui/unwrap_or.fixed
+++ b/src/tools/clippy/tests/ui/unwrap_or.fixed
@@ -3,11 +3,11 @@
 
 fn main() {
     let s = Some(String::from("test string")).unwrap_or_else(|| "Fail".to_string()).len();
-    //~^ ERROR: use of `unwrap_or` followed by a function call
+    //~^ ERROR: function call inside of `unwrap_or`
     //~| NOTE: `-D clippy::or-fun-call` implied by `-D warnings`
 }
 
 fn new_lines() {
     let s = Some(String::from("test string")).unwrap_or_else(|| "Fail".to_string()).len();
-    //~^ ERROR: use of `unwrap_or` followed by a function call
+    //~^ ERROR: function call inside of `unwrap_or`
 }
diff --git a/src/tools/clippy/tests/ui/unwrap_or.rs b/src/tools/clippy/tests/ui/unwrap_or.rs
index 914bfb939b8..e8e4b6b7168 100644
--- a/src/tools/clippy/tests/ui/unwrap_or.rs
+++ b/src/tools/clippy/tests/ui/unwrap_or.rs
@@ -3,11 +3,11 @@
 
 fn main() {
     let s = Some(String::from("test string")).unwrap_or("Fail".to_string()).len();
-    //~^ ERROR: use of `unwrap_or` followed by a function call
+    //~^ ERROR: function call inside of `unwrap_or`
     //~| NOTE: `-D clippy::or-fun-call` implied by `-D warnings`
 }
 
 fn new_lines() {
     let s = Some(String::from("test string")).unwrap_or("Fail".to_string()).len();
-    //~^ ERROR: use of `unwrap_or` followed by a function call
+    //~^ ERROR: function call inside of `unwrap_or`
 }
diff --git a/src/tools/clippy/tests/ui/unwrap_or.stderr b/src/tools/clippy/tests/ui/unwrap_or.stderr
index 6aa0b9df29b..b712f8cf693 100644
--- a/src/tools/clippy/tests/ui/unwrap_or.stderr
+++ b/src/tools/clippy/tests/ui/unwrap_or.stderr
@@ -1,4 +1,4 @@
-error: use of `unwrap_or` followed by a function call
+error: function call inside of `unwrap_or`
   --> tests/ui/unwrap_or.rs:5:47
    |
 LL |     let s = Some(String::from("test string")).unwrap_or("Fail".to_string()).len();
@@ -7,7 +7,7 @@ LL |     let s = Some(String::from("test string")).unwrap_or("Fail".to_string())
    = note: `-D clippy::or-fun-call` implied by `-D warnings`
    = help: to override `-D warnings` add `#[allow(clippy::or_fun_call)]`
 
-error: use of `unwrap_or` followed by a function call
+error: function call inside of `unwrap_or`
   --> tests/ui/unwrap_or.rs:11:47
    |
 LL |     let s = Some(String::from("test string")).unwrap_or("Fail".to_string()).len();
diff --git a/src/tools/clippy/tests/versioncheck.rs b/src/tools/clippy/tests/versioncheck.rs
index 68328333937..e29898f068d 100644
--- a/src/tools/clippy/tests/versioncheck.rs
+++ b/src/tools/clippy/tests/versioncheck.rs
@@ -24,7 +24,6 @@ fn consistent_clippy_crate_versions() {
     let clippy_version = read_version("Cargo.toml");
 
     let paths = [
-        "declare_clippy_lint/Cargo.toml",
         "clippy_config/Cargo.toml",
         "clippy_lints/Cargo.toml",
         "clippy_utils/Cargo.toml",
diff --git a/src/tools/clippy/triagebot.toml b/src/tools/clippy/triagebot.toml
index dcf00e4e384..cd9641eedd8 100644
--- a/src/tools/clippy/triagebot.toml
+++ b/src/tools/clippy/triagebot.toml
@@ -35,6 +35,7 @@ users_on_vacation = [
     "@Alexendoo",
     "@dswij",
     "@Jarcho",
+    "@blyxyas",
     "@y21",
     "@Centri3",
 ]
diff --git a/src/tools/clippy/util/gh-pages/index.html b/src/tools/clippy/util/gh-pages/index.html
deleted file mode 100644
index f3d7e504fdf..00000000000
--- a/src/tools/clippy/util/gh-pages/index.html
+++ /dev/null
@@ -1,330 +0,0 @@
-<!DOCTYPE html>
-<!--
-Welcome to a Clippy's lint list, at least the source code of it. If you are
-interested in contributing to this website checkout `util/gh-pages/index.html`
-inside the rust-clippy repository.
-
-Otherwise, have a great day =^.^=
--->
-<html lang="en">
-<head>
-    <meta charset="UTF-8"/>
-    <meta name="viewport" content="width=device-width, initial-scale=1"/>
-    <meta name="description" content="A collection of lints to catch common mistakes and improve your Rust code.">
-
-    <title>Clippy Lints</title>
-
-    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.3.6/css/bootstrap.min.css"/>
-    <link id="githubLightHighlight" rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.6.0/styles/github.min.css" disabled="true" />
-    <link id="githubDarkHighlight" rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.6.0/styles/github-dark.min.css" disabled="true" />
-
-    <!-- The files are not copied over into the Clippy project since they use the MPL-2.0 License -->
-    <link rel="stylesheet" href="https://rust-lang.github.io/mdBook/css/variables.css"/>
-    <link id="styleHighlight" rel="stylesheet" href="https://rust-lang.github.io/mdBook/highlight.css">
-    <link id="styleNight" rel="stylesheet" href="https://rust-lang.github.io/mdBook/tomorrow-night.css" disabled="true">
-    <link id="styleAyu" rel="stylesheet" href="https://rust-lang.github.io/mdBook/ayu-highlight.css" disabled="true">
-    <link rel="stylesheet" href="style.css">
-</head>
-<body ng-app="clippy" ng-controller="lintList">
-    <div id="settings-dropdown">
-        <div class="settings-icon" tabindex="-1"></div>
-        <div class="settings-menu" tabindex="-1">
-            <div class="setting-radio-name">Theme</div>
-            <select id="theme-choice" onchange="setTheme(this.value, true)">
-                <option value="ayu">Ayu</option>
-                <option value="coal">Coal</option>
-                <option value="light">Light</option>
-                <option value="navy">Navy</option>
-                <option value="rust">Rust</option>
-            </select>
-            <label>
-                <input type="checkbox" id="disable-shortcuts" onchange="changeSetting(this)">
-                <span>Disable keyboard shortcuts</span>
-            </label>
-        </div>
-    </div>
-
-    <div class="container">
-        <div class="page-header">
-            <h1>Clippy Lints</h1>
-        </div>
-
-        <noscript>
-            <div class="alert alert-danger" role="alert">
-                Sorry, this site only works with JavaScript! :(
-            </div>
-        </noscript>
-
-        <div ng-cloak>
-
-            <div class="alert alert-info" role="alert" ng-if="loading">
-                Loading&#x2026;
-            </div>
-            <div class="alert alert-danger" role="alert" ng-if="error">
-                Error loading lints!
-            </div>
-
-            <div class="panel panel-default" ng-show="data">
-                <div class="panel-body row">
-                    <div id="upper-filters" class="col-12 col-md-5">
-                        <div class="btn-group" filter-dropdown>
-                            <button type="button" class="btn btn-default dropdown-toggle">
-                                Lint levels <span class="badge">{{selectedValuesCount(levels)}}</span> <span class="caret"></span>
-                            </button>
-                            <ul class="dropdown-menu">
-                                <li class="checkbox">
-                                    <label ng-click="toggleLevels(true)">
-                                        <input type="checkbox" class="invisible" />
-                                        All
-                                    </label>
-                                </li>
-                                <li class="checkbox">
-                                    <label ng-click="toggleLevels(false)">
-                                        <input type="checkbox" class="invisible" />
-                                        None
-                                    </label>
-                                </li>
-                                <li role="separator" class="divider"></li>
-                                <li class="checkbox" ng-repeat="(level, enabled) in levels">
-                                    <label class="text-capitalize">
-                                        <input type="checkbox" ng-model="levels[level]" />
-                                        {{level}}
-                                    </label>
-                                </li>
-                            </ul>
-                        </div>
-                        <div class="btn-group" filter-dropdown>
-                            <button type="button" class="btn btn-default dropdown-toggle">
-                                Lint groups <span class="badge">{{selectedValuesCount(groups)}}</span> <span class="caret"></span>
-                            </button>
-                            <ul class="dropdown-menu">
-                                <li class="checkbox">
-                                    <label ng-click="toggleGroups(true)">
-                                        <input type="checkbox" class="invisible" />
-                                        All
-                                    </label>
-                                </li>
-                                <li class="checkbox">
-                                    <label ng-click="resetGroupsToDefault()">
-                                        <input type="checkbox" class="invisible" />
-                                        Default
-                                    </label>
-                                </li>
-                                <li class="checkbox">
-                                    <label ng-click="toggleGroups(false)">
-                                        <input type="checkbox" class="invisible" />
-                                        None
-                                    </label>
-                                </li>
-                                <li role="separator" class="divider"></li>
-                                <li class="checkbox" ng-repeat="(group, enabled) in groups">
-                                    <label class="text-capitalize">
-                                        <input type="checkbox" ng-model="groups[group]" />
-                                        {{group}}
-                                    </label>
-                                </li>
-                            </ul>
-                        </div>
-                        <div id="version-filter">
-                            <div class="btn-group" filter-dropdown>
-                                <button type="button" class="btn btn-default dropdown-toggle">
-                                    Version
-                                    <span id="version-filter-count" class="badge">
-                                        {{versionFilterCount(versionFilters)}}
-                                    </span>
-                                    <span class="caret"></span>
-                                </button>
-                                <ul id="version-filter-selector" class="dropdown-menu">
-                                    <li class="checkbox">
-                                        <label ng-click="clearVersionFilters()">
-                                            <input type="checkbox" class="invisible" />
-                                            Clear filters
-                                        </label>
-                                    </li>
-                                    <li role="separator" class="divider"></li>
-                                    <li class="checkbox" ng-repeat="(filter, vars) in versionFilters">
-                                        <label ng-attr-for="filter-{filter}">{{filter}}</label>
-                                        <span>1.</span>
-                                        <input type="number"
-                                                min="29"
-                                                ng-attr-id="filter-{filter}"
-                                                class="version-filter-input form-control filter-input"
-                                                maxlength="2"
-                                                ng-model="versionFilters[filter].minorVersion"
-                                                ng-model-options="{debounce: 50}"
-                                                ng-change="updateVersionFilters()" />
-                                        <span>.0</span>
-                                    </li>
-                                </ul>
-                            </div>
-                        </div>
-                        <div class="btn-group" filter-dropdown>
-                            <button type="button" class="btn btn-default dropdown-toggle">
-                                Applicability <span class="badge">{{selectedValuesCount(applicabilities)}}</span> <span class="caret"></span>
-                            </button>
-                            <ul class="dropdown-menu">
-                                <li class="checkbox">
-                                    <label ng-click="toggleApplicabilities(true)">
-                                        <input type="checkbox" class="invisible" />
-                                        All
-                                    </label>
-                                </li>
-                                <li class="checkbox">
-                                    <label ng-click="toggleApplicabilities(false)">
-                                        <input type="checkbox" class="invisible" />
-                                        None
-                                    </label>
-                                </li>
-                                <li role="separator" class="divider"></li>
-                                <li class="checkbox" ng-repeat="(applicability, enabled) in applicabilities">
-                                    <label class="text-capitalize">
-                                        <input type="checkbox" ng-model="applicabilities[applicability]" />
-                                        {{applicability}}
-                                    </label>
-                                </li>
-                            </ul>
-                        </div>
-                    </div>
-                    <div class="col-12 col-md-5 search-control">
-                        <div class="input-group">
-                            <label class="input-group-addon" id="filter-label" for="search-input">Filter:</label>
-                            <input type="text" class="form-control filter-input" placeholder="Keywords or search string (`S` or `/` to focus)" id="search-input"
-                                ng-model="search" ng-blur="updatePath()" ng-keyup="$event.keyCode == 13 && updatePath()"
-                                ng-model-options="{debounce: 50}" />
-                            <span class="input-group-btn">
-                                <button class="filter-clear btn" type="button" ng-click="search = ''; updatePath();">
-                                    Clear
-                                </button>
-                            </span>
-                        </div>
-                    </div>
-                    <div class="col-12 col-md-2 btn-group expansion-group">
-                        <button title="Collapse All" class="btn btn-default expansion-control" type="button" ng-click="toggleExpansion(data, false)">
-                            <span class="glyphicon glyphicon-collapse-up"></span>
-                        </button>
-                        <button title="Expand All" class="btn btn-default expansion-control" type="button" ng-click="toggleExpansion(data, true)">
-                            <span class="glyphicon glyphicon-collapse-down"></span>
-                        </button>
-                    </div>
-                </div>
-            </div>
-            <!-- The order of the filters should be from most likely to remove a lint to least likely to improve performance. -->
-            <article class="panel panel-default" id="{{lint.id}}" ng-repeat="lint in data | filter:bySearch | filter:byGroups | filter:byLevels | filter:byVersion | filter:byApplicabilities">
-                <header class="panel-heading" ng-click="open[lint.id] = !open[lint.id]">
-                    <h2 class="panel-title">
-                        <div class="panel-title-name">
-                            <span>{{lint.id}}</span>
-                            <a href="#{{lint.id}}" class="anchor label label-default"
-                                ng-click="openLint(lint); $event.preventDefault(); $event.stopPropagation()">&para;</a>
-                            <a href="" id="clipboard-{{lint.id}}" class="anchor label label-default" ng-click="copyToClipboard(lint); $event.stopPropagation()">
-                                &#128203;
-                            </a>
-                        </div>
-
-                        <div class="panel-title-addons">
-                            <span class="label label-lint-group label-default label-group-{{lint.group}}">{{lint.group}}</span>
-
-                            <span class="label label-lint-level label-lint-level-{{lint.level}}">{{lint.level}}</span>
-
-
-                            <span class="label label-doc-folding" ng-show="open[lint.id]">&minus;</span>
-                            <span class="label label-doc-folding" ng-hide="open[lint.id]">&plus;</span>
-                        </div>
-                    </h2>
-                </header>
-
-                <div class="list-group lint-docs" ng-if="open[lint.id]" ng-class="{collapse: true, in: open[lint.id]}">
-                    <div class="list-group-item lint-doc-md" ng-bind-html="lint.docs | markdown"></div>
-                    <div class="lint-additional-info-container">
-                        <!-- Applicability -->
-                        <div class="lint-additional-info-item">
-                            <span> Applicability: </span>
-                            <span class="label label-default label-applicability">{{lint.applicability}}</span>
-                            <a href="https://doc.rust-lang.org/nightly/nightly-rustc/rustc_lint_defs/enum.Applicability.html#variants">(?)</a>
-                        </div>
-                        <!-- Clippy version -->
-                        <div class="lint-additional-info-item">
-                            <span>{{lint.group == "deprecated" ? "Deprecated" : "Added"}} in: </span>
-                            <span class="label label-default label-version">{{lint.version}}</span>
-                        </div>
-                        <!-- Open related issues -->
-                        <div class="lint-additional-info-item">
-                            <a href="https://github.com/rust-lang/rust-clippy/issues?q=is%3Aissue+{{lint.id}}">Related Issues</a>
-                        </div>
-                        <!-- Jump to source -->
-                        <div class="lint-additional-info-item" ng-if="lint.id_location">
-                            <a href="https://github.com/rust-lang/rust-clippy/blob/{{docVersion}}/{{lint.id_location}}">View Source</a>
-                        </div>
-                    </div>
-                </div>
-            </article>
-        </div>
-    </div>
-
-    <a
-        aria-label="View source on GitHub"
-        class="github-corner"
-        href="https://github.com/rust-lang/rust-clippy"
-        rel="noopener noreferrer"
-        target="_blank"
-    >
-        <svg
-            width="80"
-            height="80"
-            viewBox="0 0 250 250"
-            style="position: absolute; top: 0; border: 0; right: 0"
-            aria-hidden="true"
-        >
-            <path d="M0,0 L115,115 L130,115 L142,142 L250,250 L250,0 Z" fill="var(--theme-color)"></path>
-            <path
-                d="M128.3,109.0 C113.8,99.7 119.0,89.6 119.0,89.6 C122.0,82.7 120.5,78.6 120.5,78.6 C119.2,72.0 123.4,76.3 123.4,76.3 C127.3,80.9 125.5,87.3 125.5,87.3 C122.9,97.6 130.6,101.9 134.4,103.2"
-                fill="currentColor"
-                style="transform-origin: 130px 106px"
-                class="octo-arm"
-            ></path>
-            <path
-                d="M115.0,115.0 C114.9,115.1 118.7,116.5 119.8,115.4 L133.7,101.6 C136.9,99.2 139.9,98.4 142.2,98.6 C133.8,88.0 127.5,74.4 143.8,58.0 C148.5,53.4 154.0,51.2 159.7,51.0 C160.3,49.4 163.2,43.6 171.4,40.1 C171.4,40.1 176.1,42.5 178.8,56.2 C183.1,58.6 187.2,61.8 190.9,65.4 C194.5,69.0 197.7,73.2 200.1,77.6 C213.8,80.2 216.3,84.9 216.3,84.9 C212.7,93.1 206.9,96.0 205.4,96.6 C205.1,102.4 203.0,107.8 198.3,112.5 C181.9,128.9 168.3,122.5 157.7,114.1 C157.9,116.9 156.7,120.9 152.7,124.9 L141.0,136.5 C139.8,137.7 141.6,141.9 141.8,141.8 Z"
-                fill="currentColor"
-                class="octo-body"
-            ></path>
-        </svg>
-        <style>
-            .github-corner svg {
-                fill: var(--fg);
-                color: var(--bg);
-            }
-            .github-corner:hover .octo-arm {
-                animation: octocat-wave 560ms ease-in-out;
-            }
-            @keyframes octocat-wave {
-                0%,
-                100% {
-                    transform: rotate(0);
-                }
-                20%,
-                60% {
-                    transform: rotate(-25deg);
-                }
-                40%,
-                80% {
-                    transform: rotate(10deg);
-                }
-            }
-            @media (max-width: 500px) {
-                .github-corner:hover .octo-arm {
-                    animation: none;
-                }
-                .github-corner .octo-arm {
-                    animation: octocat-wave 560ms ease-in-out;
-                }
-            }
-        </style>
-    </a>
-
-    <script src="https://cdnjs.cloudflare.com/ajax/libs/markdown-it/12.3.2/markdown-it.min.js"></script>
-    <script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.6.0/highlight.min.js"></script>
-    <script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.6.0/languages/rust.min.js"></script>
-    <script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.4.12/angular.min.js"></script>
-    <script src="script.js"></script>
-</body>
-</html>
diff --git a/src/tools/clippy/util/gh-pages/index_template.html b/src/tools/clippy/util/gh-pages/index_template.html
new file mode 100644
index 00000000000..2412f0fd181
--- /dev/null
+++ b/src/tools/clippy/util/gh-pages/index_template.html
@@ -0,0 +1,232 @@
+<!DOCTYPE html>
+<!--
+Welcome to a Clippy's lint list, at least the source code of it. If you are
+interested in contributing to this website checkout `util/gh-pages/index_template.html`
+inside the rust-clippy repository.
+
+Otherwise, have a great day =^.^=
+-->
+<html lang="en"> {# #}
+<head> {# #}
+    <meta charset="UTF-8"/> {# #}
+    <meta name="viewport" content="width=device-width, initial-scale=1"/> {# #}
+    <meta name="description" content="A collection of lints to catch common mistakes and improve your Rust code."> {# #}
+
+    <title>Clippy Lints</title> {# #}
+
+    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.3.6/css/bootstrap.min.css"/> {# #}
+    <link id="githubLightHighlight" rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.6.0/styles/github.min.css" disabled="true" /> {# #}
+    <link id="githubDarkHighlight" rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.6.0/styles/github-dark.min.css" disabled="true" /> {# #}
+
+    <!-- The files are not copied over into the Clippy project since they use the MPL-2.0 License -->
+    <link rel="stylesheet" href="https://rust-lang.github.io/mdBook/css/variables.css"/> {# #}
+    <link id="styleHighlight" rel="stylesheet" href="https://rust-lang.github.io/mdBook/highlight.css"> {# #}
+    <link id="styleNight" rel="stylesheet" href="https://rust-lang.github.io/mdBook/tomorrow-night.css" disabled="true"> {# #}
+    <link id="styleAyu" rel="stylesheet" href="https://rust-lang.github.io/mdBook/ayu-highlight.css" disabled="true"> {# #}
+    <link rel="stylesheet" href="style.css"> {# #}
+</head> {# #}
+<body> {# #}
+    <script src="theme.js"></script> {# #}
+    <div id="settings-dropdown"> {# #}
+        <button class="settings-icon" tabindex="-1"></button> {# #}
+        <div class="settings-menu" tabindex="-1"> {# #}
+            <div class="setting-radio-name">Theme</div> {# #}
+            <select id="theme-choice" onchange="setTheme(this.value, true)"> {# #}
+                <option value="ayu">Ayu</option> {# #}
+                <option value="coal">Coal</option> {# #}
+                <option value="light">Light</option> {# #}
+                <option value="navy">Navy</option> {# #}
+                <option value="rust">Rust</option> {# #}
+            </select> {# #}
+            <label> {# #}
+                <input type="checkbox" id="disable-shortcuts" onchange="changeSetting(this)"> {#+ #}
+                <span>Disable keyboard shortcuts</span> {# #}
+            </label> {# #}
+        </div> {# #}
+    </div> {# #}
+
+    <div class="container"> {# #}
+        <div class="page-header"> {# #}
+            <h1>Clippy Lints</h1> {# #}
+        </div> {# #}
+
+        <noscript> {# #}
+            <div class="alert alert-danger" role="alert"> {# #}
+                Sorry, this site only works with JavaScript! :( {# #}
+            </div> {# #}
+        </noscript> {# #}
+
+        <div> {# #}
+            <div class="panel panel-default"> {# #}
+                <div class="panel-body row"> {# #}
+                    <div id="upper-filters" class="col-12 col-md-5"> {# #}
+                        <div class="btn-group" id="lint-levels" tabindex="-1"> {# #}
+                            <button type="button" class="btn btn-default dropdown-toggle"> {# #}
+                                Lint levels <span class="badge">4</span> <span class="caret"></span> {# #}
+                            </button> {# #}
+                            <ul class="dropdown-menu" id="lint-levels-selector"> {# #}
+                                <li class="checkbox"> {# #}
+                                    <button onclick="toggleElements('levels_filter', true)">All</button> {# #}
+                                </li> {# #}
+                                <li class="checkbox"> {# #}
+                                    <button onclick="toggleElements('levels_filter', false)">None</button> {# #}
+                                </li> {# #}
+                                <li role="separator" class="divider"></li> {# #}
+                            </ul> {# #}
+                        </div> {# #}
+                        <div class="btn-group" id="lint-groups" tabindex="-1"> {# #}
+                            <button type="button" class="btn btn-default dropdown-toggle"> {# #}
+                                Lint groups <span class="badge">9</span> <span class="caret"></span> {# #}
+                            </button> {# #}
+                            <ul class="dropdown-menu" id="lint-groups-selector"> {# #}
+                                <li class="checkbox"> {# #}
+                                    <button onclick="toggleElements('groups_filter', true)">All</button> {# #}
+                                </li> {# #}
+                                <li class="checkbox"> {# #}
+                                    <button onclick="resetGroupsToDefault()">Default</button> {# #}
+                                </li> {# #}
+                                <li class="checkbox"> {# #}
+                                    <button onclick="toggleElements('groups_filter', false)">None</button> {# #}
+                                </li> {# #}
+                                <li role="separator" class="divider"></li> {# #}
+                            </ul> {# #}
+                        </div> {# #}
+                        <div class="btn-group" id="version-filter" tabindex="-1"> {# #}
+                            <button type="button" class="btn btn-default dropdown-toggle"> {# #}
+                                Version {#+ #}
+                                <span id="version-filter-count" class="badge">0</span> {#+ #}
+                                <span class="caret"></span> {# #}
+                            </button> {# #}
+                            <ul id="version-filter-selector" class="dropdown-menu"> {# #}
+                                <li class="checkbox"> {# #}
+                                    <button onclick="clearVersionFilters()">Clear filters</button> {# #}
+                                </li> {# #}
+                                <li role="separator" class="divider"></li> {# #}
+                            </ul> {# #}
+                        </div> {# #}
+                        <div class="btn-group", id="lint-applicabilities" tabindex="-1"> {# #}
+                            <button type="button" class="btn btn-default dropdown-toggle"> {# #}
+                                Applicability {#+ #}
+                                <span class="badge">4</span> {#+ #}
+                                <span class="caret"></span> {# #}
+                            </button> {# #}
+                            <ul class="dropdown-menu" id="lint-applicabilities-selector"> {# #}
+                                <li class="checkbox"> {# #}
+                                    <button onclick="toggleElements('applicabilities_filter', true)">All</button> {# #}
+                                </li> {# #}
+                                <li class="checkbox"> {# #}
+                                    <button onclick="toggleElements('applicabilities_filter', false)">None</button> {# #}
+                                </li> {# #}
+                                <li role="separator" class="divider"></li> {# #}
+                            </ul> {# #}
+                        </div> {# #}
+                    </div> {# #}
+                    <div class="col-12 col-md-5 search-control"> {# #}
+                        <div class="input-group"> {# #}
+                            <label class="input-group-addon" id="filter-label" for="search-input">Filter:</label> {# #}
+                            <input type="text" class="form-control filter-input" placeholder="Keywords or search string (`S` or `/` to focus)" id="search-input" /> {# #}
+                            <span class="input-group-btn"> {# #}
+                                <button class="filter-clear btn" type="button" onclick="searchState.clearInput(event)"> {# #}
+                                    Clear {# #}
+                                </button> {# #}
+                            </span> {# #}
+                        </div> {# #}
+                    </div> {# #}
+                    <div class="col-12 col-md-2 btn-group expansion-group"> {# #}
+                        <button title="Collapse All" class="btn btn-default expansion-control" type="button" onclick="toggleExpansion(false)"> {# #}
+                            <span class="glyphicon glyphicon-collapse-up"></span> {# #}
+                        </button> {# #}
+                        <button title="Expand All" class="btn btn-default expansion-control" type="button" onclick="toggleExpansion(true)"> {# #}
+                            <span class="glyphicon glyphicon-collapse-down"></span> {# #}
+                        </button> {# #}
+                    </div> {# #}
+                </div> {# #}
+            </div>
+            {% for lint in lints %}
+                <article class="panel panel-default collapsed" id="{{lint.id}}"> {# #}
+                    <header class="panel-heading" onclick="expandLint('{{lint.id}}')"> {# #}
+                        <h2 class="panel-title"> {# #}
+                            <div class="panel-title-name" id="lint-{{lint.id}}"> {# #}
+                                <span>{{lint.id}}</span> {#+ #}
+                                <a href="#{{lint.id}}" class="anchor label label-default" onclick="openLint(event)">&para;</a> {#+ #}
+                                <a href="" class="anchor label label-default" onclick="copyToClipboard(event)"> {# #}
+                                    &#128203; {# #}
+                                </a> {# #}
+                            </div> {# #}
+
+                            <div class="panel-title-addons"> {# #}
+                                <span class="label label-lint-group label-default label-group-{{lint.group}}">{{lint.group}}</span> {#+ #}
+
+                                <span class="label label-lint-level label-lint-level-{{lint.level}}">{{lint.level}}</span> {#+ #}
+
+                                <span class="label label-doc-folding">&plus;</span> {# #}
+                            </div> {# #}
+                        </h2> {# #}
+                    </header> {# #}
+
+                    <div class="list-group lint-docs"> {# #}
+                        <div class="list-group-item lint-doc-md">{{Self::markdown(lint.docs)}}</div> {# #}
+                        <div class="lint-additional-info-container">
+                            {# Applicability #}
+                            <div class="lint-additional-info-item"> {# #}
+                                <span> Applicability: </span> {# #}
+                                <span class="label label-default label-applicability">{{ lint.applicability_str() }}</span> {# #}
+                                <a href="https://doc.rust-lang.org/nightly/nightly-rustc/rustc_lint_defs/enum.Applicability.html#variants">(?)</a> {# #}
+                            </div>
+                            {# Clippy version #}
+                            <div class="lint-additional-info-item"> {# #}
+                                <span>{% if lint.group == "deprecated" %}Deprecated{% else %} Added{% endif +%} in: </span> {# #}
+                                <span class="label label-default label-version">{{lint.version}}</span> {# #}
+                            </div>
+                            {# Open related issues #}
+                            <div class="lint-additional-info-item"> {# #}
+                                <a href="https://github.com/rust-lang/rust-clippy/issues?q=is%3Aissue+{{lint.id}}">Related Issues</a> {# #}
+                            </div>
+
+                            {# Jump to source #}
+                            {% if let Some(id_location) = lint.id_location %}
+                                <div class="lint-additional-info-item"> {# #}
+                                    <a href="https://github.com/rust-lang/rust-clippy/blob/master/{{id_location}}">View Source</a> {# #}
+                                </div>
+                            {% endif %}
+                        </div> {# #}
+                    </div> {# #}
+                </article>
+            {% endfor %}
+        </div> {# #}
+    </div> {# #}
+
+    <a {#+ #}
+        aria-label="View source on GitHub" {#+ #}
+        class="github-corner" {#+ #}
+        href="https://github.com/rust-lang/rust-clippy" {#+ #}
+        rel="noopener noreferrer" {#+ #}
+        target="_blank" {# #}
+    > {# #}
+        <svg {#+ #}
+            width="80" {#+ #}
+            height="80" {#+ #}
+            viewBox="0 0 250 250" {#+ #}
+            style="position: absolute; top: 0; border: 0; right: 0" {#+ #}
+            aria-hidden="true" {# #}
+        > {# #}
+            <path d="M0,0 L115,115 L130,115 L142,142 L250,250 L250,0 Z" fill="var(--theme-color)"></path> {# #}
+            <path {#+ #}
+                d="M128.3,109.0 C113.8,99.7 119.0,89.6 119.0,89.6 C122.0,82.7 120.5,78.6 120.5,78.6 C119.2,72.0 123.4,76.3 123.4,76.3 C127.3,80.9 125.5,87.3 125.5,87.3 C122.9,97.6 130.6,101.9 134.4,103.2" {#+ #}
+                fill="currentColor" {#+ #}
+                style="transform-origin: 130px 106px" {#+ #}
+                class="octo-arm" {# #}
+            ></path> {# #}
+            <path {#+ #}
+                d="M115.0,115.0 C114.9,115.1 118.7,116.5 119.8,115.4 L133.7,101.6 C136.9,99.2 139.9,98.4 142.2,98.6 C133.8,88.0 127.5,74.4 143.8,58.0 C148.5,53.4 154.0,51.2 159.7,51.0 C160.3,49.4 163.2,43.6 171.4,40.1 C171.4,40.1 176.1,42.5 178.8,56.2 C183.1,58.6 187.2,61.8 190.9,65.4 C194.5,69.0 197.7,73.2 200.1,77.6 C213.8,80.2 216.3,84.9 216.3,84.9 C212.7,93.1 206.9,96.0 205.4,96.6 C205.1,102.4 203.0,107.8 198.3,112.5 C181.9,128.9 168.3,122.5 157.7,114.1 C157.9,116.9 156.7,120.9 152.7,124.9 L141.0,136.5 C139.8,137.7 141.6,141.9 141.8,141.8 Z" {#+ #}
+                fill="currentColor" {#+ #}
+                class="octo-body" {# #}
+            ></path> {# #}
+        </svg> {# #}
+    </a> {# #}
+
+    <script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.6.0/highlight.min.js"></script> {# #}
+    <script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.6.0/languages/rust.min.js"></script> {# #}
+    <script src="script.js"></script> {# #}
+</body> {# #}
+</html> {# #}
diff --git a/src/tools/clippy/util/gh-pages/script.js b/src/tools/clippy/util/gh-pages/script.js
index 1a5330bc0e5..cc22a39b3d1 100644
--- a/src/tools/clippy/util/gh-pages/script.js
+++ b/src/tools/clippy/util/gh-pages/script.js
@@ -1,629 +1,567 @@
-(function () {
-    const md = window.markdownit({
-        html: true,
-        linkify: true,
-        typographer: true,
-        highlight: function (str, lang) {
-            if (lang && hljs.getLanguage(lang)) {
-                try {
-                    return '<pre class="hljs"><code>' +
-                        hljs.highlight(str, { language: lang, ignoreIllegals: true }).value +
-                        '</code></pre>';
-                } catch (__) {}
-            }
-
-            return '<pre class="hljs"><code>' + md.utils.escapeHtml(str) + '</code></pre>';
-        }
-    });
-
-    function scrollToLint(lintId) {
-        const target = document.getElementById(lintId);
-        if (!target) {
-            return;
-        }
-        target.scrollIntoView();
-    }
-
-    function scrollToLintByURL($scope, $location) {
-        const removeListener = $scope.$on('ngRepeatFinished', function (ngRepeatFinishedEvent) {
-            scrollToLint($location.path().substring(1));
-            removeListener();
-        });
-    }
-
-    function selectGroup($scope, selectedGroup) {
-        const groups = $scope.groups;
-        for (const group in groups) {
-            if (groups.hasOwnProperty(group)) {
-                groups[group] = group === selectedGroup;
-            }
+window.searchState = {
+    timeout: null,
+    inputElem: document.getElementById("search-input"),
+    lastSearch: '',
+    clearInput: () => {
+        searchState.inputElem.value = "";
+        searchState.filterLints();
+    },
+    clearInputTimeout: () => {
+        if (searchState.timeout !== null) {
+            clearTimeout(searchState.timeout);
+            searchState.timeout = null
         }
-    }
-
-    angular.module("clippy", [])
-        .filter('markdown', function ($sce) {
-            return function (text) {
-                return $sce.trustAsHtml(
-                    md.render(text || '')
-                        // Oh deer, what a hack :O
-                        .replace('<table', '<table class="table"')
-                );
-            };
-        })
-        .directive('filterDropdown', function ($document) {
-            return {
-                restrict: 'A',
-                link: function ($scope, $element, $attr) {
-                    $element.bind('click', function (event) {
-                        if (event.target.closest('button')) {
-                            $element.toggleClass('open');
-                        } else {
-                            $element.addClass('open');
-                        }
-                        $element.addClass('open-recent');
-                    });
-
-                    $document.bind('click', function () {
-                        if (!$element.hasClass('open-recent')) {
-                            $element.removeClass('open');
-                        }
-                        $element.removeClass('open-recent');
-                    })
-                }
+    },
+    resetInputTimeout: () => {
+        searchState.clearInputTimeout();
+        setTimeout(searchState.filterLints, 50);
+    },
+    filterLints: () => {
+        function matchesSearch(lint, terms, searchStr) {
+            // Search by id
+            if (lint.elem.id.indexOf(searchStr) !== -1) {
+                return true;
             }
-        })
-        .directive('onFinishRender', function ($timeout) {
-            return {
-                restrict: 'A',
-                link: function (scope, element, attr) {
-                    if (scope.$last === true) {
-                        $timeout(function () {
-                            scope.$emit(attr.onFinishRender);
-                        });
-                    }
+            // Search the description
+            // The use of `for`-loops instead of `foreach` enables us to return early
+            const docsLowerCase = lint.elem.textContent.toLowerCase();
+            for (const term of terms) {
+                // This is more likely and will therefore be checked first
+                if (docsLowerCase.indexOf(term) !== -1) {
+                    return true;
                 }
-            };
-        })
-        .controller("lintList", function ($scope, $http, $location, $timeout) {
-            // Level filter
-            const LEVEL_FILTERS_DEFAULT = {allow: true, warn: true, deny: true, none: true};
-            $scope.levels = { ...LEVEL_FILTERS_DEFAULT };
-            $scope.byLevels = function (lint) {
-                return $scope.levels[lint.level];
-            };
-
-            const GROUPS_FILTER_DEFAULT = {
-                cargo: true,
-                complexity: true,
-                correctness: true,
-                nursery: true,
-                pedantic: true,
-                perf: true,
-                restriction: true,
-                style: true,
-                suspicious: true,
-                deprecated: false,
-            }
-
-            $scope.groups = {
-                ...GROUPS_FILTER_DEFAULT
-            };
-
-            $scope.versionFilters = {
-                "≥": {enabled: false, minorVersion: null },
-                "≤": {enabled: false, minorVersion: null },
-                "=": {enabled: false, minorVersion: null },
-            };
-
-            // Map the versionFilters to the query parameters in a way that is easier to work with in a URL
-            const versionFilterKeyMap = {
-                "≥": "gte",
-                "≤": "lte",
-                "=": "eq"
-            };
-            const reverseVersionFilterKeyMap = Object.fromEntries(
-                Object.entries(versionFilterKeyMap).map(([key, value]) => [value, key])
-            );
-
-            const APPLICABILITIES_FILTER_DEFAULT = {
-                MachineApplicable: true,
-                MaybeIncorrect: true,
-                HasPlaceholders: true,
-                Unspecified: true,
-            };
-
-            $scope.applicabilities = {
-                ...APPLICABILITIES_FILTER_DEFAULT
-            }
-
-            // loadFromURLParameters retrieves filter settings from the URL parameters and assigns them
-            // to corresponding $scope variables.
-            function loadFromURLParameters() {
-                // Extract parameters from URL
-                const urlParameters = $location.search();
-
-                // Define a helper function that assigns URL parameters to a provided scope variable
-                const handleParameter = (parameter, scopeVariable, defaultValues) => {
-                    if (urlParameters[parameter]) {
-                        const items = urlParameters[parameter].split(',');
-                        for (const key in scopeVariable) {
-                            if (scopeVariable.hasOwnProperty(key)) {
-                                scopeVariable[key] = items.includes(key);
-                            }
-                        }
-                    } else if (defaultValues) {
-                        for (const key in defaultValues) {
-                            if (scopeVariable.hasOwnProperty(key)) {
-                                scopeVariable[key] = defaultValues[key];
-                            }
-                        }
-                    }
-                };
-
-                handleParameter('levels', $scope.levels, LEVEL_FILTERS_DEFAULT);
-                handleParameter('groups', $scope.groups, GROUPS_FILTER_DEFAULT);
-                handleParameter('applicabilities', $scope.applicabilities, APPLICABILITIES_FILTER_DEFAULT);
-
-                // Handle 'versions' parameter separately because it needs additional processing
-                if (urlParameters.versions) {
-                    const versionFilters = urlParameters.versions.split(',');
-                    for (const versionFilter of versionFilters) {
-                        const [key, minorVersion] = versionFilter.split(':');
-                        const parsedMinorVersion = parseInt(minorVersion);
 
-                        // Map the key from the URL parameter to its original form
-                        const originalKey = reverseVersionFilterKeyMap[key];
-
-                        if (originalKey in $scope.versionFilters && !isNaN(parsedMinorVersion)) {
-                            $scope.versionFilters[originalKey].enabled = true;
-                            $scope.versionFilters[originalKey].minorVersion = parsedMinorVersion;
-                        }
-                    }
+                if (lint.elem.id.indexOf(term) !== -1) {
+                    return true;
                 }
 
-                // Load the search parameter from the URL path
-                const searchParameter = $location.path().substring(1); // Remove the leading slash
-                if (searchParameter) {
-                    $scope.search = searchParameter;
-                    $scope.open[searchParameter] = true;
-                    scrollToLintByURL($scope, $location);
-                }
+                return false;
             }
+            return true;
+        }
 
-            // updateURLParameter updates the URL parameter with the given key to the given value
-            function updateURLParameter(filterObj, urlKey, defaultValue = {}, processFilter = filter => filter) {
-                const parameter = Object.keys(filterObj)
-                    .filter(filter => filterObj[filter])
-                    .sort()
-                    .map(processFilter)
-                    .filter(Boolean) // Filters out any falsy values, including null
-                    .join(',');
-
-                const defaultParameter = Object.keys(defaultValue)
-                    .filter(filter => defaultValue[filter])
-                    .sort()
-                    .map(processFilter)
-                    .filter(Boolean) // Filters out any falsy values, including null
-                    .join(',');
-
-                // if we ended up back at the defaults, just remove it from the URL
-                if (parameter === defaultParameter) {
-                    $location.search(urlKey, null);
-                } else {
-                    $location.search(urlKey, parameter || null);
-                }
-            }
+        searchState.clearInputTimeout();
 
-            // updateVersionURLParameter updates the version URL parameter with the given version filters
-            function updateVersionURLParameter(versionFilters) {
-                updateURLParameter(
-                    versionFilters,
-                    'versions', {},
-                    versionFilter => versionFilters[versionFilter].enabled && versionFilters[versionFilter].minorVersion != null
-                        ? `${versionFilterKeyMap[versionFilter]}:${versionFilters[versionFilter].minorVersion}`
-                        : null
-                );
+        let searchStr = searchState.inputElem.value.trim().toLowerCase();
+        if (searchStr.startsWith("clippy::")) {
+            searchStr = searchStr.slice(8);
+        }
+        if (searchState.lastSearch === searchStr) {
+            return;
+        }
+        searchState.lastSearch = searchStr;
+        const terms = searchStr.split(" ");
+        const cleanedSearchStr = searchStr.replaceAll("-", "_");
+
+        for (const lint of filters.getAllLints()) {
+            lint.searchFilteredOut = !matchesSearch(lint, terms, cleanedSearchStr);
+            if (lint.filteredOut) {
+                continue;
             }
-
-            // updateAllURLParameters updates all the URL parameters with the current filter settings
-            function updateAllURLParameters() {
-                updateURLParameter($scope.levels, 'levels', LEVEL_FILTERS_DEFAULT);
-                updateURLParameter($scope.groups, 'groups', GROUPS_FILTER_DEFAULT);
-                updateVersionURLParameter($scope.versionFilters);
-                updateURLParameter($scope.applicabilities, 'applicabilities', APPLICABILITIES_FILTER_DEFAULT);
+            if (lint.searchFilteredOut) {
+                lint.elem.style.display = "none";
+            } else {
+                lint.elem.style.display = "";
             }
+        }
+        if (searchStr.length > 0) {
+            window.location.hash = `/${searchStr}`;
+        } else {
+            window.location.hash = '';
+        }
+    },
+};
 
-            // Add $watches to automatically update URL parameters when the data changes
-            $scope.$watch('levels', function (newVal, oldVal) {
-                if (newVal !== oldVal) {
-                    updateURLParameter(newVal, 'levels', LEVEL_FILTERS_DEFAULT);
-                }
-            }, true);
-
-            $scope.$watch('groups', function (newVal, oldVal) {
-                if (newVal !== oldVal) {
-                    updateURLParameter(newVal, 'groups', GROUPS_FILTER_DEFAULT);
-                }
-            }, true);
-
-            $scope.$watch('versionFilters', function (newVal, oldVal) {
-                if (newVal !== oldVal) {
-                    updateVersionURLParameter(newVal);
-                }
-            }, true);
-
-            $scope.$watch('applicabilities', function (newVal, oldVal) {
-                if (newVal !== oldVal) {
-                    updateURLParameter(newVal, 'applicabilities', APPLICABILITIES_FILTER_DEFAULT)
-                }
-            }, true);
-
-            // Watch for changes in the URL path and update the search and lint display
-            $scope.$watch(function () { return $location.path(); }, function (newPath) {
-                const searchParameter = newPath.substring(1);
-                if ($scope.search !== searchParameter) {
-                    $scope.search = searchParameter;
-                    $scope.open[searchParameter] = true;
-                    scrollToLintByURL($scope, $location);
-                }
-            });
-
-            let debounceTimeout;
-            $scope.$watch('search', function (newVal, oldVal) {
-                if (newVal !== oldVal) {
-                    if (debounceTimeout) {
-                        $timeout.cancel(debounceTimeout);
-                    }
-
-                    debounceTimeout = $timeout(function () {
-                        $location.path(newVal);
-                    }, 1000);
-                }
-            });
-
-            $scope.$watch(function () { return $location.search(); }, function (newParameters) {
-                loadFromURLParameters();
-            }, true);
-
-            $scope.updatePath = function () {
-                if (debounceTimeout) {
-                    $timeout.cancel(debounceTimeout);
-                }
+function handleInputChanged(event) {
+    if (event.target !== document.activeElement) {
+        return;
+    }
+    searchState.resetInputTimeout();
+}
 
-                $location.path($scope.search);
-            }
+function handleShortcut(ev) {
+    if (ev.ctrlKey || ev.altKey || ev.metaKey || disableShortcuts) {
+        return;
+    }
 
-            $scope.toggleLevels = function (value) {
-                const levels = $scope.levels;
-                for (const key in levels) {
-                    if (levels.hasOwnProperty(key)) {
-                        levels[key] = value;
-                    }
-                }
-            };
+    if (document.activeElement.tagName === "INPUT") {
+        if (ev.key === "Escape") {
+            document.activeElement.blur();
+        }
+    } else {
+        switch (ev.key) {
+            case "s":
+            case "S":
+            case "/":
+                ev.preventDefault(); // To prevent the key to be put into the input.
+                document.getElementById("search-input").focus();
+                break;
+            default:
+                break;
+        }
+    }
+}
 
-            $scope.toggleGroups = function (value) {
-                const groups = $scope.groups;
-                for (const key in groups) {
-                    if (groups.hasOwnProperty(key)) {
-                        groups[key] = value;
-                    }
-                }
-            };
+function toggleElements(filter, value) {
+    let needsUpdate = false;
+    let count = 0;
 
-            $scope.toggleApplicabilities = function (value) {
-                const applicabilities = $scope.applicabilities;
-                for (const key in applicabilities) {
-                    if (applicabilities.hasOwnProperty(key)) {
-                        applicabilities[key] = value;
-                    }
-                }
+    const element = document.getElementById(filters[filter].id);
+    onEachLazy(
+        element.querySelectorAll("ul input"),
+        el => {
+            if (el.checked !== value) {
+                el.checked = value;
+                filters[filter][el.getAttribute("data-value")] = value;
+                needsUpdate = true;
             }
+            count += 1;
+        }
+    );
+    element.querySelector(".badge").innerText = value ? count : 0;
+    if (needsUpdate) {
+        filters.filterLints();
+    }
+}
 
-            $scope.resetGroupsToDefault = function () {
-                $scope.groups = {
-                    ...GROUPS_FILTER_DEFAULT
-                };
-            };
+function changeSetting(elem) {
+    if (elem.id === "disable-shortcuts") {
+        disableShortcuts = elem.checked;
+        storeValue(elem.id, elem.checked);
+    }
+}
 
-            $scope.selectedValuesCount = function (obj) {
-                return Object.values(obj).filter(x => x).length;
-            }
+function onEachLazy(lazyArray, func) {
+    const arr = Array.prototype.slice.call(lazyArray);
+    for (const el of arr) {
+        func(el);
+    }
+}
 
-            $scope.clearVersionFilters = function () {
-                for (const filter in $scope.versionFilters) {
-                    $scope.versionFilters[filter] = { enabled: false, minorVersion: null };
-                }
-            }
+function highlightIfNeeded(elem) {
+    onEachLazy(elem.querySelectorAll("pre > code.language-rust:not(.highlighted)"), el => {
+        hljs.highlightElement(el.parentElement)
+        el.classList.add("highlighted");
+    });
+}
 
-            $scope.versionFilterCount = function(obj) {
-                return Object.values(obj).filter(x => x.enabled).length;
-            }
+function expandLint(lintId) {
+    const lintElem = document.getElementById(lintId);
+    const isCollapsed = lintElem.classList.toggle("collapsed");
+    lintElem.querySelector(".label-doc-folding").innerText = isCollapsed ? "+" : "−";
+    highlightIfNeeded(lintElem);
+}
 
-            $scope.updateVersionFilters = function() {
-                for (const filter in $scope.versionFilters) {
-                    const minorVersion = $scope.versionFilters[filter].minorVersion;
+// Show details for one lint
+function openLint(event) {
+    event.preventDefault();
+    event.stopPropagation();
+    expandLint(event.target.getAttribute("href").slice(1));
+}
 
-                    // 1.29.0 and greater
-                    if (minorVersion && minorVersion > 28) {
-                        $scope.versionFilters[filter].enabled = true;
-                        continue;
-                    }
+function copyToClipboard(event) {
+    event.preventDefault();
+    event.stopPropagation();
 
-                    $scope.versionFilters[filter].enabled = false;
-                }
-            }
+    const clipboard = event.target;
 
-            $scope.byVersion = function(lint) {
-                const filters = $scope.versionFilters;
-                for (const filter in filters) {
-                    if (filters[filter].enabled) {
-                        const minorVersion = filters[filter].minorVersion;
-
-                        // Strip the "pre " prefix for pre 1.29.0 lints
-                        const lintVersion = lint.version.startsWith("pre ") ? lint.version.substring(4, lint.version.length) : lint.version;
-                        const lintMinorVersion = lintVersion.substring(2, 4);
-
-                        switch (filter) {
-                            // "=" gets the highest priority, since all filters are inclusive
-                            case "=":
-                                return (lintMinorVersion == minorVersion);
-                            case "≥":
-                                if (lintMinorVersion < minorVersion) { return false; }
-                                break;
-                            case "≤":
-                                if (lintMinorVersion > minorVersion) { return false; }
-                                break;
-                            default:
-                                return true
-                        }
-                    }
-                }
+    let resetClipboardTimeout = null;
+    const resetClipboardIcon = clipboard.innerHTML;
 
-                return true;
-            }
+    function resetClipboard() {
+        resetClipboardTimeout = null;
+        clipboard.innerHTML = resetClipboardIcon;
+    }
 
-            $scope.byGroups = function (lint) {
-                return $scope.groups[lint.group];
-            };
+    navigator.clipboard.writeText("clippy::" + clipboard.parentElement.id.slice(5));
 
-            $scope.bySearch = function (lint, index, array) {
-                let searchStr = $scope.search;
-                // It can be `null` I haven't missed this value
-                if (searchStr == null) {
-                    return true;
-                }
-                searchStr = searchStr.toLowerCase();
-                if (searchStr.startsWith("clippy::")) {
-                    searchStr = searchStr.slice(8);
-                }
+    clipboard.innerHTML = "&#10003;";
+    if (resetClipboardTimeout !== null) {
+        clearTimeout(resetClipboardTimeout);
+    }
+    resetClipboardTimeout = setTimeout(resetClipboard, 1000);
+}
 
-                // Search by id
-                if (lint.id.indexOf(searchStr.replaceAll("-", "_")) !== -1) {
-                    return true;
-                }
+function handleBlur(event, elementId) {
+    const parent = document.getElementById(elementId);
+    if (!parent.contains(document.activeElement) &&
+        !parent.contains(event.relatedTarget)
+    ) {
+        parent.classList.remove("open");
+    }
+}
 
-                // Search the description
-                // The use of `for`-loops instead of `foreach` enables us to return early
-                const terms = searchStr.split(" ");
-                const docsLowerCase = lint.docs.toLowerCase();
-                for (index = 0; index < terms.length; index++) {
-                    // This is more likely and will therefore be checked first
-                    if (docsLowerCase.indexOf(terms[index]) !== -1) {
-                        continue;
-                    }
+function toggleExpansion(expand) {
+    onEachLazy(
+        document.querySelectorAll("article"),
+        expand ? el => {
+            el.classList.remove("collapsed");
+            highlightIfNeeded(el);
+        } : el => el.classList.add("collapsed"),
+    );
+}
 
-                    if (lint.id.indexOf(terms[index]) !== -1) {
-                        continue;
-                    }
+// Returns the current URL without any query parameter or hash.
+function getNakedUrl() {
+    return window.location.href.split("?")[0].split("#")[0];
+}
 
-                    return false;
+const GROUPS_FILTER_DEFAULT = {
+    cargo: true,
+    complexity: true,
+    correctness: true,
+    nursery: true,
+    pedantic: true,
+    perf: true,
+    restriction: true,
+    style: true,
+    suspicious: true,
+    deprecated: false,
+};
+const LEVEL_FILTERS_DEFAULT = {
+    allow: true,
+    warn: true,
+    deny: true,
+    none: true,
+};
+const APPLICABILITIES_FILTER_DEFAULT = {
+    Unspecified: true,
+    MachineApplicable: true,
+    MaybeIncorrect: true,
+    HasPlaceholders: true,
+};
+const URL_PARAMS_CORRESPONDANCE = {
+    "groups_filter": "groups",
+    "levels_filter": "levels",
+    "applicabilities_filter": "applicabilities",
+    "version_filter": "versions",
+};
+const VERSIONS_CORRESPONDANCE = {
+    "lte": "≤",
+    "gte": "≥",
+    "eq": "=",
+};
+
+window.filters = {
+    groups_filter: { id: "lint-groups", ...GROUPS_FILTER_DEFAULT },
+    levels_filter: { id: "lint-levels", ...LEVEL_FILTERS_DEFAULT },
+    applicabilities_filter: { id: "lint-applicabilities", ...APPLICABILITIES_FILTER_DEFAULT },
+    version_filter: {
+        "≥": null,
+        "≤": null,
+        "=": null,
+    },
+    allLints: null,
+    getAllLints: () => {
+        if (filters.allLints === null) {
+            filters.allLints = Array.prototype.slice.call(
+                document.getElementsByTagName("article"),
+            ).map(elem => {
+                let version = elem.querySelector(".label-version").innerText;
+                // Strip the "pre " prefix for pre 1.29.0 lints
+                if (version.startsWith("pre ")) {
+                    version = version.slice(4);
                 }
+                return {
+                    elem: elem,
+                    group: elem.querySelector(".label-lint-group").innerText,
+                    level: elem.querySelector(".label-lint-level").innerText,
+                    version: parseInt(version.split(".")[1]),
+                    applicability: elem.querySelector(".label-applicability").innerText,
+                    filteredOut: false,
+                    searchFilteredOut: false,
+                };
+            });
+        }
+        return filters.allLints;
+    },
+    regenerateURLparams: () => {
+        const urlParams = new URLSearchParams(window.location.search);
 
-                return true;
-            }
-
-            $scope.byApplicabilities = function (lint) {
-                return $scope.applicabilities[lint.applicability];
-            };
-
-            // Show details for one lint
-            $scope.openLint = function (lint) {
-                $scope.open[lint.id] = true;
-                $location.path(lint.id);
-            };
-
-            $scope.toggleExpansion = function(lints, isExpanded) {
-                lints.forEach(lint => {
-                    $scope.open[lint.id] = isExpanded;
-                });
+        function compareObjects(obj1, obj2) {
+            return (JSON.stringify(obj1) === JSON.stringify({ id: obj1.id, ...obj2 }));
+        }
+        function updateIfNeeded(filterName, obj2) {
+            const obj1 = filters[filterName];
+            const name = URL_PARAMS_CORRESPONDANCE[filterName];
+            if (!compareObjects(obj1, obj2)) {
+                urlParams.set(
+                    name,
+                    Object.entries(obj1).filter(
+                        ([key, value]) => value && key !== "id"
+                    ).map(
+                        ([key, _]) => key
+                    ).join(","),
+                );
+            } else {
+                urlParams.delete(name);
             }
+        }
 
-            $scope.copyToClipboard = function (lint) {
-                const clipboard = document.getElementById("clipboard-" + lint.id);
-                if (clipboard) {
-                    let resetClipboardTimeout = null;
-                    const resetClipboardIcon = clipboard.innerHTML;
+        updateIfNeeded("groups_filter", GROUPS_FILTER_DEFAULT);
+        updateIfNeeded("levels_filter", LEVEL_FILTERS_DEFAULT);
+        updateIfNeeded(
+            "applicabilities_filter", APPLICABILITIES_FILTER_DEFAULT);
 
-                    function resetClipboard() {
-                        resetClipboardTimeout = null;
-                        clipboard.innerHTML = resetClipboardIcon;
-                    }
+        const versions = [];
+        if (filters.version_filter["="] !== null) {
+            versions.push(`eq:${filters.version_filter["="]}`);
+        }
+        if (filters.version_filter["≥"] !== null) {
+            versions.push(`gte:${filters.version_filter["≥"]}`);
+        }
+        if (filters.version_filter["≤"] !== null) {
+            versions.push(`lte:${filters.version_filter["≤"]}`);
+        }
+        if (versions.length !== 0) {
+            urlParams.set(URL_PARAMS_CORRESPONDANCE["version_filter"], versions.join(","));
+        } else {
+            urlParams.delete(URL_PARAMS_CORRESPONDANCE["version_filter"]);
+        }
 
-                    navigator.clipboard.writeText("clippy::" + lint.id);
+        let params = urlParams.toString();
+        if (params.length !== 0) {
+            params = `?${params}`;
+        }
 
-                    clipboard.innerHTML = "&#10003;";
-                    if (resetClipboardTimeout !== null) {
-                        clearTimeout(resetClipboardTimeout);
-                    }
-                    resetClipboardTimeout = setTimeout(resetClipboard, 1000);
-                }
+        const url = getNakedUrl() + params + window.location.hash
+        if (!history.state) {
+            history.pushState(null, "", url);
+        } else {
+            history.replaceState(null, "", url);
+        }
+    },
+    filterLints: () => {
+        // First we regenerate the URL parameters.
+        filters.regenerateURLparams();
+        for (const lint of filters.getAllLints()) {
+            lint.filteredOut = (!filters.groups_filter[lint.group]
+                || !filters.levels_filter[lint.level]
+                || !filters.applicabilities_filter[lint.applicability]
+                || !(filters.version_filter["="] === null || lint.version === filters.version_filter["="])
+                || !(filters.version_filter["≥"] === null || lint.version > filters.version_filter["≥"])
+                || !(filters.version_filter["≤"] === null || lint.version < filters.version_filter["≤"])
+            );
+            if (lint.filteredOut || lint.searchFilteredOut) {
+                lint.elem.style.display = "none";
+            } else {
+                lint.elem.style.display = "";
             }
-
-            // Get data
-            $scope.open = {};
-            $scope.loading = true;
-
-            // This will be used to jump into the source code of the version that this documentation is for.
-            $scope.docVersion = window.location.pathname.split('/')[2] || "master";
-
-            // Set up the filters from the URL parameters before we start loading the data
-            loadFromURLParameters();
-
-            $http.get('./lints.json')
-                .success(function (data) {
-                    $scope.data = data;
-                    $scope.loading = false;
-
-                    const selectedGroup = getQueryVariable("sel");
-                    if (selectedGroup) {
-                        selectGroup($scope, selectedGroup.toLowerCase());
-                    }
-
-                    scrollToLintByURL($scope, $location);
-
-                    setTimeout(function () {
-                        const el = document.getElementById('filter-input');
-                        if (el) { el.focus() }
-                    }, 0);
-                })
-                .error(function (data) {
-                    $scope.error = data;
-                    $scope.loading = false;
-                });
-        });
-})();
-
-function getQueryVariable(variable) {
-    const query = window.location.search.substring(1);
-    const vars = query.split('&');
-    for (const entry of vars) {
-        const pair = entry.split('=');
-        if (decodeURIComponent(pair[0]) == variable) {
-            return decodeURIComponent(pair[1]);
+        }
+    },
+};
+
+function updateFilter(elem, filter, skipLintsFiltering) {
+    const value = elem.getAttribute("data-value");
+    if (filters[filter][value] !== elem.checked) {
+        filters[filter][value] = elem.checked;
+        const counter = document.querySelector(`#${filters[filter].id} .badge`);
+        counter.innerText = parseInt(counter.innerText) + (elem.checked ? 1 : -1);
+        if (!skipLintsFiltering) {
+            filters.filterLints();
         }
     }
 }
 
-function storeValue(settingName, value) {
-    try {
-        localStorage.setItem(`clippy-lint-list-${settingName}`, value);
-    } catch (e) { }
-}
-
-function loadValue(settingName) {
-    return localStorage.getItem(`clippy-lint-list-${settingName}`);
-}
-
-function setTheme(theme, store) {
-    let enableHighlight = false;
-    let enableNight = false;
-    let enableAyu = false;
-
-    switch(theme) {
-        case "ayu":
-            enableAyu = true;
-            break;
-        case "coal":
-        case "navy":
-            enableNight = true;
-            break;
-        case "rust":
-            enableHighlight = true;
-            break;
-        default:
-            enableHighlight = true;
-            theme = "light";
-            break;
+function updateVersionFilters(elem, skipLintsFiltering) {
+    let value = elem.value.trim();
+    if (value.length === 0) {
+        value = null;
+    } else if (/^\d+$/.test(value)) {
+        value = parseInt(value);
+    } else {
+        console.error(`Failed to get version number from "${value}"`);
+        return;
     }
 
-    document.getElementsByTagName("body")[0].className = theme;
+    const counter = document.querySelector("#version-filter .badge");
+    let count = 0;
+    onEachLazy(document.querySelectorAll("#version-filter input"), el => {
+        if (el.value.trim().length !== 0) {
+            count += 1;
+        }
+    });
+    counter.innerText = count;
 
-    document.getElementById("githubLightHighlight").disabled = enableNight || !enableHighlight;
-    document.getElementById("githubDarkHighlight").disabled = !enableNight && !enableAyu;
+    const comparisonKind = elem.getAttribute("data-value");
+    if (filters.version_filter[comparisonKind] !== value) {
+        filters.version_filter[comparisonKind] = value;
+        if (!skipLintsFiltering) {
+            filters.filterLints();
+        }
+    }
+}
 
-    document.getElementById("styleHighlight").disabled = !enableHighlight;
-    document.getElementById("styleNight").disabled = !enableNight;
-    document.getElementById("styleAyu").disabled = !enableAyu;
+function clearVersionFilters() {
+    let needsUpdate = false;
 
-    if (store) {
-        storeValue("theme", theme);
-    } else {
-        document.getElementById(`theme-choice`).value = theme;
+    onEachLazy(document.querySelectorAll("#version-filter input"), el => {
+        el.value = "";
+        const comparisonKind = el.getAttribute("data-value");
+        if (filters.version_filter[comparisonKind] !== null) {
+            needsUpdate = true;
+            filters.version_filter[comparisonKind] = null;
+        }
+    });
+    document.querySelector("#version-filter .badge").innerText = 0;
+    if (needsUpdate) {
+        filters.filterLints();
     }
 }
 
-function handleShortcut(ev) {
-    if (ev.ctrlKey || ev.altKey || ev.metaKey || disableShortcuts) {
-        return;
+function resetGroupsToDefault() {
+    let needsUpdate = false;
+    let count = 0;
+
+    onEachLazy(document.querySelectorAll("#lint-groups-selector input"), el => {
+        const key = el.getAttribute("data-value");
+        const value = GROUPS_FILTER_DEFAULT[key];
+        if (filters.groups_filter[key] !== value) {
+            filters.groups_filter[key] = value;
+            el.checked = value;
+            needsUpdate = true;
+        }
+        if (value) {
+            count += 1;
+        }
+    });
+    document.querySelector("#lint-groups .badge").innerText = count;
+    if (needsUpdate) {
+        filters.filterLints();
     }
+}
 
-    if (document.activeElement.tagName === "INPUT") {
-        if (ev.key === "Escape") {
-            document.activeElement.blur();
-        }
-    } else {
-        switch (ev.key) {
-            case "s":
-            case "S":
-            case "/":
-                ev.preventDefault(); // To prevent the key to be put into the input.
-                document.getElementById("search-input").focus();
-                break;
-            default:
-                break;
+function generateListOfOptions(list, elementId, filter) {
+    let html = '';
+    let nbEnabled = 0;
+    for (const [key, value] of Object.entries(list)) {
+        const attr = value ? " checked" : "";
+        html += `\
+<li class="checkbox">\
+    <label class="text-capitalize">\
+        <input type="checkbox" data-value="${key}" \
+               onchange="updateFilter(this, '${filter}')"${attr}/>${key}\
+    </label>\
+</li>`;
+        if (value) {
+            nbEnabled += 1;
         }
     }
+
+    const elem = document.getElementById(`${elementId}-selector`);
+    elem.previousElementSibling.querySelector(".badge").innerText = `${nbEnabled}`;
+    elem.innerHTML += html;
+
+    setupDropdown(elementId);
 }
 
-document.addEventListener("keypress", handleShortcut);
-document.addEventListener("keydown", handleShortcut);
+function setupDropdown(elementId) {
+    const elem = document.getElementById(elementId);
+    const button = document.querySelector(`#${elementId} > button`);
+    button.onclick = () => elem.classList.toggle("open");
+
+    const setBlur = child => {
+        child.onblur = event => handleBlur(event, elementId);
+    };
+    onEachLazy(elem.children, setBlur);
+    onEachLazy(elem.querySelectorAll("select"), setBlur);
+    onEachLazy(elem.querySelectorAll("input"), setBlur);
+    onEachLazy(elem.querySelectorAll("ul button"), setBlur);
+}
 
-function changeSetting(elem) {
-    if (elem.id === "disable-shortcuts") {
-        disableShortcuts = elem.checked;
-        storeValue(elem.id, elem.checked);
+function generateSettings() {
+    setupDropdown("settings-dropdown");
+
+    generateListOfOptions(LEVEL_FILTERS_DEFAULT, "lint-levels", "levels_filter");
+    generateListOfOptions(GROUPS_FILTER_DEFAULT, "lint-groups", "groups_filter");
+    generateListOfOptions(
+        APPLICABILITIES_FILTER_DEFAULT, "lint-applicabilities", "applicabilities_filter");
+
+    let html = '';
+    for (const kind of ["≥", "≤", "="]) {
+        html += `\
+<li class="checkbox">\
+    <label>${kind}</label>\
+    <span>1.</span> \
+    <input type="number" \
+           min="29" \
+           class="version-filter-input form-control filter-input" \
+           maxlength="2" \
+           data-value="${kind}" \
+           onchange="updateVersionFilters(this)" \
+           oninput="updateVersionFilters(this)" \
+           onkeydown="updateVersionFilters(this)" \
+           onkeyup="updateVersionFilters(this)" \
+           onpaste="updateVersionFilters(this)" \
+    />
+    <span>.0</span>\
+</li>`;
     }
+    document.getElementById("version-filter-selector").innerHTML += html;
+    setupDropdown("version-filter");
 }
 
-function onEachLazy(lazyArray, func) {
-    const arr = Array.prototype.slice.call(lazyArray);
-    for (const el of arr) {
-        func(el);
-    }
+function generateSearch() {
+    searchState.inputElem.addEventListener("change", handleInputChanged);
+    searchState.inputElem.addEventListener("input", handleInputChanged);
+    searchState.inputElem.addEventListener("keydown", handleInputChanged);
+    searchState.inputElem.addEventListener("keyup", handleInputChanged);
+    searchState.inputElem.addEventListener("paste", handleInputChanged);
 }
 
-function handleBlur(event) {
-    const parent = document.getElementById("settings-dropdown");
-    if (!parent.contains(document.activeElement) &&
-        !parent.contains(event.relatedTarget)
-    ) {
-        parent.classList.remove("open");
+function scrollToLint(lintId) {
+    const target = document.getElementById(lintId);
+    if (!target) {
+        return;
     }
+    target.scrollIntoView();
+    expandLint(lintId);
 }
 
-function generateSettings() {
-    const settings = document.getElementById("settings-dropdown");
-    const settingsButton = settings.querySelector(".settings-icon")
-    settingsButton.onclick = () => settings.classList.toggle("open");
-    settingsButton.onblur = handleBlur;
-    const settingsMenu = settings.querySelector(".settings-menu");
-    settingsMenu.onblur = handleBlur;
-    onEachLazy(
-        settingsMenu.querySelectorAll("input"),
-        el => el.onblur = handleBlur,
-    );
+// If the page we arrive on has link to a given lint, we scroll to it.
+function scrollToLintByURL() {
+    const lintId = window.location.hash.substring(2);
+    if (lintId.length > 0) {
+        scrollToLint(lintId);
+    }
 }
 
-generateSettings();
+function parseURLFilters() {
+    const urlParams = new URLSearchParams(window.location.search);
+
+    for (const [key, value] of urlParams.entries()) {
+        for (const [corres_key, corres_value] of Object.entries(URL_PARAMS_CORRESPONDANCE)) {
+            if (corres_value === key) {
+                if (key !== "versions") {
+                    const settings  = new Set(value.split(","));
+                    onEachLazy(document.querySelectorAll(`#lint-${key} ul input`), elem => {
+                        elem.checked = settings.has(elem.getAttribute("data-value"));
+                        updateFilter(elem, corres_key, true);
+                    });
+                } else {
+                    const settings = value.split(",").map(elem => elem.split(":"));
 
-// loading the theme after the initial load
-const prefersDark = window.matchMedia("(prefers-color-scheme: dark)");
-const theme = loadValue('theme');
-if (prefersDark.matches && !theme) {
-    setTheme("coal", false);
-} else {
-    setTheme(theme, false);
+                    for (const [kind, value] of settings) {
+                        const elem = document.querySelector(
+                            `#version-filter input[data-value="${VERSIONS_CORRESPONDANCE[kind]}"]`);
+                        elem.value = value;
+                        updateVersionFilters(elem, true);
+                    }
+                }
+            }
+        }
+    }
 }
+
+document.getElementById(`theme-choice`).value = loadValue("theme");
 let disableShortcuts = loadValue('disable-shortcuts') === "true";
 document.getElementById("disable-shortcuts").checked = disableShortcuts;
+
+document.addEventListener("keypress", handleShortcut);
+document.addEventListener("keydown", handleShortcut);
+
+generateSettings();
+generateSearch();
+parseURLFilters();
+scrollToLintByURL();
+filters.filterLints();
diff --git a/src/tools/clippy/util/gh-pages/style.css b/src/tools/clippy/util/gh-pages/style.css
index a9485d51104..a68a10b1401 100644
--- a/src/tools/clippy/util/gh-pages/style.css
+++ b/src/tools/clippy/util/gh-pages/style.css
@@ -272,8 +272,9 @@ L4.75,12h2.5l0.5393066-2.1572876  c0.2276001-0.1062012,0.4459839-0.2269287,0.649
   height: 18px;
   display: block;
   filter: invert(0.7);
-  padding-left: 4px;
-  padding-top: 3px;
+  position: absolute;
+  top: 4px;
+  left: 5px;
 }
 
 .settings-menu * {
@@ -329,6 +330,18 @@ L4.75,12h2.5l0.5393066-2.1572876  c0.2276001-0.1062012,0.4459839-0.2269287,0.649
     display: flex;
 }
 
+ul.dropdown-menu li.checkbox > button {
+    border: 0;
+    width: 100%;
+    background: var(--theme-popup-bg);
+    color: var(--fg);
+}
+
+ul.dropdown-menu li.checkbox > button:hover {
+    background: var(--theme-hover);
+    box-shadow: none;
+}
+
 #version-filter {
     min-width: available;
 }
@@ -396,3 +409,37 @@ body {
     background: var(--bg);
     color: var(--fg);
 }
+
+article.collapsed .lint-docs {
+    display: none;
+}
+
+.github-corner svg {
+    fill: var(--fg);
+    color: var(--bg);
+}
+.github-corner:hover .octo-arm {
+    animation: octocat-wave 560ms ease-in-out;
+}
+@keyframes octocat-wave {
+    0%,
+    100% {
+        transform: rotate(0);
+    }
+    20%,
+    60% {
+        transform: rotate(-25deg);
+    }
+    40%,
+    80% {
+        transform: rotate(10deg);
+    }
+}
+@media (max-width: 500px) {
+    .github-corner:hover .octo-arm {
+        animation: none;
+    }
+    .github-corner .octo-arm {
+        animation: octocat-wave 560ms ease-in-out;
+    }
+}
diff --git a/src/tools/clippy/util/gh-pages/theme.js b/src/tools/clippy/util/gh-pages/theme.js
new file mode 100644
index 00000000000..bc296955ddf
--- /dev/null
+++ b/src/tools/clippy/util/gh-pages/theme.js
@@ -0,0 +1,56 @@
+function storeValue(settingName, value) {
+    try {
+        localStorage.setItem(`clippy-lint-list-${settingName}`, value);
+    } catch (e) { }
+}
+
+function loadValue(settingName) {
+    return localStorage.getItem(`clippy-lint-list-${settingName}`);
+}
+
+function setTheme(theme, store) {
+    let enableHighlight = false;
+    let enableNight = false;
+    let enableAyu = false;
+
+    switch(theme) {
+        case "ayu":
+            enableAyu = true;
+            break;
+        case "coal":
+        case "navy":
+            enableNight = true;
+            break;
+        case "rust":
+            enableHighlight = true;
+            break;
+        default:
+            enableHighlight = true;
+            theme = "light";
+            break;
+    }
+
+    document.body.className = theme;
+
+    document.getElementById("githubLightHighlight").disabled = enableNight || !enableHighlight;
+    document.getElementById("githubDarkHighlight").disabled = !enableNight && !enableAyu;
+
+    document.getElementById("styleHighlight").disabled = !enableHighlight;
+    document.getElementById("styleNight").disabled = !enableNight;
+    document.getElementById("styleAyu").disabled = !enableAyu;
+
+    if (store) {
+        storeValue("theme", theme);
+    }
+}
+
+(function() {
+    // loading the theme after the initial load
+    const prefersDark = window.matchMedia("(prefers-color-scheme: dark)");
+    const theme = loadValue("theme");
+    if (prefersDark.matches && !theme) {
+        setTheme("coal", false);
+    } else {
+        setTheme(theme, false);
+    }
+})();
diff --git a/src/tools/compiletest/src/common.rs b/src/tools/compiletest/src/common.rs
index 1ee00a3a4e8..ff059940f7c 100644
--- a/src/tools/compiletest/src/common.rs
+++ b/src/tools/compiletest/src/common.rs
@@ -338,8 +338,8 @@ pub struct Config {
     /// created in `/<build_base>/rustfix_missing_coverage.txt`
     pub rustfix_coverage: bool,
 
-    /// whether to run `tidy` when a rustdoc test fails
-    pub has_tidy: bool,
+    /// whether to run `tidy` (html-tidy) when a rustdoc test fails
+    pub has_html_tidy: bool,
 
     /// whether to run `enzyme` autodiff tests
     pub has_enzyme: bool,
diff --git a/src/tools/compiletest/src/lib.rs b/src/tools/compiletest/src/lib.rs
index 2e66c084dd7..7d6ede9bcda 100644
--- a/src/tools/compiletest/src/lib.rs
+++ b/src/tools/compiletest/src/lib.rs
@@ -230,14 +230,14 @@ pub fn parse_config(args: Vec<String>) -> Config {
     let run_ignored = matches.opt_present("ignored");
     let with_debug_assertions = matches.opt_present("with-debug-assertions");
     let mode = matches.opt_str("mode").unwrap().parse().expect("invalid mode");
-    let has_tidy = if mode == Mode::Rustdoc {
+    let has_html_tidy = if mode == Mode::Rustdoc {
         Command::new("tidy")
             .arg("--version")
             .stdout(Stdio::null())
             .status()
             .map_or(false, |status| status.success())
     } else {
-        // Avoid spawning an external command when we know tidy won't be used.
+        // Avoid spawning an external command when we know html-tidy won't be used.
         false
     };
     let has_enzyme = matches.opt_present("has-enzyme");
@@ -336,7 +336,7 @@ pub fn parse_config(args: Vec<String>) -> Config {
             .opt_str("compare-mode")
             .map(|s| s.parse().expect("invalid --compare-mode provided")),
         rustfix_coverage: matches.opt_present("rustfix-coverage"),
-        has_tidy,
+        has_html_tidy,
         has_enzyme,
         channel: matches.opt_str("channel").unwrap(),
         git_hash: matches.opt_present("git-hash"),
@@ -464,9 +464,7 @@ pub fn run_tests(config: Arc<Config>) {
     // structure for each test (or each revision of a multi-revision test).
     let mut tests = Vec::new();
     for c in configs {
-        let mut found_paths = HashSet::new();
-        make_tests(c, &mut tests, &mut found_paths);
-        check_overlapping_tests(&found_paths);
+        tests.extend(collect_and_make_tests(c));
     }
 
     tests.sort_by(|a, b| a.desc.name.as_slice().cmp(&b.desc.name.as_slice()));
@@ -545,46 +543,62 @@ pub fn test_opts(config: &Config) -> test::TestOpts {
     }
 }
 
+/// Read-only context data used during test collection.
+struct TestCollectorCx {
+    config: Arc<Config>,
+    cache: HeadersCache,
+    common_inputs_stamp: Stamp,
+    modified_tests: Vec<PathBuf>,
+}
+
+/// Mutable state used during test collection.
+struct TestCollector {
+    tests: Vec<test::TestDescAndFn>,
+    found_path_stems: HashSet<PathBuf>,
+    poisoned: bool,
+}
+
 /// Creates libtest structures for every test/revision in the test suite directory.
 ///
 /// This always inspects _all_ test files in the suite (e.g. all 17k+ ui tests),
 /// regardless of whether any filters/tests were specified on the command-line,
 /// because filtering is handled later by libtest.
-pub fn make_tests(
-    config: Arc<Config>,
-    tests: &mut Vec<test::TestDescAndFn>,
-    found_paths: &mut HashSet<PathBuf>,
-) {
+pub fn collect_and_make_tests(config: Arc<Config>) -> Vec<test::TestDescAndFn> {
     debug!("making tests from {:?}", config.src_base.display());
-    let inputs = common_inputs_stamp(&config);
+    let common_inputs_stamp = common_inputs_stamp(&config);
     let modified_tests = modified_tests(&config, &config.src_base).unwrap_or_else(|err| {
         panic!("modified_tests got error from dir: {}, error: {}", config.src_base.display(), err)
     });
-
     let cache = HeadersCache::load(&config);
-    let mut poisoned = false;
-    collect_tests_from_dir(
-        config.clone(),
-        &cache,
-        &config.src_base,
-        &PathBuf::new(),
-        &inputs,
-        tests,
-        found_paths,
-        &modified_tests,
-        &mut poisoned,
-    )
-    .unwrap_or_else(|reason| {
-        panic!("Could not read tests from {}: {reason}", config.src_base.display())
-    });
+
+    let cx = TestCollectorCx { config, cache, common_inputs_stamp, modified_tests };
+    let mut collector =
+        TestCollector { tests: vec![], found_path_stems: HashSet::new(), poisoned: false };
+
+    collect_tests_from_dir(&cx, &mut collector, &cx.config.src_base, &PathBuf::new())
+        .unwrap_or_else(|reason| {
+            panic!("Could not read tests from {}: {reason}", cx.config.src_base.display())
+        });
+
+    let TestCollector { tests, found_path_stems, poisoned } = collector;
 
     if poisoned {
         eprintln!();
         panic!("there are errors in tests");
     }
+
+    check_for_overlapping_test_paths(&found_path_stems);
+
+    tests
 }
 
-/// Returns a stamp constructed from input files common to all test cases.
+/// Returns the most recent last-modified timestamp from among the input files
+/// that are considered relevant to all tests (e.g. the compiler, std, and
+/// compiletest itself).
+///
+/// (Some of these inputs aren't actually relevant to _all_ tests, but they are
+/// common to some subset of tests, and are hopefully unlikely to be modified
+/// while working on other tests.)
 fn common_inputs_stamp(config: &Config) -> Stamp {
     let rust_src_dir = config.find_rust_src_root().expect("Could not find Rust source root");
 
@@ -662,15 +676,10 @@ fn modified_tests(config: &Config, dir: &Path) -> Result<Vec<PathBuf>, String> {
 /// Recursively scans a directory to find test files and create test structures
 /// that will be handed over to libtest.
 fn collect_tests_from_dir(
-    config: Arc<Config>,
-    cache: &HeadersCache,
+    cx: &TestCollectorCx,
+    collector: &mut TestCollector,
     dir: &Path,
     relative_dir_path: &Path,
-    inputs: &Stamp,
-    tests: &mut Vec<test::TestDescAndFn>,
-    found_paths: &mut HashSet<PathBuf>,
-    modified_tests: &Vec<PathBuf>,
-    poisoned: &mut bool,
 ) -> io::Result<()> {
     // Ignore directories that contain a file named `compiletest-ignore-dir`.
     if dir.join("compiletest-ignore-dir").exists() {
@@ -679,7 +688,7 @@ fn collect_tests_from_dir(
 
     // For run-make tests, a "test file" is actually a directory that contains
     // an `rmake.rs` or `Makefile`"
-    if config.mode == Mode::RunMake {
+    if cx.config.mode == Mode::RunMake {
         if dir.join("Makefile").exists() && dir.join("rmake.rs").exists() {
             return Err(io::Error::other(
                 "run-make tests cannot have both `Makefile` and `rmake.rs`",
@@ -691,7 +700,7 @@ fn collect_tests_from_dir(
                 file: dir.to_path_buf(),
                 relative_dir: relative_dir_path.parent().unwrap().to_path_buf(),
             };
-            tests.extend(make_test(config, cache, &paths, inputs, poisoned));
+            make_test(cx, collector, &paths);
             // This directory is a test, so don't try to find other tests inside it.
             return Ok(());
         }
@@ -703,7 +712,7 @@ fn collect_tests_from_dir(
     // sequential loop because otherwise, if we do it in the
     // tests themselves, they race for the privilege of
     // creating the directories and sometimes fail randomly.
-    let build_dir = output_relative_path(&config, relative_dir_path);
+    let build_dir = output_relative_path(&cx.config, relative_dir_path);
     fs::create_dir_all(&build_dir).unwrap();
 
     // Add each `.rs` file as a test, and recurse further on any
@@ -715,33 +724,25 @@ fn collect_tests_from_dir(
         let file_path = file.path();
         let file_name = file.file_name();
 
-        if is_test(&file_name) && (!config.only_modified || modified_tests.contains(&file_path)) {
+        if is_test(&file_name)
+            && (!cx.config.only_modified || cx.modified_tests.contains(&file_path))
+        {
             // We found a test file, so create the corresponding libtest structures.
             debug!("found test file: {:?}", file_path.display());
 
             // Record the stem of the test file, to check for overlaps later.
             let rel_test_path = relative_dir_path.join(file_path.file_stem().unwrap());
-            found_paths.insert(rel_test_path);
+            collector.found_path_stems.insert(rel_test_path);
 
             let paths =
                 TestPaths { file: file_path, relative_dir: relative_dir_path.to_path_buf() };
-            tests.extend(make_test(config.clone(), cache, &paths, inputs, poisoned))
+            make_test(cx, collector, &paths);
         } else if file_path.is_dir() {
             // Recurse to find more tests in a subdirectory.
             let relative_file_path = relative_dir_path.join(file.file_name());
             if &file_name != "auxiliary" {
                 debug!("found directory: {:?}", file_path.display());
-                collect_tests_from_dir(
-                    config.clone(),
-                    cache,
-                    &file_path,
-                    &relative_file_path,
-                    inputs,
-                    tests,
-                    found_paths,
-                    modified_tests,
-                    poisoned,
-                )?;
+                collect_tests_from_dir(cx, collector, &file_path, &relative_file_path)?;
             }
         } else {
             debug!("found other file/directory: {:?}", file_path.display());
@@ -765,17 +766,11 @@ pub fn is_test(file_name: &OsString) -> bool {
 
 /// For a single test file, creates one or more test structures (one per revision)
 /// that can be handed over to libtest to run, possibly in parallel.
-fn make_test(
-    config: Arc<Config>,
-    cache: &HeadersCache,
-    testpaths: &TestPaths,
-    inputs: &Stamp,
-    poisoned: &mut bool,
-) -> Vec<test::TestDescAndFn> {
+fn make_test(cx: &TestCollectorCx, collector: &mut TestCollector, testpaths: &TestPaths) {
     // For run-make tests, each "test file" is actually a _directory_ containing
     // an `rmake.rs` or `Makefile`. But for the purposes of directive parsing,
     // we want to look at that recipe file, not the directory itself.
-    let test_path = if config.mode == Mode::RunMake {
+    let test_path = if cx.config.mode == Mode::RunMake {
         if testpaths.file.join("rmake.rs").exists() && testpaths.file.join("Makefile").exists() {
             panic!("run-make tests cannot have both `rmake.rs` and `Makefile`");
         }
@@ -792,7 +787,7 @@ fn make_test(
     };
 
     // Scan the test file to discover its revisions, if any.
-    let early_props = EarlyProps::from_file(&config, &test_path);
+    let early_props = EarlyProps::from_file(&cx.config, &test_path);
 
     // Normally we create one libtest structure per revision, with two exceptions:
     // - If a test doesn't use revisions, create a dummy revision (None) so that
@@ -800,49 +795,49 @@ fn make_test(
     // - Incremental tests inherently can't run their revisions in parallel, so
     //   we treat them like non-revisioned tests here. Incremental revisions are
     //   handled internally by `runtest::run` instead.
-    let revisions = if early_props.revisions.is_empty() || config.mode == Mode::Incremental {
+    let revisions = if early_props.revisions.is_empty() || cx.config.mode == Mode::Incremental {
         vec![None]
     } else {
         early_props.revisions.iter().map(|r| Some(r.as_str())).collect()
     };
 
-    // For each revision (or the sole dummy revision), create and return a
+    // For each revision (or the sole dummy revision), create and append a
     // `test::TestDescAndFn` that can be handed over to libtest.
-    revisions
-        .into_iter()
-        .map(|revision| {
-            // Create a test name and description to hand over to libtest.
-            let src_file =
-                std::fs::File::open(&test_path).expect("open test file to parse ignores");
-            let test_name = crate::make_test_name(&config, testpaths, revision);
-            // Create a libtest description for the test/revision.
-            // This is where `ignore-*`/`only-*`/`needs-*` directives are handled,
-            // because they need to set the libtest ignored flag.
-            let mut desc = make_test_description(
-                &config, cache, test_name, &test_path, src_file, revision, poisoned,
-            );
+    collector.tests.extend(revisions.into_iter().map(|revision| {
+        // Create a test name and description to hand over to libtest.
+        let src_file = fs::File::open(&test_path).expect("open test file to parse ignores");
+        let test_name = make_test_name(&cx.config, testpaths, revision);
+        // Create a libtest description for the test/revision.
+        // This is where `ignore-*`/`only-*`/`needs-*` directives are handled,
+        // because they need to set the libtest ignored flag.
+        let mut desc = make_test_description(
+            &cx.config,
+            &cx.cache,
+            test_name,
+            &test_path,
+            src_file,
+            revision,
+            &mut collector.poisoned,
+        );
 
-            // If a test's inputs haven't changed since the last time it ran,
-            // mark it as ignored so that libtest will skip it.
-            if !config.force_rerun
-                && is_up_to_date(&config, testpaths, &early_props, revision, inputs)
-            {
-                desc.ignore = true;
-                // Keep this in sync with the "up-to-date" message detected by bootstrap.
-                desc.ignore_message = Some("up-to-date");
-            }
+        // If a test's inputs haven't changed since the last time it ran,
+        // mark it as ignored so that libtest will skip it.
+        if !cx.config.force_rerun && is_up_to_date(cx, testpaths, &early_props, revision) {
+            desc.ignore = true;
+            // Keep this in sync with the "up-to-date" message detected by bootstrap.
+            desc.ignore_message = Some("up-to-date");
+        }
 
-            // Create the callback that will run this test/revision when libtest calls it.
-            let testfn = make_test_closure(config.clone(), testpaths, revision);
+        // Create the callback that will run this test/revision when libtest calls it.
+        let testfn = make_test_closure(Arc::clone(&cx.config), testpaths, revision);
 
-            test::TestDescAndFn { desc, testfn }
-        })
-        .collect()
+        test::TestDescAndFn { desc, testfn }
+    }));
 }
 
 /// The path of the `stamp` file that gets created or updated whenever a
 /// particular test completes successfully.
-fn stamp(config: &Config, testpaths: &TestPaths, revision: Option<&str>) -> PathBuf {
+fn stamp_file_path(config: &Config, testpaths: &TestPaths, revision: Option<&str>) -> PathBuf {
     output_base_dir(config, testpaths, revision).join("stamp")
 }
 
@@ -891,21 +886,20 @@ fn files_related_to_test(
 /// (This is not very reliable in some circumstances, so the `--force-rerun`
 /// flag can be used to ignore up-to-date checking and always re-run tests.)
 fn is_up_to_date(
-    config: &Config,
+    cx: &TestCollectorCx,
     testpaths: &TestPaths,
     props: &EarlyProps,
     revision: Option<&str>,
-    inputs: &Stamp, // Last-modified timestamp of the compiler, compiletest etc
 ) -> bool {
-    let stamp_name = stamp(config, testpaths, revision);
+    let stamp_file_path = stamp_file_path(&cx.config, testpaths, revision);
     // Check the config hash inside the stamp file.
-    let contents = match fs::read_to_string(&stamp_name) {
+    let contents = match fs::read_to_string(&stamp_file_path) {
         Ok(f) => f,
         Err(ref e) if e.kind() == ErrorKind::InvalidData => panic!("Can't read stamp contents"),
         // The test hasn't succeeded yet, so it is not up-to-date.
         Err(_) => return false,
     };
-    let expected_hash = runtest::compute_stamp_hash(config);
+    let expected_hash = runtest::compute_stamp_hash(&cx.config);
     if contents != expected_hash {
         // Some part of compiletest configuration has changed since the test
         // last succeeded, so it is not up-to-date.
@@ -914,14 +908,14 @@ fn is_up_to_date(
 
     // Check the timestamp of the stamp file against the last modified time
     // of all files known to be relevant to the test.
-    let mut inputs = inputs.clone();
-    for path in files_related_to_test(config, testpaths, props, revision) {
-        inputs.add_path(&path);
+    let mut inputs_stamp = cx.common_inputs_stamp.clone();
+    for path in files_related_to_test(&cx.config, testpaths, props, revision) {
+        inputs_stamp.add_path(&path);
     }
 
     // If no relevant files have been modified since the stamp file was last
     // written, the test is up-to-date.
-    inputs < Stamp::from_path(&stamp_name)
+    inputs_stamp < Stamp::from_path(&stamp_file_path)
 }
 
 /// The maximum of a set of file-modified timestamps.
@@ -1029,11 +1023,11 @@ fn make_test_closure(
 /// To avoid problems, we forbid test names from overlapping in this way.
 ///
 /// See <https://github.com/rust-lang/rust/pull/109509> for more context.
-fn check_overlapping_tests(found_paths: &HashSet<PathBuf>) {
+fn check_for_overlapping_test_paths(found_path_stems: &HashSet<PathBuf>) {
     let mut collisions = Vec::new();
-    for path in found_paths {
+    for path in found_path_stems {
         for ancestor in path.ancestors().skip(1) {
-            if found_paths.contains(ancestor) {
+            if found_path_stems.contains(ancestor) {
                 collisions.push((path, ancestor));
             }
         }
diff --git a/src/tools/compiletest/src/main.rs b/src/tools/compiletest/src/main.rs
index b0f87593f95..2f0c7d8ddc5 100644
--- a/src/tools/compiletest/src/main.rs
+++ b/src/tools/compiletest/src/main.rs
@@ -18,8 +18,8 @@ fn main() {
 
     let config = Arc::new(parse_config(env::args().collect()));
 
-    if !config.has_tidy && config.mode == Mode::Rustdoc {
-        eprintln!("warning: `tidy` is not installed; diffs will not be generated");
+    if !config.has_html_tidy && config.mode == Mode::Rustdoc {
+        eprintln!("warning: `tidy` (html-tidy.org) is not installed; diffs will not be generated");
     }
 
     if !config.profiler_runtime && config.mode == Mode::CoverageRun {
diff --git a/src/tools/compiletest/src/runtest.rs b/src/tools/compiletest/src/runtest.rs
index 69a47fcd0fb..36c5106ddad 100644
--- a/src/tools/compiletest/src/runtest.rs
+++ b/src/tools/compiletest/src/runtest.rs
@@ -1,5 +1,3 @@
-// ignore-tidy-filelength
-
 use std::borrow::Cow;
 use std::collections::{HashMap, HashSet};
 use std::ffi::OsString;
@@ -29,7 +27,7 @@ use crate::errors::{self, Error, ErrorKind};
 use crate::header::TestProps;
 use crate::read2::{Truncated, read2_abbreviated};
 use crate::util::{PathBufExt, add_dylib_path, logv, static_regex};
-use crate::{ColorConfig, json};
+use crate::{ColorConfig, json, stamp_file_path};
 
 mod debugger;
 
@@ -468,7 +466,19 @@ impl<'test> TestCx<'test> {
 
         if let Some(revision) = self.revision {
             let normalized_revision = normalize_revision(revision);
-            cmd.args(&["--cfg", &normalized_revision]);
+            let cfg_arg = ["--cfg", &normalized_revision];
+            let arg = format!("--cfg={normalized_revision}");
+            if self
+                .props
+                .compile_flags
+                .windows(2)
+                .any(|args| args == cfg_arg || args[0] == arg || args[1] == arg)
+            {
+                panic!(
+                    "error: redundant cfg argument `{normalized_revision}` is already created by the revision"
+                );
+            }
+            cmd.args(cfg_arg);
         }
 
         if !self.props.no_auto_check_cfg {
@@ -1885,7 +1895,7 @@ impl<'test> TestCx<'test> {
     }
 
     fn compare_to_default_rustdoc(&mut self, out_dir: &Path) {
-        if !self.config.has_tidy {
+        if !self.config.has_html_tidy {
             return;
         }
         println!("info: generating a diff against nightly rustdoc");
@@ -2595,8 +2605,8 @@ impl<'test> TestCx<'test> {
     }
 
     fn create_stamp(&self) {
-        let stamp = crate::stamp(&self.config, self.testpaths, self.revision);
-        fs::write(&stamp, compute_stamp_hash(&self.config)).unwrap();
+        let stamp_file_path = stamp_file_path(&self.config, self.testpaths, self.revision);
+        fs::write(&stamp_file_path, compute_stamp_hash(&self.config)).unwrap();
     }
 
     fn init_incremental_test(&self) {
diff --git a/src/tools/jsondoclint/Cargo.toml b/src/tools/jsondoclint/Cargo.toml
index 1318a1f4476..cc8ecefd530 100644
--- a/src/tools/jsondoclint/Cargo.toml
+++ b/src/tools/jsondoclint/Cargo.toml
@@ -9,7 +9,7 @@ edition = "2021"
 anyhow = "1.0.62"
 clap = { version = "4.0.15", features = ["derive"] }
 fs-err = "2.8.1"
-rustc-hash = "1.1.0"
+rustc-hash = "2.0.0"
 rustdoc-json-types = { version = "0.1.0", path = "../../rustdoc-json-types" }
 serde = { version = "1.0", features = ["derive"] }
 serde_json = "1.0.85"
diff --git a/src/tools/linkchecker/main.rs b/src/tools/linkchecker/main.rs
index fa77ab1e011..833e41df537 100644
--- a/src/tools/linkchecker/main.rs
+++ b/src/tools/linkchecker/main.rs
@@ -100,6 +100,7 @@ fn main() {
         links_ignored_external: 0,
         links_ignored_exception: 0,
         intra_doc_exceptions: 0,
+        has_broken_urls: false,
     };
     checker.walk(&docs, &mut report);
     report.report();
@@ -116,6 +117,8 @@ struct Checker {
 
 struct Report {
     errors: u32,
+    // Used to provide help message to remind the user to register a page in `SUMMARY.md`.
+    has_broken_urls: bool,
     start: Instant,
     html_files: u32,
     html_redirects: u32,
@@ -274,6 +277,7 @@ impl Checker {
                     report.links_ignored_exception += 1;
                 } else {
                     report.errors += 1;
+                    report.has_broken_urls = true;
                     println!("{}:{}: broken link - `{}`", pretty_path, i, target_pretty_path);
                 }
                 return;
@@ -438,6 +442,13 @@ impl Report {
         println!("number of links ignored due to exceptions: {}", self.links_ignored_exception);
         println!("number of intra doc links ignored: {}", self.intra_doc_exceptions);
         println!("errors found: {}", self.errors);
+
+        if self.has_broken_urls {
+            eprintln!(
+                "NOTE: if you are adding or renaming a markdown file in a mdBook, don't forget to \
+                register the page in SUMMARY.md"
+            );
+        }
     }
 }
 
diff --git a/src/tools/miri/src/borrow_tracker/stacked_borrows/diagnostics.rs b/src/tools/miri/src/borrow_tracker/stacked_borrows/diagnostics.rs
index 146f9902f6f..1684abeec6b 100644
--- a/src/tools/miri/src/borrow_tracker/stacked_borrows/diagnostics.rs
+++ b/src/tools/miri/src/borrow_tracker/stacked_borrows/diagnostics.rs
@@ -13,7 +13,7 @@ fn err_sb_ub<'tcx>(
     msg: String,
     help: Vec<String>,
     history: Option<TagHistory>,
-) -> InterpError<'tcx> {
+) -> InterpErrorKind<'tcx> {
     err_machine_stop!(TerminationInfo::StackedBorrowsUb { msg, help, history })
 }
 
@@ -376,7 +376,7 @@ impl<'history, 'ecx, 'tcx> DiagnosticCx<'history, 'ecx, 'tcx> {
 
     /// Report a descriptive error when `new` could not be granted from `derived_from`.
     #[inline(never)] // This is only called on fatal code paths
-    pub(super) fn grant_error(&self, stack: &Stack) -> InterpError<'tcx> {
+    pub(super) fn grant_error(&self, stack: &Stack) -> InterpErrorKind<'tcx> {
         let Operation::Retag(op) = &self.operation else {
             unreachable!("grant_error should only be called during a retag")
         };
@@ -402,7 +402,7 @@ impl<'history, 'ecx, 'tcx> DiagnosticCx<'history, 'ecx, 'tcx> {
 
     /// Report a descriptive error when `access` is not permitted based on `tag`.
     #[inline(never)] // This is only called on fatal code paths
-    pub(super) fn access_error(&self, stack: &Stack) -> InterpError<'tcx> {
+    pub(super) fn access_error(&self, stack: &Stack) -> InterpErrorKind<'tcx> {
         // Deallocation and retagging also do an access as part of their thing, so handle that here, too.
         let op = match &self.operation {
             Operation::Access(op) => op,
@@ -424,7 +424,7 @@ impl<'history, 'ecx, 'tcx> DiagnosticCx<'history, 'ecx, 'tcx> {
     }
 
     #[inline(never)] // This is only called on fatal code paths
-    pub(super) fn protector_error(&self, item: &Item, kind: ProtectorKind) -> InterpError<'tcx> {
+    pub(super) fn protector_error(&self, item: &Item, kind: ProtectorKind) -> InterpErrorKind<'tcx> {
         let protected = match kind {
             ProtectorKind::WeakProtector => "weakly protected",
             ProtectorKind::StrongProtector => "strongly protected",
@@ -445,7 +445,7 @@ impl<'history, 'ecx, 'tcx> DiagnosticCx<'history, 'ecx, 'tcx> {
     }
 
     #[inline(never)] // This is only called on fatal code paths
-    pub fn dealloc_error(&self, stack: &Stack) -> InterpError<'tcx> {
+    pub fn dealloc_error(&self, stack: &Stack) -> InterpErrorKind<'tcx> {
         let Operation::Dealloc(op) = &self.operation else {
             unreachable!("dealloc_error should only be called during a deallocation")
         };
diff --git a/src/tools/miri/src/borrow_tracker/tree_borrows/diagnostics.rs b/src/tools/miri/src/borrow_tracker/tree_borrows/diagnostics.rs
index cb840f19e3b..b2fd9b2bf05 100644
--- a/src/tools/miri/src/borrow_tracker/tree_borrows/diagnostics.rs
+++ b/src/tools/miri/src/borrow_tracker/tree_borrows/diagnostics.rs
@@ -298,7 +298,7 @@ pub(super) struct TbError<'node> {
 
 impl TbError<'_> {
     /// Produce a UB error.
-    pub fn build<'tcx>(self) -> InterpError<'tcx> {
+    pub fn build<'tcx>(self) -> InterpErrorKind<'tcx> {
         use TransitionError::*;
         let cause = self.access_cause;
         let accessed = self.accessed_info;
diff --git a/src/tools/miri/src/borrow_tracker/tree_borrows/tree.rs b/src/tools/miri/src/borrow_tracker/tree_borrows/tree.rs
index 15cefab1a68..a551b017dfc 100644
--- a/src/tools/miri/src/borrow_tracker/tree_borrows/tree.rs
+++ b/src/tools/miri/src/borrow_tracker/tree_borrows/tree.rs
@@ -674,7 +674,7 @@ impl<'tcx> Tree {
                             Ok(())
                         }
                     },
-                    |args: ErrHandlerArgs<'_, TransitionError>| -> InterpError<'tcx> {
+                    |args: ErrHandlerArgs<'_, TransitionError>| -> InterpErrorKind<'tcx> {
                         let ErrHandlerArgs { error_kind, conflicting_info, accessed_info } = args;
                         TbError {
                             conflicting_info,
@@ -772,7 +772,7 @@ impl<'tcx> Tree {
         let err_handler = |perms_range: Range<u64>,
                            access_cause: diagnostics::AccessCause,
                            args: ErrHandlerArgs<'_, TransitionError>|
-         -> InterpError<'tcx> {
+         -> InterpErrorKind<'tcx> {
             let ErrHandlerArgs { error_kind, conflicting_info, accessed_info } = args;
             TbError {
                 conflicting_info,
diff --git a/src/tools/miri/src/diagnostics.rs b/src/tools/miri/src/diagnostics.rs
index 475139a3b51..f055662891e 100644
--- a/src/tools/miri/src/diagnostics.rs
+++ b/src/tools/miri/src/diagnostics.rs
@@ -214,7 +214,7 @@ pub fn report_error<'tcx>(
     ecx: &InterpCx<'tcx, MiriMachine<'tcx>>,
     e: InterpErrorInfo<'tcx>,
 ) -> Option<(i64, bool)> {
-    use InterpError::*;
+    use InterpErrorKind::*;
     use UndefinedBehaviorInfo::*;
 
     let mut msg = vec![];
diff --git a/src/tools/miri/src/intrinsics/simd.rs b/src/tools/miri/src/intrinsics/simd.rs
index de293495e86..0799b93dbb0 100644
--- a/src/tools/miri/src/intrinsics/simd.rs
+++ b/src/tools/miri/src/intrinsics/simd.rs
@@ -1,8 +1,7 @@
 use either::Either;
 use rustc_apfloat::{Float, Round};
-use rustc_middle::ty::FloatTy;
-use rustc_middle::ty::layout::{HasParamEnv, LayoutOf};
-use rustc_middle::{mir, ty};
+use rustc_middle::ty::layout::LayoutOf;
+use rustc_middle::{mir, ty, ty::FloatTy};
 use rustc_span::{Symbol, sym};
 use rustc_target::abi::{Endian, HasDataLayout};
 
@@ -245,17 +244,17 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
                     let val = match which {
                         Op::MirOp(mir_op) => {
                             // This does NaN adjustments.
-                            let val = this.binary_op(mir_op, &left, &right).map_err(|err| {
-                                match err.kind() {
-                                    &InterpError::UndefinedBehavior(UndefinedBehaviorInfo::ShiftOverflow { shift_amount, .. }) => {
+                            let val = this.binary_op(mir_op, &left, &right).map_err_kind(|kind| {
+                                match kind {
+                                    InterpErrorKind::UndefinedBehavior(UndefinedBehaviorInfo::ShiftOverflow { shift_amount, .. }) => {
                                         // This resets the interpreter backtrace, but it's not worth avoiding that.
                                         let shift_amount = match shift_amount {
                                             Either::Left(v) => v.to_string(),
                                             Either::Right(v) => v.to_string(),
                                         };
-                                        err_ub_format!("overflowing shift by {shift_amount} in `simd_{intrinsic_name}` in lane {i}").into()
+                                        err_ub_format!("overflowing shift by {shift_amount} in `simd_{intrinsic_name}` in lane {i}")
                                     }
-                                    _ => err
+                                    kind => kind
                                 }
                             })?;
                             if matches!(mir_op, BinOp::Eq | BinOp::Ne | BinOp::Lt | BinOp::Le | BinOp::Gt | BinOp::Ge) {
@@ -633,9 +632,9 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
 
                 let index = generic_args[2]
                     .expect_const()
-                    .eval(*this.tcx, this.param_env(), this.tcx.span)
+                    .try_to_valtree()
                     .unwrap()
-                    .1
+                    .0
                     .unwrap_branch();
                 let index_len = index.len();
 
diff --git a/src/tools/miri/src/lib.rs b/src/tools/miri/src/lib.rs
index 9814858beaa..660f2e493bc 100644
--- a/src/tools/miri/src/lib.rs
+++ b/src/tools/miri/src/lib.rs
@@ -11,8 +11,6 @@
 #![feature(let_chains)]
 #![feature(trait_upcasting)]
 #![feature(strict_overflow_ops)]
-#![feature(strict_provenance)]
-#![feature(exposed_provenance)]
 #![feature(pointer_is_aligned_to)]
 #![feature(unqualified_local_imports)]
 // Configure clippy and other lints
diff --git a/src/tools/miri/src/shims/foreign_items.rs b/src/tools/miri/src/shims/foreign_items.rs
index 7fce5b63306..f6f91e58969 100644
--- a/src/tools/miri/src/shims/foreign_items.rs
+++ b/src/tools/miri/src/shims/foreign_items.rs
@@ -289,11 +289,10 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
             "miri_get_alloc_id" => {
                 let [ptr] = this.check_shim(abi, Abi::Rust, link_name, args)?;
                 let ptr = this.read_pointer(ptr)?;
-                let (alloc_id, _, _) = this.ptr_get_alloc_id(ptr, 0).map_err(|_e| {
+                let (alloc_id, _, _) = this.ptr_get_alloc_id(ptr, 0).map_err_kind(|_e| {
                     err_machine_stop!(TerminationInfo::Abort(format!(
                         "pointer passed to `miri_get_alloc_id` must not be dangling, got {ptr:?}"
                     )))
-                    .into()
                 })?;
                 this.write_scalar(Scalar::from_u64(alloc_id.0.get()), dest)?;
             }
diff --git a/src/tools/miri/tests/fail/dangling_pointers/dangling_pointer_to_raw_pointer.rs b/src/tools/miri/tests/fail/dangling_pointers/dangling_pointer_to_raw_pointer.rs
index 3e20b8da622..c63e926376d 100644
--- a/src/tools/miri/tests/fail/dangling_pointers/dangling_pointer_to_raw_pointer.rs
+++ b/src/tools/miri/tests/fail/dangling_pointers/dangling_pointer_to_raw_pointer.rs
@@ -1,4 +1,3 @@
-#![feature(strict_provenance)]
 use std::ptr;
 
 fn direct_raw(x: *const (i32, i32)) -> *const i32 {
diff --git a/src/tools/miri/tests/fail/dangling_pointers/deref_dangling_box.rs b/src/tools/miri/tests/fail/dangling_pointers/deref_dangling_box.rs
index fa40f942b8f..b22c1b4c5e8 100644
--- a/src/tools/miri/tests/fail/dangling_pointers/deref_dangling_box.rs
+++ b/src/tools/miri/tests/fail/dangling_pointers/deref_dangling_box.rs
@@ -1,6 +1,5 @@
 // Should be caught even without retagging
 //@compile-flags: -Zmiri-disable-stacked-borrows
-#![feature(strict_provenance)]
 use std::ptr::{self, addr_of_mut};
 
 // Deref'ing a dangling raw pointer is fine, but for a dangling box it is not.
diff --git a/src/tools/miri/tests/fail/dangling_pointers/deref_dangling_ref.rs b/src/tools/miri/tests/fail/dangling_pointers/deref_dangling_ref.rs
index 036ef2580a8..eeab7c333e5 100644
--- a/src/tools/miri/tests/fail/dangling_pointers/deref_dangling_ref.rs
+++ b/src/tools/miri/tests/fail/dangling_pointers/deref_dangling_ref.rs
@@ -1,6 +1,5 @@
 // Should be caught even without retagging
 //@compile-flags: -Zmiri-disable-stacked-borrows
-#![feature(strict_provenance)]
 use std::ptr::{self, addr_of_mut};
 
 // Deref'ing a dangling raw pointer is fine, but for a dangling reference it is not.
diff --git a/src/tools/miri/tests/fail/intrinsics/ptr_offset_from_different_ints.rs b/src/tools/miri/tests/fail/intrinsics/ptr_offset_from_different_ints.rs
index c307dfddb6c..0acda559d3a 100644
--- a/src/tools/miri/tests/fail/intrinsics/ptr_offset_from_different_ints.rs
+++ b/src/tools/miri/tests/fail/intrinsics/ptr_offset_from_different_ints.rs
@@ -1,4 +1,3 @@
-#![feature(strict_provenance)]
 use core::ptr;
 
 fn main() {
diff --git a/src/tools/miri/tests/fail/provenance/int_copy_looses_provenance3.rs b/src/tools/miri/tests/fail/provenance/int_copy_looses_provenance3.rs
index d6bbfd7d94c..da381596284 100644
--- a/src/tools/miri/tests/fail/provenance/int_copy_looses_provenance3.rs
+++ b/src/tools/miri/tests/fail/provenance/int_copy_looses_provenance3.rs
@@ -1,4 +1,3 @@
-#![feature(strict_provenance)]
 use std::mem;
 
 #[repr(C, usize)]
diff --git a/src/tools/miri/tests/fail/provenance/provenance_transmute.rs b/src/tools/miri/tests/fail/provenance/provenance_transmute.rs
index bc5dd53dcf5..d72f10530d7 100644
--- a/src/tools/miri/tests/fail/provenance/provenance_transmute.rs
+++ b/src/tools/miri/tests/fail/provenance/provenance_transmute.rs
@@ -1,5 +1,4 @@
 //@compile-flags: -Zmiri-permissive-provenance
-#![feature(strict_provenance)]
 
 use std::mem;
 
diff --git a/src/tools/miri/tests/fail/provenance/ptr_int_unexposed.rs b/src/tools/miri/tests/fail/provenance/ptr_int_unexposed.rs
index f89378fcb3c..a43ba13e13d 100644
--- a/src/tools/miri/tests/fail/provenance/ptr_int_unexposed.rs
+++ b/src/tools/miri/tests/fail/provenance/ptr_int_unexposed.rs
@@ -1,5 +1,4 @@
 //@compile-flags: -Zmiri-permissive-provenance
-#![feature(strict_provenance, exposed_provenance)]
 
 fn main() {
     let x: i32 = 3;
diff --git a/src/tools/miri/tests/fail/provenance/ptr_invalid.rs b/src/tools/miri/tests/fail/provenance/ptr_invalid.rs
index 512473cd894..d4479f32e56 100644
--- a/src/tools/miri/tests/fail/provenance/ptr_invalid.rs
+++ b/src/tools/miri/tests/fail/provenance/ptr_invalid.rs
@@ -1,4 +1,3 @@
-#![feature(strict_provenance, exposed_provenance)]
 
 // Ensure that a `ptr::without_provenance` ptr is truly invalid.
 fn main() {
diff --git a/src/tools/miri/tests/fail/provenance/ptr_invalid_offset.rs b/src/tools/miri/tests/fail/provenance/ptr_invalid_offset.rs
index c8be521ef82..53a16ce5d1b 100644
--- a/src/tools/miri/tests/fail/provenance/ptr_invalid_offset.rs
+++ b/src/tools/miri/tests/fail/provenance/ptr_invalid_offset.rs
@@ -1,5 +1,4 @@
 //@compile-flags: -Zmiri-strict-provenance
-#![feature(strict_provenance)]
 
 fn main() {
     let x = 22;
diff --git a/src/tools/miri/tests/fail/provenance/strict_provenance_cast.rs b/src/tools/miri/tests/fail/provenance/strict_provenance_cast.rs
index d7b54f640f6..4674ba841b4 100644
--- a/src/tools/miri/tests/fail/provenance/strict_provenance_cast.rs
+++ b/src/tools/miri/tests/fail/provenance/strict_provenance_cast.rs
@@ -1,5 +1,4 @@
 //@compile-flags: -Zmiri-strict-provenance
-#![feature(exposed_provenance)]
 
 fn main() {
     let addr = &0 as *const i32 as usize;
diff --git a/src/tools/miri/tests/fail/stacked_borrows/exposed_only_ro.rs b/src/tools/miri/tests/fail/stacked_borrows/exposed_only_ro.rs
index 608ab718919..dfd7d4bb0be 100644
--- a/src/tools/miri/tests/fail/stacked_borrows/exposed_only_ro.rs
+++ b/src/tools/miri/tests/fail/stacked_borrows/exposed_only_ro.rs
@@ -1,5 +1,4 @@
 //@compile-flags: -Zmiri-permissive-provenance
-#![feature(exposed_provenance)]
 
 // If we have only exposed read-only pointers, doing a write through a wildcard ptr should fail.
 
diff --git a/src/tools/miri/tests/fail/unaligned_pointers/promise_alignment.rs b/src/tools/miri/tests/fail/unaligned_pointers/promise_alignment.rs
index e075db66039..009ce1eb882 100644
--- a/src/tools/miri/tests/fail/unaligned_pointers/promise_alignment.rs
+++ b/src/tools/miri/tests/fail/unaligned_pointers/promise_alignment.rs
@@ -1,6 +1,5 @@
 //@compile-flags: -Zmiri-symbolic-alignment-check
 //@revisions: call_unaligned_ptr read_unaligned_ptr
-#![feature(strict_provenance)]
 
 #[path = "../../utils/mod.rs"]
 mod utils;
diff --git a/src/tools/miri/tests/fail/uninit/uninit_alloc_diagnostic_with_provenance.rs b/src/tools/miri/tests/fail/uninit/uninit_alloc_diagnostic_with_provenance.rs
index 954571f4a22..1cf70d9d9a3 100644
--- a/src/tools/miri/tests/fail/uninit/uninit_alloc_diagnostic_with_provenance.rs
+++ b/src/tools/miri/tests/fail/uninit/uninit_alloc_diagnostic_with_provenance.rs
@@ -1,7 +1,6 @@
 //@compile-flags: -Zmiri-disable-validation
 //@error-in-other-file: memory is uninitialized at [0x4..0x8]
 //@normalize-stderr-test: "a[0-9]+" -> "ALLOC"
-#![feature(strict_provenance)]
 #![allow(dropping_copy_types)]
 
 // Test printing allocations that contain single-byte provenance.
diff --git a/src/tools/miri/tests/pass-dep/libc/libc-affinity.rs b/src/tools/miri/tests/pass-dep/libc/libc-affinity.rs
index 8aa8c7dcb8e..3c4311efc4c 100644
--- a/src/tools/miri/tests/pass-dep/libc/libc-affinity.rs
+++ b/src/tools/miri/tests/pass-dep/libc/libc-affinity.rs
@@ -3,7 +3,6 @@
 //@compile-flags: -Zmiri-disable-isolation -Zmiri-num-cpus=4
 #![feature(io_error_more)]
 #![feature(pointer_is_aligned_to)]
-#![feature(strict_provenance)]
 
 use std::mem::{size_of, size_of_val};
 
diff --git a/src/tools/miri/tests/pass-dep/libc/libc-epoll-no-blocking.rs b/src/tools/miri/tests/pass-dep/libc/libc-epoll-no-blocking.rs
index 3ea34513376..288c1d41f30 100644
--- a/src/tools/miri/tests/pass-dep/libc/libc-epoll-no-blocking.rs
+++ b/src/tools/miri/tests/pass-dep/libc/libc-epoll-no-blocking.rs
@@ -1,6 +1,5 @@
 //@only-target: linux
 
-#![feature(strict_provenance)]
 use std::convert::TryInto;
 
 fn main() {
diff --git a/src/tools/miri/tests/pass-dep/libc/libc-mem.rs b/src/tools/miri/tests/pass-dep/libc/libc-mem.rs
index c399616b9ae..10be78798a6 100644
--- a/src/tools/miri/tests/pass-dep/libc/libc-mem.rs
+++ b/src/tools/miri/tests/pass-dep/libc/libc-mem.rs
@@ -1,4 +1,4 @@
-#![feature(strict_provenance, pointer_is_aligned_to)]
+#![feature(pointer_is_aligned_to)]
 use std::{mem, ptr, slice};
 
 fn test_memcpy() {
diff --git a/src/tools/miri/tests/pass-dep/libc/libc-misc.rs b/src/tools/miri/tests/pass-dep/libc/libc-misc.rs
index f3261eaa43c..f07007fa705 100644
--- a/src/tools/miri/tests/pass-dep/libc/libc-misc.rs
+++ b/src/tools/miri/tests/pass-dep/libc/libc-misc.rs
@@ -2,7 +2,6 @@
 //@compile-flags: -Zmiri-disable-isolation
 #![feature(io_error_more)]
 #![feature(pointer_is_aligned_to)]
-#![feature(strict_provenance)]
 
 use std::mem::transmute;
 
diff --git a/src/tools/miri/tests/pass-dep/libc/mmap.rs b/src/tools/miri/tests/pass-dep/libc/mmap.rs
index db305acbf4a..bfd840d2fb8 100644
--- a/src/tools/miri/tests/pass-dep/libc/mmap.rs
+++ b/src/tools/miri/tests/pass-dep/libc/mmap.rs
@@ -1,6 +1,5 @@
 //@ignore-target: windows # No mmap on Windows
 //@compile-flags: -Zmiri-disable-isolation -Zmiri-permissive-provenance
-#![feature(strict_provenance)]
 
 use std::io::Error;
 use std::{ptr, slice};
diff --git a/src/tools/miri/tests/pass/align_offset_symbolic.rs b/src/tools/miri/tests/pass/align_offset_symbolic.rs
index 9647277821f..c836a3b07cb 100644
--- a/src/tools/miri/tests/pass/align_offset_symbolic.rs
+++ b/src/tools/miri/tests/pass/align_offset_symbolic.rs
@@ -1,5 +1,4 @@
 //@compile-flags: -Zmiri-symbolic-alignment-check
-#![feature(strict_provenance)]
 
 use std::mem;
 
diff --git a/src/tools/miri/tests/pass/atomic.rs b/src/tools/miri/tests/pass/atomic.rs
index f84fe825d01..2b2e89e6d70 100644
--- a/src/tools/miri/tests/pass/atomic.rs
+++ b/src/tools/miri/tests/pass/atomic.rs
@@ -2,7 +2,7 @@
 //@[tree]compile-flags: -Zmiri-tree-borrows
 //@compile-flags: -Zmiri-strict-provenance
 
-#![feature(strict_provenance, strict_provenance_atomic_ptr)]
+#![feature(strict_provenance_atomic_ptr)]
 // FIXME(static_mut_refs): Do not allow `static_mut_refs` lint
 #![allow(static_mut_refs)]
 
diff --git a/src/tools/miri/tests/pass/box-custom-alloc-aliasing.rs b/src/tools/miri/tests/pass/box-custom-alloc-aliasing.rs
index 0a61db960c1..63329118283 100644
--- a/src/tools/miri/tests/pass/box-custom-alloc-aliasing.rs
+++ b/src/tools/miri/tests/pass/box-custom-alloc-aliasing.rs
@@ -5,7 +5,6 @@
 //@revisions: stack tree
 //@[tree]compile-flags: -Zmiri-tree-borrows
 #![feature(allocator_api)]
-#![feature(strict_provenance)]
 
 use std::alloc::{AllocError, Allocator, Layout};
 use std::cell::{Cell, UnsafeCell};
diff --git a/src/tools/miri/tests/pass/concurrency/address_reuse_happens_before.rs b/src/tools/miri/tests/pass/concurrency/address_reuse_happens_before.rs
index 255f4061abc..a293dd0bef1 100644
--- a/src/tools/miri/tests/pass/concurrency/address_reuse_happens_before.rs
+++ b/src/tools/miri/tests/pass/concurrency/address_reuse_happens_before.rs
@@ -1,7 +1,6 @@
 //! Regression test for <https://github.com/rust-lang/miri/issues/3450>:
 //! When the address gets reused, there should be a happens-before relation.
 //@compile-flags: -Zmiri-address-reuse-cross-thread-rate=1.0
-#![feature(strict_provenance)]
 #![feature(sync_unsafe_cell)]
 
 use std::cell::SyncUnsafeCell;
diff --git a/src/tools/miri/tests/pass/const-addrs.rs b/src/tools/miri/tests/pass/const-addrs.rs
index 727c67ebfb5..af68b28b2b8 100644
--- a/src/tools/miri/tests/pass/const-addrs.rs
+++ b/src/tools/miri/tests/pass/const-addrs.rs
@@ -7,7 +7,6 @@
 // MIR inlining will put every evaluation of the const we're repeatedly evaluating into the same
 // stack frame, breaking this test.
 //@compile-flags: -Zinline-mir=no
-#![feature(strict_provenance)]
 
 const EVALS: usize = 256;
 
diff --git a/src/tools/miri/tests/pass/drop_type_without_drop_glue.rs b/src/tools/miri/tests/pass/drop_type_without_drop_glue.rs
index 43ddc8a4d8b..2f8665e8d62 100644
--- a/src/tools/miri/tests/pass/drop_type_without_drop_glue.rs
+++ b/src/tools/miri/tests/pass/drop_type_without_drop_glue.rs
@@ -1,4 +1,4 @@
-#![feature(custom_mir, core_intrinsics, strict_provenance)]
+#![feature(custom_mir, core_intrinsics)]
 use std::intrinsics::mir::*;
 
 // The `Drop` terminator on a type with no drop glue should be a NOP.
diff --git a/src/tools/miri/tests/pass/dyn-upcast.rs b/src/tools/miri/tests/pass/dyn-upcast.rs
index ff995f38196..306e9ab9c67 100644
--- a/src/tools/miri/tests/pass/dyn-upcast.rs
+++ b/src/tools/miri/tests/pass/dyn-upcast.rs
@@ -9,6 +9,7 @@ fn main() {
     struct_();
     replace_vptr();
     vtable_nop_cast();
+    drop_principal();
 }
 
 fn vtable_nop_cast() {
@@ -430,3 +431,53 @@ fn replace_vptr() {
     let s = S(42);
     invoke_outer(&s);
 }
+
+fn drop_principal() {
+    use std::{alloc::Layout, any::Any};
+
+    const fn yeet_principal(x: Box<dyn Any + Send>) -> Box<dyn Send> {
+        x
+    }
+
+    trait Bar: Send + Sync {}
+
+    impl<T: Send + Sync> Bar for T {}
+
+    const fn yeet_principal_2(x: Box<dyn Bar>) -> Box<dyn Send> {
+        x
+    }
+
+    struct CallMe<F: FnOnce()>(Option<F>);
+
+    impl<F: FnOnce()> CallMe<F> {
+        fn new(f: F) -> Self {
+            CallMe(Some(f))
+        }
+    }
+
+    impl<F: FnOnce()> Drop for CallMe<F> {
+        fn drop(&mut self) {
+            (self.0.take().unwrap())();
+        }
+    }
+
+    fn goodbye() {
+        println!("goodbye");
+    }
+
+    let x = Box::new(CallMe::new(goodbye)) as Box<dyn Any + Send>;
+    let x_layout = Layout::for_value(&*x);
+    let y = yeet_principal(x);
+    let y_layout = Layout::for_value(&*y);
+    assert_eq!(x_layout, y_layout);
+    println!("before");
+    drop(y);
+
+    let x = Box::new(CallMe::new(goodbye)) as Box<dyn Bar>;
+    let x_layout = Layout::for_value(&*x);
+    let y = yeet_principal_2(x);
+    let y_layout = Layout::for_value(&*y);
+    assert_eq!(x_layout, y_layout);
+    println!("before");
+    drop(y);
+}
diff --git a/src/tools/miri/tests/pass/dyn-upcast.stdout b/src/tools/miri/tests/pass/dyn-upcast.stdout
new file mode 100644
index 00000000000..edd99a114a1
--- /dev/null
+++ b/src/tools/miri/tests/pass/dyn-upcast.stdout
@@ -0,0 +1,4 @@
+before
+goodbye
+before
+goodbye
diff --git a/src/tools/miri/tests/pass/extern_types.rs b/src/tools/miri/tests/pass/extern_types.rs
index eade5c6ac08..08e9caf4f1c 100644
--- a/src/tools/miri/tests/pass/extern_types.rs
+++ b/src/tools/miri/tests/pass/extern_types.rs
@@ -1,6 +1,6 @@
 //@revisions: stack tree
 //@[tree]compile-flags: -Zmiri-tree-borrows
-#![feature(extern_types, strict_provenance)]
+#![feature(extern_types)]
 
 use std::ptr;
 
diff --git a/src/tools/miri/tests/pass/provenance.rs b/src/tools/miri/tests/pass/provenance.rs
index 2e4d240cc48..8c52b703190 100644
--- a/src/tools/miri/tests/pass/provenance.rs
+++ b/src/tools/miri/tests/pass/provenance.rs
@@ -1,6 +1,5 @@
 //@revisions: stack tree
 //@[tree]compile-flags: -Zmiri-tree-borrows
-#![feature(strict_provenance)]
 use std::{mem, ptr};
 
 const PTR_SIZE: usize = mem::size_of::<&i32>();
diff --git a/src/tools/miri/tests/pass/ptr_int_from_exposed.rs b/src/tools/miri/tests/pass/ptr_int_from_exposed.rs
index ade5f537785..9a76c8dd349 100644
--- a/src/tools/miri/tests/pass/ptr_int_from_exposed.rs
+++ b/src/tools/miri/tests/pass/ptr_int_from_exposed.rs
@@ -2,7 +2,6 @@
 // Tree Borrows doesn't support int2ptr casts, but let's make sure we don't immediately crash either.
 //@[tree]compile-flags: -Zmiri-tree-borrows
 //@[stack]compile-flags: -Zmiri-permissive-provenance
-#![feature(strict_provenance, exposed_provenance)]
 
 use std::ptr;
 
diff --git a/src/tools/miri/tests/pass/ptr_raw.rs b/src/tools/miri/tests/pass/ptr_raw.rs
index dcf13d97ce3..c958dfddf69 100644
--- a/src/tools/miri/tests/pass/ptr_raw.rs
+++ b/src/tools/miri/tests/pass/ptr_raw.rs
@@ -1,4 +1,3 @@
-#![feature(strict_provenance)]
 use std::mem;
 use std::ptr::{self, addr_of};
 
diff --git a/src/tools/miri/tests/pass/shims/ptr_mask.rs b/src/tools/miri/tests/pass/shims/ptr_mask.rs
index fb8bb6b13db..6872ba577d2 100644
--- a/src/tools/miri/tests/pass/shims/ptr_mask.rs
+++ b/src/tools/miri/tests/pass/shims/ptr_mask.rs
@@ -1,5 +1,4 @@
 #![feature(ptr_mask)]
-#![feature(strict_provenance)]
 
 fn main() {
     let v: u32 = 0xABCDABCD;
diff --git a/src/tools/miri/tests/pass/slices.rs b/src/tools/miri/tests/pass/slices.rs
index 39e1d1cb97f..dd18a061cdd 100644
--- a/src/tools/miri/tests/pass/slices.rs
+++ b/src/tools/miri/tests/pass/slices.rs
@@ -4,7 +4,6 @@
 #![feature(slice_as_chunks)]
 #![feature(slice_partition_dedup)]
 #![feature(layout_for_ptr)]
-#![feature(strict_provenance)]
 
 use std::{ptr, slice};
 
diff --git a/src/tools/miri/tests/pass/stacked-borrows/int-to-ptr.rs b/src/tools/miri/tests/pass/stacked-borrows/int-to-ptr.rs
index c89d79b42e3..8a05fca3f31 100644
--- a/src/tools/miri/tests/pass/stacked-borrows/int-to-ptr.rs
+++ b/src/tools/miri/tests/pass/stacked-borrows/int-to-ptr.rs
@@ -1,5 +1,4 @@
 //@compile-flags: -Zmiri-permissive-provenance
-#![feature(exposed_provenance)]
 use std::ptr;
 
 // Just to make sure that casting a ref to raw, to int and back to raw
diff --git a/src/tools/miri/tests/pass/stacked-borrows/stack-printing.rs b/src/tools/miri/tests/pass/stacked-borrows/stack-printing.rs
index 1478b21d6c1..e507f49b955 100644
--- a/src/tools/miri/tests/pass/stacked-borrows/stack-printing.rs
+++ b/src/tools/miri/tests/pass/stacked-borrows/stack-printing.rs
@@ -2,7 +2,6 @@
 // printing, not how it interacts with the GC.
 //@compile-flags: -Zmiri-permissive-provenance -Zmiri-provenance-gc=0
 
-#![feature(strict_provenance)]
 use std::alloc::{self, Layout};
 use std::mem::ManuallyDrop;
 
diff --git a/src/tools/miri/tests/pass/stacked-borrows/unknown-bottom-gc.rs b/src/tools/miri/tests/pass/stacked-borrows/unknown-bottom-gc.rs
index 55356814a1a..b0f53283cda 100644
--- a/src/tools/miri/tests/pass/stacked-borrows/unknown-bottom-gc.rs
+++ b/src/tools/miri/tests/pass/stacked-borrows/unknown-bottom-gc.rs
@@ -1,5 +1,4 @@
 //@compile-flags: -Zmiri-permissive-provenance
-#![feature(exposed_provenance)]
 
 use std::ptr;
 
diff --git a/src/tools/miri/tests/pass/transmute_ptr.rs b/src/tools/miri/tests/pass/transmute_ptr.rs
index ce6d86b7068..0944781c6de 100644
--- a/src/tools/miri/tests/pass/transmute_ptr.rs
+++ b/src/tools/miri/tests/pass/transmute_ptr.rs
@@ -1,6 +1,5 @@
 //@revisions: stack tree
 //@[tree]compile-flags: -Zmiri-tree-borrows
-#![feature(strict_provenance)]
 use std::{mem, ptr};
 
 fn t1() {
diff --git a/src/tools/miri/tests/pass/underscore_pattern.rs b/src/tools/miri/tests/pass/underscore_pattern.rs
index f59bb9f5c82..2f203bdc01e 100644
--- a/src/tools/miri/tests/pass/underscore_pattern.rs
+++ b/src/tools/miri/tests/pass/underscore_pattern.rs
@@ -1,5 +1,4 @@
 // Various tests ensuring that underscore patterns really just construct the place, but don't check its contents.
-#![feature(strict_provenance)]
 #![feature(never_type)]
 
 use std::ptr;
diff --git a/src/tools/miri/tests/pass/zero-sized-accesses-and-offsets.rs b/src/tools/miri/tests/pass/zero-sized-accesses-and-offsets.rs
index a3356b682a6..6c0d9bc253a 100644
--- a/src/tools/miri/tests/pass/zero-sized-accesses-and-offsets.rs
+++ b/src/tools/miri/tests/pass/zero-sized-accesses-and-offsets.rs
@@ -1,5 +1,4 @@
 //! Tests specific for <https://github.com/rust-lang/rust/issues/117945>: zero-sized operations.
-#![feature(strict_provenance)]
 
 use std::ptr;
 
diff --git a/src/tools/miropt-test-tools/src/lib.rs b/src/tools/miropt-test-tools/src/lib.rs
index 4317f23a822..41b53d2ad0e 100644
--- a/src/tools/miropt-test-tools/src/lib.rs
+++ b/src/tools/miropt-test-tools/src/lib.rs
@@ -129,6 +129,9 @@ pub fn files_for_miropt_test(
 
             out.push(MiroptTestFile { expected_file, from_file, to_file });
         }
+        if !run_filecheck && l.trim_start().starts_with("// CHECK") {
+            panic!("error: test contains filecheck directive but is marked `skip-filecheck`");
+        }
     }
 
     MiroptTest { run_filecheck, suffix, files: out, passes }
diff --git a/src/tools/nix-dev-shell/envrc-flake b/src/tools/nix-dev-shell/envrc-flake
new file mode 100644
index 00000000000..218d88d8721
--- /dev/null
+++ b/src/tools/nix-dev-shell/envrc-flake
@@ -0,0 +1,8 @@
+# If you want to use this as an .envrc file to create a shell with necessery components 
+# to develop rustc, use the following command in the root of the rusr checkout:
+#	
+# ln -s ./src/tools/nix-dev-shell/envrc-flake ./.envrc && echo .envrc >> .git/info/exclude
+
+if nix flake show path:./src/tools/nix-dev-shell &> /dev/null; then
+  use flake path:./src/tools/nix-dev-shell
+fi
diff --git a/src/tools/nix-dev-shell/envrc-shell b/src/tools/nix-dev-shell/envrc-shell
new file mode 100644
index 00000000000..fb7231a6c30
--- /dev/null
+++ b/src/tools/nix-dev-shell/envrc-shell
@@ -0,0 +1,7 @@
+# If you want to use this as an .envrc file to create a shell with necessery components 
+# to develop rustc, use the following command in the root of the rusr checkout:
+#	
+# ln -s ./src/tools/nix-dev-shell/envrc-shell ./.envrc && echo .envrc >> .git/info/exclude
+
+use nix ./src/tools/nix-dev-shell/shell.nix
+  
diff --git a/src/tools/nix-dev-shell/flake.nix b/src/tools/nix-dev-shell/flake.nix
new file mode 100644
index 00000000000..8ab5e097427
--- /dev/null
+++ b/src/tools/nix-dev-shell/flake.nix
@@ -0,0 +1,33 @@
+{
+  description = "rustc dev shell";
+
+  inputs = {
+    nixpkgs.url      = "github:NixOS/nixpkgs/nixos-unstable";
+    flake-utils.url  = "github:numtide/flake-utils";
+  };
+
+  outputs = { self, nixpkgs, flake-utils, ... }:
+	flake-utils.lib.eachDefaultSystem (system:
+      let
+        pkgs = import nixpkgs { inherit system; };
+        x = import ./x { inherit pkgs; };
+      in
+      {
+        devShells.default = with pkgs; mkShell {
+          name = "rustc-dev-shell";
+          nativeBuildInputs = with pkgs; [
+            binutils cmake ninja pkg-config python3 git curl cacert patchelf nix
+          ];
+          buildInputs = with pkgs; [
+            openssl glibc.out glibc.static x
+          ];
+          # Avoid creating text files for ICEs.
+          RUSTC_ICE = "0";
+          # Provide `libstdc++.so.6` for the self-contained lld.
+          LD_LIBRARY_PATH = "${with pkgs; lib.makeLibraryPath [
+            stdenv.cc.cc.lib
+          ]}";
+        };
+      }
+    );
+}
diff --git a/src/tools/nix-dev-shell/shell.nix b/src/tools/nix-dev-shell/shell.nix
new file mode 100644
index 00000000000..8a5cbb7c89e
--- /dev/null
+++ b/src/tools/nix-dev-shell/shell.nix
@@ -0,0 +1,19 @@
+{ pkgs ? import <nixpkgs> {} }:
+let 
+  x = import ./x { inherit pkgs; };
+in
+pkgs.mkShell {
+  name = "rustc";
+  nativeBuildInputs = with pkgs; [
+    binutils cmake ninja pkg-config python3 git curl cacert patchelf nix
+  ];
+  buildInputs = with pkgs; [
+    openssl glibc.out glibc.static x
+  ];
+  # Avoid creating text files for ICEs.
+  RUSTC_ICE = "0";
+  # Provide `libstdc++.so.6` for the self-contained lld.
+  LD_LIBRARY_PATH = "${with pkgs; lib.makeLibraryPath [
+    stdenv.cc.cc.lib
+  ]}";
+}
diff --git a/src/tools/nix-dev-shell/x/default.nix b/src/tools/nix-dev-shell/x/default.nix
new file mode 100644
index 00000000000..e6dfbad6f19
--- /dev/null
+++ b/src/tools/nix-dev-shell/x/default.nix
@@ -0,0 +1,22 @@
+{
+  pkgs ? import <nixpkgs> { },
+}:
+pkgs.stdenv.mkDerivation {
+  name = "x";
+
+  src = ./x.rs;
+  dontUnpack = true;
+
+  nativeBuildInputs = with pkgs; [ rustc ];
+
+  buildPhase = ''
+    PYTHON=${pkgs.lib.getExe pkgs.python3} rustc -Copt-level=3 --crate-name x $src --out-dir $out/bin
+  '';
+
+  meta = with pkgs.lib; {
+    description = "Helper for rust-lang/rust x.py";
+    homepage = "https://github.com/rust-lang/rust/blob/master/src/tools/x";
+    license = licenses.mit;
+    mainProgram = "x";
+  };
+}
diff --git a/src/tools/nix-dev-shell/x/x.rs b/src/tools/nix-dev-shell/x/x.rs
new file mode 100644
index 00000000000..9f83b8fd62e
--- /dev/null
+++ b/src/tools/nix-dev-shell/x/x.rs
@@ -0,0 +1,50 @@
+// git clone https://github.com/rust-lang/rust/blob/0ea7ddcc35a2fcaa5da8a7dcfc118c9fb4a63b95/src/tools/x/src/main.rs
+// patched to stop doing python probing, stop the probe, please dont, i have a python
+//! Run bootstrap from any subdirectory of a rust compiler checkout.
+//!
+//! We prefer `exec`, to avoid adding an extra process in the process tree.
+//! However, since `exec` isn't available on Windows, we indirect through
+//! `exec_or_status`, which will call `exec` on unix and `status` on Windows.
+//!
+//! We use `powershell.exe x.ps1` on Windows, and `sh -c x` on Unix, those are
+//! the ones that call `x.py`. We use `sh -c` on Unix, because it is a standard.
+//! We also don't use `pwsh` on Windows, because it is not installed by default;
+
+use std::env;
+use std::os::unix::process::CommandExt;
+use std::process::{self, Command};
+
+fn main() {
+    match env::args().skip(1).next().as_deref() {
+        Some("--wrapper-version") => {
+            println!("0.1.0");
+            return;
+        }
+        _ => {}
+    }
+    let current = match env::current_dir() {
+        Ok(dir) => dir,
+        Err(err) => {
+            eprintln!("Failed to get current directory: {err}");
+            process::exit(1);
+        }
+    };
+
+    for dir in current.ancestors() {
+        let candidate = dir.join("x.py");
+        if candidate.exists() {
+            let mut cmd = Command::new(env!("PYTHON"));
+            cmd.arg(dir.join("x.py"));
+            cmd.args(env::args().skip(1)).current_dir(dir);
+
+            let error = cmd.exec();
+            eprintln!("Failed to invoke `{:?}`: {}", cmd, error);
+        }
+    }
+
+    eprintln!(
+        "x.py not found. Please run inside of a checkout of `https://github.com/rust-lang/rust`."
+    );
+
+    process::exit(1);
+}
diff --git a/src/tools/rust-analyzer/Cargo.lock b/src/tools/rust-analyzer/Cargo.lock
index 951c7f8bbac..fd569571b38 100644
--- a/src/tools/rust-analyzer/Cargo.lock
+++ b/src/tools/rust-analyzer/Cargo.lock
@@ -1497,9 +1497,9 @@ dependencies = [
 
 [[package]]
 name = "ra-ap-rustc_abi"
-version = "0.71.0"
+version = "0.73.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c6999d098000b98415939f13158dac78cb3eeeb7b0c073847f3e4b623866e27c"
+checksum = "879ece0781e3c1cb670b9f29775c81a43a16db789d1296fad6bc5c74065b2fac"
 dependencies = [
  "bitflags 2.6.0",
  "ra-ap-rustc_index",
@@ -1508,9 +1508,9 @@ dependencies = [
 
 [[package]]
 name = "ra-ap-rustc_index"
-version = "0.71.0"
+version = "0.73.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ae9fb312d942817dab10790881f555928c1f6a11a85186e8e573ad4a86c7d3be"
+checksum = "6910087ff89bb9f3db114bfcd86b5139042731fe7278d3ff4ceaa69a140154a7"
 dependencies = [
  "arrayvec",
  "ra-ap-rustc_index_macros",
@@ -1519,9 +1519,9 @@ dependencies = [
 
 [[package]]
 name = "ra-ap-rustc_index_macros"
-version = "0.71.0"
+version = "0.73.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "766e3990eb1066a06deefc561b5a01b32ca5c9211feea31cbf4ed50611519872"
+checksum = "3b6f7bd12b678fbb37444ba77f3b0cfc13b7394a6dc7b0c799491fc9df0a9997"
 dependencies = [
  "proc-macro2",
  "quote",
@@ -1530,9 +1530,9 @@ dependencies = [
 
 [[package]]
 name = "ra-ap-rustc_lexer"
-version = "0.71.0"
+version = "0.73.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f4afa98eb7889c137d5a3f1cd189089e16da04d1e4837d358a67aa3dab10ffbe"
+checksum = "119bc05b5b6bc3e7f5b67ce8b8080e185da94bd83c447f91b6b3f3ecf60cbab1"
 dependencies = [
  "unicode-properties",
  "unicode-xid",
@@ -1540,9 +1540,9 @@ dependencies = [
 
 [[package]]
 name = "ra-ap-rustc_parse_format"
-version = "0.71.0"
+version = "0.73.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d9234c96ffb0565286790407fb7eb7f55ebf69267de4db382fdec0a17f14b0e2"
+checksum = "70ed6150ae71d905c064dc88d7824ebb0fa81083f45d7477cba7b57176f2f635"
 dependencies = [
  "ra-ap-rustc_index",
  "ra-ap-rustc_lexer",
@@ -1550,12 +1550,12 @@ dependencies = [
 
 [[package]]
 name = "ra-ap-rustc_pattern_analysis"
-version = "0.71.0"
+version = "0.73.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "273d5f72926a58c7eea27aebc898d1d5b32d23d2342f692a94a2cf8746aa4a2f"
+checksum = "6e830862a0ec85fce211d34735315686bb8d6a12d418d6d735fb534aa1cd3293"
 dependencies = [
  "ra-ap-rustc_index",
- "rustc-hash 1.1.0",
+ "rustc-hash 2.0.0",
  "rustc_apfloat",
  "smallvec",
  "tracing",
diff --git a/src/tools/rust-analyzer/Cargo.toml b/src/tools/rust-analyzer/Cargo.toml
index 397c68319de..9db62de9abf 100644
--- a/src/tools/rust-analyzer/Cargo.toml
+++ b/src/tools/rust-analyzer/Cargo.toml
@@ -85,11 +85,11 @@ tt = { path = "./crates/tt", version = "0.0.0" }
 vfs-notify = { path = "./crates/vfs-notify", version = "0.0.0" }
 vfs = { path = "./crates/vfs", version = "0.0.0" }
 
-ra-ap-rustc_lexer = { version = "0.71.0", default-features = false }
-ra-ap-rustc_parse_format = { version = "0.71.0", default-features = false }
-ra-ap-rustc_index = { version = "0.71.0", default-features = false }
-ra-ap-rustc_abi = { version = "0.71.0", default-features = false }
-ra-ap-rustc_pattern_analysis = { version = "0.71.0", default-features = false }
+ra-ap-rustc_lexer = { version = "0.73", default-features = false }
+ra-ap-rustc_parse_format = { version = "0.73", default-features = false }
+ra-ap-rustc_index = { version = "0.73", default-features = false }
+ra-ap-rustc_abi = { version = "0.73", default-features = false }
+ra-ap-rustc_pattern_analysis = { version = "0.73", default-features = false }
 
 # local crates that aren't published to crates.io. These should not have versions.
 test-fixture = { path = "./crates/test-fixture" }
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/layout.rs b/src/tools/rust-analyzer/crates/hir-ty/src/layout.rs
index bfbae2941da..80831440720 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/layout.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/layout.rs
@@ -72,16 +72,15 @@ pub type Variants = hir_def::layout::Variants<RustcFieldIdx, RustcEnumVariantIdx
 
 #[derive(Debug, PartialEq, Eq, Clone)]
 pub enum LayoutError {
-    EmptyUnion,
+    // FIXME: Remove more variants once they get added to LayoutCalculatorError
+    BadCalc(LayoutCalculatorError<()>),
     HasErrorConst,
     HasErrorType,
     HasPlaceholder,
     InvalidSimdType,
     NotImplemented,
     RecursiveTypeWithoutIndirection,
-    SizeOverflow,
     TargetLayoutNotAvailable,
-    UnexpectedUnsized,
     Unknown,
     UserReprTooSmall,
 }
@@ -90,7 +89,7 @@ impl std::error::Error for LayoutError {}
 impl fmt::Display for LayoutError {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
         match self {
-            LayoutError::EmptyUnion => write!(f, "type is an union with no fields"),
+            LayoutError::BadCalc(err) => err.fallback_fmt(f),
             LayoutError::HasErrorConst => write!(f, "type contains an unevaluatable const"),
             LayoutError::HasErrorType => write!(f, "type contains an error"),
             LayoutError::HasPlaceholder => write!(f, "type contains placeholders"),
@@ -99,11 +98,7 @@ impl fmt::Display for LayoutError {
             LayoutError::RecursiveTypeWithoutIndirection => {
                 write!(f, "recursive type without indirection")
             }
-            LayoutError::SizeOverflow => write!(f, "size overflow"),
             LayoutError::TargetLayoutNotAvailable => write!(f, "target layout not available"),
-            LayoutError::UnexpectedUnsized => {
-                write!(f, "an unsized type was found where a sized type was expected")
-            }
             LayoutError::Unknown => write!(f, "unknown"),
             LayoutError::UserReprTooSmall => {
                 write!(f, "the `#[repr]` hint is too small to hold the discriminants of the enum")
@@ -114,11 +109,7 @@ impl fmt::Display for LayoutError {
 
 impl<F> From<LayoutCalculatorError<F>> for LayoutError {
     fn from(err: LayoutCalculatorError<F>) -> Self {
-        match err {
-            LayoutCalculatorError::EmptyUnion => LayoutError::EmptyUnion,
-            LayoutCalculatorError::UnexpectedUnsized(_) => LayoutError::UnexpectedUnsized,
-            LayoutCalculatorError::SizeOverflow => LayoutError::SizeOverflow,
-        }
+        LayoutError::BadCalc(err.without_payload())
     }
 }
 
@@ -182,7 +173,10 @@ fn layout_of_simd_ty(
     };
 
     // Compute the size and alignment of the vector:
-    let size = e_ly.size.checked_mul(e_len, dl).ok_or(LayoutError::SizeOverflow)?;
+    let size = e_ly
+        .size
+        .checked_mul(e_len, dl)
+        .ok_or(LayoutError::BadCalc(LayoutCalculatorError::SizeOverflow))?;
     let align = dl.vector_align(size);
     let size = size.align_to(align.abi);
 
@@ -295,7 +289,10 @@ pub fn layout_of_ty_query(
         TyKind::Array(element, count) => {
             let count = try_const_usize(db, count).ok_or(LayoutError::HasErrorConst)? as u64;
             let element = db.layout_of_ty(element.clone(), trait_env)?;
-            let size = element.size.checked_mul(count, dl).ok_or(LayoutError::SizeOverflow)?;
+            let size = element
+                .size
+                .checked_mul(count, dl)
+                .ok_or(LayoutError::BadCalc(LayoutCalculatorError::SizeOverflow))?;
 
             let abi = if count != 0 && matches!(element.abi, Abi::Uninhabited) {
                 Abi::Uninhabited
diff --git a/src/tools/rust-analyzer/rust-version b/src/tools/rust-analyzer/rust-version
index d0f9fa7ac42..bc324402a96 100644
--- a/src/tools/rust-analyzer/rust-version
+++ b/src/tools/rust-analyzer/rust-version
@@ -1 +1 @@
-dd5127615ad626741a1116d022cf784637ac05df
+1de57a5ce952c722f7053aeacfc6c90bc139b678
diff --git a/src/tools/tidy/Cargo.toml b/src/tools/tidy/Cargo.toml
index 928867be64f..42e608ff5ce 100644
--- a/src/tools/tidy/Cargo.toml
+++ b/src/tools/tidy/Cargo.toml
@@ -13,7 +13,7 @@ walkdir = "2"
 ignore = "0.4.18"
 semver = "1.0"
 termcolor = "1.1.3"
-rustc-hash = "1.1.0"
+rustc-hash = "2.0.0"
 fluent-syntax = "0.11.1"
 similar = "2.5.0"
 
diff --git a/src/tools/tidy/src/issues.txt b/src/tools/tidy/src/issues.txt
index 22126674c15..97c42752c12 100644
--- a/src/tools/tidy/src/issues.txt
+++ b/src/tools/tidy/src/issues.txt
@@ -2758,7 +2758,6 @@ ui/lint/issue-63364.rs
 ui/lint/issue-70819-dont-override-forbid-in-same-scope.rs
 ui/lint/issue-79546-fuel-ice.rs
 ui/lint/issue-79744.rs
-ui/lint/issue-80988.rs
 ui/lint/issue-81218.rs
 ui/lint/issue-83477.rs
 ui/lint/issue-87274-paren-parent.rs
diff --git a/src/tools/unicode-table-generator/Cargo.toml b/src/tools/unicode-table-generator/Cargo.toml
index ef01877c0b9..f8a500922d0 100644
--- a/src/tools/unicode-table-generator/Cargo.toml
+++ b/src/tools/unicode-table-generator/Cargo.toml
@@ -1,5 +1,5 @@
 [package]
-name = "unicode-bdd"
+name = "unicode-table-generator"
 version = "0.1.0"
 edition = "2021"
 
diff --git a/src/tools/unicode-table-generator/src/range_search.rs b/src/tools/unicode-table-generator/src/range_search.rs
index 3a5b869f72f..221e5d75ef5 100644
--- a/src/tools/unicode-table-generator/src/range_search.rs
+++ b/src/tools/unicode-table-generator/src/range_search.rs
@@ -16,16 +16,14 @@ const fn bitset_search<
     let bucket_idx = (needle / 64) as usize;
     let chunk_map_idx = bucket_idx / CHUNK_SIZE;
     let chunk_piece = bucket_idx % CHUNK_SIZE;
-    // FIXME: const-hack: Revert to `slice::get` after `const_slice_index`
-    // feature stabilizes.
+    // FIXME(const-hack): Revert to `slice::get` when slice indexing becomes possible in const.
     let chunk_idx = if chunk_map_idx < chunk_idx_map.len() {
         chunk_idx_map[chunk_map_idx]
     } else {
         return false;
     };
     let idx = bitset_chunk_idx[chunk_idx as usize][chunk_piece] as usize;
-    // FIXME: const-hack: Revert to `slice::get` after `const_slice_index`
-    // feature stabilizes.
+    // FIXME(const-hack): Revert to `slice::get` when slice indexing becomes possible in const.
     let word = if idx < bitset_canonical.len() {
         bitset_canonical[idx]
     } else {