about summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2024-10-20 05:57:13 +0000
committerbors <bors@rust-lang.org>2024-10-20 05:57:13 +0000
commit45a9a7ca5aba461c91afd0423cbf567853bae847 (patch)
tree7aebdb581d6b5aa4a67efc5c52d84cc0bf58e802 /src
parent3386ae21ccab4303aa94bf71e2840f9634ffcb13 (diff)
parentccd680ee01564d32cad783a94c4dcccf228922c1 (diff)
downloadrust-45a9a7ca5aba461c91afd0423cbf567853bae847.tar.gz
rust-45a9a7ca5aba461c91afd0423cbf567853bae847.zip
Auto merge of #3980 - rust-lang:rustup-2024-10-20, r=RalfJung
Automatic Rustup
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.rs1
-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
-rwxr-xr-xsrc/ci/docker/host-x86_64/x86_64-gnu-tools/checktools.sh2
-rw-r--r--src/ci/github-actions/jobs.yml21
-rw-r--r--src/etc/installer/msi/rust.wxs41
-rw-r--r--src/librustdoc/Cargo.toml1
-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.rs24
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.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/infinite_loop.rs19
-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/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/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/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/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/trait_bounds.rs96
-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.rs5
-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.rs48
-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/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/miri/rust-version2
-rw-r--r--src/tools/miri/src/borrow_tracker/stacked_borrows/diagnostics.rs14
-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.rs10
-rw-r--r--src/tools/miri/src/shims/foreign_items.rs3
-rw-r--r--src/tools/miri/tests/pass/dyn-upcast.rs52
-rw-r--r--src/tools/miri/tests/pass/dyn-upcast.stdout4
-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.lock60
-rw-r--r--src/tools/rust-analyzer/Cargo.toml15
-rw-r--r--src/tools/rust-analyzer/crates/base-db/Cargo.toml2
-rw-r--r--src/tools/rust-analyzer/crates/base-db/src/change.rs2
-rw-r--r--src/tools/rust-analyzer/crates/base-db/src/input.rs5
-rw-r--r--src/tools/rust-analyzer/crates/base-db/src/lib.rs30
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/data.rs13
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/db.rs146
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/lib.rs42
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/nameres/collector.rs323
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/nameres/tests.rs1
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/test_db.rs16
-rw-r--r--src/tools/rust-analyzer/crates/hir-expand/src/builtin/attr_macro.rs3
-rw-r--r--src/tools/rust-analyzer/crates/hir-expand/src/change.rs2
-rw-r--r--src/tools/rust-analyzer/crates/hir-expand/src/db.rs40
-rw-r--r--src/tools/rust-analyzer/crates/hir-expand/src/hygiene.rs8
-rw-r--r--src/tools/rust-analyzer/crates/hir-expand/src/lib.rs2
-rw-r--r--src/tools/rust-analyzer/crates/hir-expand/src/prettify_macro_expansion_.rs5
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/consteval.rs2
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/db.rs150
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/display.rs32
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/infer.rs3
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/infer/cast.rs34
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/infer/closure.rs3
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/infer/coerce.rs49
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs404
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/infer/mutability.rs26
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/infer/pat.rs6
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/interner.rs2
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/layout.rs11
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/layout/adt.rs2
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/lib.rs2
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/lower.rs2
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/mapping.rs22
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/mir/lower.rs2
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/mir/monomorphization.rs2
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/test_db.rs22
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/tests/closure_captures.rs2
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/tests/macros.rs4
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/tests/never_type.rs246
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/tests/patterns.rs12
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/tests/regression.rs8
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/tests/simple.rs50
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/tests/traits.rs30
-rw-r--r--src/tools/rust-analyzer/crates/ide-db/src/apply_change.rs2
-rw-r--r--src/tools/rust-analyzer/crates/ide-db/src/lib.rs20
-rw-r--r--src/tools/rust-analyzer/crates/ide-db/src/prime_caches.rs2
-rw-r--r--src/tools/rust-analyzer/crates/ide-db/src/search.rs2
-rw-r--r--src/tools/rust-analyzer/crates/ide-db/src/symbol_index.rs16
-rw-r--r--src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/invalid_cast.rs54
-rw-r--r--src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/mutability_errors.rs25
-rw-r--r--src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/type_mismatch.rs38
-rw-r--r--src/tools/rust-analyzer/crates/ide-ssr/src/tests.rs2
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/call_hierarchy.rs118
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/highlight_related.rs174
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/hover/tests.rs30
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/lib.rs22
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/navigation_target.rs1
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/rename.rs53
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/ssr.rs2
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/status.rs2
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_extern_crate.html7
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/tests.rs13
-rw-r--r--src/tools/rust-analyzer/crates/parser/src/grammar/generic_params.rs2
-rw-r--r--src/tools/rust-analyzer/crates/parser/src/lexed_str.rs5
-rw-r--r--src/tools/rust-analyzer/crates/parser/test_data/parser/err/0055_impl_use.rast26
-rw-r--r--src/tools/rust-analyzer/crates/parser/test_data/parser/err/0055_impl_use.rs2
-rw-r--r--src/tools/rust-analyzer/crates/project-model/src/workspace.rs2
-rw-r--r--src/tools/rust-analyzer/crates/ra-salsa/Cargo.toml (renamed from src/tools/rust-analyzer/crates/salsa/Cargo.toml)4
-rw-r--r--src/tools/rust-analyzer/crates/ra-salsa/FAQ.md (renamed from src/tools/rust-analyzer/crates/salsa/FAQ.md)0
-rw-r--r--src/tools/rust-analyzer/crates/ra-salsa/LICENSE-APACHE (renamed from src/tools/rust-analyzer/crates/salsa/LICENSE-APACHE)0
-rw-r--r--src/tools/rust-analyzer/crates/ra-salsa/LICENSE-MIT (renamed from src/tools/rust-analyzer/crates/salsa/LICENSE-MIT)0
-rw-r--r--src/tools/rust-analyzer/crates/ra-salsa/README.md (renamed from src/tools/rust-analyzer/crates/salsa/README.md)0
-rw-r--r--src/tools/rust-analyzer/crates/ra-salsa/ra-salsa-macros/Cargo.toml (renamed from src/tools/rust-analyzer/crates/salsa/salsa-macros/Cargo.toml)2
-rw-r--r--src/tools/rust-analyzer/crates/ra-salsa/ra-salsa-macros/LICENSE-APACHE (renamed from src/tools/rust-analyzer/crates/salsa/salsa-macros/LICENSE-APACHE)0
-rw-r--r--src/tools/rust-analyzer/crates/ra-salsa/ra-salsa-macros/LICENSE-MIT (renamed from src/tools/rust-analyzer/crates/salsa/salsa-macros/LICENSE-MIT)0
-rw-r--r--src/tools/rust-analyzer/crates/ra-salsa/ra-salsa-macros/README.md (renamed from src/tools/rust-analyzer/crates/salsa/salsa-macros/README.md)0
-rw-r--r--src/tools/rust-analyzer/crates/ra-salsa/ra-salsa-macros/src/database_storage.rs (renamed from src/tools/rust-analyzer/crates/salsa/salsa-macros/src/database_storage.rs)48
-rw-r--r--src/tools/rust-analyzer/crates/ra-salsa/ra-salsa-macros/src/lib.rs (renamed from src/tools/rust-analyzer/crates/salsa/salsa-macros/src/lib.rs)0
-rw-r--r--src/tools/rust-analyzer/crates/ra-salsa/ra-salsa-macros/src/parenthesized.rs (renamed from src/tools/rust-analyzer/crates/salsa/salsa-macros/src/parenthesized.rs)0
-rw-r--r--src/tools/rust-analyzer/crates/ra-salsa/ra-salsa-macros/src/query_group.rs (renamed from src/tools/rust-analyzer/crates/salsa/salsa-macros/src/query_group.rs)126
-rw-r--r--src/tools/rust-analyzer/crates/ra-salsa/src/debug.rs (renamed from src/tools/rust-analyzer/crates/salsa/src/debug.rs)0
-rw-r--r--src/tools/rust-analyzer/crates/ra-salsa/src/derived.rs (renamed from src/tools/rust-analyzer/crates/salsa/src/derived.rs)0
-rw-r--r--src/tools/rust-analyzer/crates/ra-salsa/src/derived/slot.rs (renamed from src/tools/rust-analyzer/crates/salsa/src/derived/slot.rs)0
-rw-r--r--src/tools/rust-analyzer/crates/ra-salsa/src/derived_lru.rs (renamed from src/tools/rust-analyzer/crates/salsa/src/derived_lru.rs)0
-rw-r--r--src/tools/rust-analyzer/crates/ra-salsa/src/derived_lru/slot.rs (renamed from src/tools/rust-analyzer/crates/salsa/src/derived_lru/slot.rs)0
-rw-r--r--src/tools/rust-analyzer/crates/ra-salsa/src/durability.rs (renamed from src/tools/rust-analyzer/crates/salsa/src/durability.rs)0
-rw-r--r--src/tools/rust-analyzer/crates/ra-salsa/src/hash.rs (renamed from src/tools/rust-analyzer/crates/salsa/src/hash.rs)0
-rw-r--r--src/tools/rust-analyzer/crates/ra-salsa/src/input.rs (renamed from src/tools/rust-analyzer/crates/salsa/src/input.rs)0
-rw-r--r--src/tools/rust-analyzer/crates/ra-salsa/src/intern_id.rs (renamed from src/tools/rust-analyzer/crates/salsa/src/intern_id.rs)10
-rw-r--r--src/tools/rust-analyzer/crates/ra-salsa/src/interned.rs (renamed from src/tools/rust-analyzer/crates/salsa/src/interned.rs)0
-rw-r--r--src/tools/rust-analyzer/crates/ra-salsa/src/lib.rs (renamed from src/tools/rust-analyzer/crates/salsa/src/lib.rs)4
-rw-r--r--src/tools/rust-analyzer/crates/ra-salsa/src/lru.rs (renamed from src/tools/rust-analyzer/crates/salsa/src/lru.rs)0
-rw-r--r--src/tools/rust-analyzer/crates/ra-salsa/src/plumbing.rs (renamed from src/tools/rust-analyzer/crates/salsa/src/plumbing.rs)0
-rw-r--r--src/tools/rust-analyzer/crates/ra-salsa/src/revision.rs (renamed from src/tools/rust-analyzer/crates/salsa/src/revision.rs)0
-rw-r--r--src/tools/rust-analyzer/crates/ra-salsa/src/runtime.rs (renamed from src/tools/rust-analyzer/crates/salsa/src/runtime.rs)0
-rw-r--r--src/tools/rust-analyzer/crates/ra-salsa/src/runtime/dependency_graph.rs (renamed from src/tools/rust-analyzer/crates/salsa/src/runtime/dependency_graph.rs)0
-rw-r--r--src/tools/rust-analyzer/crates/ra-salsa/src/runtime/local_state.rs (renamed from src/tools/rust-analyzer/crates/salsa/src/runtime/local_state.rs)0
-rw-r--r--src/tools/rust-analyzer/crates/ra-salsa/src/storage.rs (renamed from src/tools/rust-analyzer/crates/salsa/src/storage.rs)0
-rw-r--r--src/tools/rust-analyzer/crates/ra-salsa/tests/cycles.rs (renamed from src/tools/rust-analyzer/crates/salsa/tests/cycles.rs)30
-rw-r--r--src/tools/rust-analyzer/crates/ra-salsa/tests/dyn_trait.rs (renamed from src/tools/rust-analyzer/crates/salsa/tests/dyn_trait.rs)10
-rw-r--r--src/tools/rust-analyzer/crates/ra-salsa/tests/incremental/constants.rs (renamed from src/tools/rust-analyzer/crates/salsa/tests/incremental/constants.rs)8
-rw-r--r--src/tools/rust-analyzer/crates/ra-salsa/tests/incremental/counter.rs (renamed from src/tools/rust-analyzer/crates/salsa/tests/incremental/counter.rs)0
-rw-r--r--src/tools/rust-analyzer/crates/ra-salsa/tests/incremental/implementation.rs (renamed from src/tools/rust-analyzer/crates/salsa/tests/incremental/implementation.rs)8
-rw-r--r--src/tools/rust-analyzer/crates/ra-salsa/tests/incremental/log.rs (renamed from src/tools/rust-analyzer/crates/salsa/tests/incremental/log.rs)0
-rw-r--r--src/tools/rust-analyzer/crates/ra-salsa/tests/incremental/main.rs (renamed from src/tools/rust-analyzer/crates/salsa/tests/incremental/main.rs)0
-rw-r--r--src/tools/rust-analyzer/crates/ra-salsa/tests/incremental/memoized_dep_inputs.rs (renamed from src/tools/rust-analyzer/crates/salsa/tests/incremental/memoized_dep_inputs.rs)8
-rw-r--r--src/tools/rust-analyzer/crates/ra-salsa/tests/incremental/memoized_inputs.rs (renamed from src/tools/rust-analyzer/crates/salsa/tests/incremental/memoized_inputs.rs)6
-rw-r--r--src/tools/rust-analyzer/crates/ra-salsa/tests/incremental/memoized_volatile.rs (renamed from src/tools/rust-analyzer/crates/salsa/tests/incremental/memoized_volatile.rs)4
-rw-r--r--src/tools/rust-analyzer/crates/ra-salsa/tests/interned.rs (renamed from src/tools/rust-analyzer/crates/salsa/tests/interned.rs)24
-rw-r--r--src/tools/rust-analyzer/crates/ra-salsa/tests/lru.rs (renamed from src/tools/rust-analyzer/crates/salsa/tests/lru.rs)14
-rw-r--r--src/tools/rust-analyzer/crates/ra-salsa/tests/macros.rs (renamed from src/tools/rust-analyzer/crates/salsa/tests/macros.rs)6
-rw-r--r--src/tools/rust-analyzer/crates/ra-salsa/tests/no_send_sync.rs (renamed from src/tools/rust-analyzer/crates/salsa/tests/no_send_sync.rs)10
-rw-r--r--src/tools/rust-analyzer/crates/ra-salsa/tests/on_demand_inputs.rs (renamed from src/tools/rust-analyzer/crates/salsa/tests/on_demand_inputs.rs)16
-rw-r--r--src/tools/rust-analyzer/crates/ra-salsa/tests/panic_safely.rs (renamed from src/tools/rust-analyzer/crates/salsa/tests/panic_safely.rs)16
-rw-r--r--src/tools/rust-analyzer/crates/ra-salsa/tests/parallel/cancellation.rs (renamed from src/tools/rust-analyzer/crates/salsa/tests/parallel/cancellation.rs)4
-rw-r--r--src/tools/rust-analyzer/crates/ra-salsa/tests/parallel/frozen.rs (renamed from src/tools/rust-analyzer/crates/salsa/tests/parallel/frozen.rs)2
-rw-r--r--src/tools/rust-analyzer/crates/ra-salsa/tests/parallel/independent.rs (renamed from src/tools/rust-analyzer/crates/salsa/tests/parallel/independent.rs)2
-rw-r--r--src/tools/rust-analyzer/crates/ra-salsa/tests/parallel/main.rs (renamed from src/tools/rust-analyzer/crates/salsa/tests/parallel/main.rs)0
-rw-r--r--src/tools/rust-analyzer/crates/ra-salsa/tests/parallel/parallel_cycle_all_recover.rs (renamed from src/tools/rust-analyzer/crates/salsa/tests/parallel/parallel_cycle_all_recover.rs)20
-rw-r--r--src/tools/rust-analyzer/crates/ra-salsa/tests/parallel/parallel_cycle_mid_recover.rs (renamed from src/tools/rust-analyzer/crates/salsa/tests/parallel/parallel_cycle_mid_recover.rs)12
-rw-r--r--src/tools/rust-analyzer/crates/ra-salsa/tests/parallel/parallel_cycle_none_recover.rs (renamed from src/tools/rust-analyzer/crates/salsa/tests/parallel/parallel_cycle_none_recover.rs)8
-rw-r--r--src/tools/rust-analyzer/crates/ra-salsa/tests/parallel/parallel_cycle_one_recovers.rs (renamed from src/tools/rust-analyzer/crates/salsa/tests/parallel/parallel_cycle_one_recovers.rs)8
-rw-r--r--src/tools/rust-analyzer/crates/ra-salsa/tests/parallel/race.rs (renamed from src/tools/rust-analyzer/crates/salsa/tests/parallel/race.rs)2
-rw-r--r--src/tools/rust-analyzer/crates/ra-salsa/tests/parallel/setup.rs (renamed from src/tools/rust-analyzer/crates/salsa/tests/parallel/setup.rs)18
-rw-r--r--src/tools/rust-analyzer/crates/ra-salsa/tests/parallel/signal.rs (renamed from src/tools/rust-analyzer/crates/salsa/tests/parallel/signal.rs)0
-rw-r--r--src/tools/rust-analyzer/crates/ra-salsa/tests/parallel/stress.rs (renamed from src/tools/rust-analyzer/crates/salsa/tests/parallel/stress.rs)20
-rw-r--r--src/tools/rust-analyzer/crates/ra-salsa/tests/parallel/true_parallel.rs (renamed from src/tools/rust-analyzer/crates/salsa/tests/parallel/true_parallel.rs)2
-rw-r--r--src/tools/rust-analyzer/crates/ra-salsa/tests/storage_varieties/implementation.rs (renamed from src/tools/rust-analyzer/crates/salsa/tests/storage_varieties/implementation.rs)6
-rw-r--r--src/tools/rust-analyzer/crates/ra-salsa/tests/storage_varieties/main.rs (renamed from src/tools/rust-analyzer/crates/salsa/tests/storage_varieties/main.rs)0
-rw-r--r--src/tools/rust-analyzer/crates/ra-salsa/tests/storage_varieties/queries.rs (renamed from src/tools/rust-analyzer/crates/salsa/tests/storage_varieties/queries.rs)4
-rw-r--r--src/tools/rust-analyzer/crates/ra-salsa/tests/storage_varieties/tests.rs (renamed from src/tools/rust-analyzer/crates/salsa/tests/storage_varieties/tests.rs)4
-rw-r--r--src/tools/rust-analyzer/crates/ra-salsa/tests/transparent.rs (renamed from src/tools/rust-analyzer/crates/salsa/tests/transparent.rs)12
-rw-r--r--src/tools/rust-analyzer/crates/ra-salsa/tests/variadic.rs (renamed from src/tools/rust-analyzer/crates/salsa/tests/variadic.rs)12
-rw-r--r--src/tools/rust-analyzer/crates/rust-analyzer/Cargo.toml2
-rw-r--r--src/tools/rust-analyzer/crates/rust-analyzer/src/cli/analysis_stats.rs6
-rw-r--r--src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs15
-rw-r--r--src/tools/rust-analyzer/crates/rust-analyzer/src/global_state.rs8
-rw-r--r--src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/request.rs12
-rw-r--r--src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/to_proto.rs18
-rw-r--r--src/tools/rust-analyzer/crates/rust-analyzer/src/main_loop.rs9
-rw-r--r--src/tools/rust-analyzer/crates/rust-analyzer/src/reload.rs18
-rw-r--r--src/tools/rust-analyzer/crates/rust-analyzer/src/tracing/config.rs6
-rw-r--r--src/tools/rust-analyzer/crates/span/Cargo.toml2
-rw-r--r--src/tools/rust-analyzer/crates/span/src/hygiene.rs8
-rw-r--r--src/tools/rust-analyzer/crates/span/src/lib.rs10
-rw-r--r--src/tools/rust-analyzer/docs/user/generated_config.adoc2
-rw-r--r--src/tools/rust-analyzer/editors/code/package.json2
-rw-r--r--src/tools/rust-analyzer/editors/code/src/bootstrap.ts39
-rw-r--r--src/tools/rust-analyzer/editors/code/src/debug.ts30
-rw-r--r--src/tools/rust-analyzer/editors/code/src/util.ts54
-rw-r--r--src/tools/rust-analyzer/editors/code/tests/unit/bootstrap.test.ts22
-rw-r--r--src/tools/rust-analyzer/rust-version2
-rw-r--r--src/tools/rust-analyzer/xtask/src/tidy.rs2
-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
377 files changed, 6817 insertions, 3142 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.rs b/src/bootstrap/src/core/builder.rs
index 15c6f303f94..8cea01434fe 100644
--- a/src/bootstrap/src/core/builder.rs
+++ b/src/bootstrap/src/core/builder.rs
@@ -1010,6 +1010,7 @@ impl<'a> Builder<'a> {
                 run::GenerateCopyright,
                 run::GenerateWindowsSys,
                 run::GenerateCompletions,
+                run::UnicodeTableGenerator,
             ),
             Kind::Setup => {
                 describe!(setup::Profile, setup::Hook, setup::Link, setup::Editor)
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/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/github-actions/jobs.yml b/src/ci/github-actions/jobs.yml
index 8f49f623afa..d84b904442c 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
 
@@ -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
@@ -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/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/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..7f7cd3672b7 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 = "1.1.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..c4e142342a8 100644
--- a/src/rustdoc-json-types/lib.rs
+++ b/src/rustdoc-json-types/lib.rs
@@ -3,17 +3,22 @@
 //! 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.
 
+#[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 +35,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 +100,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.
@@ -1082,8 +1087,11 @@ pub struct Trait {
     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.
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..f4c00d8287d 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};
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/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_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/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/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/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/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/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/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..a1cfb7be647 100644
--- a/src/tools/clippy/clippy_utils/src/consts.rs
+++ b/src/tools/clippy/clippy_utils/src/consts.rs
@@ -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) {
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..b7a3569ccf0 100644
--- a/src/tools/clippy/clippy_utils/src/ty.rs
+++ b/src/tools/clippy/clippy_utils/src/ty.rs
@@ -1168,7 +1168,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 +1194,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_eval_target_usize(cx.tcx, cx.param_env) != 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 +1230,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/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/miri/rust-version b/src/tools/miri/rust-version
index d0f9fa7ac42..778061e1310 100644
--- a/src/tools/miri/rust-version
+++ b/src/tools/miri/rust-version
@@ -1 +1 @@
-dd5127615ad626741a1116d022cf784637ac05df
+54791efd8235805dcfbdad3b8788e08f2142c50b
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..5624c4c479e 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,11 @@ 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 +449,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..59776a484b5 100644
--- a/src/tools/miri/src/intrinsics/simd.rs
+++ b/src/tools/miri/src/intrinsics/simd.rs
@@ -245,17 +245,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) {
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/pass/dyn-upcast.rs b/src/tools/miri/tests/pass/dyn-upcast.rs
index ff995f38196..61410f7c4e0 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,54 @@ fn replace_vptr() {
     let s = S(42);
     invoke_outer(&s);
 }
+
+fn drop_principal() {
+    use std::alloc::Layout;
+    use std::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/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 7891edc2447..4a6da47a47d 100644
--- a/src/tools/rust-analyzer/Cargo.lock
+++ b/src/tools/rust-analyzer/Cargo.lock
@@ -1188,6 +1188,15 @@ dependencies = [
 ]
 
 [[package]]
+name = "num_threads"
+version = "0.1.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5c7398b9c8b70908f6371f47ed36737907c87c52af34c268fed0bf0ceb92ead9"
+dependencies = [
+ "libc",
+]
+
+[[package]]
 name = "object"
 version = "0.33.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1439,9 +1448,9 @@ dependencies = [
 
 [[package]]
 name = "protobuf"
-version = "3.2.0"
+version = "3.7.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b55bad9126f378a853655831eb7363b7b01b81d19f8cb1218861086ca4a1a61e"
+checksum = "a3a7c64d9bf75b1b8d981124c14c179074e8caa7dfe7b6a12e6222ddcd0c8f72"
 dependencies = [
  "once_cell",
  "protobuf-support",
@@ -1450,9 +1459,9 @@ dependencies = [
 
 [[package]]
 name = "protobuf-support"
-version = "3.2.0"
+version = "3.7.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a5d4d7b8601c814cfb36bcebb79f0e61e45e1e93640cf778837833bbed05c372"
+checksum = "b088fd20b938a875ea00843b6faf48579462630015c3788d397ad6a786663252"
 dependencies = [
  "thiserror",
 ]
@@ -1488,9 +1497,9 @@ dependencies = [
 
 [[package]]
 name = "ra-ap-rustc_abi"
-version = "0.68.0"
+version = "0.71.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2a8cb51bb4534ac3e9c74f1d9bd90e607e60f94f734b1cf1a66f753ad2af6ed7"
+checksum = "c6999d098000b98415939f13158dac78cb3eeeb7b0c073847f3e4b623866e27c"
 dependencies = [
  "bitflags 2.6.0",
  "ra-ap-rustc_index",
@@ -1499,9 +1508,9 @@ dependencies = [
 
 [[package]]
 name = "ra-ap-rustc_index"
-version = "0.68.0"
+version = "0.71.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8b640fba2b7ef4f875459e2e76daeb846ef341d1d376fa758962ac0eba79bce6"
+checksum = "ae9fb312d942817dab10790881f555928c1f6a11a85186e8e573ad4a86c7d3be"
 dependencies = [
  "arrayvec",
  "ra-ap-rustc_index_macros",
@@ -1510,9 +1519,9 @@ dependencies = [
 
 [[package]]
 name = "ra-ap-rustc_index_macros"
-version = "0.68.0"
+version = "0.71.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "faef502419ba5ac9d3079b1a835c6e5b4e605388254bbe55eb5683936f541be9"
+checksum = "766e3990eb1066a06deefc561b5a01b32ca5c9211feea31cbf4ed50611519872"
 dependencies = [
  "proc-macro2",
  "quote",
@@ -1521,9 +1530,9 @@ dependencies = [
 
 [[package]]
 name = "ra-ap-rustc_lexer"
-version = "0.68.0"
+version = "0.71.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5da7f9d533b8d5be6704558da741ff20b982ad4647b1e9e08632853e4fecf9d5"
+checksum = "f4afa98eb7889c137d5a3f1cd189089e16da04d1e4837d358a67aa3dab10ffbe"
 dependencies = [
  "unicode-properties",
  "unicode-xid",
@@ -1531,9 +1540,9 @@ dependencies = [
 
 [[package]]
 name = "ra-ap-rustc_parse_format"
-version = "0.68.0"
+version = "0.71.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "94389cf81c651b1bda9ac45d3de6a2d851bb6fd4cb893875daa44e419c94205f"
+checksum = "d9234c96ffb0565286790407fb7eb7f55ebf69267de4db382fdec0a17f14b0e2"
 dependencies = [
  "ra-ap-rustc_index",
  "ra-ap-rustc_lexer",
@@ -1541,9 +1550,9 @@ dependencies = [
 
 [[package]]
 name = "ra-ap-rustc_pattern_analysis"
-version = "0.68.0"
+version = "0.71.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3679d8dd0114ed6000918309f843782738e51c99d8e4baec0d0f706e4d948819"
+checksum = "273d5f72926a58c7eea27aebc898d1d5b32d23d2342f692a94a2cf8746aa4a2f"
 dependencies = [
  "ra-ap-rustc_index",
  "rustc-hash",
@@ -1765,9 +1774,9 @@ dependencies = [
 
 [[package]]
 name = "scip"
-version = "0.3.3"
+version = "0.5.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e5dc1bd66649133af84ab62436ddd2856c2605182b02dec2cd197f684dfe15ef"
+checksum = "8dfafd2fa14c6237fa1fc4310f739d02fa915d92977fa069426591f1de046f81"
 dependencies = [
  "protobuf",
 ]
@@ -2093,10 +2102,14 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "5dfd88e563464686c916c7e46e623e520ddc6d79fa6641390f2e3fa86e83e885"
 dependencies = [
  "deranged",
+ "itoa",
+ "libc",
  "num-conv",
+ "num_threads",
  "powerfmt",
  "serde",
  "time-core",
+ "time-macros",
 ]
 
 [[package]]
@@ -2106,6 +2119,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3"
 
 [[package]]
+name = "time-macros"
+version = "0.2.18"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3f252a68540fde3a3877aeea552b832b40ab9a69e318efd078774a01ddee1ccf"
+dependencies = [
+ "num-conv",
+ "time-core",
+]
+
+[[package]]
 name = "tinyvec"
 version = "1.8.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -2213,6 +2236,7 @@ checksum = "ad0f048c97dbd9faa9b7df56362b8ebcaa52adb06b498c050d2f4e32f90a7a8b"
 dependencies = [
  "sharded-slab",
  "thread_local",
+ "time",
  "tracing-core",
  "tracing-log",
 ]
diff --git a/src/tools/rust-analyzer/Cargo.toml b/src/tools/rust-analyzer/Cargo.toml
index 0b3d6e2a1ef..8c099f324b4 100644
--- a/src/tools/rust-analyzer/Cargo.toml
+++ b/src/tools/rust-analyzer/Cargo.toml
@@ -74,7 +74,7 @@ proc-macro-srv = { path = "./crates/proc-macro-srv", version = "0.0.0" }
 proc-macro-srv-cli = { path = "./crates/proc-macro-srv-cli", version = "0.0.0" }
 profile = { path = "./crates/profile", version = "0.0.0" }
 project-model = { path = "./crates/project-model", version = "0.0.0" }
-salsa = { path = "./crates/salsa", version = "0.0.0" }
+ra-salsa = { path = "./crates/ra-salsa", package = "salsa", version = "0.0.0" }
 span = { path = "./crates/span", version = "0.0.0" }
 stdx = { path = "./crates/stdx", version = "0.0.0" }
 syntax = { path = "./crates/syntax", version = "0.0.0" }
@@ -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.68.0", default-features = false }
-ra-ap-rustc_parse_format = { version = "0.68.0", default-features = false }
-ra-ap-rustc_index = { version = "0.68.0", default-features = false }
-ra-ap-rustc_abi = { version = "0.68.0", default-features = false }
-ra-ap-rustc_pattern_analysis = { version = "0.68.0", default-features = false }
+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 }
 
 # local crates that aren't published to crates.io. These should not have versions.
 test-fixture = { path = "./crates/test-fixture" }
@@ -153,6 +153,9 @@ tracing-tree = "0.3.0"
 tracing-subscriber = { version = "0.3.18", default-features = false, features = [
   "registry",
   "fmt",
+  "local-time",
+  "std",
+  "time",
   "tracing-log",
 ] }
 triomphe = { version = "0.1.10", default-features = false, features = ["std"] }
diff --git a/src/tools/rust-analyzer/crates/base-db/Cargo.toml b/src/tools/rust-analyzer/crates/base-db/Cargo.toml
index b17b08a720c..788ceb8857e 100644
--- a/src/tools/rust-analyzer/crates/base-db/Cargo.toml
+++ b/src/tools/rust-analyzer/crates/base-db/Cargo.toml
@@ -16,7 +16,7 @@ doctest = false
 lz4_flex = { version = "0.11", default-features = false }
 
 la-arena.workspace = true
-salsa.workspace = true
+ra-salsa.workspace = true
 rustc-hash.workspace = true
 triomphe.workspace = true
 semver.workspace = true
diff --git a/src/tools/rust-analyzer/crates/base-db/src/change.rs b/src/tools/rust-analyzer/crates/base-db/src/change.rs
index 4fb6654b612..7e40f5408f1 100644
--- a/src/tools/rust-analyzer/crates/base-db/src/change.rs
+++ b/src/tools/rust-analyzer/crates/base-db/src/change.rs
@@ -3,8 +3,8 @@
 
 use std::fmt;
 
+use ra_salsa::Durability;
 use rustc_hash::FxHashMap;
-use salsa::Durability;
 use triomphe::Arc;
 use vfs::FileId;
 
diff --git a/src/tools/rust-analyzer/crates/base-db/src/input.rs b/src/tools/rust-analyzer/crates/base-db/src/input.rs
index f5109339ad1..57522d69321 100644
--- a/src/tools/rust-analyzer/crates/base-db/src/input.rs
+++ b/src/tools/rust-analyzer/crates/base-db/src/input.rs
@@ -288,6 +288,11 @@ pub struct CrateData {
     /// The cfg options that could be used by the crate
     pub potential_cfg_options: Option<Arc<CfgOptions>>,
     pub env: Env,
+    /// The dependencies of this crate.
+    ///
+    /// Note that this may contain more dependencies than the crate actually uses.
+    /// A common example is the test crate which is included but only actually is active when
+    /// declared in source via `extern crate test`.
     pub dependencies: Vec<Dependency>,
     pub origin: CrateOrigin,
     pub is_proc_macro: bool,
diff --git a/src/tools/rust-analyzer/crates/base-db/src/lib.rs b/src/tools/rust-analyzer/crates/base-db/src/lib.rs
index 46e258d46f5..0a9e83bc3ba 100644
--- a/src/tools/rust-analyzer/crates/base-db/src/lib.rs
+++ b/src/tools/rust-analyzer/crates/base-db/src/lib.rs
@@ -5,8 +5,8 @@ mod input;
 
 use std::panic;
 
+use ra_salsa::Durability;
 use rustc_hash::FxHashMap;
-use salsa::Durability;
 use span::EditionedFileId;
 use syntax::{ast, Parse, SourceFile, SyntaxError};
 use triomphe::Arc;
@@ -20,7 +20,7 @@ pub use crate::{
         TargetLayoutLoadResult,
     },
 };
-pub use salsa::{self, Cancelled};
+pub use ra_salsa::{self, Cancelled};
 pub use vfs::{file_set::FileSet, AnchoredPath, AnchoredPathBuf, VfsPath};
 
 pub use semver::{BuildMetadata, Prerelease, Version, VersionReq};
@@ -28,11 +28,11 @@ pub use semver::{BuildMetadata, Prerelease, Version, VersionReq};
 #[macro_export]
 macro_rules! impl_intern_key {
     ($name:ident) => {
-        impl $crate::salsa::InternKey for $name {
-            fn from_intern_id(v: $crate::salsa::InternId) -> Self {
+        impl $crate::ra_salsa::InternKey for $name {
+            fn from_intern_id(v: $crate::ra_salsa::InternId) -> Self {
                 $name(v)
             }
-            fn as_intern_id(&self) -> $crate::salsa::InternId {
+            fn as_intern_id(&self) -> $crate::ra_salsa::InternId {
                 self.0
             }
         }
@@ -55,30 +55,30 @@ pub trait FileLoader {
 
 /// Database which stores all significant input facts: source code and project
 /// model. Everything else in rust-analyzer is derived from these queries.
-#[salsa::query_group(SourceDatabaseStorage)]
+#[ra_salsa::query_group(SourceDatabaseStorage)]
 pub trait SourceDatabase: FileLoader + std::fmt::Debug {
-    #[salsa::input]
+    #[ra_salsa::input]
     fn compressed_file_text(&self, file_id: FileId) -> Arc<[u8]>;
 
     /// Text of the file.
-    #[salsa::lru]
+    #[ra_salsa::lru]
     fn file_text(&self, file_id: FileId) -> Arc<str>;
 
     /// Parses the file into the syntax tree.
-    #[salsa::lru]
+    #[ra_salsa::lru]
     fn parse(&self, file_id: EditionedFileId) -> Parse<ast::SourceFile>;
 
     /// Returns the set of errors obtained from parsing the file including validation errors.
     fn parse_errors(&self, file_id: EditionedFileId) -> Option<Arc<[SyntaxError]>>;
 
     /// The crate graph.
-    #[salsa::input]
+    #[ra_salsa::input]
     fn crate_graph(&self) -> Arc<CrateGraph>;
 
-    #[salsa::input]
+    #[ra_salsa::input]
     fn crate_workspace_data(&self) -> Arc<FxHashMap<CrateId, Arc<CrateWorkspaceData>>>;
 
-    #[salsa::transparent]
+    #[ra_salsa::transparent]
     fn toolchain_channel(&self, krate: CrateId) -> Option<ReleaseChannel>;
 }
 
@@ -126,14 +126,14 @@ fn file_text(db: &dyn SourceDatabase, file_id: FileId) -> Arc<str> {
 
 /// We don't want to give HIR knowledge of source roots, hence we extract these
 /// methods into a separate DB.
-#[salsa::query_group(SourceRootDatabaseStorage)]
+#[ra_salsa::query_group(SourceRootDatabaseStorage)]
 pub trait SourceRootDatabase: SourceDatabase {
     /// Path to a file, relative to the root of its source root.
     /// Source root of the file.
-    #[salsa::input]
+    #[ra_salsa::input]
     fn file_source_root(&self, file_id: FileId) -> SourceRootId;
     /// Contents of the source root.
-    #[salsa::input]
+    #[ra_salsa::input]
     fn source_root(&self, id: SourceRootId) -> Arc<SourceRoot>;
 
     /// Crates whose root fool is in `id`.
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/data.rs b/src/tools/rust-analyzer/crates/hir-def/src/data.rs
index d17ebd7ff92..3ecb57c7567 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/data.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/data.rs
@@ -506,14 +506,17 @@ impl ExternCrateDeclData {
         let crate_id = if name == sym::self_.clone() {
             Some(krate)
         } else {
-            db.crate_def_map(krate)
-                .extern_prelude()
-                .find(|&(prelude_name, ..)| *prelude_name == name)
-                .map(|(_, (root, _))| root.krate())
+            db.crate_graph()[krate].dependencies.iter().find_map(|dep| {
+                if dep.name.symbol() == name.symbol() {
+                    Some(dep.crate_id)
+                } else {
+                    None
+                }
+            })
         };
 
         Arc::new(Self {
-            name: extern_crate.name.clone(),
+            name,
             visibility: item_tree[extern_crate.visibility].clone(),
             alias: extern_crate.alias.clone(),
             crate_id,
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/db.rs b/src/tools/rust-analyzer/crates/hir-def/src/db.rs
index b1103d35cab..aeda302f35c 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/db.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/db.rs
@@ -1,5 +1,5 @@
 //! Defines database & queries for name resolution.
-use base_db::{salsa, CrateId, SourceDatabase, Upcast};
+use base_db::{ra_salsa, CrateId, SourceDatabase, Upcast};
 use either::Either;
 use hir_expand::{db::ExpandDatabase, HirFileId, MacroDefId};
 use intern::{sym, Interned};
@@ -31,71 +31,71 @@ use crate::{
     UseId, UseLoc, VariantId,
 };
 
-#[salsa::query_group(InternDatabaseStorage)]
+#[ra_salsa::query_group(InternDatabaseStorage)]
 pub trait InternDatabase: SourceDatabase {
     // region: items
-    #[salsa::interned]
+    #[ra_salsa::interned]
     fn intern_use(&self, loc: UseLoc) -> UseId;
-    #[salsa::interned]
+    #[ra_salsa::interned]
     fn intern_extern_crate(&self, loc: ExternCrateLoc) -> ExternCrateId;
-    #[salsa::interned]
+    #[ra_salsa::interned]
     fn intern_function(&self, loc: FunctionLoc) -> FunctionId;
-    #[salsa::interned]
+    #[ra_salsa::interned]
     fn intern_struct(&self, loc: StructLoc) -> StructId;
-    #[salsa::interned]
+    #[ra_salsa::interned]
     fn intern_union(&self, loc: UnionLoc) -> UnionId;
-    #[salsa::interned]
+    #[ra_salsa::interned]
     fn intern_enum(&self, loc: EnumLoc) -> EnumId;
-    #[salsa::interned]
+    #[ra_salsa::interned]
     fn intern_enum_variant(&self, loc: EnumVariantLoc) -> EnumVariantId;
-    #[salsa::interned]
+    #[ra_salsa::interned]
     fn intern_const(&self, loc: ConstLoc) -> ConstId;
-    #[salsa::interned]
+    #[ra_salsa::interned]
     fn intern_static(&self, loc: StaticLoc) -> StaticId;
-    #[salsa::interned]
+    #[ra_salsa::interned]
     fn intern_trait(&self, loc: TraitLoc) -> TraitId;
-    #[salsa::interned]
+    #[ra_salsa::interned]
     fn intern_trait_alias(&self, loc: TraitAliasLoc) -> TraitAliasId;
-    #[salsa::interned]
+    #[ra_salsa::interned]
     fn intern_type_alias(&self, loc: TypeAliasLoc) -> TypeAliasId;
-    #[salsa::interned]
+    #[ra_salsa::interned]
     fn intern_impl(&self, loc: ImplLoc) -> ImplId;
-    #[salsa::interned]
+    #[ra_salsa::interned]
     fn intern_extern_block(&self, loc: ExternBlockLoc) -> ExternBlockId;
-    #[salsa::interned]
+    #[ra_salsa::interned]
     fn intern_macro2(&self, loc: Macro2Loc) -> Macro2Id;
-    #[salsa::interned]
+    #[ra_salsa::interned]
     fn intern_proc_macro(&self, loc: ProcMacroLoc) -> ProcMacroId;
-    #[salsa::interned]
+    #[ra_salsa::interned]
     fn intern_macro_rules(&self, loc: MacroRulesLoc) -> MacroRulesId;
     // endregion: items
 
-    #[salsa::interned]
+    #[ra_salsa::interned]
     fn intern_block(&self, loc: BlockLoc) -> BlockId;
-    #[salsa::interned]
+    #[ra_salsa::interned]
     fn intern_anonymous_const(&self, id: ConstBlockLoc) -> ConstBlockId;
-    #[salsa::interned]
+    #[ra_salsa::interned]
     fn intern_in_type_const(&self, id: InTypeConstLoc) -> InTypeConstId;
 }
 
-#[salsa::query_group(DefDatabaseStorage)]
+#[ra_salsa::query_group(DefDatabaseStorage)]
 pub trait DefDatabase: InternDatabase + ExpandDatabase + Upcast<dyn ExpandDatabase> {
     /// Whether to expand procedural macros during name resolution.
-    #[salsa::input]
+    #[ra_salsa::input]
     fn expand_proc_attr_macros(&self) -> bool;
 
     /// Computes an [`ItemTree`] for the given file or macro expansion.
-    #[salsa::invoke(ItemTree::file_item_tree_query)]
+    #[ra_salsa::invoke(ItemTree::file_item_tree_query)]
     fn file_item_tree(&self, file_id: HirFileId) -> Arc<ItemTree>;
 
-    #[salsa::invoke(ItemTree::block_item_tree_query)]
+    #[ra_salsa::invoke(ItemTree::block_item_tree_query)]
     fn block_item_tree(&self, block_id: BlockId) -> Arc<ItemTree>;
 
-    #[salsa::invoke(DefMap::crate_def_map_query)]
+    #[ra_salsa::invoke(DefMap::crate_def_map_query)]
     fn crate_def_map(&self, krate: CrateId) -> Arc<DefMap>;
 
     /// Computes the block-level `DefMap`.
-    #[salsa::invoke(DefMap::block_def_map_query)]
+    #[ra_salsa::invoke(DefMap::block_def_map_query)]
     fn block_def_map(&self, block: BlockId) -> Arc<DefMap>;
 
     /// Turns a MacroId into a MacroDefId, describing the macro's definition post name resolution.
@@ -103,139 +103,139 @@ pub trait DefDatabase: InternDatabase + ExpandDatabase + Upcast<dyn ExpandDataba
 
     // region:data
 
-    #[salsa::transparent]
-    #[salsa::invoke(StructData::struct_data_query)]
+    #[ra_salsa::transparent]
+    #[ra_salsa::invoke(StructData::struct_data_query)]
     fn struct_data(&self, id: StructId) -> Arc<StructData>;
 
-    #[salsa::invoke(StructData::struct_data_with_diagnostics_query)]
+    #[ra_salsa::invoke(StructData::struct_data_with_diagnostics_query)]
     fn struct_data_with_diagnostics(&self, id: StructId) -> (Arc<StructData>, DefDiagnostics);
 
-    #[salsa::transparent]
-    #[salsa::invoke(StructData::union_data_query)]
+    #[ra_salsa::transparent]
+    #[ra_salsa::invoke(StructData::union_data_query)]
     fn union_data(&self, id: UnionId) -> Arc<StructData>;
 
-    #[salsa::invoke(StructData::union_data_with_diagnostics_query)]
+    #[ra_salsa::invoke(StructData::union_data_with_diagnostics_query)]
     fn union_data_with_diagnostics(&self, id: UnionId) -> (Arc<StructData>, DefDiagnostics);
 
-    #[salsa::invoke(EnumData::enum_data_query)]
+    #[ra_salsa::invoke(EnumData::enum_data_query)]
     fn enum_data(&self, e: EnumId) -> Arc<EnumData>;
 
-    #[salsa::transparent]
-    #[salsa::invoke(EnumVariantData::enum_variant_data_query)]
+    #[ra_salsa::transparent]
+    #[ra_salsa::invoke(EnumVariantData::enum_variant_data_query)]
     fn enum_variant_data(&self, id: EnumVariantId) -> Arc<EnumVariantData>;
 
-    #[salsa::invoke(EnumVariantData::enum_variant_data_with_diagnostics_query)]
+    #[ra_salsa::invoke(EnumVariantData::enum_variant_data_with_diagnostics_query)]
     fn enum_variant_data_with_diagnostics(
         &self,
         id: EnumVariantId,
     ) -> (Arc<EnumVariantData>, DefDiagnostics);
 
-    #[salsa::transparent]
-    #[salsa::invoke(VariantData::variant_data)]
+    #[ra_salsa::transparent]
+    #[ra_salsa::invoke(VariantData::variant_data)]
     fn variant_data(&self, id: VariantId) -> Arc<VariantData>;
-    #[salsa::transparent]
-    #[salsa::invoke(ImplData::impl_data_query)]
+    #[ra_salsa::transparent]
+    #[ra_salsa::invoke(ImplData::impl_data_query)]
     fn impl_data(&self, e: ImplId) -> Arc<ImplData>;
 
-    #[salsa::invoke(ImplData::impl_data_with_diagnostics_query)]
+    #[ra_salsa::invoke(ImplData::impl_data_with_diagnostics_query)]
     fn impl_data_with_diagnostics(&self, e: ImplId) -> (Arc<ImplData>, DefDiagnostics);
 
-    #[salsa::transparent]
-    #[salsa::invoke(TraitData::trait_data_query)]
+    #[ra_salsa::transparent]
+    #[ra_salsa::invoke(TraitData::trait_data_query)]
     fn trait_data(&self, e: TraitId) -> Arc<TraitData>;
 
-    #[salsa::invoke(TraitData::trait_data_with_diagnostics_query)]
+    #[ra_salsa::invoke(TraitData::trait_data_with_diagnostics_query)]
     fn trait_data_with_diagnostics(&self, tr: TraitId) -> (Arc<TraitData>, DefDiagnostics);
 
-    #[salsa::invoke(TraitAliasData::trait_alias_query)]
+    #[ra_salsa::invoke(TraitAliasData::trait_alias_query)]
     fn trait_alias_data(&self, e: TraitAliasId) -> Arc<TraitAliasData>;
 
-    #[salsa::invoke(TypeAliasData::type_alias_data_query)]
+    #[ra_salsa::invoke(TypeAliasData::type_alias_data_query)]
     fn type_alias_data(&self, e: TypeAliasId) -> Arc<TypeAliasData>;
 
-    #[salsa::invoke(FunctionData::fn_data_query)]
+    #[ra_salsa::invoke(FunctionData::fn_data_query)]
     fn function_data(&self, func: FunctionId) -> Arc<FunctionData>;
 
-    #[salsa::invoke(ConstData::const_data_query)]
+    #[ra_salsa::invoke(ConstData::const_data_query)]
     fn const_data(&self, konst: ConstId) -> Arc<ConstData>;
 
-    #[salsa::invoke(StaticData::static_data_query)]
+    #[ra_salsa::invoke(StaticData::static_data_query)]
     fn static_data(&self, statik: StaticId) -> Arc<StaticData>;
 
-    #[salsa::invoke(Macro2Data::macro2_data_query)]
+    #[ra_salsa::invoke(Macro2Data::macro2_data_query)]
     fn macro2_data(&self, makro: Macro2Id) -> Arc<Macro2Data>;
 
-    #[salsa::invoke(MacroRulesData::macro_rules_data_query)]
+    #[ra_salsa::invoke(MacroRulesData::macro_rules_data_query)]
     fn macro_rules_data(&self, makro: MacroRulesId) -> Arc<MacroRulesData>;
 
-    #[salsa::invoke(ProcMacroData::proc_macro_data_query)]
+    #[ra_salsa::invoke(ProcMacroData::proc_macro_data_query)]
     fn proc_macro_data(&self, makro: ProcMacroId) -> Arc<ProcMacroData>;
 
-    #[salsa::invoke(ExternCrateDeclData::extern_crate_decl_data_query)]
+    #[ra_salsa::invoke(ExternCrateDeclData::extern_crate_decl_data_query)]
     fn extern_crate_decl_data(&self, extern_crate: ExternCrateId) -> Arc<ExternCrateDeclData>;
 
     // endregion:data
 
-    #[salsa::invoke(Body::body_with_source_map_query)]
-    #[salsa::lru]
+    #[ra_salsa::invoke(Body::body_with_source_map_query)]
+    #[ra_salsa::lru]
     fn body_with_source_map(&self, def: DefWithBodyId) -> (Arc<Body>, Arc<BodySourceMap>);
 
-    #[salsa::invoke(Body::body_query)]
+    #[ra_salsa::invoke(Body::body_query)]
     fn body(&self, def: DefWithBodyId) -> Arc<Body>;
 
-    #[salsa::invoke(ExprScopes::expr_scopes_query)]
+    #[ra_salsa::invoke(ExprScopes::expr_scopes_query)]
     fn expr_scopes(&self, def: DefWithBodyId) -> Arc<ExprScopes>;
 
-    #[salsa::invoke(GenericParams::generic_params_query)]
+    #[ra_salsa::invoke(GenericParams::generic_params_query)]
     fn generic_params(&self, def: GenericDefId) -> Interned<GenericParams>;
 
     // region:attrs
 
-    #[salsa::invoke(Attrs::fields_attrs_query)]
+    #[ra_salsa::invoke(Attrs::fields_attrs_query)]
     fn fields_attrs(&self, def: VariantId) -> Arc<ArenaMap<LocalFieldId, Attrs>>;
 
     // should this really be a query?
-    #[salsa::invoke(crate::attr::fields_attrs_source_map)]
+    #[ra_salsa::invoke(crate::attr::fields_attrs_source_map)]
     fn fields_attrs_source_map(
         &self,
         def: VariantId,
     ) -> Arc<ArenaMap<LocalFieldId, AstPtr<Either<ast::TupleField, ast::RecordField>>>>;
 
-    #[salsa::invoke(AttrsWithOwner::attrs_query)]
+    #[ra_salsa::invoke(AttrsWithOwner::attrs_query)]
     fn attrs(&self, def: AttrDefId) -> Attrs;
 
-    #[salsa::transparent]
-    #[salsa::invoke(lang_item::lang_attr)]
+    #[ra_salsa::transparent]
+    #[ra_salsa::invoke(lang_item::lang_attr)]
     fn lang_attr(&self, def: AttrDefId) -> Option<LangItem>;
 
     // endregion:attrs
 
-    #[salsa::invoke(LangItems::lang_item_query)]
+    #[ra_salsa::invoke(LangItems::lang_item_query)]
     fn lang_item(&self, start_crate: CrateId, item: LangItem) -> Option<LangItemTarget>;
 
-    #[salsa::invoke(ImportMap::import_map_query)]
+    #[ra_salsa::invoke(ImportMap::import_map_query)]
     fn import_map(&self, krate: CrateId) -> Arc<ImportMap>;
 
     // region:visibilities
 
-    #[salsa::invoke(visibility::field_visibilities_query)]
+    #[ra_salsa::invoke(visibility::field_visibilities_query)]
     fn field_visibilities(&self, var: VariantId) -> Arc<ArenaMap<LocalFieldId, Visibility>>;
 
     // FIXME: unify function_visibility and const_visibility?
-    #[salsa::invoke(visibility::function_visibility_query)]
+    #[ra_salsa::invoke(visibility::function_visibility_query)]
     fn function_visibility(&self, def: FunctionId) -> Visibility;
 
-    #[salsa::invoke(visibility::const_visibility_query)]
+    #[ra_salsa::invoke(visibility::const_visibility_query)]
     fn const_visibility(&self, def: ConstId) -> Visibility;
 
     // endregion:visibilities
 
-    #[salsa::invoke(LangItems::crate_lang_items_query)]
+    #[ra_salsa::invoke(LangItems::crate_lang_items_query)]
     fn crate_lang_items(&self, krate: CrateId) -> Option<Arc<LangItems>>;
 
-    #[salsa::invoke(crate::lang_item::notable_traits_in_deps)]
+    #[ra_salsa::invoke(crate::lang_item::notable_traits_in_deps)]
     fn notable_traits_in_deps(&self, krate: CrateId) -> Arc<[Arc<[TraitId]>]>;
-    #[salsa::invoke(crate::lang_item::crate_notable_traits)]
+    #[ra_salsa::invoke(crate::lang_item::crate_notable_traits)]
     fn crate_notable_traits(&self, krate: CrateId) -> Option<Arc<[TraitId]>>;
 
     fn crate_supports_no_std(&self, crate_id: CrateId) -> bool;
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/lib.rs b/src/tools/rust-analyzer/crates/hir-def/src/lib.rs
index 0213bd904b6..157c9ef0805 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/lib.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/lib.rs
@@ -71,7 +71,7 @@ use std::{
 
 use base_db::{
     impl_intern_key,
-    salsa::{self, InternValueTrivial},
+    ra_salsa::{self, InternValueTrivial},
     CrateId,
 };
 use hir_expand::{
@@ -206,85 +206,85 @@ macro_rules! impl_loc {
 }
 
 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
-pub struct FunctionId(salsa::InternId);
+pub struct FunctionId(ra_salsa::InternId);
 type FunctionLoc = AssocItemLoc<Function>;
 impl_intern!(FunctionId, FunctionLoc, intern_function, lookup_intern_function);
 impl_loc!(FunctionLoc, id: Function, container: ItemContainerId);
 
 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
-pub struct StructId(salsa::InternId);
+pub struct StructId(ra_salsa::InternId);
 type StructLoc = ItemLoc<Struct>;
 impl_intern!(StructId, StructLoc, intern_struct, lookup_intern_struct);
 impl_loc!(StructLoc, id: Struct, container: ModuleId);
 
 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
-pub struct UnionId(salsa::InternId);
+pub struct UnionId(ra_salsa::InternId);
 pub type UnionLoc = ItemLoc<Union>;
 impl_intern!(UnionId, UnionLoc, intern_union, lookup_intern_union);
 impl_loc!(UnionLoc, id: Union, container: ModuleId);
 
 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
-pub struct EnumId(salsa::InternId);
+pub struct EnumId(ra_salsa::InternId);
 pub type EnumLoc = ItemLoc<Enum>;
 impl_intern!(EnumId, EnumLoc, intern_enum, lookup_intern_enum);
 impl_loc!(EnumLoc, id: Enum, container: ModuleId);
 
 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
-pub struct ConstId(salsa::InternId);
+pub struct ConstId(ra_salsa::InternId);
 type ConstLoc = AssocItemLoc<Const>;
 impl_intern!(ConstId, ConstLoc, intern_const, lookup_intern_const);
 impl_loc!(ConstLoc, id: Const, container: ItemContainerId);
 
 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
-pub struct StaticId(salsa::InternId);
+pub struct StaticId(ra_salsa::InternId);
 pub type StaticLoc = AssocItemLoc<Static>;
 impl_intern!(StaticId, StaticLoc, intern_static, lookup_intern_static);
 impl_loc!(StaticLoc, id: Static, container: ItemContainerId);
 
 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
-pub struct TraitId(salsa::InternId);
+pub struct TraitId(ra_salsa::InternId);
 pub type TraitLoc = ItemLoc<Trait>;
 impl_intern!(TraitId, TraitLoc, intern_trait, lookup_intern_trait);
 impl_loc!(TraitLoc, id: Trait, container: ModuleId);
 
 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
-pub struct TraitAliasId(salsa::InternId);
+pub struct TraitAliasId(ra_salsa::InternId);
 pub type TraitAliasLoc = ItemLoc<TraitAlias>;
 impl_intern!(TraitAliasId, TraitAliasLoc, intern_trait_alias, lookup_intern_trait_alias);
 impl_loc!(TraitAliasLoc, id: TraitAlias, container: ModuleId);
 
 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
-pub struct TypeAliasId(salsa::InternId);
+pub struct TypeAliasId(ra_salsa::InternId);
 type TypeAliasLoc = AssocItemLoc<TypeAlias>;
 impl_intern!(TypeAliasId, TypeAliasLoc, intern_type_alias, lookup_intern_type_alias);
 impl_loc!(TypeAliasLoc, id: TypeAlias, container: ItemContainerId);
 
 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Ord, PartialOrd)]
-pub struct ImplId(salsa::InternId);
+pub struct ImplId(ra_salsa::InternId);
 type ImplLoc = ItemLoc<Impl>;
 impl_intern!(ImplId, ImplLoc, intern_impl, lookup_intern_impl);
 impl_loc!(ImplLoc, id: Impl, container: ModuleId);
 
 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Ord, PartialOrd)]
-pub struct UseId(salsa::InternId);
+pub struct UseId(ra_salsa::InternId);
 type UseLoc = ItemLoc<Use>;
 impl_intern!(UseId, UseLoc, intern_use, lookup_intern_use);
 impl_loc!(UseLoc, id: Use, container: ModuleId);
 
 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Ord, PartialOrd)]
-pub struct ExternCrateId(salsa::InternId);
+pub struct ExternCrateId(ra_salsa::InternId);
 type ExternCrateLoc = ItemLoc<ExternCrate>;
 impl_intern!(ExternCrateId, ExternCrateLoc, intern_extern_crate, lookup_intern_extern_crate);
 impl_loc!(ExternCrateLoc, id: ExternCrate, container: ModuleId);
 
 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Ord, PartialOrd)]
-pub struct ExternBlockId(salsa::InternId);
+pub struct ExternBlockId(ra_salsa::InternId);
 type ExternBlockLoc = ItemLoc<ExternBlock>;
 impl_intern!(ExternBlockId, ExternBlockLoc, intern_extern_block, lookup_intern_extern_block);
 impl_loc!(ExternBlockLoc, id: ExternBlock, container: ModuleId);
 
 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
-pub struct EnumVariantId(salsa::InternId);
+pub struct EnumVariantId(ra_salsa::InternId);
 
 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
 pub struct EnumVariantLoc {
@@ -296,7 +296,7 @@ impl_intern!(EnumVariantId, EnumVariantLoc, intern_enum_variant, lookup_intern_e
 impl_loc!(EnumVariantLoc, id: Variant, parent: EnumId);
 
 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Ord, PartialOrd)]
-pub struct Macro2Id(salsa::InternId);
+pub struct Macro2Id(ra_salsa::InternId);
 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
 pub struct Macro2Loc {
     pub container: ModuleId,
@@ -309,7 +309,7 @@ impl_intern!(Macro2Id, Macro2Loc, intern_macro2, lookup_intern_macro2);
 impl_loc!(Macro2Loc, id: Macro2, container: ModuleId);
 
 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Ord, PartialOrd)]
-pub struct MacroRulesId(salsa::InternId);
+pub struct MacroRulesId(ra_salsa::InternId);
 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
 pub struct MacroRulesLoc {
     pub container: ModuleId,
@@ -338,7 +338,7 @@ pub enum MacroExpander {
     BuiltInEager(EagerExpander),
 }
 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Ord, PartialOrd)]
-pub struct ProcMacroId(salsa::InternId);
+pub struct ProcMacroId(ra_salsa::InternId);
 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
 pub struct ProcMacroLoc {
     pub container: CrateRootModuleId,
@@ -351,7 +351,7 @@ impl_intern!(ProcMacroId, ProcMacroLoc, intern_proc_macro, lookup_intern_proc_ma
 impl_loc!(ProcMacroLoc, id: Function, container: CrateRootModuleId);
 
 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Ord, PartialOrd)]
-pub struct BlockId(salsa::InternId);
+pub struct BlockId(ra_salsa::InternId);
 #[derive(Debug, Hash, PartialEq, Eq, Clone)]
 pub struct BlockLoc {
     ast_id: AstId<ast::BlockExpr>,
@@ -363,7 +363,7 @@ impl_intern!(BlockId, BlockLoc, intern_block, lookup_intern_block);
 /// Id of the anonymous const block expression and patterns. This is very similar to `ClosureId` and
 /// shouldn't be a `DefWithBodyId` since its type inference is dependent on its parent.
 #[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)]
-pub struct ConstBlockId(salsa::InternId);
+pub struct ConstBlockId(ra_salsa::InternId);
 impl_intern!(ConstBlockId, ConstBlockLoc, intern_anonymous_const, lookup_intern_anonymous_const);
 
 #[derive(Debug, Hash, PartialEq, Eq, Clone)]
@@ -803,7 +803,7 @@ impl Clone for Box<dyn OpaqueInternableThing> {
 /// length (like `[u8; 2 + 2]`). These constants are body owner and are a variant of `DefWithBodyId`. These
 /// are not called `AnonymousConstId` to prevent confusion with [`ConstBlockId`].
 #[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)]
-pub struct InTypeConstId(salsa::InternId);
+pub struct InTypeConstId(ra_salsa::InternId);
 impl_intern!(InTypeConstId, InTypeConstLoc, intern_in_type_const, lookup_intern_in_type_const);
 
 // We would like to set `derive(PartialEq)`
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/nameres/collector.rs b/src/tools/rust-analyzer/crates/hir-def/src/nameres/collector.rs
index 7f1d19719da..22b9c2b4e37 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/nameres/collector.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/nameres/collector.rs
@@ -30,8 +30,8 @@ use crate::{
     db::DefDatabase,
     item_scope::{ImportId, ImportOrExternCrate, ImportType, PerNsGlobImports},
     item_tree::{
-        self, AttrOwner, ExternCrate, FieldsShape, FileItemTreeId, ImportKind, ItemTree,
-        ItemTreeId, ItemTreeNode, Macro2, MacroCall, MacroRules, Mod, ModItem, ModKind, TreeId,
+        self, AttrOwner, FieldsShape, FileItemTreeId, ImportKind, ItemTree, ItemTreeId,
+        ItemTreeNode, Macro2, MacroCall, MacroRules, Mod, ModItem, ModKind, TreeId,
     },
     macro_call_as_call_id, macro_call_as_call_id_with_eager,
     nameres::{
@@ -93,6 +93,7 @@ pub(super) fn collect_defs(db: &dyn DefDatabase, def_map: DefMap, tree_id: TreeI
         proc_macros,
         from_glob_import: Default::default(),
         skip_attrs: Default::default(),
+        unresolved_extern_crates: Default::default(),
         is_proc_macro: krate.is_proc_macro,
     };
     if tree_id.is_block() {
@@ -126,9 +127,11 @@ impl PartialResolvedImport {
 }
 
 #[derive(Clone, Debug, Eq, PartialEq)]
-enum ImportSource {
-    Use { use_tree: Idx<ast::UseTree>, id: UseId, is_prelude: bool, kind: ImportKind },
-    ExternCrate { id: ExternCrateId },
+struct ImportSource {
+    use_tree: Idx<ast::UseTree>,
+    id: UseId,
+    is_prelude: bool,
+    kind: ImportKind,
 }
 
 #[derive(Debug, Eq, PartialEq)]
@@ -154,25 +157,10 @@ impl Import {
                 path,
                 alias,
                 visibility: visibility.clone(),
-                source: ImportSource::Use { use_tree: idx, id, is_prelude, kind },
+                source: ImportSource { use_tree: idx, id, is_prelude, kind },
             });
         });
     }
-
-    fn from_extern_crate(
-        tree: &ItemTree,
-        item_tree_id: ItemTreeId<item_tree::ExternCrate>,
-        id: ExternCrateId,
-    ) -> Self {
-        let it = &tree[item_tree_id.value];
-        let visibility = &tree[it.visibility];
-        Self {
-            path: ModPath::from_segments(PathKind::Plain, iter::once(it.name.clone())),
-            alias: it.alias.clone(),
-            visibility: visibility.clone(),
-            source: ImportSource::ExternCrate { id },
-        }
-    }
 }
 
 #[derive(Debug, Eq, PartialEq)]
@@ -218,11 +206,16 @@ enum MacroDirectiveKind {
 struct DefCollector<'a> {
     db: &'a dyn DefDatabase,
     def_map: DefMap,
+    // The dependencies of the current crate, including optional deps like `test`.
     deps: FxHashMap<Name, Dependency>,
     glob_imports: FxHashMap<LocalModuleId, Vec<(LocalModuleId, Visibility, UseId)>>,
     unresolved_imports: Vec<ImportDirective>,
     indeterminate_imports: Vec<(ImportDirective, PerNs)>,
     unresolved_macros: Vec<MacroDirective>,
+    // We'd like to avoid emitting a diagnostics avalanche when some `extern crate` doesn't
+    // resolve. When we emit diagnostics for unresolved imports, we only do so if the import
+    // doesn't start with an unresolved crate's name.
+    unresolved_extern_crates: FxHashSet<Name>,
     mod_dirs: FxHashMap<LocalModuleId, ModDir>,
     cfg_options: &'a CfgOptions,
     /// List of procedural macros defined by this crate. This is read from the dynamic library
@@ -310,6 +303,7 @@ impl DefCollector<'_> {
         }
 
         for (name, dep) in &self.deps {
+            // Add all
             if dep.is_prelude() {
                 // This is a bit confusing but the gist is that `no_core` and `no_std` remove the
                 // sysroot dependence on `core` and `std` respectively. Our `CrateGraph` is eagerly
@@ -329,6 +323,7 @@ impl DefCollector<'_> {
                 if skip {
                     continue;
                 }
+
                 crate_data
                     .extern_prelude
                     .insert(name.clone(), (CrateRootModuleId { krate: dep.crate_id }, None));
@@ -788,61 +783,31 @@ impl DefCollector<'_> {
         let _p = tracing::info_span!("resolve_import", import_path = %import.path.display(self.db.upcast(), Edition::LATEST))
             .entered();
         tracing::debug!("resolving import: {:?} ({:?})", import, self.def_map.data.edition);
-        match import.source {
-            ImportSource::ExternCrate { .. } => {
-                let name = import
-                    .path
-                    .as_ident()
-                    .expect("extern crate should have been desugared to one-element path");
-
-                let res = self.resolve_extern_crate(name);
-
-                match res {
-                    Some(res) => PartialResolvedImport::Resolved(PerNs::types(
-                        res.into(),
-                        Visibility::Public,
-                        None,
-                    )),
-                    None => PartialResolvedImport::Unresolved,
-                }
-            }
-            ImportSource::Use { .. } => {
-                let res = self.def_map.resolve_path_fp_with_macro(
-                    self.db,
-                    ResolveMode::Import,
-                    module_id,
-                    &import.path,
-                    BuiltinShadowMode::Module,
-                    None, // An import may resolve to any kind of macro.
-                );
-
-                let def = res.resolved_def;
-                if res.reached_fixedpoint == ReachedFixedPoint::No || def.is_none() {
-                    return PartialResolvedImport::Unresolved;
-                }
+        let res = self.def_map.resolve_path_fp_with_macro(
+            self.db,
+            ResolveMode::Import,
+            module_id,
+            &import.path,
+            BuiltinShadowMode::Module,
+            None, // An import may resolve to any kind of macro.
+        );
 
-                if res.from_differing_crate {
-                    return PartialResolvedImport::Resolved(
-                        def.filter_visibility(|v| matches!(v, Visibility::Public)),
-                    );
-                }
+        let def = res.resolved_def;
+        if res.reached_fixedpoint == ReachedFixedPoint::No || def.is_none() {
+            return PartialResolvedImport::Unresolved;
+        }
 
-                // Check whether all namespaces are resolved.
-                if def.is_full() {
-                    PartialResolvedImport::Resolved(def)
-                } else {
-                    PartialResolvedImport::Indeterminate(def)
-                }
-            }
+        if res.from_differing_crate {
+            return PartialResolvedImport::Resolved(
+                def.filter_visibility(|v| matches!(v, Visibility::Public)),
+            );
         }
-    }
 
-    fn resolve_extern_crate(&self, name: &Name) -> Option<CrateRootModuleId> {
-        if *name == sym::self_.clone() {
-            cov_mark::hit!(extern_crate_self_as);
-            Some(self.def_map.crate_root())
+        // Check whether all namespaces are resolved.
+        if def.is_full() {
+            PartialResolvedImport::Resolved(def)
         } else {
-            self.deps.get(name).map(|dep| CrateRootModuleId { krate: dep.crate_id })
+            PartialResolvedImport::Indeterminate(def)
         }
     }
 
@@ -858,8 +823,12 @@ impl DefCollector<'_> {
             .unwrap_or(Visibility::Public);
 
         match import.source {
-            ImportSource::ExternCrate { .. }
-            | ImportSource::Use { kind: ImportKind::Plain | ImportKind::TypeOnly, .. } => {
+            ImportSource {
+                kind: kind @ (ImportKind::Plain | ImportKind::TypeOnly),
+                id,
+                use_tree,
+                ..
+            } => {
                 let name = match &import.alias {
                     Some(ImportAlias::Alias(name)) => Some(name),
                     Some(ImportAlias::Underscore) => None,
@@ -872,40 +841,20 @@ impl DefCollector<'_> {
                     },
                 };
 
-                let imp = match import.source {
-                    // extern crates in the crate root are special-cased to insert entries into the extern prelude: rust-lang/rust#54658
-                    ImportSource::ExternCrate { id, .. } => {
-                        if self.def_map.block.is_none() && module_id == DefMap::ROOT {
-                            if let (Some(ModuleDefId::ModuleId(def)), Some(name)) =
-                                (def.take_types(), name)
-                            {
-                                if let Ok(def) = def.try_into() {
-                                    Arc::get_mut(&mut self.def_map.data)
-                                        .unwrap()
-                                        .extern_prelude
-                                        .insert(name.clone(), (def, Some(id)));
-                                }
-                            }
-                        }
-                        ImportType::ExternCrate(id)
-                    }
-                    ImportSource::Use { kind, id, use_tree, .. } => {
-                        if kind == ImportKind::TypeOnly {
-                            def.values = None;
-                            def.macros = None;
-                        }
-                        ImportType::Import(ImportId { import: id, idx: use_tree })
-                    }
-                };
+                if kind == ImportKind::TypeOnly {
+                    def.values = None;
+                    def.macros = None;
+                }
+                let imp = ImportType::Import(ImportId { import: id, idx: use_tree });
                 tracing::debug!("resolved import {:?} ({:?}) to {:?}", name, import, def);
 
                 self.update(module_id, &[(name.cloned(), def)], vis, Some(imp));
             }
-            ImportSource::Use { kind: ImportKind::Glob, id, .. } => {
+            ImportSource { kind: ImportKind::Glob, id, is_prelude, .. } => {
                 tracing::debug!("glob import: {:?}", import);
                 match def.take_types() {
                     Some(ModuleDefId::ModuleId(m)) => {
-                        if let ImportSource::Use { id, is_prelude: true, .. } = import.source {
+                        if is_prelude {
                             // Note: This dodgily overrides the injected prelude. The rustc
                             // implementation seems to work the same though.
                             cov_mark::hit!(std_prelude);
@@ -1323,7 +1272,7 @@ impl DefCollector<'_> {
                         _ => return Resolved::No,
                     };
 
-                    // Skip #[test]/#[bench] expansion, which would merely result in more memory usage
+                    // Skip #[test]/#[bench]/#[test_case] expansion, which would merely result in more memory usage
                     // due to duplicating functions into macro expansions, but only if `cfg(test)` is active,
                     // otherwise they are expanded to nothing and this can impact e.g. diagnostics (due to things
                     // being cfg'ed out).
@@ -1332,7 +1281,7 @@ impl DefCollector<'_> {
                     if matches!(
                         def.kind,
                         MacroDefKind::BuiltInAttr(_, expander)
-                        if expander.is_test() || expander.is_bench()
+                        if expander.is_test() || expander.is_bench() || expander.is_test_case()
                     ) {
                         let test_is_active =
                             self.cfg_options.check_atom(&CfgAtom::Flag(sym::test.clone()));
@@ -1560,45 +1509,29 @@ impl DefCollector<'_> {
         }
 
         // Emit diagnostics for all remaining unresolved imports.
-
-        // We'd like to avoid emitting a diagnostics avalanche when some `extern crate` doesn't
-        // resolve. We first emit diagnostics for unresolved extern crates and collect the missing
-        // crate names. Then we emit diagnostics for unresolved imports, but only if the import
-        // doesn't start with an unresolved crate's name. Due to renaming and reexports, this is a
-        // heuristic, but it works in practice.
-        let mut diagnosed_extern_crates = FxHashSet::default();
-        for directive in &self.unresolved_imports {
-            if let ImportSource::ExternCrate { id } = directive.import.source {
-                let item_tree_id = id.lookup(self.db).id;
-                let item_tree = item_tree_id.item_tree(self.db);
-                let extern_crate = &item_tree[item_tree_id.value];
-
-                diagnosed_extern_crates.insert(extern_crate.name.clone());
-
-                self.def_map.diagnostics.push(DefDiagnostic::unresolved_extern_crate(
-                    directive.module_id,
-                    InFile::new(item_tree_id.file_id(), extern_crate.ast_id),
-                ));
-            }
-        }
-
-        for directive in &self.unresolved_imports {
-            if let ImportSource::Use { use_tree, id, is_prelude: _, kind: _ } =
-                directive.import.source
-            {
-                if matches!(
-                    (directive.import.path.segments().first(), &directive.import.path.kind),
-                    (Some(krate), PathKind::Plain | PathKind::Abs) if diagnosed_extern_crates.contains(krate)
-                ) {
-                    continue;
-                }
-                let item_tree_id = id.lookup(self.db).id;
-                self.def_map.diagnostics.push(DefDiagnostic::unresolved_import(
-                    directive.module_id,
-                    item_tree_id,
-                    use_tree,
-                ));
+        for import in &self.unresolved_imports {
+            let &ImportDirective {
+                module_id,
+                import:
+                    Import {
+                        ref path,
+                        source: ImportSource { use_tree, id, is_prelude: _, kind: _ },
+                        ..
+                    },
+                ..
+            } = import;
+            if matches!(
+                (path.segments().first(), &path.kind),
+                (Some(krate), PathKind::Plain | PathKind::Abs) if self.unresolved_extern_crates.contains(krate)
+            ) {
+                continue;
             }
+            let item_tree_id = id.lookup(self.db).id;
+            self.def_map.diagnostics.push(DefDiagnostic::unresolved_import(
+                module_id,
+                item_tree_id,
+                use_tree,
+            ));
         }
 
         self.def_map
@@ -1623,7 +1556,8 @@ impl ModCollector<'_, '_> {
 
     fn collect(&mut self, items: &[ModItem], container: ItemContainerId) {
         let krate = self.def_collector.def_map.krate;
-        let is_crate_root = self.module_id == DefMap::ROOT;
+        let is_crate_root =
+            self.module_id == DefMap::ROOT && self.def_collector.def_map.block.is_none();
 
         // Note: don't assert that inserted value is fresh: it's simply not true
         // for macros.
@@ -1632,10 +1566,7 @@ impl ModCollector<'_, '_> {
         // Prelude module is always considered to be `#[macro_use]`.
         if let Some((prelude_module, _use)) = self.def_collector.def_map.prelude {
             // Don't insert macros from the prelude into blocks, as they can be shadowed by other macros.
-            if prelude_module.krate != krate
-                && is_crate_root
-                && self.def_collector.def_map.block.is_none()
-            {
+            if prelude_module.krate != krate && is_crate_root {
                 cov_mark::hit!(prelude_is_macro_use);
                 self.def_collector.import_macros_from_extern_crate(
                     prelude_module.krate,
@@ -1709,26 +1640,73 @@ impl ModCollector<'_, '_> {
                         id: ItemTreeId::new(self.tree_id, item_tree_id),
                     }
                     .intern(db);
-                    if is_crate_root {
-                        self.process_macro_use_extern_crate(
-                            item_tree_id,
-                            id,
-                            attrs.by_key(&sym::macro_use).attrs(),
+                    def_map.modules[self.module_id].scope.define_extern_crate_decl(id);
+
+                    let item_tree::ExternCrate { name, visibility, alias, ast_id } =
+                        &self.item_tree[item_tree_id];
+
+                    let is_self = *name == sym::self_;
+                    let resolved = if is_self {
+                        cov_mark::hit!(extern_crate_self_as);
+                        Some(def_map.crate_root())
+                    } else {
+                        self.def_collector
+                            .deps
+                            .get(name)
+                            .map(|dep| CrateRootModuleId { krate: dep.crate_id })
+                    };
+
+                    let name = match alias {
+                        Some(ImportAlias::Alias(name)) => Some(name),
+                        Some(ImportAlias::Underscore) => None,
+                        None => Some(name),
+                    };
+
+                    if let Some(resolved) = resolved {
+                        let vis = resolve_vis(def_map, &self.item_tree[*visibility]);
+
+                        if is_crate_root {
+                            // extern crates in the crate root are special-cased to insert entries into the extern prelude: rust-lang/rust#54658
+                            if let Some(name) = name {
+                                Arc::get_mut(&mut def_map.data)
+                                    .unwrap()
+                                    .extern_prelude
+                                    .insert(name.clone(), (resolved, Some(id)));
+                            }
+                            // they also allow `#[macro_use]`
+                            if !is_self {
+                                self.process_macro_use_extern_crate(
+                                    id,
+                                    attrs.by_key(&sym::macro_use).attrs(),
+                                    resolved.krate,
+                                );
+                            }
+                        }
+
+                        self.def_collector.update(
+                            module_id,
+                            &[(
+                                name.cloned(),
+                                PerNs::types(
+                                    resolved.into(),
+                                    vis,
+                                    Some(ImportOrExternCrate::ExternCrate(id)),
+                                ),
+                            )],
+                            vis,
+                            Some(ImportType::ExternCrate(id)),
+                        );
+                    } else {
+                        if let Some(name) = name {
+                            self.def_collector.unresolved_extern_crates.insert(name.clone());
+                        }
+                        self.def_collector.def_map.diagnostics.push(
+                            DefDiagnostic::unresolved_extern_crate(
+                                module_id,
+                                InFile::new(self.file_id(), *ast_id),
+                            ),
                         );
                     }
-
-                    self.def_collector.def_map.modules[self.module_id]
-                        .scope
-                        .define_extern_crate_decl(id);
-                    self.def_collector.unresolved_imports.push(ImportDirective {
-                        module_id: self.module_id,
-                        import: Import::from_extern_crate(
-                            self.item_tree,
-                            ItemTreeId::new(self.tree_id, item_tree_id),
-                            id,
-                        ),
-                        status: PartialResolvedImport::Unresolved,
-                    })
                 }
                 ModItem::ExternBlock(block) => self.collect(
                     &self.item_tree[block].children,
@@ -1939,27 +1917,15 @@ impl ModCollector<'_, '_> {
 
     fn process_macro_use_extern_crate<'a>(
         &mut self,
-        extern_crate: FileItemTreeId<ExternCrate>,
         extern_crate_id: ExternCrateId,
         macro_use_attrs: impl Iterator<Item = &'a Attr>,
+        target_crate: CrateId,
     ) {
-        let db = self.def_collector.db;
-
-        let target_crate =
-            match self.def_collector.resolve_extern_crate(&self.item_tree[extern_crate].name) {
-                Some(m) if m.krate == self.def_collector.def_map.krate => {
-                    cov_mark::hit!(ignore_macro_use_extern_crate_self);
-                    return;
-                }
-                Some(m) => m.krate,
-                None => return,
-            };
-
         cov_mark::hit!(macro_rules_from_other_crates_are_visible_with_macro_use);
-
         let mut single_imports = Vec::new();
         for attr in macro_use_attrs {
-            let Some(paths) = attr.parse_path_comma_token_tree(db.upcast()) else {
+            let Some(paths) = attr.parse_path_comma_token_tree(self.def_collector.db.upcast())
+            else {
                 // `#[macro_use]` (without any paths) found, forget collected names and just import
                 // all visible macros.
                 self.def_collector.import_macros_from_extern_crate(
@@ -2523,6 +2489,7 @@ mod tests {
             from_glob_import: Default::default(),
             skip_attrs: Default::default(),
             is_proc_macro: false,
+            unresolved_extern_crates: Default::default(),
         };
         collector.seed_with_top_level();
         collector.collect();
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/nameres/tests.rs b/src/tools/rust-analyzer/crates/hir-def/src/nameres/tests.rs
index 17e82dc16c4..7b02a89e5de 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/nameres/tests.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/nameres/tests.rs
@@ -416,7 +416,6 @@ pub struct Arc;
 
 #[test]
 fn macro_use_extern_crate_self() {
-    cov_mark::check!(ignore_macro_use_extern_crate_self);
     check(
         r#"
 //- /main.rs crate:main
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/test_db.rs b/src/tools/rust-analyzer/crates/hir-def/src/test_db.rs
index df9dec69d46..4db21eb46bd 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/test_db.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/test_db.rs
@@ -3,7 +3,7 @@
 use std::{fmt, panic, sync::Mutex};
 
 use base_db::{
-    salsa::{self, Durability},
+    ra_salsa::{self, Durability},
     AnchoredPath, CrateId, FileLoader, FileLoaderDelegate, SourceDatabase, Upcast,
 };
 use hir_expand::{db::ExpandDatabase, files::FilePosition, InFile};
@@ -18,7 +18,7 @@ use crate::{
     LocalModuleId, Lookup, ModuleDefId, ModuleId,
 };
 
-#[salsa::database(
+#[ra_salsa::database(
     base_db::SourceRootDatabaseStorage,
     base_db::SourceDatabaseStorage,
     hir_expand::db::ExpandDatabaseStorage,
@@ -26,8 +26,8 @@ use crate::{
     crate::db::DefDatabaseStorage
 )]
 pub(crate) struct TestDB {
-    storage: salsa::Storage<TestDB>,
-    events: Mutex<Option<Vec<salsa::Event>>>,
+    storage: ra_salsa::Storage<TestDB>,
+    events: Mutex<Option<Vec<ra_salsa::Event>>>,
 }
 
 impl Default for TestDB {
@@ -51,8 +51,8 @@ impl Upcast<dyn DefDatabase> for TestDB {
     }
 }
 
-impl salsa::Database for TestDB {
-    fn salsa_event(&self, event: salsa::Event) {
+impl ra_salsa::Database for TestDB {
+    fn salsa_event(&self, event: ra_salsa::Event) {
         let mut events = self.events.lock().unwrap();
         if let Some(events) = &mut *events {
             events.push(event);
@@ -215,7 +215,7 @@ impl TestDB {
         None
     }
 
-    pub(crate) fn log(&self, f: impl FnOnce()) -> Vec<salsa::Event> {
+    pub(crate) fn log(&self, f: impl FnOnce()) -> Vec<ra_salsa::Event> {
         *self.events.lock().unwrap() = Some(Vec::new());
         f();
         self.events.lock().unwrap().take().unwrap()
@@ -228,7 +228,7 @@ impl TestDB {
             .filter_map(|e| match e.kind {
                 // This is pretty horrible, but `Debug` is the only way to inspect
                 // QueryDescriptor at the moment.
-                salsa::EventKind::WillExecute { database_key } => {
+                ra_salsa::EventKind::WillExecute { database_key } => {
                     Some(format!("{:?}", database_key.debug(self)))
                 }
                 _ => None,
diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/builtin/attr_macro.rs b/src/tools/rust-analyzer/crates/hir-expand/src/builtin/attr_macro.rs
index 2a8691b461c..74effd2fb16 100644
--- a/src/tools/rust-analyzer/crates/hir-expand/src/builtin/attr_macro.rs
+++ b/src/tools/rust-analyzer/crates/hir-expand/src/builtin/attr_macro.rs
@@ -51,6 +51,9 @@ impl BuiltinAttrExpander {
     pub fn is_bench(self) -> bool {
         matches!(self, BuiltinAttrExpander::Bench)
     }
+    pub fn is_test_case(self) -> bool {
+        matches!(self, BuiltinAttrExpander::TestCase)
+    }
 }
 
 register_builtin! {
diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/change.rs b/src/tools/rust-analyzer/crates/hir-expand/src/change.rs
index de3a7b9f561..1fdf251ba52 100644
--- a/src/tools/rust-analyzer/crates/hir-expand/src/change.rs
+++ b/src/tools/rust-analyzer/crates/hir-expand/src/change.rs
@@ -1,7 +1,7 @@
 //! Defines a unit of change that can applied to the database to get the next
 //! state. Changes are transactional.
 use base_db::{
-    salsa::Durability, CrateGraph, CrateId, CrateWorkspaceData, FileChange, SourceRoot,
+    ra_salsa::Durability, CrateGraph, CrateId, CrateWorkspaceData, FileChange, SourceRoot,
     SourceRootDatabase,
 };
 use rustc_hash::FxHashMap;
diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/db.rs b/src/tools/rust-analyzer/crates/hir-expand/src/db.rs
index 484a8662eb1..d412bf4eee5 100644
--- a/src/tools/rust-analyzer/crates/hir-expand/src/db.rs
+++ b/src/tools/rust-analyzer/crates/hir-expand/src/db.rs
@@ -1,6 +1,6 @@
 //! Defines database & queries for macro expansion.
 
-use base_db::{salsa, CrateId, SourceDatabase};
+use base_db::{ra_salsa, CrateId, SourceDatabase};
 use either::Either;
 use limit::Limit;
 use mbe::MatchedArmIndex;
@@ -53,32 +53,32 @@ pub enum TokenExpander {
     ProcMacro(CustomProcMacroExpander),
 }
 
-#[salsa::query_group(ExpandDatabaseStorage)]
+#[ra_salsa::query_group(ExpandDatabaseStorage)]
 pub trait ExpandDatabase: SourceDatabase {
     /// The proc macros.
-    #[salsa::input]
+    #[ra_salsa::input]
     fn proc_macros(&self) -> Arc<ProcMacros>;
 
     fn ast_id_map(&self, file_id: HirFileId) -> Arc<AstIdMap>;
 
     /// Main public API -- parses a hir file, not caring whether it's a real
     /// file or a macro expansion.
-    #[salsa::transparent]
+    #[ra_salsa::transparent]
     fn parse_or_expand(&self, file_id: HirFileId) -> SyntaxNode;
     /// Implementation for the macro case.
-    #[salsa::lru]
+    #[ra_salsa::lru]
     fn parse_macro_expansion(
         &self,
         macro_file: MacroFileId,
     ) -> ExpandResult<(Parse<SyntaxNode>, Arc<ExpansionSpanMap>)>;
-    #[salsa::transparent]
-    #[salsa::invoke(SpanMap::new)]
+    #[ra_salsa::transparent]
+    #[ra_salsa::invoke(SpanMap::new)]
     fn span_map(&self, file_id: HirFileId) -> SpanMap;
 
-    #[salsa::transparent]
-    #[salsa::invoke(crate::span_map::expansion_span_map)]
+    #[ra_salsa::transparent]
+    #[ra_salsa::invoke(crate::span_map::expansion_span_map)]
     fn expansion_span_map(&self, file_id: MacroFileId) -> Arc<ExpansionSpanMap>;
-    #[salsa::invoke(crate::span_map::real_span_map)]
+    #[ra_salsa::invoke(crate::span_map::real_span_map)]
     fn real_span_map(&self, file_id: EditionedFileId) -> Arc<RealSpanMap>;
 
     /// Macro ids. That's probably the tricksiest bit in rust-analyzer, and the
@@ -86,15 +86,15 @@ pub trait ExpandDatabase: SourceDatabase {
     ///
     /// We encode macro definitions into ids of macro calls, this what allows us
     /// to be incremental.
-    #[salsa::interned]
+    #[ra_salsa::interned]
     fn intern_macro_call(&self, macro_call: MacroCallLoc) -> MacroCallId;
-    #[salsa::interned]
+    #[ra_salsa::interned]
     fn intern_syntax_context(&self, ctx: SyntaxContextData) -> SyntaxContextId;
 
-    #[salsa::transparent]
+    #[ra_salsa::transparent]
     fn setup_syntax_context_root(&self) -> ();
-    #[salsa::transparent]
-    #[salsa::invoke(crate::hygiene::dump_syntax_contexts)]
+    #[ra_salsa::transparent]
+    #[ra_salsa::invoke(crate::hygiene::dump_syntax_contexts)]
     fn dump_syntax_contexts(&self) -> String;
 
     /// Lowers syntactic macro call to a token tree representation. That's a firewall
@@ -102,18 +102,18 @@ pub trait ExpandDatabase: SourceDatabase {
     /// subtree.
     #[deprecated = "calling this is incorrect, call `macro_arg_considering_derives` instead"]
     fn macro_arg(&self, id: MacroCallId) -> MacroArgResult;
-    #[salsa::transparent]
+    #[ra_salsa::transparent]
     fn macro_arg_considering_derives(
         &self,
         id: MacroCallId,
         kind: &MacroCallKind,
     ) -> MacroArgResult;
     /// Fetches the expander for this macro.
-    #[salsa::transparent]
-    #[salsa::invoke(TokenExpander::macro_expander)]
+    #[ra_salsa::transparent]
+    #[ra_salsa::invoke(TokenExpander::macro_expander)]
     fn macro_expander(&self, id: MacroDefId) -> TokenExpander;
     /// Fetches (and compiles) the expander of this decl macro.
-    #[salsa::invoke(DeclarativeMacroExpander::expander)]
+    #[ra_salsa::invoke(DeclarativeMacroExpander::expander)]
     fn decl_macro_expander(
         &self,
         def_crate: CrateId,
@@ -135,7 +135,7 @@ pub trait ExpandDatabase: SourceDatabase {
         &self,
         macro_call: MacroCallId,
     ) -> Option<Arc<ExpandResult<Arc<[SyntaxError]>>>>;
-    #[salsa::transparent]
+    #[ra_salsa::transparent]
     fn syntax_context(&self, file: HirFileId) -> SyntaxContextId;
 }
 
diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/hygiene.rs b/src/tools/rust-analyzer/crates/hir-expand/src/hygiene.rs
index 5e1448f7950..a8191189157 100644
--- a/src/tools/rust-analyzer/crates/hir-expand/src/hygiene.rs
+++ b/src/tools/rust-analyzer/crates/hir-expand/src/hygiene.rs
@@ -97,7 +97,7 @@ fn apply_mark_internal(
     call_id: MacroCallId,
     transparency: Transparency,
 ) -> SyntaxContextId {
-    use base_db::salsa;
+    use base_db::ra_salsa;
 
     let call_id = Some(call_id);
 
@@ -107,7 +107,7 @@ fn apply_mark_internal(
 
     if transparency >= Transparency::Opaque {
         let parent = opaque;
-        opaque = salsa::plumbing::get_query_table::<InternSyntaxContextQuery>(db).get_or_insert(
+        opaque = ra_salsa::plumbing::get_query_table::<InternSyntaxContextQuery>(db).get_or_insert(
             (parent, call_id, transparency),
             |new_opaque| SyntaxContextData {
                 outer_expn: call_id,
@@ -122,7 +122,7 @@ fn apply_mark_internal(
     if transparency >= Transparency::SemiTransparent {
         let parent = opaque_and_semitransparent;
         opaque_and_semitransparent =
-            salsa::plumbing::get_query_table::<InternSyntaxContextQuery>(db).get_or_insert(
+            ra_salsa::plumbing::get_query_table::<InternSyntaxContextQuery>(db).get_or_insert(
                 (parent, call_id, transparency),
                 |new_opaque_and_semitransparent| SyntaxContextData {
                     outer_expn: call_id,
@@ -200,7 +200,7 @@ pub fn marks_rev(
 
 pub(crate) fn dump_syntax_contexts(db: &dyn ExpandDatabase) -> String {
     use crate::db::{InternMacroCallLookupQuery, InternSyntaxContextLookupQuery};
-    use base_db::salsa::debug::DebugQueryTable;
+    use base_db::ra_salsa::debug::DebugQueryTable;
 
     let mut s = String::from("Expansions:");
     let mut entries = InternMacroCallLookupQuery.in_db(db).entries::<Vec<_>>();
diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/lib.rs b/src/tools/rust-analyzer/crates/hir-expand/src/lib.rs
index 56cb5fd375c..5d5f72490d0 100644
--- a/src/tools/rust-analyzer/crates/hir-expand/src/lib.rs
+++ b/src/tools/rust-analyzer/crates/hir-expand/src/lib.rs
@@ -30,7 +30,7 @@ use triomphe::Arc;
 
 use std::hash::Hash;
 
-use base_db::{salsa::InternValueTrivial, CrateId};
+use base_db::{ra_salsa::InternValueTrivial, CrateId};
 use either::Either;
 use span::{
     Edition, EditionedFileId, ErasedFileAstId, FileAstId, HirFileIdRepr, Span, SpanAnchor,
diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/prettify_macro_expansion_.rs b/src/tools/rust-analyzer/crates/hir-expand/src/prettify_macro_expansion_.rs
index d928cafdefc..6ff7831fd81 100644
--- a/src/tools/rust-analyzer/crates/hir-expand/src/prettify_macro_expansion_.rs
+++ b/src/tools/rust-analyzer/crates/hir-expand/src/prettify_macro_expansion_.rs
@@ -15,11 +15,14 @@ pub fn prettify_macro_expansion(
     span_map: &ExpansionSpanMap,
     target_crate_id: CrateId,
 ) -> SyntaxNode {
+    // Because `syntax_bridge::prettify_macro_expansion::prettify_macro_expansion()` clones subtree for `syn`,
+    // that means it will be offsetted to the beginning.
+    let span_offset = syn.text_range().start();
     let crate_graph = db.crate_graph();
     let target_crate = &crate_graph[target_crate_id];
     let mut syntax_ctx_id_to_dollar_crate_replacement = FxHashMap::default();
     syntax_bridge::prettify_macro_expansion::prettify_macro_expansion(syn, &mut |dollar_crate| {
-        let ctx = span_map.span_at(dollar_crate.text_range().start()).ctx;
+        let ctx = span_map.span_at(dollar_crate.text_range().start() + span_offset).ctx;
         let replacement =
             syntax_ctx_id_to_dollar_crate_replacement.entry(ctx).or_insert_with(|| {
                 let ctx_data = db.lookup_intern_syntax_context(ctx);
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/consteval.rs b/src/tools/rust-analyzer/crates/hir-ty/src/consteval.rs
index 968a828e9df..e41058aac2a 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/consteval.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/consteval.rs
@@ -1,6 +1,6 @@
 //! Constant evaluation details
 
-use base_db::{salsa::Cycle, CrateId};
+use base_db::{ra_salsa::Cycle, CrateId};
 use chalk_ir::{cast::Cast, BoundVar, DebruijnIndex};
 use hir_def::{
     body::Body,
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/db.rs b/src/tools/rust-analyzer/crates/hir-ty/src/db.rs
index 5620d80adb5..3a3a05c369a 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/db.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/db.rs
@@ -5,7 +5,7 @@ use std::sync;
 
 use base_db::{
     impl_intern_key,
-    salsa::{self, InternValueTrivial},
+    ra_salsa::{self, InternValueTrivial},
     CrateId, Upcast,
 };
 use hir_def::{
@@ -30,22 +30,22 @@ use crate::{
 };
 use hir_expand::name::Name;
 
-#[salsa::query_group(HirDatabaseStorage)]
+#[ra_salsa::query_group(HirDatabaseStorage)]
 pub trait HirDatabase: DefDatabase + Upcast<dyn DefDatabase> {
-    #[salsa::invoke(crate::infer::infer_query)]
+    #[ra_salsa::invoke(crate::infer::infer_query)]
     fn infer(&self, def: DefWithBodyId) -> Arc<InferenceResult>;
 
     // region:mir
 
-    #[salsa::invoke(crate::mir::mir_body_query)]
-    #[salsa::cycle(crate::mir::mir_body_recover)]
+    #[ra_salsa::invoke(crate::mir::mir_body_query)]
+    #[ra_salsa::cycle(crate::mir::mir_body_recover)]
     fn mir_body(&self, def: DefWithBodyId) -> Result<Arc<MirBody>, MirLowerError>;
 
-    #[salsa::invoke(crate::mir::mir_body_for_closure_query)]
+    #[ra_salsa::invoke(crate::mir::mir_body_for_closure_query)]
     fn mir_body_for_closure(&self, def: ClosureId) -> Result<Arc<MirBody>, MirLowerError>;
 
-    #[salsa::invoke(crate::mir::monomorphized_mir_body_query)]
-    #[salsa::cycle(crate::mir::monomorphized_mir_body_recover)]
+    #[ra_salsa::invoke(crate::mir::monomorphized_mir_body_query)]
+    #[ra_salsa::cycle(crate::mir::monomorphized_mir_body_recover)]
     fn monomorphized_mir_body(
         &self,
         def: DefWithBodyId,
@@ -53,7 +53,7 @@ pub trait HirDatabase: DefDatabase + Upcast<dyn DefDatabase> {
         env: Arc<TraitEnvironment>,
     ) -> Result<Arc<MirBody>, MirLowerError>;
 
-    #[salsa::invoke(crate::mir::monomorphized_mir_body_for_closure_query)]
+    #[ra_salsa::invoke(crate::mir::monomorphized_mir_body_for_closure_query)]
     fn monomorphized_mir_body_for_closure(
         &self,
         def: ClosureId,
@@ -61,12 +61,12 @@ pub trait HirDatabase: DefDatabase + Upcast<dyn DefDatabase> {
         env: Arc<TraitEnvironment>,
     ) -> Result<Arc<MirBody>, MirLowerError>;
 
-    #[salsa::invoke(crate::mir::borrowck_query)]
-    #[salsa::lru]
+    #[ra_salsa::invoke(crate::mir::borrowck_query)]
+    #[ra_salsa::lru]
     fn borrowck(&self, def: DefWithBodyId) -> Result<Arc<[BorrowckResult]>, MirLowerError>;
 
-    #[salsa::invoke(crate::consteval::const_eval_query)]
-    #[salsa::cycle(crate::consteval::const_eval_recover)]
+    #[ra_salsa::invoke(crate::consteval::const_eval_query)]
+    #[ra_salsa::cycle(crate::consteval::const_eval_recover)]
     fn const_eval(
         &self,
         def: GeneralConstId,
@@ -74,15 +74,15 @@ pub trait HirDatabase: DefDatabase + Upcast<dyn DefDatabase> {
         trait_env: Option<Arc<TraitEnvironment>>,
     ) -> Result<Const, ConstEvalError>;
 
-    #[salsa::invoke(crate::consteval::const_eval_static_query)]
-    #[salsa::cycle(crate::consteval::const_eval_static_recover)]
+    #[ra_salsa::invoke(crate::consteval::const_eval_static_query)]
+    #[ra_salsa::cycle(crate::consteval::const_eval_static_recover)]
     fn const_eval_static(&self, def: StaticId) -> Result<Const, ConstEvalError>;
 
-    #[salsa::invoke(crate::consteval::const_eval_discriminant_variant)]
-    #[salsa::cycle(crate::consteval::const_eval_discriminant_recover)]
+    #[ra_salsa::invoke(crate::consteval::const_eval_discriminant_variant)]
+    #[ra_salsa::cycle(crate::consteval::const_eval_discriminant_recover)]
     fn const_eval_discriminant(&self, def: EnumVariantId) -> Result<i128, ConstEvalError>;
 
-    #[salsa::invoke(crate::method_resolution::lookup_impl_method_query)]
+    #[ra_salsa::invoke(crate::method_resolution::lookup_impl_method_query)]
     fn lookup_impl_method(
         &self,
         env: Arc<TraitEnvironment>,
@@ -92,8 +92,8 @@ pub trait HirDatabase: DefDatabase + Upcast<dyn DefDatabase> {
 
     // endregion:mir
 
-    #[salsa::invoke(crate::layout::layout_of_adt_query)]
-    #[salsa::cycle(crate::layout::layout_of_adt_recover)]
+    #[ra_salsa::invoke(crate::layout::layout_of_adt_query)]
+    #[ra_salsa::cycle(crate::layout::layout_of_adt_recover)]
     fn layout_of_adt(
         &self,
         def: AdtId,
@@ -101,49 +101,49 @@ pub trait HirDatabase: DefDatabase + Upcast<dyn DefDatabase> {
         env: Arc<TraitEnvironment>,
     ) -> Result<Arc<Layout>, LayoutError>;
 
-    #[salsa::invoke(crate::layout::layout_of_ty_query)]
-    #[salsa::cycle(crate::layout::layout_of_ty_recover)]
+    #[ra_salsa::invoke(crate::layout::layout_of_ty_query)]
+    #[ra_salsa::cycle(crate::layout::layout_of_ty_recover)]
     fn layout_of_ty(&self, ty: Ty, env: Arc<TraitEnvironment>) -> Result<Arc<Layout>, LayoutError>;
 
-    #[salsa::invoke(crate::layout::target_data_layout_query)]
+    #[ra_salsa::invoke(crate::layout::target_data_layout_query)]
     fn target_data_layout(&self, krate: CrateId) -> Result<Arc<TargetDataLayout>, Arc<str>>;
 
-    #[salsa::invoke(crate::dyn_compatibility::dyn_compatibility_of_trait_query)]
+    #[ra_salsa::invoke(crate::dyn_compatibility::dyn_compatibility_of_trait_query)]
     fn dyn_compatibility_of_trait(&self, trait_: TraitId) -> Option<DynCompatibilityViolation>;
 
-    #[salsa::invoke(crate::lower::ty_query)]
-    #[salsa::cycle(crate::lower::ty_recover)]
+    #[ra_salsa::invoke(crate::lower::ty_query)]
+    #[ra_salsa::cycle(crate::lower::ty_recover)]
     fn ty(&self, def: TyDefId) -> Binders<Ty>;
 
     /// Returns the type of the value of the given constant, or `None` if the `ValueTyDefId` is
     /// a `StructId` or `EnumVariantId` with a record constructor.
-    #[salsa::invoke(crate::lower::value_ty_query)]
+    #[ra_salsa::invoke(crate::lower::value_ty_query)]
     fn value_ty(&self, def: ValueTyDefId) -> Option<Binders<Ty>>;
 
-    #[salsa::invoke(crate::lower::impl_self_ty_query)]
-    #[salsa::cycle(crate::lower::impl_self_ty_recover)]
+    #[ra_salsa::invoke(crate::lower::impl_self_ty_query)]
+    #[ra_salsa::cycle(crate::lower::impl_self_ty_recover)]
     fn impl_self_ty(&self, def: ImplId) -> Binders<Ty>;
 
-    #[salsa::invoke(crate::lower::const_param_ty_query)]
+    #[ra_salsa::invoke(crate::lower::const_param_ty_query)]
     fn const_param_ty(&self, def: ConstParamId) -> Ty;
 
-    #[salsa::invoke(crate::lower::impl_trait_query)]
+    #[ra_salsa::invoke(crate::lower::impl_trait_query)]
     fn impl_trait(&self, def: ImplId) -> Option<Binders<TraitRef>>;
 
-    #[salsa::invoke(crate::lower::field_types_query)]
+    #[ra_salsa::invoke(crate::lower::field_types_query)]
     fn field_types(&self, var: VariantId) -> Arc<ArenaMap<LocalFieldId, Binders<Ty>>>;
 
-    #[salsa::invoke(crate::lower::callable_item_sig)]
+    #[ra_salsa::invoke(crate::lower::callable_item_sig)]
     fn callable_item_signature(&self, def: CallableDefId) -> PolyFnSig;
 
-    #[salsa::invoke(crate::lower::return_type_impl_traits)]
+    #[ra_salsa::invoke(crate::lower::return_type_impl_traits)]
     fn return_type_impl_traits(&self, def: FunctionId) -> Option<Arc<Binders<ImplTraits>>>;
 
-    #[salsa::invoke(crate::lower::type_alias_impl_traits)]
+    #[ra_salsa::invoke(crate::lower::type_alias_impl_traits)]
     fn type_alias_impl_traits(&self, def: TypeAliasId) -> Option<Arc<Binders<ImplTraits>>>;
 
-    #[salsa::invoke(crate::lower::generic_predicates_for_param_query)]
-    #[salsa::cycle(crate::lower::generic_predicates_for_param_recover)]
+    #[ra_salsa::invoke(crate::lower::generic_predicates_for_param_query)]
+    #[ra_salsa::cycle(crate::lower::generic_predicates_for_param_recover)]
     fn generic_predicates_for_param(
         &self,
         def: GenericDefId,
@@ -151,118 +151,118 @@ pub trait HirDatabase: DefDatabase + Upcast<dyn DefDatabase> {
         assoc_name: Option<Name>,
     ) -> GenericPredicates;
 
-    #[salsa::invoke(crate::lower::generic_predicates_query)]
+    #[ra_salsa::invoke(crate::lower::generic_predicates_query)]
     fn generic_predicates(&self, def: GenericDefId) -> GenericPredicates;
 
-    #[salsa::invoke(crate::lower::generic_predicates_without_parent_query)]
+    #[ra_salsa::invoke(crate::lower::generic_predicates_without_parent_query)]
     fn generic_predicates_without_parent(&self, def: GenericDefId) -> GenericPredicates;
 
-    #[salsa::invoke(crate::lower::trait_environment_for_body_query)]
-    #[salsa::transparent]
+    #[ra_salsa::invoke(crate::lower::trait_environment_for_body_query)]
+    #[ra_salsa::transparent]
     fn trait_environment_for_body(&self, def: DefWithBodyId) -> Arc<TraitEnvironment>;
 
-    #[salsa::invoke(crate::lower::trait_environment_query)]
+    #[ra_salsa::invoke(crate::lower::trait_environment_query)]
     fn trait_environment(&self, def: GenericDefId) -> Arc<TraitEnvironment>;
 
-    #[salsa::invoke(crate::lower::generic_defaults_query)]
-    #[salsa::cycle(crate::lower::generic_defaults_recover)]
+    #[ra_salsa::invoke(crate::lower::generic_defaults_query)]
+    #[ra_salsa::cycle(crate::lower::generic_defaults_recover)]
     fn generic_defaults(&self, def: GenericDefId) -> GenericDefaults;
 
-    #[salsa::invoke(InherentImpls::inherent_impls_in_crate_query)]
+    #[ra_salsa::invoke(InherentImpls::inherent_impls_in_crate_query)]
     fn inherent_impls_in_crate(&self, krate: CrateId) -> Arc<InherentImpls>;
 
-    #[salsa::invoke(InherentImpls::inherent_impls_in_block_query)]
+    #[ra_salsa::invoke(InherentImpls::inherent_impls_in_block_query)]
     fn inherent_impls_in_block(&self, block: BlockId) -> Option<Arc<InherentImpls>>;
 
     /// Collects all crates in the dependency graph that have impls for the
     /// given fingerprint. This is only used for primitive types and types
     /// annotated with `rustc_has_incoherent_inherent_impls`; for other types
     /// we just look at the crate where the type is defined.
-    #[salsa::invoke(crate::method_resolution::incoherent_inherent_impl_crates)]
+    #[ra_salsa::invoke(crate::method_resolution::incoherent_inherent_impl_crates)]
     fn incoherent_inherent_impl_crates(
         &self,
         krate: CrateId,
         fp: TyFingerprint,
     ) -> SmallVec<[CrateId; 2]>;
 
-    #[salsa::invoke(TraitImpls::trait_impls_in_crate_query)]
+    #[ra_salsa::invoke(TraitImpls::trait_impls_in_crate_query)]
     fn trait_impls_in_crate(&self, krate: CrateId) -> Arc<TraitImpls>;
 
-    #[salsa::invoke(TraitImpls::trait_impls_in_block_query)]
+    #[ra_salsa::invoke(TraitImpls::trait_impls_in_block_query)]
     fn trait_impls_in_block(&self, block: BlockId) -> Option<Arc<TraitImpls>>;
 
-    #[salsa::invoke(TraitImpls::trait_impls_in_deps_query)]
+    #[ra_salsa::invoke(TraitImpls::trait_impls_in_deps_query)]
     fn trait_impls_in_deps(&self, krate: CrateId) -> Arc<[Arc<TraitImpls>]>;
 
     // Interned IDs for Chalk integration
-    #[salsa::interned]
+    #[ra_salsa::interned]
     fn intern_callable_def(&self, callable_def: CallableDefId) -> InternedCallableDefId;
-    #[salsa::interned]
+    #[ra_salsa::interned]
     fn intern_type_or_const_param_id(
         &self,
         param_id: TypeOrConstParamId,
     ) -> InternedTypeOrConstParamId;
-    #[salsa::interned]
+    #[ra_salsa::interned]
     fn intern_lifetime_param_id(&self, param_id: LifetimeParamId) -> InternedLifetimeParamId;
-    #[salsa::interned]
+    #[ra_salsa::interned]
     fn intern_impl_trait_id(&self, id: ImplTraitId) -> InternedOpaqueTyId;
-    #[salsa::interned]
+    #[ra_salsa::interned]
     fn intern_closure(&self, id: InternedClosure) -> InternedClosureId;
-    #[salsa::interned]
+    #[ra_salsa::interned]
     fn intern_coroutine(&self, id: InternedCoroutine) -> InternedCoroutineId;
 
-    #[salsa::invoke(chalk_db::associated_ty_data_query)]
+    #[ra_salsa::invoke(chalk_db::associated_ty_data_query)]
     fn associated_ty_data(
         &self,
         id: chalk_db::AssocTypeId,
     ) -> sync::Arc<chalk_db::AssociatedTyDatum>;
 
-    #[salsa::invoke(chalk_db::trait_datum_query)]
+    #[ra_salsa::invoke(chalk_db::trait_datum_query)]
     fn trait_datum(
         &self,
         krate: CrateId,
         trait_id: chalk_db::TraitId,
     ) -> sync::Arc<chalk_db::TraitDatum>;
 
-    #[salsa::invoke(chalk_db::adt_datum_query)]
+    #[ra_salsa::invoke(chalk_db::adt_datum_query)]
     fn adt_datum(
         &self,
         krate: CrateId,
         struct_id: chalk_db::AdtId,
     ) -> sync::Arc<chalk_db::AdtDatum>;
 
-    #[salsa::invoke(chalk_db::impl_datum_query)]
+    #[ra_salsa::invoke(chalk_db::impl_datum_query)]
     fn impl_datum(
         &self,
         krate: CrateId,
         impl_id: chalk_db::ImplId,
     ) -> sync::Arc<chalk_db::ImplDatum>;
 
-    #[salsa::invoke(chalk_db::fn_def_datum_query)]
+    #[ra_salsa::invoke(chalk_db::fn_def_datum_query)]
     fn fn_def_datum(&self, fn_def_id: FnDefId) -> sync::Arc<chalk_db::FnDefDatum>;
 
-    #[salsa::invoke(chalk_db::fn_def_variance_query)]
+    #[ra_salsa::invoke(chalk_db::fn_def_variance_query)]
     fn fn_def_variance(&self, fn_def_id: FnDefId) -> chalk_db::Variances;
 
-    #[salsa::invoke(chalk_db::adt_variance_query)]
+    #[ra_salsa::invoke(chalk_db::adt_variance_query)]
     fn adt_variance(&self, adt_id: chalk_db::AdtId) -> chalk_db::Variances;
 
-    #[salsa::invoke(chalk_db::associated_ty_value_query)]
+    #[ra_salsa::invoke(chalk_db::associated_ty_value_query)]
     fn associated_ty_value(
         &self,
         krate: CrateId,
         id: chalk_db::AssociatedTyValueId,
     ) -> sync::Arc<chalk_db::AssociatedTyValue>;
 
-    #[salsa::invoke(crate::traits::normalize_projection_query)]
-    #[salsa::transparent]
+    #[ra_salsa::invoke(crate::traits::normalize_projection_query)]
+    #[ra_salsa::transparent]
     fn normalize_projection(
         &self,
         projection: crate::ProjectionTy,
         env: Arc<TraitEnvironment>,
     ) -> Ty;
 
-    #[salsa::invoke(crate::traits::trait_solve_query)]
+    #[ra_salsa::invoke(crate::traits::trait_solve_query)]
     fn trait_solve(
         &self,
         krate: CrateId,
@@ -270,7 +270,7 @@ pub trait HirDatabase: DefDatabase + Upcast<dyn DefDatabase> {
         goal: crate::Canonical<crate::InEnvironment<crate::Goal>>,
     ) -> Option<crate::Solution>;
 
-    #[salsa::invoke(chalk_db::program_clauses_for_chalk_env_query)]
+    #[ra_salsa::invoke(chalk_db::program_clauses_for_chalk_env_query)]
     fn program_clauses_for_chalk_env(
         &self,
         krate: CrateId,
@@ -285,23 +285,23 @@ fn hir_database_is_dyn_compatible() {
 }
 
 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
-pub struct InternedTypeOrConstParamId(salsa::InternId);
+pub struct InternedTypeOrConstParamId(ra_salsa::InternId);
 impl_intern_key!(InternedTypeOrConstParamId);
 
 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
-pub struct InternedLifetimeParamId(salsa::InternId);
+pub struct InternedLifetimeParamId(ra_salsa::InternId);
 impl_intern_key!(InternedLifetimeParamId);
 
 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
-pub struct InternedConstParamId(salsa::InternId);
+pub struct InternedConstParamId(ra_salsa::InternId);
 impl_intern_key!(InternedConstParamId);
 
 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
-pub struct InternedOpaqueTyId(salsa::InternId);
+pub struct InternedOpaqueTyId(ra_salsa::InternId);
 impl_intern_key!(InternedOpaqueTyId);
 
 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
-pub struct InternedClosureId(salsa::InternId);
+pub struct InternedClosureId(ra_salsa::InternId);
 impl_intern_key!(InternedClosureId);
 
 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
@@ -310,7 +310,7 @@ pub struct InternedClosure(pub DefWithBodyId, pub ExprId);
 impl InternValueTrivial for InternedClosure {}
 
 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
-pub struct InternedCoroutineId(salsa::InternId);
+pub struct InternedCoroutineId(ra_salsa::InternId);
 impl_intern_key!(InternedCoroutineId);
 
 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
@@ -320,5 +320,5 @@ impl InternValueTrivial for InternedCoroutine {}
 /// This exists just for Chalk, because Chalk just has a single `FnDefId` where
 /// we have different IDs for struct and enum variant constructors.
 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Ord, PartialOrd)]
-pub struct InternedCallableDefId(salsa::InternId);
+pub struct InternedCallableDefId(ra_salsa::InternId);
 impl_intern_key!(InternedCallableDefId);
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/display.rs b/src/tools/rust-analyzer/crates/hir-ty/src/display.rs
index 70c03477c4c..10f5bcdad86 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/display.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/display.rs
@@ -1008,34 +1008,28 @@ impl HirDisplay for Ty {
                 if let Safety::Unsafe = sig.safety {
                     write!(f, "unsafe ")?;
                 }
-                if !matches!(sig.abi, FnAbi::Rust) {
+                if !matches!(sig.abi, FnAbi::Rust | FnAbi::RustCall) {
                     f.write_str("extern \"")?;
                     f.write_str(sig.abi.as_str())?;
                     f.write_str("\" ")?;
                 }
 
+                write!(f, "fn ")?;
+                f.start_location_link(def.into());
                 match def {
-                    CallableDefId::FunctionId(ff) => {
-                        write!(f, "fn ")?;
-                        f.start_location_link(def.into());
-                        write!(
-                            f,
-                            "{}",
-                            db.function_data(ff).name.display(f.db.upcast(), f.edition())
-                        )?
-                    }
+                    CallableDefId::FunctionId(ff) => write!(
+                        f,
+                        "{}",
+                        db.function_data(ff).name.display(f.db.upcast(), f.edition())
+                    )?,
                     CallableDefId::StructId(s) => {
-                        f.start_location_link(def.into());
                         write!(f, "{}", db.struct_data(s).name.display(f.db.upcast(), f.edition()))?
                     }
-                    CallableDefId::EnumVariantId(e) => {
-                        f.start_location_link(def.into());
-                        write!(
-                            f,
-                            "{}",
-                            db.enum_variant_data(e).name.display(f.db.upcast(), f.edition())
-                        )?
-                    }
+                    CallableDefId::EnumVariantId(e) => write!(
+                        f,
+                        "{}",
+                        db.enum_variant_data(e).name.display(f.db.upcast(), f.edition())
+                    )?,
                 };
                 f.end_location_link();
 
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs
index 8bc3c50725d..88334b492d5 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs
@@ -57,7 +57,7 @@ use crate::{
     db::HirDatabase,
     fold_tys,
     generics::Generics,
-    infer::{coerce::CoerceMany, unify::InferenceTable},
+    infer::{coerce::CoerceMany, expr::ExprIsRead, unify::InferenceTable},
     lower::ImplTraitLoweringMode,
     mir::MirSpan,
     to_assoc_type_id,
@@ -1154,6 +1154,7 @@ impl<'a> InferenceContext<'a> {
                 _ = self.infer_expr_coerce(
                     self.body.body_expr,
                     &Expectation::has_type(self.return_ty.clone()),
+                    ExprIsRead::Yes,
                 )
             }
         }
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/cast.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/cast.rs
index caa3960a227..21d0be6ed5f 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/cast.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/cast.rs
@@ -5,8 +5,9 @@ use hir_def::{hir::ExprId, AdtId};
 use stdx::never;
 
 use crate::{
-    infer::unify::InferenceTable, Adjustment, Binders, DynTy, InferenceDiagnostic, Interner,
-    PlaceholderIndex, QuantifiedWhereClauses, Ty, TyExt, TyKind, TypeFlags, WhereClause,
+    infer::{coerce::CoerceNever, unify::InferenceTable},
+    Adjustment, Binders, DynTy, InferenceDiagnostic, Interner, PlaceholderIndex,
+    QuantifiedWhereClauses, Ty, TyExt, TyKind, TypeFlags, WhereClause,
 };
 
 #[derive(Debug)]
@@ -70,8 +71,9 @@ pub enum CastError {
     NeedViaThinPtr,
     NeedViaInt,
     NonScalar,
-    UnknownCastPtrKind,
-    UnknownExprPtrKind,
+    // We don't want to report errors with unknown types currently.
+    // UnknownCastPtrKind,
+    // UnknownExprPtrKind,
 }
 
 impl CastError {
@@ -127,7 +129,7 @@ impl CastCheck {
             return Ok(());
         }
 
-        if let Ok((adj, _)) = table.coerce(&self.expr_ty, &self.cast_ty) {
+        if let Ok((adj, _)) = table.coerce(&self.expr_ty, &self.cast_ty, CoerceNever::Yes) {
             apply_adjustments(self.source_expr, adj);
             set_coercion_cast(self.source_expr);
             return Ok(());
@@ -153,7 +155,8 @@ impl CastCheck {
                         let sig = self.expr_ty.callable_sig(table.db).expect("FnDef had no sig");
                         let sig = table.normalize_associated_types_in(sig);
                         let fn_ptr = TyKind::Function(sig.to_fn_ptr()).intern(Interner);
-                        if let Ok((adj, _)) = table.coerce(&self.expr_ty, &fn_ptr) {
+                        if let Ok((adj, _)) = table.coerce(&self.expr_ty, &fn_ptr, CoerceNever::Yes)
+                        {
                             apply_adjustments(self.source_expr, adj);
                         } else {
                             return Err(CastError::IllegalCast);
@@ -240,7 +243,8 @@ impl CastCheck {
             if let TyKind::Array(ety, _) = t_expr.kind(Interner) {
                 // Coerce to a raw pointer so that we generate RawPtr in MIR.
                 let array_ptr_type = TyKind::Raw(m_expr, t_expr.clone()).intern(Interner);
-                if let Ok((adj, _)) = table.coerce(&self.expr_ty, &array_ptr_type) {
+                if let Ok((adj, _)) = table.coerce(&self.expr_ty, &array_ptr_type, CoerceNever::Yes)
+                {
                     apply_adjustments(self.source_expr, adj);
                 } else {
                     never!(
@@ -252,7 +256,7 @@ impl CastCheck {
 
                 // This is a less strict condition than rustc's `demand_eqtype`,
                 // but false negative is better than false positive
-                if table.coerce(ety, t_cast).is_ok() {
+                if table.coerce(ety, t_cast, CoerceNever::Yes).is_ok() {
                     return Ok(());
                 }
             }
@@ -272,9 +276,10 @@ impl CastCheck {
 
         match (src_kind, dst_kind) {
             (Some(PointerKind::Error), _) | (_, Some(PointerKind::Error)) => Ok(()),
-            (_, None) => Err(CastError::UnknownCastPtrKind),
+            // (_, None) => Err(CastError::UnknownCastPtrKind),
+            // (None, _) => Err(CastError::UnknownExprPtrKind),
+            (_, None) | (None, _) => Ok(()),
             (_, Some(PointerKind::Thin)) => Ok(()),
-            (None, _) => Err(CastError::UnknownExprPtrKind),
             (Some(PointerKind::Thin), _) => Err(CastError::SizedUnsizedCast),
             (Some(PointerKind::VTable(src_tty)), Some(PointerKind::VTable(dst_tty))) => {
                 let principal = |tty: &Binders<QuantifiedWhereClauses>| {
@@ -315,7 +320,8 @@ impl CastCheck {
         expr_ty: &Ty,
     ) -> Result<(), CastError> {
         match pointer_kind(expr_ty, table).map_err(|_| CastError::Unknown)? {
-            None => Err(CastError::UnknownExprPtrKind),
+            // None => Err(CastError::UnknownExprPtrKind),
+            None => Ok(()),
             Some(PointerKind::Error) => Ok(()),
             Some(PointerKind::Thin) => Ok(()),
             _ => Err(CastError::NeedViaThinPtr),
@@ -328,7 +334,8 @@ impl CastCheck {
         cast_ty: &Ty,
     ) -> Result<(), CastError> {
         match pointer_kind(cast_ty, table).map_err(|_| CastError::Unknown)? {
-            None => Err(CastError::UnknownCastPtrKind),
+            // None => Err(CastError::UnknownCastPtrKind),
+            None => Ok(()),
             Some(PointerKind::Error) => Ok(()),
             Some(PointerKind::Thin) => Ok(()),
             Some(PointerKind::VTable(_)) => Err(CastError::IntToFatCast),
@@ -343,7 +350,8 @@ impl CastCheck {
         cast_ty: &Ty,
     ) -> Result<(), CastError> {
         match pointer_kind(cast_ty, table).map_err(|_| CastError::Unknown)? {
-            None => Err(CastError::UnknownCastPtrKind),
+            // None => Err(CastError::UnknownCastPtrKind),
+            None => Ok(()),
             Some(PointerKind::Error) => Ok(()),
             Some(PointerKind::Thin) => Ok(()),
             _ => Err(CastError::IllegalCast),
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure.rs
index 5cad08b9395..e9825cf0998 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure.rs
@@ -29,6 +29,7 @@ use crate::{
     db::{HirDatabase, InternedClosure},
     error_lifetime, from_chalk_trait_id, from_placeholder_idx,
     generics::Generics,
+    infer::coerce::CoerceNever,
     make_binders,
     mir::{BorrowKind, MirSpan, MutBorrowKind, ProjectionElem},
     to_chalk_trait_id,
@@ -65,7 +66,7 @@ impl InferenceContext<'_> {
         }
 
         // Deduction from where-clauses in scope, as well as fn-pointer coercion are handled here.
-        let _ = self.coerce(Some(closure_expr), closure_ty, &expected_ty);
+        let _ = self.coerce(Some(closure_expr), closure_ty, &expected_ty, CoerceNever::Yes);
 
         // Coroutines are not Fn* so return early.
         if matches!(closure_ty.kind(Interner), TyKind::Coroutine(..)) {
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/coerce.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/coerce.rs
index 7e758c0b517..366c3cb0f17 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/coerce.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/coerce.rs
@@ -139,8 +139,8 @@ impl CoerceMany {
         };
         if let Some(sig) = sig {
             let target_ty = TyKind::Function(sig.to_fn_ptr()).intern(Interner);
-            let result1 = ctx.table.coerce_inner(self.merged_ty(), &target_ty);
-            let result2 = ctx.table.coerce_inner(expr_ty.clone(), &target_ty);
+            let result1 = ctx.table.coerce_inner(self.merged_ty(), &target_ty, CoerceNever::Yes);
+            let result2 = ctx.table.coerce_inner(expr_ty.clone(), &target_ty, CoerceNever::Yes);
             if let (Ok(result1), Ok(result2)) = (result1, result2) {
                 ctx.table.register_infer_ok(InferOk { value: (), goals: result1.goals });
                 for &e in &self.expressions {
@@ -159,9 +159,9 @@ impl CoerceMany {
         // type is a type variable and the new one is `!`, trying it the other
         // way around first would mean we make the type variable `!`, instead of
         // just marking it as possibly diverging.
-        if let Ok(res) = ctx.coerce(expr, &expr_ty, &self.merged_ty()) {
+        if let Ok(res) = ctx.coerce(expr, &expr_ty, &self.merged_ty(), CoerceNever::Yes) {
             self.final_ty = Some(res);
-        } else if let Ok(res) = ctx.coerce(expr, &self.merged_ty(), &expr_ty) {
+        } else if let Ok(res) = ctx.coerce(expr, &self.merged_ty(), &expr_ty, CoerceNever::Yes) {
             self.final_ty = Some(res);
         } else {
             match cause {
@@ -197,7 +197,7 @@ pub(crate) fn coerce(
     let vars = table.fresh_subst(tys.binders.as_slice(Interner));
     let ty1_with_vars = vars.apply(tys.value.0.clone(), Interner);
     let ty2_with_vars = vars.apply(tys.value.1.clone(), Interner);
-    let (adjustments, ty) = table.coerce(&ty1_with_vars, &ty2_with_vars)?;
+    let (adjustments, ty) = table.coerce(&ty1_with_vars, &ty2_with_vars, CoerceNever::Yes)?;
     // default any type vars that weren't unified back to their original bound vars
     // (kind of hacky)
     let find_var = |iv| {
@@ -219,6 +219,12 @@ pub(crate) fn coerce(
     Ok((adjustments, table.resolve_with_fallback(ty, &fallback)))
 }
 
+#[derive(Clone, Copy, PartialEq, Eq)]
+pub(crate) enum CoerceNever {
+    Yes,
+    No,
+}
+
 impl InferenceContext<'_> {
     /// Unify two types, but may coerce the first one to the second one
     /// using "implicit coercion rules" if needed.
@@ -227,10 +233,16 @@ impl InferenceContext<'_> {
         expr: Option<ExprId>,
         from_ty: &Ty,
         to_ty: &Ty,
+        // [Comment from rustc](https://github.com/rust-lang/rust/blob/4cc494bbfe9911d24f3ee521f98d5c6bb7e3ffe8/compiler/rustc_hir_typeck/src/coercion.rs#L85-L89)
+        // Whether we allow `NeverToAny` coercions. This is unsound if we're
+        // coercing a place expression without it counting as a read in the MIR.
+        // This is a side-effect of HIR not really having a great distinction
+        // between places and values.
+        coerce_never: CoerceNever,
     ) -> Result<Ty, TypeError> {
         let from_ty = self.resolve_ty_shallow(from_ty);
         let to_ty = self.resolve_ty_shallow(to_ty);
-        let (adjustments, ty) = self.table.coerce(&from_ty, &to_ty)?;
+        let (adjustments, ty) = self.table.coerce(&from_ty, &to_ty, coerce_never)?;
         if let Some(expr) = expr {
             self.write_expr_adj(expr, adjustments);
         }
@@ -245,10 +257,11 @@ impl InferenceTable<'_> {
         &mut self,
         from_ty: &Ty,
         to_ty: &Ty,
+        coerce_never: CoerceNever,
     ) -> Result<(Vec<Adjustment>, Ty), TypeError> {
         let from_ty = self.resolve_ty_shallow(from_ty);
         let to_ty = self.resolve_ty_shallow(to_ty);
-        match self.coerce_inner(from_ty, &to_ty) {
+        match self.coerce_inner(from_ty, &to_ty, coerce_never) {
             Ok(InferOk { value: (adjustments, ty), goals }) => {
                 self.register_infer_ok(InferOk { value: (), goals });
                 Ok((adjustments, ty))
@@ -260,19 +273,23 @@ impl InferenceTable<'_> {
         }
     }
 
-    fn coerce_inner(&mut self, from_ty: Ty, to_ty: &Ty) -> CoerceResult {
+    fn coerce_inner(&mut self, from_ty: Ty, to_ty: &Ty, coerce_never: CoerceNever) -> CoerceResult {
         if from_ty.is_never() {
-            // Subtle: If we are coercing from `!` to `?T`, where `?T` is an unbound
-            // type variable, we want `?T` to fallback to `!` if not
-            // otherwise constrained. An example where this arises:
-            //
-            //     let _: Option<?T> = Some({ return; });
-            //
-            // here, we would coerce from `!` to `?T`.
             if let TyKind::InferenceVar(tv, TyVariableKind::General) = to_ty.kind(Interner) {
                 self.set_diverging(*tv, true);
             }
-            return success(simple(Adjust::NeverToAny)(to_ty.clone()), to_ty.clone(), vec![]);
+            if coerce_never == CoerceNever::Yes {
+                // Subtle: If we are coercing from `!` to `?T`, where `?T` is an unbound
+                // type variable, we want `?T` to fallback to `!` if not
+                // otherwise constrained. An example where this arises:
+                //
+                //     let _: Option<?T> = Some({ return; });
+                //
+                // here, we would coerce from `!` to `?T`.
+                return success(simple(Adjust::NeverToAny)(to_ty.clone()), to_ty.clone(), vec![]);
+            } else {
+                return self.unify_and(&from_ty, to_ty, identity);
+            }
         }
 
         // If we are coercing into a TAIT, coerce into its proxy inference var, instead.
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs
index a04e7b17ae6..657e4d77966 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs
@@ -10,10 +10,11 @@ use either::Either;
 use hir_def::{
     hir::{
         ArithOp, Array, AsmOperand, AsmOptions, BinaryOp, ClosureKind, Expr, ExprId, LabelId,
-        Literal, Statement, UnaryOp,
+        Literal, Pat, PatId, Statement, UnaryOp,
     },
     lang_item::{LangItem, LangItemTarget},
     path::{GenericArg, GenericArgs, Path},
+    resolver::ValueNs,
     BlockId, FieldId, GenericDefId, GenericParamId, ItemContainerId, Lookup, TupleFieldId, TupleId,
 };
 use hir_expand::name::Name;
@@ -28,7 +29,7 @@ use crate::{
     error_lifetime,
     generics::{generics, Generics},
     infer::{
-        coerce::{CoerceMany, CoercionCause},
+        coerce::{CoerceMany, CoerceNever, CoercionCause},
         find_continuable,
         pat::contains_explicit_ref_binding,
         BreakableKind,
@@ -52,9 +53,20 @@ use super::{
     Expectation, InferenceContext, InferenceDiagnostic, TypeMismatch,
 };
 
+#[derive(Clone, Copy, PartialEq, Eq)]
+pub(crate) enum ExprIsRead {
+    Yes,
+    No,
+}
+
 impl InferenceContext<'_> {
-    pub(crate) fn infer_expr(&mut self, tgt_expr: ExprId, expected: &Expectation) -> Ty {
-        let ty = self.infer_expr_inner(tgt_expr, expected);
+    pub(crate) fn infer_expr(
+        &mut self,
+        tgt_expr: ExprId,
+        expected: &Expectation,
+        is_read: ExprIsRead,
+    ) -> Ty {
+        let ty = self.infer_expr_inner(tgt_expr, expected, is_read);
         if let Some(expected_ty) = expected.only_has_type(&mut self.table) {
             let could_unify = self.unify(&ty, &expected_ty);
             if !could_unify {
@@ -67,16 +79,26 @@ impl InferenceContext<'_> {
         ty
     }
 
-    pub(crate) fn infer_expr_no_expect(&mut self, tgt_expr: ExprId) -> Ty {
-        self.infer_expr_inner(tgt_expr, &Expectation::None)
+    pub(crate) fn infer_expr_no_expect(&mut self, tgt_expr: ExprId, is_read: ExprIsRead) -> Ty {
+        self.infer_expr_inner(tgt_expr, &Expectation::None, is_read)
     }
 
     /// Infer type of expression with possibly implicit coerce to the expected type.
     /// Return the type after possible coercion.
-    pub(super) fn infer_expr_coerce(&mut self, expr: ExprId, expected: &Expectation) -> Ty {
-        let ty = self.infer_expr_inner(expr, expected);
+    pub(super) fn infer_expr_coerce(
+        &mut self,
+        expr: ExprId,
+        expected: &Expectation,
+        is_read: ExprIsRead,
+    ) -> Ty {
+        let ty = self.infer_expr_inner(expr, expected, is_read);
         if let Some(target) = expected.only_has_type(&mut self.table) {
-            match self.coerce(Some(expr), &ty, &target) {
+            let coerce_never = if self.expr_guaranteed_to_constitute_read_for_never(expr, is_read) {
+                CoerceNever::Yes
+            } else {
+                CoerceNever::No
+            };
+            match self.coerce(Some(expr), &ty, &target, coerce_never) {
                 Ok(res) => res,
                 Err(_) => {
                     self.result.type_mismatches.insert(
@@ -91,8 +113,137 @@ impl InferenceContext<'_> {
         }
     }
 
-    fn infer_expr_coerce_never(&mut self, expr: ExprId, expected: &Expectation) -> Ty {
-        let ty = self.infer_expr_inner(expr, expected);
+    /// Whether this expression constitutes a read of value of the type that
+    /// it evaluates to.
+    ///
+    /// This is used to determine if we should consider the block to diverge
+    /// if the expression evaluates to `!`, and if we should insert a `NeverToAny`
+    /// coercion for values of type `!`.
+    ///
+    /// This function generally returns `false` if the expression is a place
+    /// expression and the *parent* expression is the scrutinee of a match or
+    /// the pointee of an `&` addr-of expression, since both of those parent
+    /// expressions take a *place* and not a value.
+    pub(super) fn expr_guaranteed_to_constitute_read_for_never(
+        &mut self,
+        expr: ExprId,
+        is_read: ExprIsRead,
+    ) -> bool {
+        // rustc does the place expr check first, but since we are feeding
+        // readness of the `expr` as a given value, we just can short-circuit
+        // the place expr check if it's true(see codes and comments below)
+        if is_read == ExprIsRead::Yes {
+            return true;
+        }
+
+        // We only care about place exprs. Anything else returns an immediate
+        // which would constitute a read. We don't care about distinguishing
+        // "syntactic" place exprs since if the base of a field projection is
+        // not a place then it would've been UB to read from it anyways since
+        // that constitutes a read.
+        if !self.is_syntactic_place_expr(expr) {
+            return true;
+        }
+
+        // rustc queries parent hir node of `expr` here and determine whether
+        // the current `expr` is read of value per its parent.
+        // But since we don't have hir node, we cannot follow such "bottom-up"
+        // method.
+        // So, we pass down such readness from the parent expression through the
+        // recursive `infer_expr*` calls in a "top-down" manner.
+        is_read == ExprIsRead::Yes
+    }
+
+    /// Whether this pattern constitutes a read of value of the scrutinee that
+    /// it is matching against. This is used to determine whether we should
+    /// perform `NeverToAny` coercions.
+    fn pat_guaranteed_to_constitute_read_for_never(&self, pat: PatId) -> bool {
+        match &self.body[pat] {
+            // Does not constitute a read.
+            Pat::Wild => false,
+
+            // This is unnecessarily restrictive when the pattern that doesn't
+            // constitute a read is unreachable.
+            //
+            // For example `match *never_ptr { value => {}, _ => {} }` or
+            // `match *never_ptr { _ if false => {}, value => {} }`.
+            //
+            // It is however fine to be restrictive here; only returning `true`
+            // can lead to unsoundness.
+            Pat::Or(subpats) => {
+                subpats.iter().all(|pat| self.pat_guaranteed_to_constitute_read_for_never(*pat))
+            }
+
+            // All of these constitute a read, or match on something that isn't `!`,
+            // which would require a `NeverToAny` coercion.
+            Pat::Bind { .. }
+            | Pat::TupleStruct { .. }
+            | Pat::Path(_)
+            | Pat::Tuple { .. }
+            | Pat::Box { .. }
+            | Pat::Ref { .. }
+            | Pat::Lit(_)
+            | Pat::Range { .. }
+            | Pat::Slice { .. }
+            | Pat::ConstBlock(_)
+            | Pat::Record { .. }
+            | Pat::Missing => true,
+        }
+    }
+
+    fn is_syntactic_place_expr(&self, expr: ExprId) -> bool {
+        match &self.body[expr] {
+            // Lang item paths cannot currently be local variables or statics.
+            Expr::Path(Path::LangItem(_, _)) => false,
+            Expr::Path(Path::Normal { type_anchor: Some(_), .. }) => false,
+            Expr::Path(path) => self
+                .resolver
+                .resolve_path_in_value_ns_fully(self.db.upcast(), path)
+                .map_or(true, |res| matches!(res, ValueNs::LocalBinding(_) | ValueNs::StaticId(_))),
+            Expr::Underscore => true,
+            Expr::UnaryOp { op: UnaryOp::Deref, .. } => true,
+            Expr::Field { .. } | Expr::Index { .. } => true,
+            Expr::Call { .. }
+            | Expr::MethodCall { .. }
+            | Expr::Tuple { .. }
+            | Expr::If { .. }
+            | Expr::Match { .. }
+            | Expr::Closure { .. }
+            | Expr::Block { .. }
+            | Expr::Array(..)
+            | Expr::Break { .. }
+            | Expr::Continue { .. }
+            | Expr::Return { .. }
+            | Expr::Become { .. }
+            | Expr::Let { .. }
+            | Expr::Loop { .. }
+            | Expr::InlineAsm(..)
+            | Expr::OffsetOf(..)
+            | Expr::Literal(..)
+            | Expr::Const(..)
+            | Expr::UnaryOp { .. }
+            | Expr::BinaryOp { .. }
+            | Expr::Yield { .. }
+            | Expr::Cast { .. }
+            | Expr::Async { .. }
+            | Expr::Unsafe { .. }
+            | Expr::Await { .. }
+            | Expr::Ref { .. }
+            | Expr::Range { .. }
+            | Expr::Box { .. }
+            | Expr::RecordLit { .. }
+            | Expr::Yeet { .. }
+            | Expr::Missing => false,
+        }
+    }
+
+    fn infer_expr_coerce_never(
+        &mut self,
+        expr: ExprId,
+        expected: &Expectation,
+        is_read: ExprIsRead,
+    ) -> Ty {
+        let ty = self.infer_expr_inner(expr, expected, is_read);
         // While we don't allow *arbitrary* coercions here, we *do* allow
         // coercions from `!` to `expected`.
         if ty.is_never() {
@@ -105,7 +256,7 @@ impl InferenceContext<'_> {
             }
 
             if let Some(target) = expected.only_has_type(&mut self.table) {
-                self.coerce(Some(expr), &ty, &target)
+                self.coerce(Some(expr), &ty, &target, CoerceNever::Yes)
                     .expect("never-to-any coercion should always succeed")
             } else {
                 ty
@@ -124,7 +275,12 @@ impl InferenceContext<'_> {
         }
     }
 
-    fn infer_expr_inner(&mut self, tgt_expr: ExprId, expected: &Expectation) -> Ty {
+    fn infer_expr_inner(
+        &mut self,
+        tgt_expr: ExprId,
+        expected: &Expectation,
+        is_read: ExprIsRead,
+    ) -> Ty {
         self.db.unwind_if_cancelled();
 
         let ty = match &self.body[tgt_expr] {
@@ -134,17 +290,18 @@ impl InferenceContext<'_> {
                 self.infer_expr_coerce_never(
                     condition,
                     &Expectation::HasType(self.result.standard_types.bool_.clone()),
+                    ExprIsRead::Yes,
                 );
 
                 let condition_diverges = mem::replace(&mut self.diverges, Diverges::Maybe);
 
-                let then_ty = self.infer_expr_inner(then_branch, expected);
+                let then_ty = self.infer_expr_inner(then_branch, expected, ExprIsRead::Yes);
                 let then_diverges = mem::replace(&mut self.diverges, Diverges::Maybe);
                 let mut coerce = CoerceMany::new(expected.coercion_target_type(&mut self.table));
                 coerce.coerce(self, Some(then_branch), &then_ty, CoercionCause::Expr(then_branch));
                 match else_branch {
                     Some(else_branch) => {
-                        let else_ty = self.infer_expr_inner(else_branch, expected);
+                        let else_ty = self.infer_expr_inner(else_branch, expected, ExprIsRead::Yes);
                         let else_diverges = mem::replace(&mut self.diverges, Diverges::Maybe);
                         coerce.coerce(
                             self,
@@ -163,7 +320,12 @@ impl InferenceContext<'_> {
                 coerce.complete(self)
             }
             &Expr::Let { pat, expr } => {
-                let input_ty = self.infer_expr(expr, &Expectation::none());
+                let child_is_read = if self.pat_guaranteed_to_constitute_read_for_never(pat) {
+                    ExprIsRead::Yes
+                } else {
+                    ExprIsRead::No
+                };
+                let input_ty = self.infer_expr(expr, &Expectation::none(), child_is_read);
                 self.infer_top_pat(pat, &input_ty);
                 self.result.standard_types.bool_.clone()
             }
@@ -176,7 +338,7 @@ impl InferenceContext<'_> {
             Expr::Const(id) => {
                 self.with_breakable_ctx(BreakableKind::Border, None, None, |this| {
                     let loc = this.db.lookup_intern_anonymous_const(*id);
-                    this.infer_expr(loc.root, expected)
+                    this.infer_expr(loc.root, expected, ExprIsRead::Yes)
                 })
                 .1
             }
@@ -189,7 +351,11 @@ impl InferenceContext<'_> {
                 let ty = self.table.new_type_var();
                 let (breaks, ()) =
                     self.with_breakable_ctx(BreakableKind::Loop, Some(ty), label, |this| {
-                        this.infer_expr(body, &Expectation::HasType(TyBuilder::unit()));
+                        this.infer_expr(
+                            body,
+                            &Expectation::HasType(TyBuilder::unit()),
+                            ExprIsRead::Yes,
+                        );
                     });
 
                 match breaks {
@@ -312,7 +478,7 @@ impl InferenceContext<'_> {
                 ty
             }
             Expr::Call { callee, args, .. } => {
-                let callee_ty = self.infer_expr(*callee, &Expectation::none());
+                let callee_ty = self.infer_expr(*callee, &Expectation::none(), ExprIsRead::Yes);
                 let mut derefs = Autoderef::new(&mut self.table, callee_ty.clone(), false);
                 let (res, derefed_callee) = loop {
                     let Some((callee_deref_ty, _)) = derefs.next() else {
@@ -393,7 +559,12 @@ impl InferenceContext<'_> {
                     expected,
                 ),
             Expr::Match { expr, arms } => {
-                let input_ty = self.infer_expr(*expr, &Expectation::none());
+                let scrutinee_is_read = arms
+                    .iter()
+                    .all(|arm| self.pat_guaranteed_to_constitute_read_for_never(arm.pat));
+                let scrutinee_is_read =
+                    if scrutinee_is_read { ExprIsRead::Yes } else { ExprIsRead::No };
+                let input_ty = self.infer_expr(*expr, &Expectation::none(), scrutinee_is_read);
 
                 if arms.is_empty() {
                     self.diverges = Diverges::Always;
@@ -423,11 +594,12 @@ impl InferenceContext<'_> {
                             self.infer_expr_coerce_never(
                                 guard_expr,
                                 &Expectation::HasType(self.result.standard_types.bool_.clone()),
+                                ExprIsRead::Yes,
                             );
                         }
                         self.diverges = Diverges::Maybe;
 
-                        let arm_ty = self.infer_expr_inner(arm.expr, &expected);
+                        let arm_ty = self.infer_expr_inner(arm.expr, &expected, ExprIsRead::Yes);
                         all_arms_diverge &= self.diverges;
                         coerce.coerce(self, Some(arm.expr), &arm_ty, CoercionCause::Expr(arm.expr));
                     }
@@ -480,7 +652,11 @@ impl InferenceContext<'_> {
                         },
                         None => self.err_ty(),
                     };
-                    self.infer_expr_inner(expr, &Expectation::HasType(opt_coerce_to))
+                    self.infer_expr_inner(
+                        expr,
+                        &Expectation::HasType(opt_coerce_to),
+                        ExprIsRead::Yes,
+                    )
                 } else {
                     TyBuilder::unit()
                 };
@@ -517,10 +693,14 @@ impl InferenceContext<'_> {
             Expr::Yield { expr } => {
                 if let Some((resume_ty, yield_ty)) = self.resume_yield_tys.clone() {
                     if let Some(expr) = expr {
-                        self.infer_expr_coerce(*expr, &Expectation::has_type(yield_ty));
+                        self.infer_expr_coerce(
+                            *expr,
+                            &Expectation::has_type(yield_ty),
+                            ExprIsRead::Yes,
+                        );
                     } else {
                         let unit = self.result.standard_types.unit.clone();
-                        let _ = self.coerce(Some(tgt_expr), &unit, &yield_ty);
+                        let _ = self.coerce(Some(tgt_expr), &unit, &yield_ty, CoerceNever::Yes);
                     }
                     resume_ty
                 } else {
@@ -530,7 +710,7 @@ impl InferenceContext<'_> {
             }
             Expr::Yeet { expr } => {
                 if let &Some(expr) = expr {
-                    self.infer_expr_no_expect(expr);
+                    self.infer_expr_no_expect(expr, ExprIsRead::Yes);
                 }
                 self.result.standard_types.never.clone()
             }
@@ -589,28 +769,37 @@ impl InferenceContext<'_> {
                             // Field type might have some unknown types
                             // FIXME: we may want to emit a single type variable for all instance of type fields?
                             let field_ty = self.insert_type_vars(field_ty);
-                            self.infer_expr_coerce(field.expr, &Expectation::has_type(field_ty));
+                            self.infer_expr_coerce(
+                                field.expr,
+                                &Expectation::has_type(field_ty),
+                                ExprIsRead::Yes,
+                            );
                         }
                     }
                     None => {
                         for field in fields.iter() {
-                            self.infer_expr_coerce(field.expr, &Expectation::None);
+                            // Field projections don't constitute reads.
+                            self.infer_expr_coerce(field.expr, &Expectation::None, ExprIsRead::No);
                         }
                     }
                 }
                 if let Some(expr) = spread {
-                    self.infer_expr(*expr, &Expectation::has_type(ty.clone()));
+                    self.infer_expr(*expr, &Expectation::has_type(ty.clone()), ExprIsRead::Yes);
                 }
                 ty
             }
             Expr::Field { expr, name } => self.infer_field_access(tgt_expr, *expr, name, expected),
             Expr::Await { expr } => {
-                let inner_ty = self.infer_expr_inner(*expr, &Expectation::none());
+                let inner_ty = self.infer_expr_inner(*expr, &Expectation::none(), ExprIsRead::Yes);
                 self.resolve_associated_type(inner_ty, self.resolve_future_future_output())
             }
             Expr::Cast { expr, type_ref } => {
                 let cast_ty = self.make_ty(type_ref);
-                let expr_ty = self.infer_expr(*expr, &Expectation::Castable(cast_ty.clone()));
+                let expr_ty = self.infer_expr(
+                    *expr,
+                    &Expectation::Castable(cast_ty.clone()),
+                    ExprIsRead::Yes,
+                );
                 self.deferred_cast_checks.push(CastCheck::new(
                     tgt_expr,
                     *expr,
@@ -638,7 +827,7 @@ impl InferenceContext<'_> {
                 } else {
                     Expectation::none()
                 };
-                let inner_ty = self.infer_expr_inner(*expr, &expectation);
+                let inner_ty = self.infer_expr_inner(*expr, &expectation, ExprIsRead::Yes);
                 match rawness {
                     Rawness::RawPtr => TyKind::Raw(mutability, inner_ty),
                     Rawness::Ref => {
@@ -650,7 +839,7 @@ impl InferenceContext<'_> {
             }
             &Expr::Box { expr } => self.infer_expr_box(expr, expected),
             Expr::UnaryOp { expr, op } => {
-                let inner_ty = self.infer_expr_inner(*expr, &Expectation::none());
+                let inner_ty = self.infer_expr_inner(*expr, &Expectation::none(), ExprIsRead::Yes);
                 let inner_ty = self.resolve_ty_shallow(&inner_ty);
                 // FIXME: Note down method resolution her
                 match op {
@@ -720,19 +909,32 @@ impl InferenceContext<'_> {
                     // cannot happen in destructuring assignments because of how
                     // they are desugared.
                     if is_ordinary {
-                        let lhs_ty = self.infer_expr(lhs, &Expectation::none());
-                        self.infer_expr_coerce(*rhs, &Expectation::has_type(lhs_ty));
+                        // LHS of assignment doesn't constitute reads.
+                        let lhs_ty = self.infer_expr(lhs, &Expectation::none(), ExprIsRead::No);
+                        self.infer_expr_coerce(
+                            *rhs,
+                            &Expectation::has_type(lhs_ty),
+                            ExprIsRead::No,
+                        );
                     } else {
-                        let rhs_ty = self.infer_expr(*rhs, &Expectation::none());
+                        let rhs_ty = self.infer_expr(*rhs, &Expectation::none(), ExprIsRead::Yes);
                         self.infer_assignee_expr(lhs, &rhs_ty);
                     }
                     self.result.standard_types.unit.clone()
                 }
                 Some(BinaryOp::LogicOp(_)) => {
                     let bool_ty = self.result.standard_types.bool_.clone();
-                    self.infer_expr_coerce(*lhs, &Expectation::HasType(bool_ty.clone()));
+                    self.infer_expr_coerce(
+                        *lhs,
+                        &Expectation::HasType(bool_ty.clone()),
+                        ExprIsRead::Yes,
+                    );
                     let lhs_diverges = self.diverges;
-                    self.infer_expr_coerce(*rhs, &Expectation::HasType(bool_ty.clone()));
+                    self.infer_expr_coerce(
+                        *rhs,
+                        &Expectation::HasType(bool_ty.clone()),
+                        ExprIsRead::Yes,
+                    );
                     // Depending on the LHS' value, the RHS can never execute.
                     self.diverges = lhs_diverges;
                     bool_ty
@@ -741,11 +943,12 @@ impl InferenceContext<'_> {
                 _ => self.err_ty(),
             },
             Expr::Range { lhs, rhs, range_type } => {
-                let lhs_ty = lhs.map(|e| self.infer_expr_inner(e, &Expectation::none()));
+                let lhs_ty =
+                    lhs.map(|e| self.infer_expr_inner(e, &Expectation::none(), ExprIsRead::Yes));
                 let rhs_expect = lhs_ty
                     .as_ref()
                     .map_or_else(Expectation::none, |ty| Expectation::has_type(ty.clone()));
-                let rhs_ty = rhs.map(|e| self.infer_expr(e, &rhs_expect));
+                let rhs_ty = rhs.map(|e| self.infer_expr(e, &rhs_expect, ExprIsRead::Yes));
                 match (range_type, lhs_ty, rhs_ty) {
                     (RangeOp::Exclusive, None, None) => match self.resolve_range_full() {
                         Some(adt) => TyBuilder::adt(self.db, adt).build(),
@@ -779,8 +982,8 @@ impl InferenceContext<'_> {
                 }
             }
             Expr::Index { base, index, is_assignee_expr } => {
-                let base_ty = self.infer_expr_inner(*base, &Expectation::none());
-                let index_ty = self.infer_expr(*index, &Expectation::none());
+                let base_ty = self.infer_expr_inner(*base, &Expectation::none(), ExprIsRead::Yes);
+                let index_ty = self.infer_expr(*index, &Expectation::none(), ExprIsRead::Yes);
 
                 if let Some(index_trait) = self.resolve_lang_trait(LangItem::Index) {
                     let canonicalized = self.canonicalize(base_ty.clone());
@@ -851,7 +1054,11 @@ impl InferenceContext<'_> {
                 };
 
                 for (expr, ty) in exprs.iter().zip(tys.iter_mut()) {
-                    *ty = self.infer_expr_coerce(*expr, &Expectation::has_type(ty.clone()));
+                    *ty = self.infer_expr_coerce(
+                        *expr,
+                        &Expectation::has_type(ty.clone()),
+                        ExprIsRead::Yes,
+                    );
                 }
 
                 TyKind::Tuple(tys.len(), Substitution::from_iter(Interner, tys)).intern(Interner)
@@ -958,7 +1165,7 @@ impl InferenceContext<'_> {
             Expr::OffsetOf(_) => TyKind::Scalar(Scalar::Uint(UintTy::Usize)).intern(Interner),
             Expr::InlineAsm(asm) => {
                 let mut check_expr_asm_operand = |expr, is_input: bool| {
-                    let ty = self.infer_expr_no_expect(expr);
+                    let ty = self.infer_expr_no_expect(expr, ExprIsRead::Yes);
 
                     // If this is an input value, we require its type to be fully resolved
                     // at this point. This allows us to provide helpful coercions which help
@@ -975,11 +1182,11 @@ impl InferenceContext<'_> {
                                     CallableSig::from_def(self.db, *def, parameters).to_fn_ptr(),
                                 )
                                 .intern(Interner);
-                                _ = self.coerce(Some(expr), &ty, &fnptr_ty);
+                                _ = self.coerce(Some(expr), &ty, &fnptr_ty, CoerceNever::Yes);
                             }
                             TyKind::Ref(mutbl, _, base_ty) => {
                                 let ptr_ty = TyKind::Raw(*mutbl, base_ty.clone()).intern(Interner);
-                                _ = self.coerce(Some(expr), &ty, &ptr_ty);
+                                _ = self.coerce(Some(expr), &ty, &ptr_ty, CoerceNever::Yes);
                             }
                             _ => {}
                         }
@@ -1016,7 +1223,9 @@ impl InferenceContext<'_> {
         // use a new type variable if we got unknown here
         let ty = self.insert_type_vars_shallow(ty);
         self.write_expr_ty(tgt_expr, ty.clone());
-        if self.resolve_ty_shallow(&ty).is_never() {
+        if self.resolve_ty_shallow(&ty).is_never()
+            && self.expr_guaranteed_to_constitute_read_for_never(tgt_expr, is_read)
+        {
             // Any expression that produces a value of type `!` must have diverged
             self.diverges = Diverges::Always;
         }
@@ -1041,7 +1250,7 @@ impl InferenceContext<'_> {
         let (_, inner_ty) = self.with_breakable_ctx(BreakableKind::Border, None, None, |this| {
             let ty = this.infer_block(tgt_expr, *id, statements, *tail, None, expected);
             if let Some(target) = expected.only_has_type(&mut this.table) {
-                match this.coerce(Some(tgt_expr), &ty, &target) {
+                match this.coerce(Some(tgt_expr), &ty, &target, CoerceNever::Yes) {
                     Ok(res) => res,
                     Err(_) => {
                         this.result.type_mismatches.insert(
@@ -1153,7 +1362,7 @@ impl InferenceContext<'_> {
             Array::ElementList { elements, .. } => {
                 let mut coerce = CoerceMany::new(elem_ty);
                 for &expr in elements.iter() {
-                    let cur_elem_ty = self.infer_expr_inner(expr, &expected);
+                    let cur_elem_ty = self.infer_expr_inner(expr, &expected, ExprIsRead::Yes);
                     coerce.coerce(self, Some(expr), &cur_elem_ty, CoercionCause::Expr(expr));
                 }
                 (
@@ -1162,13 +1371,17 @@ impl InferenceContext<'_> {
                 )
             }
             &Array::Repeat { initializer, repeat } => {
-                self.infer_expr_coerce(initializer, &Expectation::has_type(elem_ty.clone()));
+                self.infer_expr_coerce(
+                    initializer,
+                    &Expectation::has_type(elem_ty.clone()),
+                    ExprIsRead::Yes,
+                );
                 let usize = TyKind::Scalar(Scalar::Uint(UintTy::Usize)).intern(Interner);
                 match self.body[repeat] {
                     Expr::Underscore => {
                         self.write_expr_ty(repeat, usize);
                     }
-                    _ => _ = self.infer_expr(repeat, &Expectation::HasType(usize)),
+                    _ => _ = self.infer_expr(repeat, &Expectation::HasType(usize), ExprIsRead::Yes),
                 }
 
                 (
@@ -1193,7 +1406,8 @@ impl InferenceContext<'_> {
             .as_mut()
             .expect("infer_return called outside function body")
             .expected_ty();
-        let return_expr_ty = self.infer_expr_inner(expr, &Expectation::HasType(ret_ty));
+        let return_expr_ty =
+            self.infer_expr_inner(expr, &Expectation::HasType(ret_ty), ExprIsRead::Yes);
         let mut coerce_many = self.return_coercion.take().unwrap();
         coerce_many.coerce(self, Some(expr), &return_expr_ty, CoercionCause::Expr(expr));
         self.return_coercion = Some(coerce_many);
@@ -1213,7 +1427,7 @@ impl InferenceContext<'_> {
             None => {
                 // FIXME: diagnose return outside of function
                 if let Some(expr) = expr {
-                    self.infer_expr_no_expect(expr);
+                    self.infer_expr_no_expect(expr, ExprIsRead::Yes);
                 }
             }
         }
@@ -1225,8 +1439,11 @@ impl InferenceContext<'_> {
             Some(return_coercion) => {
                 let ret_ty = return_coercion.expected_ty();
 
-                let call_expr_ty =
-                    self.infer_expr_inner(expr, &Expectation::HasType(ret_ty.clone()));
+                let call_expr_ty = self.infer_expr_inner(
+                    expr,
+                    &Expectation::HasType(ret_ty.clone()),
+                    ExprIsRead::Yes,
+                );
 
                 // NB: this should *not* coerce.
                 //     tail calls don't support any coercions except lifetimes ones (like `&'static u8 -> &'a u8`).
@@ -1234,7 +1451,7 @@ impl InferenceContext<'_> {
             }
             None => {
                 // FIXME: diagnose `become` outside of functions
-                self.infer_expr_no_expect(expr);
+                self.infer_expr_no_expect(expr, ExprIsRead::Yes);
             }
         }
 
@@ -1255,7 +1472,7 @@ impl InferenceContext<'_> {
                 })
                 .unwrap_or_else(Expectation::none);
 
-            let inner_ty = self.infer_expr_inner(inner_expr, &inner_exp);
+            let inner_ty = self.infer_expr_inner(inner_expr, &inner_exp, ExprIsRead::Yes);
             TyBuilder::adt(self.db, box_id)
                 .push(inner_ty)
                 .fill_with_defaults(self.db, || self.table.new_type_var())
@@ -1333,12 +1550,13 @@ impl InferenceContext<'_> {
             Expr::Underscore => rhs_ty.clone(),
             _ => {
                 // `lhs` is a place expression, a unit struct, or an enum variant.
-                let lhs_ty = self.infer_expr_inner(lhs, &Expectation::none());
+                // LHS of assignment doesn't constitute reads.
+                let lhs_ty = self.infer_expr_inner(lhs, &Expectation::none(), ExprIsRead::No);
 
                 // This is the only branch where this function may coerce any type.
                 // We are returning early to avoid the unifiability check below.
                 let lhs_ty = self.insert_type_vars_shallow(lhs_ty);
-                let ty = match self.coerce(None, &rhs_ty, &lhs_ty) {
+                let ty = match self.coerce(None, &rhs_ty, &lhs_ty, CoerceNever::Yes) {
                     Ok(ty) => ty,
                     Err(_) => {
                         self.result.type_mismatches.insert(
@@ -1373,7 +1591,12 @@ impl InferenceContext<'_> {
         tgt_expr: ExprId,
     ) -> Ty {
         let lhs_expectation = Expectation::none();
-        let lhs_ty = self.infer_expr(lhs, &lhs_expectation);
+        let is_read = if matches!(op, BinaryOp::Assignment { .. }) {
+            ExprIsRead::Yes
+        } else {
+            ExprIsRead::No
+        };
+        let lhs_ty = self.infer_expr(lhs, &lhs_expectation, is_read);
         let rhs_ty = self.table.new_type_var();
 
         let trait_func = lang_items_for_bin_op(op).and_then(|(name, lang_item)| {
@@ -1396,7 +1619,7 @@ impl InferenceContext<'_> {
                     self.err_ty()
                 };
 
-                self.infer_expr_coerce(rhs, &Expectation::has_type(rhs_ty));
+                self.infer_expr_coerce(rhs, &Expectation::has_type(rhs_ty), ExprIsRead::Yes);
 
                 return ret_ty;
             }
@@ -1415,7 +1638,7 @@ impl InferenceContext<'_> {
         let method_ty = self.db.value_ty(func.into()).unwrap().substitute(Interner, &subst);
         self.register_obligations_for_call(&method_ty);
 
-        self.infer_expr_coerce(rhs, &Expectation::has_type(rhs_ty.clone()));
+        self.infer_expr_coerce(rhs, &Expectation::has_type(rhs_ty.clone()), ExprIsRead::Yes);
 
         let ret_ty = match method_ty.callable_sig(self.db) {
             Some(sig) => {
@@ -1487,12 +1710,25 @@ impl InferenceContext<'_> {
                                 .unwrap_or_else(|| this.table.new_type_var());
 
                             let ty = if let Some(expr) = initializer {
+                                // If we have a subpattern that performs a read, we want to consider this
+                                // to diverge for compatibility to support something like `let x: () = *never_ptr;`.
+                                let target_is_read =
+                                    if this.pat_guaranteed_to_constitute_read_for_never(*pat) {
+                                        ExprIsRead::Yes
+                                    } else {
+                                        ExprIsRead::No
+                                    };
                                 let ty = if contains_explicit_ref_binding(this.body, *pat) {
-                                    this.infer_expr(*expr, &Expectation::has_type(decl_ty.clone()))
+                                    this.infer_expr(
+                                        *expr,
+                                        &Expectation::has_type(decl_ty.clone()),
+                                        target_is_read,
+                                    )
                                 } else {
                                     this.infer_expr_coerce(
                                         *expr,
                                         &Expectation::has_type(decl_ty.clone()),
+                                        target_is_read,
                                     )
                                 };
                                 if type_ref.is_some() {
@@ -1512,17 +1748,19 @@ impl InferenceContext<'_> {
                                 this.infer_expr_coerce(
                                     *expr,
                                     &Expectation::HasType(this.result.standard_types.never.clone()),
+                                    ExprIsRead::Yes,
                                 );
                                 this.diverges = previous_diverges;
                             }
                         }
                         &Statement::Expr { expr, has_semi } => {
                             if has_semi {
-                                this.infer_expr(expr, &Expectation::none());
+                                this.infer_expr(expr, &Expectation::none(), ExprIsRead::Yes);
                             } else {
                                 this.infer_expr_coerce(
                                     expr,
                                     &Expectation::HasType(this.result.standard_types.unit.clone()),
+                                    ExprIsRead::Yes,
                                 );
                             }
                         }
@@ -1532,7 +1770,7 @@ impl InferenceContext<'_> {
 
                 // FIXME: This should make use of the breakable CoerceMany
                 if let Some(expr) = tail {
-                    this.infer_expr_coerce(expr, expected)
+                    this.infer_expr_coerce(expr, expected, ExprIsRead::Yes)
                 } else {
                     // Citing rustc: if there is no explicit tail expression,
                     // that is typically equivalent to a tail expression
@@ -1545,8 +1783,20 @@ impl InferenceContext<'_> {
                         // we don't even make an attempt at coercion
                         this.table.new_maybe_never_var()
                     } else if let Some(t) = expected.only_has_type(&mut this.table) {
+                        let coerce_never = if this
+                            .expr_guaranteed_to_constitute_read_for_never(expr, ExprIsRead::Yes)
+                        {
+                            CoerceNever::Yes
+                        } else {
+                            CoerceNever::No
+                        };
                         if this
-                            .coerce(Some(expr), &this.result.standard_types.unit.clone(), &t)
+                            .coerce(
+                                Some(expr),
+                                &this.result.standard_types.unit.clone(),
+                                &t,
+                                coerce_never,
+                            )
                             .is_err()
                         {
                             this.result.type_mismatches.insert(
@@ -1658,7 +1908,8 @@ impl InferenceContext<'_> {
         name: &Name,
         expected: &Expectation,
     ) -> Ty {
-        let receiver_ty = self.infer_expr_inner(receiver, &Expectation::none());
+        // Field projections don't constitute reads.
+        let receiver_ty = self.infer_expr_inner(receiver, &Expectation::none(), ExprIsRead::No);
 
         if name.is_missing() {
             // Bail out early, don't even try to look up field. Also, we don't issue an unresolved
@@ -1730,7 +1981,7 @@ impl InferenceContext<'_> {
         generic_args: Option<&GenericArgs>,
         expected: &Expectation,
     ) -> Ty {
-        let receiver_ty = self.infer_expr_inner(receiver, &Expectation::none());
+        let receiver_ty = self.infer_expr_inner(receiver, &Expectation::none(), ExprIsRead::Yes);
         let canonicalized_receiver = self.canonicalize(receiver_ty.clone());
 
         let resolved = method_resolution::lookup_method(
@@ -1917,7 +2168,7 @@ impl InferenceContext<'_> {
                 let expected_ty = self.normalize_associated_types_in(expected_ty);
                 let expected = Expectation::rvalue_hint(self, expected_ty);
                 // infer with the expected type we have...
-                let ty = self.infer_expr_inner(arg, &expected);
+                let ty = self.infer_expr_inner(arg, &expected, ExprIsRead::Yes);
 
                 // then coerce to either the expected type or just the formal parameter type
                 let coercion_target = if let Some(ty) = expected.only_has_type(&mut self.table) {
@@ -1931,7 +2182,20 @@ impl InferenceContext<'_> {
                 // The function signature may contain some unknown types, so we need to insert
                 // type vars here to avoid type mismatch false positive.
                 let coercion_target = self.insert_type_vars(coercion_target);
-                if self.coerce(Some(arg), &ty, &coercion_target).is_err() && !arg_count_mismatch {
+
+                // Any expression that produces a value of type `!` must have diverged,
+                // unless it's a place expression that isn't being read from, in which case
+                // diverging would be unsound since we may never actually read the `!`.
+                // e.g. `let _ = *never_ptr;` with `never_ptr: *const !`.
+                let coerce_never =
+                    if self.expr_guaranteed_to_constitute_read_for_never(arg, ExprIsRead::Yes) {
+                        CoerceNever::Yes
+                    } else {
+                        CoerceNever::No
+                    };
+                if self.coerce(Some(arg), &ty, &coercion_target, coerce_never).is_err()
+                    && !arg_count_mismatch
+                {
                     self.result.type_mismatches.insert(
                         arg.into(),
                         TypeMismatch { expected: coercion_target, actual: ty.clone() },
@@ -2106,7 +2370,7 @@ impl InferenceContext<'_> {
             }
             let _ty = arg.data(Interner).ty.clone();
             let expected = Expectation::none(); // FIXME use actual const ty, when that is lowered correctly
-            self.infer_expr(args[arg_idx as usize], &expected);
+            self.infer_expr(args[arg_idx as usize], &expected, ExprIsRead::Yes);
             // FIXME: evaluate and unify with the const
         }
         let mut indices = legacy_const_generics_indices.as_ref().clone();
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/mutability.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/mutability.rs
index 8e52725e536..6a0daee6ea9 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/mutability.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/mutability.rs
@@ -12,12 +12,11 @@ use hir_expand::name::Name;
 use intern::sym;
 
 use crate::{
-    infer::Expectation, lower::lower_to_chalk_mutability, Adjust, Adjustment, AutoBorrow, Interner,
-    OverloadedDeref, TyBuilder, TyKind,
+    infer::{expr::ExprIsRead, Expectation, InferenceContext},
+    lower::lower_to_chalk_mutability,
+    Adjust, Adjustment, AutoBorrow, Interner, OverloadedDeref, TyBuilder, TyKind,
 };
 
-use super::InferenceContext;
-
 impl InferenceContext<'_> {
     pub(crate) fn infer_mut_body(&mut self) {
         self.infer_mut_expr(self.body.body_expr, Mutability::Not);
@@ -164,7 +163,11 @@ impl InferenceContext<'_> {
                                         if let Some(ty) = self.result.type_of_expr.get(index) {
                                             ty.clone()
                                         } else {
-                                            self.infer_expr(index, &Expectation::none())
+                                            self.infer_expr(
+                                                index,
+                                                &Expectation::none(),
+                                                ExprIsRead::Yes,
+                                            )
                                         };
                                     let trait_ref = TyBuilder::trait_ref(self.db, index_trait)
                                         .push(base_ty)
@@ -180,6 +183,7 @@ impl InferenceContext<'_> {
                 self.infer_mut_expr(index, Mutability::Not);
             }
             Expr::UnaryOp { expr, op: UnaryOp::Deref } => {
+                let mut mutability = mutability;
                 if let Some((f, _)) = self.result.method_resolutions.get_mut(&tgt_expr) {
                     if mutability == Mutability::Mut {
                         if let Some(deref_trait) = self
@@ -187,7 +191,17 @@ impl InferenceContext<'_> {
                             .lang_item(self.table.trait_env.krate, LangItem::DerefMut)
                             .and_then(|l| l.as_trait())
                         {
-                            if let Some(deref_fn) = self
+                            let ty = self.result.type_of_expr.get(*expr);
+                            let is_mut_ptr = ty.is_some_and(|ty| {
+                                let ty = self.table.resolve_ty_shallow(ty);
+                                matches!(
+                                    ty.kind(Interner),
+                                    chalk_ir::TyKind::Raw(Mutability::Mut, _)
+                                )
+                            });
+                            if is_mut_ptr {
+                                mutability = Mutability::Not;
+                            } else if let Some(deref_fn) = self
                                 .db
                                 .trait_data(deref_trait)
                                 .method_by_name(&Name::new_symbol_root(sym::deref_mut.clone()))
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/pat.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/pat.rs
index 50542b2acd4..fee6755408e 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/pat.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/pat.rs
@@ -12,7 +12,7 @@ use stdx::TupleExt;
 
 use crate::{
     consteval::{try_const_usize, usize_const},
-    infer::{BindingMode, Expectation, InferenceContext, TypeMismatch},
+    infer::{expr::ExprIsRead, BindingMode, Expectation, InferenceContext, TypeMismatch},
     lower::lower_to_chalk_mutability,
     primitive::UintTy,
     static_lifetime, InferenceDiagnostic, Interner, Mutability, Scalar, Substitution, Ty,
@@ -361,7 +361,7 @@ impl InferenceContext<'_> {
                 None => self.err_ty(),
             },
             Pat::ConstBlock(expr) => {
-                self.infer_expr(*expr, &Expectation::has_type(expected.clone()))
+                self.infer_expr(*expr, &Expectation::has_type(expected.clone()), ExprIsRead::Yes)
             }
             Pat::Missing => self.err_ty(),
         };
@@ -497,7 +497,7 @@ impl InferenceContext<'_> {
             }
         }
 
-        self.infer_expr(expr, &Expectation::has_type(expected.clone()))
+        self.infer_expr(expr, &Expectation::has_type(expected.clone()), ExprIsRead::Yes)
     }
 
     fn is_non_ref_pat(&mut self, body: &hir_def::body::Body, pat: PatId) -> bool {
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/interner.rs b/src/tools/rust-analyzer/crates/hir-ty/src/interner.rs
index 7f994783c11..3dbefc5cec8 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/interner.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/interner.rs
@@ -8,7 +8,7 @@ use crate::{
     ProgramClauseData, ProgramClauses, ProjectionTy, QuantifiedWhereClause, QuantifiedWhereClauses,
     Substitution, Ty, TyData, TyKind, VariableKind, VariableKinds,
 };
-use base_db::salsa::InternId;
+use base_db::ra_salsa::InternId;
 use chalk_ir::{ProgramClauseImplication, SeparatorTraitRef, Variance};
 use hir_def::TypeAliasId;
 use intern::{impl_internable, Interned};
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 4cdc0db46a1..2c68d50013e 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/layout.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/layout.rs
@@ -2,7 +2,7 @@
 
 use std::fmt;
 
-use base_db::salsa::Cycle;
+use base_db::ra_salsa::Cycle;
 use chalk_ir::{AdtId, FloatTy, IntTy, TyKind, UintTy};
 use hir_def::{
     layout::{
@@ -72,6 +72,8 @@ pub type Variants = hir_def::layout::Variants<RustcFieldIdx, RustcEnumVariantIdx
 
 #[derive(Debug, PartialEq, Eq, Clone)]
 pub enum LayoutError {
+    // FIXME: Remove variants that duplicate LayoutCalculatorError's variants after sync
+    BadCalc(LayoutCalculatorError<()>),
     EmptyUnion,
     HasErrorConst,
     HasErrorType,
@@ -90,6 +92,7 @@ impl std::error::Error for LayoutError {}
 impl fmt::Display for LayoutError {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
         match self {
+            LayoutError::BadCalc(err) => err.fallback_fmt(f),
             LayoutError::EmptyUnion => write!(f, "type is an union with no fields"),
             LayoutError::HasErrorConst => write!(f, "type contains an unevaluatable const"),
             LayoutError::HasErrorType => write!(f, "type contains an error"),
@@ -114,11 +117,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())
     }
 }
 
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/layout/adt.rs b/src/tools/rust-analyzer/crates/hir-ty/src/layout/adt.rs
index a060ebfe6be..c1a67fcc407 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/layout/adt.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/layout/adt.rs
@@ -2,7 +2,7 @@
 
 use std::{cmp, ops::Bound};
 
-use base_db::salsa::Cycle;
+use base_db::ra_salsa::Cycle;
 use hir_def::{
     data::adt::VariantData,
     layout::{Integer, ReprOptions, TargetDataLayout},
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs b/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs
index ef570a20556..649cf88bb8d 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs
@@ -56,7 +56,7 @@ use std::{
     hash::{BuildHasherDefault, Hash},
 };
 
-use base_db::salsa::InternValueTrivial;
+use base_db::ra_salsa::InternValueTrivial;
 use chalk_ir::{
     fold::{Shift, TypeFoldable},
     interner::HasInterner,
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs b/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs
index c6c2108e34a..c7ed68448bb 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs
@@ -11,7 +11,7 @@ use std::{
     ops::{self, Not as _},
 };
 
-use base_db::{salsa::Cycle, CrateId};
+use base_db::{ra_salsa::Cycle, CrateId};
 use chalk_ir::{
     cast::Cast,
     fold::{Shift, TypeFoldable},
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mapping.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mapping.rs
index c61d8277142..2f38e8fa14c 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/mapping.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/mapping.rs
@@ -5,7 +5,7 @@
 
 use chalk_solve::rust_ir;
 
-use base_db::salsa::{self, InternKey};
+use base_db::ra_salsa::{self, InternKey};
 use hir_def::{LifetimeParamId, TraitId, TypeAliasId, TypeOrConstParamId};
 
 use crate::{
@@ -116,24 +116,24 @@ impl From<crate::db::InternedCoroutineId> for chalk_ir::CoroutineId<Interner> {
 }
 
 pub fn to_foreign_def_id(id: TypeAliasId) -> ForeignDefId {
-    chalk_ir::ForeignDefId(salsa::InternKey::as_intern_id(&id))
+    chalk_ir::ForeignDefId(ra_salsa::InternKey::as_intern_id(&id))
 }
 
 pub fn from_foreign_def_id(id: ForeignDefId) -> TypeAliasId {
-    salsa::InternKey::from_intern_id(id.0)
+    ra_salsa::InternKey::from_intern_id(id.0)
 }
 
 pub fn to_assoc_type_id(id: TypeAliasId) -> AssocTypeId {
-    chalk_ir::AssocTypeId(salsa::InternKey::as_intern_id(&id))
+    chalk_ir::AssocTypeId(ra_salsa::InternKey::as_intern_id(&id))
 }
 
 pub fn from_assoc_type_id(id: AssocTypeId) -> TypeAliasId {
-    salsa::InternKey::from_intern_id(id.0)
+    ra_salsa::InternKey::from_intern_id(id.0)
 }
 
 pub fn from_placeholder_idx(db: &dyn HirDatabase, idx: PlaceholderIndex) -> TypeOrConstParamId {
     assert_eq!(idx.ui, chalk_ir::UniverseIndex::ROOT);
-    let interned_id = salsa::InternKey::from_intern_id(salsa::InternId::from(idx.idx));
+    let interned_id = ra_salsa::InternKey::from_intern_id(ra_salsa::InternId::from(idx.idx));
     db.lookup_intern_type_or_const_param_id(interned_id)
 }
 
@@ -141,13 +141,13 @@ pub fn to_placeholder_idx(db: &dyn HirDatabase, id: TypeOrConstParamId) -> Place
     let interned_id = db.intern_type_or_const_param_id(id);
     PlaceholderIndex {
         ui: chalk_ir::UniverseIndex::ROOT,
-        idx: salsa::InternKey::as_intern_id(&interned_id).as_usize(),
+        idx: ra_salsa::InternKey::as_intern_id(&interned_id).as_usize(),
     }
 }
 
 pub fn lt_from_placeholder_idx(db: &dyn HirDatabase, idx: PlaceholderIndex) -> LifetimeParamId {
     assert_eq!(idx.ui, chalk_ir::UniverseIndex::ROOT);
-    let interned_id = salsa::InternKey::from_intern_id(salsa::InternId::from(idx.idx));
+    let interned_id = ra_salsa::InternKey::from_intern_id(ra_salsa::InternId::from(idx.idx));
     db.lookup_intern_lifetime_param_id(interned_id)
 }
 
@@ -155,14 +155,14 @@ pub fn lt_to_placeholder_idx(db: &dyn HirDatabase, id: LifetimeParamId) -> Place
     let interned_id = db.intern_lifetime_param_id(id);
     PlaceholderIndex {
         ui: chalk_ir::UniverseIndex::ROOT,
-        idx: salsa::InternKey::as_intern_id(&interned_id).as_usize(),
+        idx: ra_salsa::InternKey::as_intern_id(&interned_id).as_usize(),
     }
 }
 
 pub fn to_chalk_trait_id(id: TraitId) -> ChalkTraitId {
-    chalk_ir::TraitId(salsa::InternKey::as_intern_id(&id))
+    chalk_ir::TraitId(ra_salsa::InternKey::as_intern_id(&id))
 }
 
 pub fn from_chalk_trait_id(id: ChalkTraitId) -> TraitId {
-    salsa::InternKey::from_intern_id(id.0)
+    ra_salsa::InternKey::from_intern_id(id.0)
 }
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower.rs
index a2cb122c543..16994cdd0c6 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower.rs
@@ -2,7 +2,7 @@
 
 use std::{fmt::Write, iter, mem};
 
-use base_db::salsa::Cycle;
+use base_db::ra_salsa::Cycle;
 use chalk_ir::{BoundVar, ConstData, DebruijnIndex, TyKind};
 use hir_def::{
     body::Body,
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir/monomorphization.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/monomorphization.rs
index 8f6582b7f80..4c6bc376e2b 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/mir/monomorphization.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir/monomorphization.rs
@@ -9,7 +9,7 @@
 
 use std::mem;
 
-use base_db::salsa::Cycle;
+use base_db::ra_salsa::Cycle;
 use chalk_ir::{
     fold::{FallibleTypeFolder, TypeFoldable, TypeSuperFoldable},
     ConstData, DebruijnIndex,
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/test_db.rs b/src/tools/rust-analyzer/crates/hir-ty/src/test_db.rs
index 0efb9c52fb6..f37dd91d8e9 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/test_db.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/test_db.rs
@@ -3,7 +3,7 @@
 use std::{fmt, panic, sync::Mutex};
 
 use base_db::{
-    salsa::{self, Durability},
+    ra_salsa::{self, Durability},
     AnchoredPath, CrateId, FileLoader, FileLoaderDelegate, SourceDatabase, Upcast,
 };
 use hir_def::{db::DefDatabase, ModuleId};
@@ -14,7 +14,7 @@ use syntax::TextRange;
 use test_utils::extract_annotations;
 use triomphe::Arc;
 
-#[salsa::database(
+#[ra_salsa::database(
     base_db::SourceRootDatabaseStorage,
     base_db::SourceDatabaseStorage,
     hir_expand::db::ExpandDatabaseStorage,
@@ -23,8 +23,8 @@ use triomphe::Arc;
     crate::db::HirDatabaseStorage
 )]
 pub(crate) struct TestDB {
-    storage: salsa::Storage<TestDB>,
-    events: Mutex<Option<Vec<salsa::Event>>>,
+    storage: ra_salsa::Storage<TestDB>,
+    events: Mutex<Option<Vec<ra_salsa::Event>>>,
 }
 
 impl Default for TestDB {
@@ -54,8 +54,8 @@ impl Upcast<dyn DefDatabase> for TestDB {
     }
 }
 
-impl salsa::Database for TestDB {
-    fn salsa_event(&self, event: salsa::Event) {
+impl ra_salsa::Database for TestDB {
+    fn salsa_event(&self, event: ra_salsa::Event) {
         let mut events = self.events.lock().unwrap();
         if let Some(events) = &mut *events {
             events.push(event);
@@ -63,9 +63,9 @@ impl salsa::Database for TestDB {
     }
 }
 
-impl salsa::ParallelDatabase for TestDB {
-    fn snapshot(&self) -> salsa::Snapshot<TestDB> {
-        salsa::Snapshot::new(TestDB {
+impl ra_salsa::ParallelDatabase for TestDB {
+    fn snapshot(&self) -> ra_salsa::Snapshot<TestDB> {
+        ra_salsa::Snapshot::new(TestDB {
             storage: self.storage.snapshot(),
             events: Default::default(),
         })
@@ -128,7 +128,7 @@ impl TestDB {
 }
 
 impl TestDB {
-    pub(crate) fn log(&self, f: impl FnOnce()) -> Vec<salsa::Event> {
+    pub(crate) fn log(&self, f: impl FnOnce()) -> Vec<ra_salsa::Event> {
         *self.events.lock().unwrap() = Some(Vec::new());
         f();
         self.events.lock().unwrap().take().unwrap()
@@ -141,7 +141,7 @@ impl TestDB {
             .filter_map(|e| match e.kind {
                 // This is pretty horrible, but `Debug` is the only way to inspect
                 // QueryDescriptor at the moment.
-                salsa::EventKind::WillExecute { database_key } => {
+                ra_salsa::EventKind::WillExecute { database_key } => {
                     Some(format!("{:?}", database_key.debug(self)))
                 }
                 _ => None,
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/closure_captures.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/closure_captures.rs
index 22cef3505bf..b63d632dd26 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/closure_captures.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/closure_captures.rs
@@ -1,4 +1,4 @@
-use base_db::salsa::InternKey;
+use base_db::ra_salsa::InternKey;
 use expect_test::{expect, Expect};
 use hir_def::db::DefDatabase;
 use hir_expand::files::InFileWrapper;
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/macros.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/macros.rs
index 53b69c12f05..446f0b21a2a 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/macros.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/macros.rs
@@ -64,7 +64,7 @@ fn infer_macros_expanded() {
         "#,
         expect![[r#"
             !0..17 '{Foo(v...,2,])}': Foo
-            !1..4 'Foo': extern "rust-call" Foo({unknown}) -> Foo
+            !1..4 'Foo': fn Foo({unknown}) -> Foo
             !1..16 'Foo(vec![1,2,])': Foo
             !5..15 'vec![1,2,]': {unknown}
             155..181 '{     ...,2); }': ()
@@ -97,7 +97,7 @@ fn infer_legacy_textual_scoped_macros_expanded() {
         "#,
         expect![[r#"
             !0..17 '{Foo(v...,2,])}': Foo
-            !1..4 'Foo': extern "rust-call" Foo({unknown}) -> Foo
+            !1..4 'Foo': fn Foo({unknown}) -> Foo
             !1..16 'Foo(vec![1,2,])': Foo
             !5..15 'vec![1,2,]': {unknown}
             194..250 '{     ...,2); }': ()
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/never_type.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/never_type.rs
index 5c63cd00f97..1ca4c9b2ad5 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/never_type.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/never_type.rs
@@ -539,3 +539,249 @@ fn test() {
 "#,
     );
 }
+
+#[test]
+fn diverging_place_match1() {
+    check_infer_with_mismatches(
+        r#"
+//- minicore: sized
+fn not_a_read() -> ! {
+    unsafe {
+        let x: *const ! = 0 as _;
+        let _: ! = *x;
+    }
+}
+"#,
+        expect![[r#"
+            21..100 '{     ...   } }': !
+            27..98 'unsafe...     }': !
+            48..49 'x': *const !
+            62..63 '0': i32
+            62..68 '0 as _': *const !
+            82..83 '_': !
+            89..91 '*x': !
+            90..91 'x': *const !
+            27..98: expected !, got ()
+        "#]],
+    )
+}
+
+#[test]
+fn diverging_place_match2() {
+    check_infer_with_mismatches(
+        r#"
+//- minicore: sized
+fn not_a_read_implicit() -> ! {
+    unsafe {
+        let x: *const ! = 0 as _;
+        let _ = *x;
+    }
+}
+"#,
+        expect![[r#"
+            30..106 '{     ...   } }': !
+            36..104 'unsafe...     }': !
+            57..58 'x': *const !
+            71..72 '0': i32
+            71..77 '0 as _': *const !
+            91..92 '_': !
+            95..97 '*x': !
+            96..97 'x': *const !
+            36..104: expected !, got ()
+        "#]],
+    )
+}
+
+#[test]
+fn diverging_place_match3() {
+    check_infer_with_mismatches(
+        r#"
+//- minicore: sized
+fn not_a_read_guide_coercion() -> ! {
+    unsafe {
+        let x: *const ! = 0 as _;
+        let _: () = *x;
+    }
+}
+"#,
+        expect![[r#"
+            36..116 '{     ...   } }': !
+            42..114 'unsafe...     }': !
+            63..64 'x': *const !
+            77..78 '0': i32
+            77..83 '0 as _': *const !
+            97..98 '_': ()
+            105..107 '*x': !
+            106..107 'x': *const !
+            42..114: expected !, got ()
+            105..107: expected (), got !
+        "#]],
+    )
+}
+
+#[test]
+fn diverging_place_match4() {
+    check_infer_with_mismatches(
+        r#"
+//- minicore: sized
+fn empty_match() -> ! {
+    unsafe {
+        let x: *const ! = 0 as _;
+        match *x { _ => {} };
+    }
+}
+"#,
+        expect![[r#"
+            22..108 '{     ...   } }': !
+            28..106 'unsafe...     }': !
+            49..50 'x': *const !
+            63..64 '0': i32
+            63..69 '0 as _': *const !
+            79..99 'match ...> {} }': ()
+            85..87 '*x': !
+            86..87 'x': *const !
+            90..91 '_': !
+            95..97 '{}': ()
+            28..106: expected !, got ()
+        "#]],
+    )
+}
+
+#[test]
+fn diverging_place_match5() {
+    check_infer_with_mismatches(
+        r#"
+//- minicore: sized
+fn field_projection() -> ! {
+    unsafe {
+        let x: *const (!, ()) = 0 as _;
+        let _ = (*x).0;
+    }
+}
+"#,
+        expect![[r#"
+            27..113 '{     ...   } }': !
+            33..111 'unsafe...     }': !
+            54..55 'x': *const (!, ())
+            74..75 '0': i32
+            74..80 '0 as _': *const (!, ())
+            94..95 '_': !
+            98..104 '(*x).0': !
+            99..101 '*x': (!, ())
+            100..101 'x': *const (!, ())
+            33..111: expected !, got ()
+        "#]],
+    )
+}
+
+#[test]
+fn diverging_place_match6() {
+    check_infer_with_mismatches(
+        r#"
+//- minicore: sized
+fn covered_arm() -> ! {
+    unsafe {
+        let x: *const ! = 0 as _;
+        let (_ | 1i32) = *x;
+    }
+}
+"#,
+        expect![[r#"
+            22..107 '{     ...   } }': !
+            28..105 'unsafe...     }': !
+            49..50 'x': *const !
+            63..64 '0': i32
+            63..69 '0 as _': *const !
+            84..85 '_': !
+            84..92 '_ | 1i32': !
+            88..92 '1i32': i32
+            88..92 '1i32': i32
+            96..98 '*x': !
+            97..98 'x': *const !
+            28..105: expected !, got ()
+            88..92: expected !, got i32
+        "#]],
+    )
+}
+
+#[test]
+fn diverging_place_match7() {
+    check_infer_with_mismatches(
+        r#"
+//- minicore: sized
+fn uncovered_arm() -> ! {
+    unsafe {
+        let x: *const ! = 0 as _;
+        let (1i32 | _) = *x;
+    }
+}
+"#,
+        expect![[r#"
+            24..109 '{     ...   } }': !
+            30..107 'unsafe...     }': !
+            51..52 'x': *const !
+            65..66 '0': i32
+            65..71 '0 as _': *const !
+            86..90 '1i32': i32
+            86..90 '1i32': i32
+            86..94 '1i32 | _': !
+            93..94 '_': !
+            98..100 '*x': !
+            99..100 'x': *const !
+            30..107: expected !, got ()
+            86..90: expected !, got i32
+        "#]],
+    )
+}
+
+#[test]
+fn diverging_place_match8() {
+    check_infer_with_mismatches(
+        r#"
+//- minicore: sized
+fn coerce_ref_binding() -> ! {
+    unsafe {
+        let x: *const ! = 0 as _;
+        let ref _x: () = *x;
+    }
+}
+"#,
+        expect![[r#"
+            29..114 '{     ...   } }': !
+            35..112 'unsafe...     }': !
+            56..57 'x': *const !
+            70..71 '0': i32
+            70..76 '0 as _': *const !
+            90..96 'ref _x': &'? ()
+            103..105 '*x': !
+            104..105 'x': *const !
+            103..105: expected (), got !
+        "#]],
+    )
+}
+
+#[test]
+fn never_place_isnt_diverging() {
+    check_infer_with_mismatches(
+        r#"
+//- minicore: sized
+fn make_up_a_pointer<T>() -> *const T {
+    unsafe {
+        let x: *const ! = 0 as _;
+        &raw const *x
+    }
+}
+"#,
+        expect![[r#"
+            38..116 '{     ...   } }': *const T
+            44..114 'unsafe...     }': *const T
+            65..66 'x': *const !
+            79..80 '0': i32
+            79..85 '0 as _': *const !
+            95..108 '&raw const *x': *const !
+            106..108 '*x': !
+            107..108 'x': *const !
+            95..108: expected *const T, got *const !
+        "#]],
+    )
+}
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/patterns.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/patterns.rs
index 51c27f8714a..4949d4016bf 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/patterns.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/patterns.rs
@@ -227,13 +227,13 @@ fn infer_pattern_match_ergonomics() {
             37..41 'A(n)': A<i32>
             39..40 'n': &'? i32
             44..49 '&A(1)': &'? A<i32>
-            45..46 'A': extern "rust-call" A<i32>(i32) -> A<i32>
+            45..46 'A': fn A<i32>(i32) -> A<i32>
             45..49 'A(1)': A<i32>
             47..48 '1': i32
             59..63 'A(n)': A<i32>
             61..62 'n': &'? mut i32
             66..75 '&mut A(1)': &'? mut A<i32>
-            71..72 'A': extern "rust-call" A<i32>(i32) -> A<i32>
+            71..72 'A': fn A<i32>(i32) -> A<i32>
             71..75 'A(1)': A<i32>
             73..74 '1': i32
         "#]],
@@ -548,18 +548,18 @@ impl Foo {
             56..64 'Self(s,)': Foo
             61..62 's': &'? usize
             67..75 '&Foo(0,)': &'? Foo
-            68..71 'Foo': extern "rust-call" Foo(usize) -> Foo
+            68..71 'Foo': fn Foo(usize) -> Foo
             68..75 'Foo(0,)': Foo
             72..73 '0': usize
             89..97 'Self(s,)': Foo
             94..95 's': &'? mut usize
             100..112 '&mut Foo(0,)': &'? mut Foo
-            105..108 'Foo': extern "rust-call" Foo(usize) -> Foo
+            105..108 'Foo': fn Foo(usize) -> Foo
             105..112 'Foo(0,)': Foo
             109..110 '0': usize
             126..134 'Self(s,)': Foo
             131..132 's': usize
-            137..140 'Foo': extern "rust-call" Foo(usize) -> Foo
+            137..140 'Foo': fn Foo(usize) -> Foo
             137..144 'Foo(0,)': Foo
             141..142 '0': usize
         "#]],
@@ -933,7 +933,7 @@ fn foo(foo: Foo) {
             48..51 'foo': Foo
             62..84 'const ... 32) }': Foo
             68..84 '{ Foo(... 32) }': Foo
-            70..73 'Foo': extern "rust-call" Foo(usize) -> Foo
+            70..73 'Foo': fn Foo(usize) -> Foo
             70..82 'Foo(15 + 32)': Foo
             74..76 '15': usize
             74..81 '15 + 32': usize
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression.rs
index a3cf12d8a16..c4822a90f9e 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression.rs
@@ -645,7 +645,7 @@ fn issue_4953() {
         "#,
         expect![[r#"
             58..72 '{ Self(0i64) }': Foo
-            60..64 'Self': extern "rust-call" Foo(i64) -> Foo
+            60..64 'Self': fn Foo(i64) -> Foo
             60..70 'Self(0i64)': Foo
             65..69 '0i64': i64
         "#]],
@@ -659,7 +659,7 @@ fn issue_4953() {
         "#,
         expect![[r#"
             64..78 '{ Self(0i64) }': Foo<i64>
-            66..70 'Self': extern "rust-call" Foo<i64>(i64) -> Foo<i64>
+            66..70 'Self': fn Foo<i64>(i64) -> Foo<i64>
             66..76 'Self(0i64)': Foo<i64>
             71..75 '0i64': i64
         "#]],
@@ -859,7 +859,7 @@ fn main() {
             94..96 '{}': ()
             109..160 '{     ...10); }': ()
             119..120 's': S<i32>
-            123..124 'S': extern "rust-call" S<i32>() -> S<i32>
+            123..124 'S': fn S<i32>() -> S<i32>
             123..126 'S()': S<i32>
             132..133 's': S<i32>
             132..144 's.g(|_x| {})': ()
@@ -1616,7 +1616,7 @@ fn main() {
             37..48 'S(.., a, b)': S
             43..44 'a': usize
             46..47 'b': {unknown}
-            51..52 'S': extern "rust-call" S(usize) -> S
+            51..52 'S': fn S(usize) -> S
             51..55 'S(1)': S
             53..54 '1': usize
             65..75 '(.., a, b)': (i32, {unknown})
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/simple.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/simple.rs
index 0473ee02fab..a8170b60606 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/simple.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/simple.rs
@@ -236,14 +236,14 @@ fn test() {
         expect![[r#"
             71..153 '{     ...a.c; }': ()
             81..82 'c': C
-            85..86 'C': extern "rust-call" C(usize) -> C
+            85..86 'C': fn C(usize) -> C
             85..89 'C(1)': C
             87..88 '1': usize
             95..96 'B': B
             106..107 'a': A
             113..132 'A { b:...C(1) }': A
             120..121 'B': B
-            126..127 'C': extern "rust-call" C(usize) -> C
+            126..127 'C': fn C(usize) -> C
             126..130 'C(1)': C
             128..129 '1': usize
             138..139 'a': A
@@ -629,12 +629,12 @@ impl E {
             86..107 '{     ...     }': ()
             96..100 'Self': S1
             134..158 '{     ...     }': ()
-            144..148 'Self': extern "rust-call" S2(isize) -> S2
+            144..148 'Self': fn S2(isize) -> S2
             144..151 'Self(1)': S2
             149..150 '1': isize
             184..230 '{     ...     }': ()
             194..202 'Self::V1': E
-            212..220 'Self::V2': extern "rust-call" V2(u32) -> E
+            212..220 'Self::V2': fn V2(u32) -> E
             212..223 'Self::V2(1)': E
             221..222 '1': u32
         "#]],
@@ -860,11 +860,11 @@ fn test() {
             256..277 'A::foo...42))))': &'? i32
             263..276 '&&B(B(A(42)))': &'? &'? B<B<A<i32>>>
             264..276 '&B(B(A(42)))': &'? B<B<A<i32>>>
-            265..266 'B': extern "rust-call" B<B<A<i32>>>(B<A<i32>>) -> B<B<A<i32>>>
+            265..266 'B': fn B<B<A<i32>>>(B<A<i32>>) -> B<B<A<i32>>>
             265..276 'B(B(A(42)))': B<B<A<i32>>>
-            267..268 'B': extern "rust-call" B<A<i32>>(A<i32>) -> B<A<i32>>
+            267..268 'B': fn B<A<i32>>(A<i32>) -> B<A<i32>>
             267..275 'B(A(42))': B<A<i32>>
-            269..270 'A': extern "rust-call" A<i32>(i32) -> A<i32>
+            269..270 'A': fn A<i32>(i32) -> A<i32>
             269..274 'A(42)': A<i32>
             271..273 '42': i32
         "#]],
@@ -914,16 +914,16 @@ fn test(a: A<i32>) {
             253..254 'a': A<i32>
             264..310 '{     ...))); }': ()
             274..275 't': &'? i32
-            278..279 'A': extern "rust-call" A<i32>(*mut i32) -> A<i32>
+            278..279 'A': fn A<i32>(*mut i32) -> A<i32>
             278..292 'A(0 as *mut _)': A<i32>
             278..307 'A(0 as...B(a)))': &'? i32
             280..281 '0': usize
             280..291 '0 as *mut _': *mut i32
             297..306 '&&B(B(a))': &'? &'? B<B<A<i32>>>
             298..306 '&B(B(a))': &'? B<B<A<i32>>>
-            299..300 'B': extern "rust-call" B<B<A<i32>>>(B<A<i32>>) -> B<B<A<i32>>>
+            299..300 'B': fn B<B<A<i32>>>(B<A<i32>>) -> B<B<A<i32>>>
             299..306 'B(B(a))': B<B<A<i32>>>
-            301..302 'B': extern "rust-call" B<A<i32>>(A<i32>) -> B<A<i32>>
+            301..302 'B': fn B<A<i32>>(A<i32>) -> B<A<i32>>
             301..305 'B(a)': B<A<i32>>
             303..304 'a': A<i32>
         "#]],
@@ -1277,16 +1277,16 @@ fn infer_tuple_struct_generics() {
         "#,
         expect![[r#"
             75..183 '{     ...one; }': ()
-            81..82 'A': extern "rust-call" A<i32>(i32) -> A<i32>
+            81..82 'A': fn A<i32>(i32) -> A<i32>
             81..86 'A(42)': A<i32>
             83..85 '42': i32
-            92..93 'A': extern "rust-call" A<u128>(u128) -> A<u128>
+            92..93 'A': fn A<u128>(u128) -> A<u128>
             92..101 'A(42u128)': A<u128>
             94..100 '42u128': u128
-            107..111 'Some': extern "rust-call" Some<&'static str>(&'static str) -> Option<&'static str>
+            107..111 'Some': fn Some<&'static str>(&'static str) -> Option<&'static str>
             107..116 'Some("x")': Option<&'static str>
             112..115 '"x"': &'static str
-            122..134 'Option::Some': extern "rust-call" Some<&'static str>(&'static str) -> Option<&'static str>
+            122..134 'Option::Some': fn Some<&'static str>(&'static str) -> Option<&'static str>
             122..139 'Option...e("x")': Option<&'static str>
             135..138 '"x"': &'static str
             145..149 'None': Option<{unknown}>
@@ -1572,7 +1572,7 @@ fn infer_type_alias() {
             204..207 'z.y': i8
             298..362 '{     ... &e; }': ()
             308..309 'e': Enum
-            312..325 'm::Alias::Foo': extern "rust-call" Foo(u8) -> Enum
+            312..325 'm::Alias::Foo': fn Foo(u8) -> Enum
             312..328 'm::Ali...Foo(0)': Enum
             326..327 '0': u8
             338..354 'm::Ali...Foo(x)': Enum
@@ -2191,10 +2191,10 @@ fn main() {
             103..231 '{     ... }); }': ()
             109..161 'async ...     }': impl Future<Output = Result<(), ()>>
             125..139 'return Err(())': !
-            132..135 'Err': extern "rust-call" Err<(), ()>(()) -> Result<(), ()>
+            132..135 'Err': fn Err<(), ()>(()) -> Result<(), ()>
             132..139 'Err(())': Result<(), ()>
             136..138 '()': ()
-            149..151 'Ok': extern "rust-call" Ok<(), ()>(()) -> Result<(), ()>
+            149..151 'Ok': fn Ok<(), ()>(()) -> Result<(), ()>
             149..155 'Ok(())': Result<(), ()>
             152..154 '()': ()
             167..171 'test': fn test<(), (), impl FnMut() -> impl Future<Output = Result<(), ()>>, impl Future<Output = Result<(), ()>>>(impl FnMut() -> impl Future<Output = Result<(), ()>>)
@@ -2202,10 +2202,10 @@ fn main() {
             172..227 '|| asy...     }': impl FnMut() -> impl Future<Output = Result<(), ()>>
             175..227 'async ...     }': impl Future<Output = Result<(), ()>>
             191..205 'return Err(())': !
-            198..201 'Err': extern "rust-call" Err<(), ()>(()) -> Result<(), ()>
+            198..201 'Err': fn Err<(), ()>(()) -> Result<(), ()>
             198..205 'Err(())': Result<(), ()>
             202..204 '()': ()
-            215..217 'Ok': extern "rust-call" Ok<(), ()>(()) -> Result<(), ()>
+            215..217 'Ok': fn Ok<(), ()>(()) -> Result<(), ()>
             215..221 'Ok(())': Result<(), ()>
             218..220 '()': ()
         "#]],
@@ -2234,7 +2234,7 @@ fn infer_generic_from_later_assignment() {
             94..127 '{     ...     }': ()
             104..107 'end': Option<bool>
             104..120 'end = ...(true)': ()
-            110..114 'Some': extern "rust-call" Some<bool>(bool) -> Option<bool>
+            110..114 'Some': fn Some<bool>(bool) -> Option<bool>
             110..120 'Some(true)': Option<bool>
             115..119 'true': bool
         "#]],
@@ -2269,7 +2269,7 @@ fn infer_loop_break_with_val() {
             111..121 'break None': !
             117..121 'None': Option<bool>
             142..158 'break ...(true)': !
-            148..152 'Some': extern "rust-call" Some<bool>(bool) -> Option<bool>
+            148..152 'Some': fn Some<bool>(bool) -> Option<bool>
             148..158 'Some(true)': Option<bool>
             153..157 'true': bool
         "#]],
@@ -2516,7 +2516,7 @@ fn generic_default_in_struct_literal() {
             254..281 'OtherT...1i32 }': OtherThing<i32>
             275..279 '1i32': i32
             291..292 'b': OtherThing<i32>
-            295..310 'OtherThing::Two': extern "rust-call" Two<i32>(i32) -> OtherThing<i32>
+            295..310 'OtherThing::Two': fn Two<i32>(i32) -> OtherThing<i32>
             295..316 'OtherT...(1i32)': OtherThing<i32>
             311..315 '1i32': i32
         "#]],
@@ -3028,7 +3028,7 @@ fn f() {
         expect![[r#"
             72..166 '{     ...   } }': ()
             78..164 'match ...     }': ()
-            84..92 'Foo::Bar': extern "rust-call" Bar(i32) -> Foo
+            84..92 'Foo::Bar': fn Bar(i32) -> Foo
             84..95 'Foo::Bar(3)': Foo
             93..94 '3': i32
             106..119 'Qux::Bar(bar)': Foo
@@ -3087,9 +3087,9 @@ fn main() {
             322..324 '{}': Foo<T>
             338..559 '{     ...r(); }': ()
             348..353 'boxed': Box<Foo<i32>>
-            356..359 'Box': extern "rust-call" Box<Foo<i32>>(Foo<i32>) -> Box<Foo<i32>>
+            356..359 'Box': fn Box<Foo<i32>>(Foo<i32>) -> Box<Foo<i32>>
             356..371 'Box(Foo(0_i32))': Box<Foo<i32>>
-            360..363 'Foo': extern "rust-call" Foo<i32>(i32) -> Foo<i32>
+            360..363 'Foo': fn Foo<i32>(i32) -> Foo<i32>
             360..370 'Foo(0_i32)': Foo<i32>
             364..369 '0_i32': i32
             382..386 'bad1': &'? i32
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/traits.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/traits.rs
index 0b2d6bdd259..9b982a124e7 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/traits.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/traits.rs
@@ -523,7 +523,7 @@ fn test() -> u64 {
         expect![[r#"
             37..86 '{     ... a.1 }': u64
             47..48 'a': S
-            51..52 'S': extern "rust-call" S(i32, u64) -> S
+            51..52 'S': fn S(i32, u64) -> S
             51..58 'S(4, 6)': S
             53..54 '4': i32
             56..57 '6': u64
@@ -549,7 +549,7 @@ fn test() -> u64 {
         expect![[r#"
             43..108 '{     ...0(2) }': u64
             53..54 'a': S
-            57..58 'S': extern "rust-call" S(fn(u32) -> u64) -> S
+            57..58 'S': fn S(fn(u32) -> u64) -> S
             57..74 'S(|i| ...s u64)': S
             59..73 '|i| 2*i as u64': impl Fn(u32) -> u64
             60..61 'i': u32
@@ -1027,7 +1027,7 @@ fn test(x: impl Trait<u64>, y: &impl Trait<u32>) {
             201..202 'x': impl Trait<u64>
             208..209 'y': &'? impl Trait<u32>
             219..220 'z': S<u16>
-            223..224 'S': extern "rust-call" S<u16>(u16) -> S<u16>
+            223..224 'S': fn S<u16>(u16) -> S<u16>
             223..227 'S(1)': S<u16>
             225..226 '1': u16
             233..236 'bar': fn bar(S<u16>)
@@ -1269,10 +1269,10 @@ fn bar() {
             226..229 'foo': fn foo<i32>([R<(), i32>; 2]) -> i32
             226..250 'foo([R...B(7)])': i32
             230..249 '[R::A(...:B(7)]': [R<(), i32>; 2]
-            231..235 'R::A': extern "rust-call" A<(), i32>(()) -> R<(), i32>
+            231..235 'R::A': fn A<(), i32>(()) -> R<(), i32>
             231..239 'R::A(())': R<(), i32>
             236..238 '()': ()
-            241..245 'R::B': extern "rust-call" B<(), i32>(i32) -> R<(), i32>
+            241..245 'R::B': fn B<(), i32>(i32) -> R<(), i32>
             241..248 'R::B(7)': R<(), i32>
             246..247 '7': i32
         "#]],
@@ -1421,7 +1421,7 @@ fn foo<const C: u8, T>() -> (impl FnOnce(&str, T), impl Trait<u8>) {
             142..147 'input': &'? str
             149..150 't': T
             152..154 '{}': ()
-            156..159 'Bar': extern "rust-call" Bar<u8>(u8) -> Bar<u8>
+            156..159 'Bar': fn Bar<u8>(u8) -> Bar<u8>
             156..162 'Bar(C)': Bar<u8>
             160..161 'C': u8
         "#]],
@@ -2046,7 +2046,7 @@ fn test() {
             118..120 '{}': ()
             136..255 '{     ... 1); }': ()
             146..147 'x': Option<u32>
-            150..162 'Option::Some': extern "rust-call" Some<u32>(u32) -> Option<u32>
+            150..162 'Option::Some': fn Some<u32>(u32) -> Option<u32>
             150..168 'Option...(1u32)': Option<u32>
             163..167 '1u32': u32
             174..175 'x': Option<u32>
@@ -2602,7 +2602,7 @@ fn test() -> impl Trait<i32> {
             178..180 '{}': ()
             213..309 '{     ...t()) }': S<i32>
             223..225 's1': S<u32>
-            228..229 'S': extern "rust-call" S<u32>(u32) -> S<u32>
+            228..229 'S': fn S<u32>(u32) -> S<u32>
             228..240 'S(default())': S<u32>
             230..237 'default': fn default<u32>() -> u32
             230..239 'default()': u32
@@ -2612,11 +2612,11 @@ fn test() -> impl Trait<i32> {
             263..264 'x': i32
             272..275 'bar': fn bar<i32>(S<i32>) -> i32
             272..289 'bar(S(...lt()))': i32
-            276..277 'S': extern "rust-call" S<i32>(i32) -> S<i32>
+            276..277 'S': fn S<i32>(i32) -> S<i32>
             276..288 'S(default())': S<i32>
             278..285 'default': fn default<i32>() -> i32
             278..287 'default()': i32
-            295..296 'S': extern "rust-call" S<i32>(i32) -> S<i32>
+            295..296 'S': fn S<i32>(i32) -> S<i32>
             295..307 'S(default())': S<i32>
             297..304 'default': fn default<i32>() -> i32
             297..306 'default()': i32
@@ -2846,7 +2846,7 @@ fn main() {
             1036..1041 'x > 0': bool
             1040..1041 '0': i32
             1042..1060 '{ Some...u32) }': Option<u32>
-            1044..1048 'Some': extern "rust-call" Some<u32>(u32) -> Option<u32>
+            1044..1048 'Some': fn Some<u32>(u32) -> Option<u32>
             1044..1058 'Some(x as u32)': Option<u32>
             1049..1050 'x': i32
             1049..1057 'x as u32': u32
@@ -2982,9 +2982,9 @@ fn test() {
             175..185 'foo.test()': bool
             191..194 'bar': fn bar<{unknown}>({unknown}) -> {unknown}
             191..201 'bar.test()': bool
-            207..213 'Struct': extern "rust-call" Struct(usize) -> Struct
+            207..213 'Struct': fn Struct(usize) -> Struct
             207..220 'Struct.test()': bool
-            226..239 'Enum::Variant': extern "rust-call" Variant(usize) -> Enum
+            226..239 'Enum::Variant': fn Variant(usize) -> Enum
             226..246 'Enum::...test()': bool
         "#]],
     );
@@ -3563,12 +3563,12 @@ fn main(){
             95..99 'self': Wrapper
             101..104 'rhs': u32
             122..150 '{     ...     }': Wrapper
-            132..139 'Wrapper': extern "rust-call" Wrapper(u32) -> Wrapper
+            132..139 'Wrapper': fn Wrapper(u32) -> Wrapper
             132..144 'Wrapper(rhs)': Wrapper
             140..143 'rhs': u32
             162..248 '{     ...um;  }': ()
             172..179 'wrapped': Wrapper
-            182..189 'Wrapper': extern "rust-call" Wrapper(u32) -> Wrapper
+            182..189 'Wrapper': fn Wrapper(u32) -> Wrapper
             182..193 'Wrapper(10)': Wrapper
             190..192 '10': u32
             203..206 'num': u32
diff --git a/src/tools/rust-analyzer/crates/ide-db/src/apply_change.rs b/src/tools/rust-analyzer/crates/ide-db/src/apply_change.rs
index 74c8fc96d4a..7474d7bc54d 100644
--- a/src/tools/rust-analyzer/crates/ide-db/src/apply_change.rs
+++ b/src/tools/rust-analyzer/crates/ide-db/src/apply_change.rs
@@ -1,7 +1,7 @@
 //! Applies changes to the IDE state transactionally.
 
 use base_db::{
-    salsa::{
+    ra_salsa::{
         debug::{DebugQueryTable, TableEntry},
         Database, Durability, Query, QueryTable,
     },
diff --git a/src/tools/rust-analyzer/crates/ide-db/src/lib.rs b/src/tools/rust-analyzer/crates/ide-db/src/lib.rs
index a45ff9a9545..aed093f0ebf 100644
--- a/src/tools/rust-analyzer/crates/ide-db/src/lib.rs
+++ b/src/tools/rust-analyzer/crates/ide-db/src/lib.rs
@@ -48,7 +48,7 @@ pub use hir::ChangeWithProcMacros;
 use std::{fmt, mem::ManuallyDrop};
 
 use base_db::{
-    salsa::{self, Durability},
+    ra_salsa::{self, Durability},
     AnchoredPath, CrateId, FileLoader, FileLoaderDelegate, SourceDatabase, Upcast,
     DEFAULT_FILE_TEXT_LRU_CAP,
 };
@@ -74,7 +74,7 @@ pub type FxIndexMap<K, V> =
 pub type FilePosition = FilePositionWrapper<FileId>;
 pub type FileRange = FileRangeWrapper<FileId>;
 
-#[salsa::database(
+#[ra_salsa::database(
     base_db::SourceRootDatabaseStorage,
     base_db::SourceDatabaseStorage,
     hir::db::ExpandDatabaseStorage,
@@ -89,7 +89,7 @@ pub struct RootDatabase {
     // `&RootDatabase -> &dyn OtherDatabase` cast will instantiate its drop glue in the vtable,
     // which duplicates `Weak::drop` and `Arc::drop` tens of thousands of times, which makes
     // compile times of all `ide_*` and downstream crates suffer greatly.
-    storage: ManuallyDrop<salsa::Storage<RootDatabase>>,
+    storage: ManuallyDrop<ra_salsa::Storage<RootDatabase>>,
 }
 
 impl Drop for RootDatabase {
@@ -134,7 +134,7 @@ impl FileLoader for RootDatabase {
     }
 }
 
-impl salsa::Database for RootDatabase {}
+impl ra_salsa::Database for RootDatabase {}
 
 impl Default for RootDatabase {
     fn default() -> RootDatabase {
@@ -144,7 +144,7 @@ impl Default for RootDatabase {
 
 impl RootDatabase {
     pub fn new(lru_capacity: Option<u16>) -> RootDatabase {
-        let mut db = RootDatabase { storage: ManuallyDrop::new(salsa::Storage::default()) };
+        let mut db = RootDatabase { storage: ManuallyDrop::new(ra_salsa::Storage::default()) };
         db.set_crate_graph_with_durability(Default::default(), Durability::HIGH);
         db.set_proc_macros_with_durability(Default::default(), Durability::HIGH);
         db.set_local_roots_with_durability(Default::default(), Durability::HIGH);
@@ -195,13 +195,15 @@ impl RootDatabase {
     }
 }
 
-impl salsa::ParallelDatabase for RootDatabase {
-    fn snapshot(&self) -> salsa::Snapshot<RootDatabase> {
-        salsa::Snapshot::new(RootDatabase { storage: ManuallyDrop::new(self.storage.snapshot()) })
+impl ra_salsa::ParallelDatabase for RootDatabase {
+    fn snapshot(&self) -> ra_salsa::Snapshot<RootDatabase> {
+        ra_salsa::Snapshot::new(RootDatabase {
+            storage: ManuallyDrop::new(self.storage.snapshot()),
+        })
     }
 }
 
-#[salsa::query_group(LineIndexDatabaseStorage)]
+#[ra_salsa::query_group(LineIndexDatabaseStorage)]
 pub trait LineIndexDatabase: base_db::SourceDatabase {
     fn line_index(&self, file_id: FileId) -> Arc<LineIndex>;
 }
diff --git a/src/tools/rust-analyzer/crates/ide-db/src/prime_caches.rs b/src/tools/rust-analyzer/crates/ide-db/src/prime_caches.rs
index 19d8a15422e..0002fda0ba7 100644
--- a/src/tools/rust-analyzer/crates/ide-db/src/prime_caches.rs
+++ b/src/tools/rust-analyzer/crates/ide-db/src/prime_caches.rs
@@ -10,7 +10,7 @@ use hir::db::DefDatabase;
 
 use crate::{
     base_db::{
-        salsa::{Database, ParallelDatabase, Snapshot},
+        ra_salsa::{Database, ParallelDatabase, Snapshot},
         Cancelled, CrateId, SourceDatabase, SourceRootDatabase,
     },
     symbol_index::SymbolsDatabase,
diff --git a/src/tools/rust-analyzer/crates/ide-db/src/search.rs b/src/tools/rust-analyzer/crates/ide-db/src/search.rs
index 852ee595be4..c5215eb3e63 100644
--- a/src/tools/rust-analyzer/crates/ide-db/src/search.rs
+++ b/src/tools/rust-analyzer/crates/ide-db/src/search.rs
@@ -7,7 +7,7 @@
 use std::mem;
 use std::{cell::LazyCell, cmp::Reverse};
 
-use base_db::{salsa::Database, SourceDatabase, SourceRootDatabase};
+use base_db::{ra_salsa::Database, SourceDatabase, SourceRootDatabase};
 use either::Either;
 use hir::{
     sym, Adt, AsAssocItem, DefWithBody, FileRange, FileRangeWrapper, HasAttrs, HasContainer,
diff --git a/src/tools/rust-analyzer/crates/ide-db/src/symbol_index.rs b/src/tools/rust-analyzer/crates/ide-db/src/symbol_index.rs
index 209b1477bac..94d354d28e5 100644
--- a/src/tools/rust-analyzer/crates/ide-db/src/symbol_index.rs
+++ b/src/tools/rust-analyzer/crates/ide-db/src/symbol_index.rs
@@ -28,7 +28,7 @@ use std::{
 };
 
 use base_db::{
-    salsa::{self, ParallelDatabase},
+    ra_salsa::{self, ParallelDatabase},
     SourceRootDatabase, SourceRootId, Upcast,
 };
 use fst::{raw::IndexedValue, Automaton, Streamer};
@@ -99,7 +99,7 @@ impl Query {
     }
 }
 
-#[salsa::query_group(SymbolsDatabaseStorage)]
+#[ra_salsa::query_group(SymbolsDatabaseStorage)]
 pub trait SymbolsDatabase: HirDatabase + SourceRootDatabase + Upcast<dyn HirDatabase> {
     /// The symbol index for a given module. These modules should only be in source roots that
     /// are inside local_roots.
@@ -108,18 +108,18 @@ pub trait SymbolsDatabase: HirDatabase + SourceRootDatabase + Upcast<dyn HirData
     /// The symbol index for a given source root within library_roots.
     fn library_symbols(&self, source_root_id: SourceRootId) -> Arc<SymbolIndex>;
 
-    #[salsa::transparent]
+    #[ra_salsa::transparent]
     /// The symbol indices of modules that make up a given crate.
     fn crate_symbols(&self, krate: Crate) -> Box<[Arc<SymbolIndex>]>;
 
     /// The set of "local" (that is, from the current workspace) roots.
     /// Files in local roots are assumed to change frequently.
-    #[salsa::input]
+    #[ra_salsa::input]
     fn local_roots(&self) -> Arc<FxHashSet<SourceRootId>>;
 
     /// The set of roots for crates.io libraries.
     /// Files in libraries are assumed to never change.
-    #[salsa::input]
+    #[ra_salsa::input]
     fn library_roots(&self) -> Arc<FxHashSet<SourceRootId>>;
 }
 
@@ -155,13 +155,13 @@ pub fn crate_symbols(db: &dyn SymbolsDatabase, krate: Crate) -> Box<[Arc<SymbolI
 
 /// Need to wrap Snapshot to provide `Clone` impl for `map_with`
 struct Snap<DB>(DB);
-impl<DB: ParallelDatabase> Snap<salsa::Snapshot<DB>> {
+impl<DB: ParallelDatabase> Snap<ra_salsa::Snapshot<DB>> {
     fn new(db: &DB) -> Self {
         Self(db.snapshot())
     }
 }
-impl<DB: ParallelDatabase> Clone for Snap<salsa::Snapshot<DB>> {
-    fn clone(&self) -> Snap<salsa::Snapshot<DB>> {
+impl<DB: ParallelDatabase> Clone for Snap<ra_salsa::Snapshot<DB>> {
+    fn clone(&self) -> Snap<ra_salsa::Snapshot<DB>> {
         Snap(self.0.snapshot())
     }
 }
diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/invalid_cast.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/invalid_cast.rs
index ad4baf5e3a4..4bd29b8c79b 100644
--- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/invalid_cast.rs
+++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/invalid_cast.rs
@@ -95,10 +95,10 @@ pub(crate) fn invalid_cast(ctx: &DiagnosticsContext<'_>, d: &hir::InvalidCast) -
             DiagnosticCode::RustcHardError("E0605"),
             format_ty!(ctx, "non-primitive cast: `{}` as `{}`", d.expr_ty, d.cast_ty),
         ),
-        CastError::UnknownCastPtrKind | CastError::UnknownExprPtrKind => (
-            DiagnosticCode::RustcHardError("E0641"),
-            "cannot cast to a pointer of an unknown kind".to_owned(),
-        ),
+        // CastError::UnknownCastPtrKind | CastError::UnknownExprPtrKind => (
+        //     DiagnosticCode::RustcHardError("E0641"),
+        //     "cannot cast to a pointer of an unknown kind".to_owned(),
+        // ),
     };
     Diagnostic::new(code, message, display_range)
 }
@@ -457,20 +457,20 @@ fn foo<T: ?Sized>() {
         );
     }
 
-    #[test]
-    fn order_dependent_cast_inference() {
-        check_diagnostics(
-            r#"
-//- minicore: sized
-fn main() {
-    let x = &"hello";
-    let mut y = 0 as *const _;
-              //^^^^^^^^^^^^^ error: cannot cast to a pointer of an unknown kind
-    y = x as *const _;
-}
-"#,
-        );
-    }
+    //     #[test]
+    //     fn order_dependent_cast_inference() {
+    //         check_diagnostics(
+    //             r#"
+    // //- minicore: sized
+    // fn main() {
+    //     let x = &"hello";
+    //     let mut y = 0 as *const _;
+    //               //^^^^^^^^^^^^^ error: cannot cast to a pointer of an unknown kind
+    //     y = x as *const _;
+    // }
+    // "#,
+    //         );
+    //     }
 
     #[test]
     fn ptr_to_ptr_different_regions() {
@@ -1111,4 +1111,22 @@ fn foo() {
             "#,
         );
     }
+
+    #[test]
+    fn cast_isize_to_infer_pointer() {
+        check_diagnostics(
+            r#"
+//- minicore: coerce_unsized
+struct Foo {}
+
+struct Wrap<'a>(&'a mut Foo);
+
+fn main() {
+    let lparam: isize = 0;
+
+    let _wrap = Wrap(unsafe { &mut *(lparam as *mut _) });
+}
+        "#,
+        );
+    }
 }
diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/mutability_errors.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/mutability_errors.rs
index 95542793915..6fa0e7a5a89 100644
--- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/mutability_errors.rs
+++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/mutability_errors.rs
@@ -1258,4 +1258,29 @@ pub unsafe fn foo(a: *mut A) {
 "#,
         );
     }
+
+    #[test]
+    fn regression_15799() {
+        check_diagnostics(
+            r#"
+//- minicore: deref_mut
+struct WrapPtr(*mut u32);
+
+impl core::ops::Deref for WrapPtr {
+    type Target = *mut u32;
+    fn deref(&self) -> &Self::Target {
+        &self.0
+    }
+}
+
+fn main() {
+    let mut x = 0u32;
+    let wrap = WrapPtr(&mut x);
+    unsafe {
+        **wrap = 6;
+    }
+}
+"#,
+        );
+    }
 }
diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/type_mismatch.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/type_mismatch.rs
index 5cce7c4aed5..90f88d6705b 100644
--- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/type_mismatch.rs
+++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/type_mismatch.rs
@@ -130,6 +130,7 @@ fn add_missing_ok_or_some(
     if d.actual.is_unit() {
         if let Expr::BlockExpr(block) = &expr {
             if block.tail_expr().is_none() {
+                // Fix for forms like `fn foo() -> Result<(), String> {}`
                 let mut builder = TextEdit::builder();
                 let block_indent = block.indent_level();
 
@@ -156,6 +157,20 @@ fn add_missing_ok_or_some(
                 acc.push(fix("insert_wrapped_unit", &name, source_change, expr_range));
             }
             return Some(());
+        } else if let Expr::ReturnExpr(ret_expr) = &expr {
+            // Fix for forms like `fn foo() -> Result<(), String> { return; }`
+            if ret_expr.expr().is_none() {
+                let mut builder = TextEdit::builder();
+                builder
+                    .insert(ret_expr.syntax().text_range().end(), format!(" {variant_name}(())"));
+                let source_change = SourceChange::from_text_edit(
+                    expr_ptr.file_id.original_file(ctx.sema.db),
+                    builder.finish(),
+                );
+                let name = format!("Insert {variant_name}(()) as the return value");
+                acc.push(fix("insert_wrapped_unit", &name, source_change, expr_range));
+            }
+            return Some(());
         }
     }
 
@@ -604,6 +619,29 @@ fn foo() -> Result<(), ()> {
     }
 
     #[test]
+    fn test_wrapped_unit_as_return_expr() {
+        check_fix(
+            r#"
+//- minicore: result
+fn foo(b: bool) -> Result<(), String> {
+    if b {
+        return$0;
+    }
+
+    Err("oh dear".to_owned())
+}"#,
+            r#"
+fn foo(b: bool) -> Result<(), String> {
+    if b {
+        return Ok(());
+    }
+
+    Err("oh dear".to_owned())
+}"#,
+        );
+    }
+
+    #[test]
     fn test_in_const_and_static() {
         check_fix(
             r#"
diff --git a/src/tools/rust-analyzer/crates/ide-ssr/src/tests.rs b/src/tools/rust-analyzer/crates/ide-ssr/src/tests.rs
index 42930889d75..d783e195252 100644
--- a/src/tools/rust-analyzer/crates/ide-ssr/src/tests.rs
+++ b/src/tools/rust-analyzer/crates/ide-ssr/src/tests.rs
@@ -1,7 +1,7 @@
 use expect_test::{expect, Expect};
 use hir::{FilePosition, FileRange};
 use ide_db::{
-    base_db::{salsa::Durability, SourceDatabase},
+    base_db::{ra_salsa::Durability, SourceDatabase},
     EditionedFileId, FxHashSet,
 };
 use test_utils::RangeOrOffset;
diff --git a/src/tools/rust-analyzer/crates/ide/src/call_hierarchy.rs b/src/tools/rust-analyzer/crates/ide/src/call_hierarchy.rs
index 155259a1380..1b82c00d1dc 100644
--- a/src/tools/rust-analyzer/crates/ide/src/call_hierarchy.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/call_hierarchy.rs
@@ -19,6 +19,12 @@ pub struct CallItem {
     pub ranges: Vec<FileRange>,
 }
 
+#[derive(Debug, Clone, Copy, PartialEq, Eq)]
+pub struct CallHierarchyConfig {
+    /// Whether to exclude tests from the call hierarchy
+    pub exclude_tests: bool,
+}
+
 pub(crate) fn call_hierarchy(
     db: &RootDatabase,
     position: FilePosition,
@@ -28,6 +34,7 @@ pub(crate) fn call_hierarchy(
 
 pub(crate) fn incoming_calls(
     db: &RootDatabase,
+    CallHierarchyConfig { exclude_tests }: CallHierarchyConfig,
     FilePosition { file_id, offset }: FilePosition,
 ) -> Option<Vec<CallItem>> {
     let sema = &Semantics::new(db);
@@ -56,11 +63,18 @@ pub(crate) fn incoming_calls(
             references.iter().filter_map(|FileReference { name, .. }| name.as_name_ref());
         for name in references {
             // This target is the containing function
-            let nav = sema.ancestors_with_macros(name.syntax().clone()).find_map(|node| {
+            let def_nav = sema.ancestors_with_macros(name.syntax().clone()).find_map(|node| {
                 let def = ast::Fn::cast(node).and_then(|fn_| sema.to_def(&fn_))?;
-                def.try_to_nav(sema.db)
+                // We should return def before check if it is a test, so that we
+                // will not continue to search for outer fn in nested fns
+                def.try_to_nav(sema.db).map(|nav| (def, nav))
             });
-            if let Some(nav) = nav {
+
+            if let Some((def, nav)) = def_nav {
+                if exclude_tests && def.is_test(db) {
+                    continue;
+                }
+
                 let range = sema.original_range(name.syntax());
                 calls.add(nav.call_site, range.into());
                 if let Some(other) = nav.def_site {
@@ -75,6 +89,7 @@ pub(crate) fn incoming_calls(
 
 pub(crate) fn outgoing_calls(
     db: &RootDatabase,
+    CallHierarchyConfig { exclude_tests }: CallHierarchyConfig,
     FilePosition { file_id, offset }: FilePosition,
 ) -> Option<Vec<CallItem>> {
     let sema = Semantics::new(db);
@@ -103,7 +118,12 @@ pub(crate) fn outgoing_calls(
                     let expr = call.expr()?;
                     let callable = sema.type_of_expr(&expr)?.original.as_callable(db)?;
                     match callable.kind() {
-                        hir::CallableKind::Function(it) => it.try_to_nav(db),
+                        hir::CallableKind::Function(it) => {
+                            if exclude_tests && it.is_test(db) {
+                                return None;
+                            }
+                            it.try_to_nav(db)
+                        }
                         hir::CallableKind::TupleEnumVariant(it) => it.try_to_nav(db),
                         hir::CallableKind::TupleStruct(it) => it.try_to_nav(db),
                         _ => None,
@@ -112,6 +132,9 @@ pub(crate) fn outgoing_calls(
                 }
                 ast::CallableExpr::MethodCall(expr) => {
                     let function = sema.resolve_method_call(&expr)?;
+                    if exclude_tests && function.is_test(db) {
+                        return None;
+                    }
                     function
                         .try_to_nav(db)
                         .zip(Some(sema.original_range(expr.name_ref()?.syntax())))
@@ -149,6 +172,7 @@ mod tests {
     use crate::fixture;
 
     fn check_hierarchy(
+        exclude_tests: bool,
         ra_fixture: &str,
         expected_nav: Expect,
         expected_incoming: Expect,
@@ -172,18 +196,21 @@ mod tests {
         let nav = navs.pop().unwrap();
         expected_nav.assert_eq(&nav.debug_render());
 
+        let config = crate::CallHierarchyConfig { exclude_tests };
+
         let item_pos =
             FilePosition { file_id: nav.file_id, offset: nav.focus_or_full_range().start() };
-        let incoming_calls = analysis.incoming_calls(item_pos).unwrap().unwrap();
+        let incoming_calls = analysis.incoming_calls(config, item_pos).unwrap().unwrap();
         expected_incoming.assert_eq(&incoming_calls.into_iter().map(debug_render).join("\n"));
 
-        let outgoing_calls = analysis.outgoing_calls(item_pos).unwrap().unwrap();
+        let outgoing_calls = analysis.outgoing_calls(config, item_pos).unwrap().unwrap();
         expected_outgoing.assert_eq(&outgoing_calls.into_iter().map(debug_render).join("\n"));
     }
 
     #[test]
     fn test_call_hierarchy_on_ref() {
         check_hierarchy(
+            false,
             r#"
 //- /lib.rs
 fn callee() {}
@@ -200,6 +227,7 @@ fn caller() {
     #[test]
     fn test_call_hierarchy_on_def() {
         check_hierarchy(
+            false,
             r#"
 //- /lib.rs
 fn call$0ee() {}
@@ -216,6 +244,7 @@ fn caller() {
     #[test]
     fn test_call_hierarchy_in_same_fn() {
         check_hierarchy(
+            false,
             r#"
 //- /lib.rs
 fn callee() {}
@@ -233,6 +262,7 @@ fn caller() {
     #[test]
     fn test_call_hierarchy_in_different_fn() {
         check_hierarchy(
+            false,
             r#"
 //- /lib.rs
 fn callee() {}
@@ -255,6 +285,7 @@ fn caller2() {
     #[test]
     fn test_call_hierarchy_in_tests_mod() {
         check_hierarchy(
+            false,
             r#"
 //- /lib.rs cfg:test
 fn callee() {}
@@ -283,6 +314,7 @@ mod tests {
     #[test]
     fn test_call_hierarchy_in_different_files() {
         check_hierarchy(
+            false,
             r#"
 //- /lib.rs
 mod foo;
@@ -304,6 +336,7 @@ pub fn callee() {}
     #[test]
     fn test_call_hierarchy_outgoing() {
         check_hierarchy(
+            false,
             r#"
 //- /lib.rs
 fn callee() {}
@@ -321,6 +354,7 @@ fn call$0er() {
     #[test]
     fn test_call_hierarchy_outgoing_in_different_files() {
         check_hierarchy(
+            false,
             r#"
 //- /lib.rs
 mod foo;
@@ -342,6 +376,7 @@ pub fn callee() {}
     #[test]
     fn test_call_hierarchy_incoming_outgoing() {
         check_hierarchy(
+            false,
             r#"
 //- /lib.rs
 fn caller1() {
@@ -365,6 +400,7 @@ fn caller3() {
     #[test]
     fn test_call_hierarchy_issue_5103() {
         check_hierarchy(
+            false,
             r#"
 fn a() {
     b()
@@ -382,6 +418,7 @@ fn main() {
         );
 
         check_hierarchy(
+            false,
             r#"
 fn a() {
     b$0()
@@ -402,6 +439,7 @@ fn main() {
     #[test]
     fn test_call_hierarchy_in_macros_incoming() {
         check_hierarchy(
+            false,
             r#"
 macro_rules! define {
     ($ident:ident) => {
@@ -423,6 +461,7 @@ fn caller() {
             expect![[]],
         );
         check_hierarchy(
+            false,
             r#"
 macro_rules! define {
     ($ident:ident) => {
@@ -448,6 +487,7 @@ fn caller() {
     #[test]
     fn test_call_hierarchy_in_macros_outgoing() {
         check_hierarchy(
+            false,
             r#"
 macro_rules! define {
     ($ident:ident) => {
@@ -473,6 +513,7 @@ fn caller$0() {
     #[test]
     fn test_call_hierarchy_in_macros_incoming_different_files() {
         check_hierarchy(
+            false,
             r#"
 //- /lib.rs
 #[macro_use]
@@ -498,6 +539,7 @@ macro_rules! call {
             expect![[]],
         );
         check_hierarchy(
+            false,
             r#"
 //- /lib.rs
 #[macro_use]
@@ -523,6 +565,7 @@ macro_rules! call {
             expect![[]],
         );
         check_hierarchy(
+            false,
             r#"
 //- /lib.rs
 #[macro_use]
@@ -558,6 +601,7 @@ macro_rules! call {
     #[test]
     fn test_call_hierarchy_in_macros_outgoing_different_files() {
         check_hierarchy(
+            false,
             r#"
 //- /lib.rs
 #[macro_use]
@@ -585,6 +629,7 @@ macro_rules! call {
             expect![[]],
         );
         check_hierarchy(
+            false,
             r#"
 //- /lib.rs
 #[macro_use]
@@ -616,6 +661,7 @@ macro_rules! call {
     #[test]
     fn test_trait_method_call_hierarchy() {
         check_hierarchy(
+            false,
             r#"
 trait T1 {
     fn call$0ee();
@@ -636,4 +682,64 @@ fn caller() {
             expect![[]],
         );
     }
+
+    #[test]
+    fn test_call_hierarchy_excluding_tests() {
+        check_hierarchy(
+            false,
+            r#"
+fn main() {
+    f1();
+}
+
+fn f1$0() {
+    f2(); f3();
+}
+
+fn f2() {
+    f1(); f3();
+}
+
+#[test]
+fn f3() {
+    f1(); f2();
+}
+"#,
+            expect!["f1 Function FileId(0) 25..52 28..30"],
+            expect![[r#"
+                main Function FileId(0) 0..23 3..7 : FileId(0):16..18
+                f2 Function FileId(0) 54..81 57..59 : FileId(0):68..70
+                f3 Function FileId(0) 83..118 94..96 : FileId(0):105..107"#]],
+            expect![[r#"
+                f2 Function FileId(0) 54..81 57..59 : FileId(0):39..41
+                f3 Function FileId(0) 83..118 94..96 : FileId(0):45..47"#]],
+        );
+
+        check_hierarchy(
+            true,
+            r#"
+fn main() {
+    f1();
+}
+
+fn f1$0() {
+    f2(); f3();
+}
+
+fn f2() {
+    f1(); f3();
+}
+
+#[test]
+fn f3() {
+    f1(); f2();
+}
+"#,
+            expect!["f1 Function FileId(0) 25..52 28..30"],
+            expect![[r#"
+                main Function FileId(0) 0..23 3..7 : FileId(0):16..18
+                f2 Function FileId(0) 54..81 57..59 : FileId(0):68..70"#]],
+            expect!["f2 Function FileId(0) 54..81 57..59 : FileId(0):39..41"],
+        );
+    }
 }
diff --git a/src/tools/rust-analyzer/crates/ide/src/highlight_related.rs b/src/tools/rust-analyzer/crates/ide/src/highlight_related.rs
index 4c8e3fc3040..fc29ba06dad 100644
--- a/src/tools/rust-analyzer/crates/ide/src/highlight_related.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/highlight_related.rs
@@ -281,99 +281,95 @@ fn highlight_references(
     }
 }
 
-// If `file_id` is None,
-pub(crate) fn highlight_exit_points(
+fn hl_exit_points(
     sema: &Semantics<'_, RootDatabase>,
-    token: SyntaxToken,
-) -> FxHashMap<EditionedFileId, Vec<HighlightedRange>> {
-    fn hl(
-        sema: &Semantics<'_, RootDatabase>,
-        def_token: Option<SyntaxToken>,
-        body: ast::Expr,
-    ) -> Option<FxHashMap<EditionedFileId, FxHashSet<HighlightedRange>>> {
-        let mut highlights: FxHashMap<EditionedFileId, FxHashSet<_>> = FxHashMap::default();
+    def_token: Option<SyntaxToken>,
+    body: ast::Expr,
+) -> Option<FxHashMap<EditionedFileId, FxHashSet<HighlightedRange>>> {
+    let mut highlights: FxHashMap<EditionedFileId, FxHashSet<_>> = FxHashMap::default();
+
+    let mut push_to_highlights = |file_id, range| {
+        if let Some(FileRange { file_id, range }) = original_frange(sema.db, file_id, range) {
+            let hrange = HighlightedRange { category: ReferenceCategory::empty(), range };
+            highlights.entry(file_id).or_default().insert(hrange);
+        }
+    };
 
-        let mut push_to_highlights = |file_id, range| {
-            if let Some(FileRange { file_id, range }) = original_frange(sema.db, file_id, range) {
-                let hrange = HighlightedRange { category: ReferenceCategory::empty(), range };
-                highlights.entry(file_id).or_default().insert(hrange);
+    if let Some(tok) = def_token {
+        let file_id = sema.hir_file_for(&tok.parent()?);
+        let range = Some(tok.text_range());
+        push_to_highlights(file_id, range);
+    }
+
+    WalkExpandedExprCtx::new(sema).walk(&body, &mut |_, expr| {
+        let file_id = sema.hir_file_for(expr.syntax());
+
+        let range = match &expr {
+            ast::Expr::TryExpr(try_) => try_.question_mark_token().map(|token| token.text_range()),
+            ast::Expr::MethodCallExpr(_) | ast::Expr::CallExpr(_) | ast::Expr::MacroExpr(_)
+                if sema.type_of_expr(&expr).map_or(false, |ty| ty.original.is_never()) =>
+            {
+                Some(expr.syntax().text_range())
             }
+            _ => None,
         };
 
-        if let Some(tok) = def_token {
-            let file_id = sema.hir_file_for(&tok.parent()?);
-            let range = Some(tok.text_range());
-            push_to_highlights(file_id, range);
-        }
+        push_to_highlights(file_id, range);
+    });
 
-        WalkExpandedExprCtx::new(sema).walk(&body, &mut |_, expr| {
+    // We should handle `return` separately, because when it is used in a `try` block,
+    // it will exit the outside function instead of the block itself.
+    WalkExpandedExprCtx::new(sema)
+        .with_check_ctx(&WalkExpandedExprCtx::is_async_const_block_or_closure)
+        .walk(&body, &mut |_, expr| {
             let file_id = sema.hir_file_for(expr.syntax());
 
             let range = match &expr {
-                ast::Expr::TryExpr(try_) => {
-                    try_.question_mark_token().map(|token| token.text_range())
-                }
-                ast::Expr::MethodCallExpr(_) | ast::Expr::CallExpr(_) | ast::Expr::MacroExpr(_)
-                    if sema.type_of_expr(&expr).map_or(false, |ty| ty.original.is_never()) =>
-                {
-                    Some(expr.syntax().text_range())
-                }
+                ast::Expr::ReturnExpr(expr) => expr.return_token().map(|token| token.text_range()),
                 _ => None,
             };
 
             push_to_highlights(file_id, range);
         });
 
-        // We should handle `return` separately, because when it is used in a `try` block,
-        // it will exit the outside function instead of the block itself.
-        WalkExpandedExprCtx::new(sema)
-            .with_check_ctx(&WalkExpandedExprCtx::is_async_const_block_or_closure)
-            .walk(&body, &mut |_, expr| {
-                let file_id = sema.hir_file_for(expr.syntax());
-
-                let range = match &expr {
-                    ast::Expr::ReturnExpr(expr) => {
-                        expr.return_token().map(|token| token.text_range())
-                    }
-                    _ => None,
-                };
-
-                push_to_highlights(file_id, range);
-            });
-
-        let tail = match body {
-            ast::Expr::BlockExpr(b) => b.tail_expr(),
-            e => Some(e),
-        };
+    let tail = match body {
+        ast::Expr::BlockExpr(b) => b.tail_expr(),
+        e => Some(e),
+    };
 
-        if let Some(tail) = tail {
-            for_each_tail_expr(&tail, &mut |tail| {
-                let file_id = sema.hir_file_for(tail.syntax());
-                let range = match tail {
-                    ast::Expr::BreakExpr(b) => b
-                        .break_token()
-                        .map_or_else(|| tail.syntax().text_range(), |tok| tok.text_range()),
-                    _ => tail.syntax().text_range(),
-                };
-                push_to_highlights(file_id, Some(range));
-            });
-        }
-        Some(highlights)
+    if let Some(tail) = tail {
+        for_each_tail_expr(&tail, &mut |tail| {
+            let file_id = sema.hir_file_for(tail.syntax());
+            let range = match tail {
+                ast::Expr::BreakExpr(b) => b
+                    .break_token()
+                    .map_or_else(|| tail.syntax().text_range(), |tok| tok.text_range()),
+                _ => tail.syntax().text_range(),
+            };
+            push_to_highlights(file_id, Some(range));
+        });
     }
+    Some(highlights)
+}
 
+// If `file_id` is None,
+pub(crate) fn highlight_exit_points(
+    sema: &Semantics<'_, RootDatabase>,
+    token: SyntaxToken,
+) -> FxHashMap<EditionedFileId, Vec<HighlightedRange>> {
     let mut res = FxHashMap::default();
     for def in goto_definition::find_fn_or_blocks(sema, &token) {
         let new_map = match_ast! {
             match def {
-                ast::Fn(fn_) => fn_.body().and_then(|body| hl(sema, fn_.fn_token(), body.into())),
+                ast::Fn(fn_) => fn_.body().and_then(|body| hl_exit_points(sema, fn_.fn_token(), body.into())),
                 ast::ClosureExpr(closure) => {
                     let pipe_tok = closure.param_list().and_then(|p| p.pipe_token());
-                    closure.body().and_then(|body| hl(sema, pipe_tok, body))
+                    closure.body().and_then(|body| hl_exit_points(sema, pipe_tok, body))
                 },
                 ast::BlockExpr(blk) => match blk.modifier() {
-                    Some(ast::BlockModifier::Async(t)) => hl(sema, Some(t), blk.into()),
+                    Some(ast::BlockModifier::Async(t)) => hl_exit_points(sema, Some(t), blk.into()),
                     Some(ast::BlockModifier::Try(t)) if token.kind() != T![return] => {
-                        hl(sema, Some(t), blk.into())
+                        hl_exit_points(sema, Some(t), blk.into())
                     },
                     _ => continue,
                 },
@@ -517,10 +513,23 @@ pub(crate) fn highlight_yield_points(
             match anc {
                 ast::Fn(fn_) => hl(sema, fn_.async_token(), fn_.body().map(ast::Expr::BlockExpr)),
                 ast::BlockExpr(block_expr) => {
-                    if block_expr.async_token().is_none() {
+                    let Some(async_token) = block_expr.async_token() else {
                         continue;
+                    };
+
+                    // Async blocks act similar to closures. So we want to
+                    // highlight their exit points too, but only if we are on
+                    // the async token.
+                    if async_token == token {
+                        let exit_points = hl_exit_points(
+                            sema,
+                            Some(async_token.clone()),
+                            block_expr.clone().into(),
+                        );
+                        merge_map(&mut res, exit_points);
                     }
-                    hl(sema, block_expr.async_token(), Some(block_expr.into()))
+
+                    hl(sema, Some(async_token), Some(block_expr.into()))
                 },
                 ast::ClosureExpr(closure) => hl(sema, closure.async_token(), closure.body()),
                 _ => continue,
@@ -877,6 +886,27 @@ pub async$0 fn foo() {
     }
 
     #[test]
+    fn test_hl_exit_points_of_async_blocks() {
+        check(
+            r#"
+pub fn foo() {
+    let x = async$0 {
+         // ^^^^^
+        0.await;
+       // ^^^^^
+       0?;
+     // ^
+       return 0;
+    // ^^^^^^
+       0
+    // ^
+    };
+}
+"#,
+        );
+    }
+
+    #[test]
     fn test_hl_let_else_yield_points() {
         check(
             r#"
@@ -925,11 +955,9 @@ async fn foo() {
 async fn foo() {
     (async {
   // ^^^^^
-        (async {
-           0.await
-        }).await$0 }
-        // ^^^^^
-    ).await;
+        (async { 0.await }).await$0
+                         // ^^^^^
+    }).await;
 }
 "#,
         );
diff --git a/src/tools/rust-analyzer/crates/ide/src/hover/tests.rs b/src/tools/rust-analyzer/crates/ide/src/hover/tests.rs
index e60be577f79..81397b07855 100644
--- a/src/tools/rust-analyzer/crates/ide/src/hover/tests.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/hover/tests.rs
@@ -8988,3 +8988,33 @@ mod m {
         "#]],
     );
 }
+
+#[test]
+fn regression_18238() {
+    check(
+        r#"
+macro_rules! foo {
+    ($name:ident) => {
+        pub static $name = Foo::new(|| {
+            $crate;
+        });
+    };
+}
+
+foo!(BAR_$0);
+"#,
+        expect![[r#"
+        *BAR_*
+
+        ```rust
+        test
+        ```
+
+        ```rust
+        pub static BAR_: {error} = Foo::new(||{
+            crate;
+        })
+        ```
+        "#]],
+    );
+}
diff --git a/src/tools/rust-analyzer/crates/ide/src/lib.rs b/src/tools/rust-analyzer/crates/ide/src/lib.rs
index c46c4c8ce94..d7163d57d22 100644
--- a/src/tools/rust-analyzer/crates/ide/src/lib.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/lib.rs
@@ -64,7 +64,7 @@ use fetch_crates::CrateInfo;
 use hir::{sym, ChangeWithProcMacros};
 use ide_db::{
     base_db::{
-        salsa::{self, ParallelDatabase},
+        ra_salsa::{self, ParallelDatabase},
         CrateOrigin, CrateWorkspaceData, Env, FileLoader, FileSet, SourceDatabase,
         SourceRootDatabase, VfsPath,
     },
@@ -79,7 +79,7 @@ use crate::navigation_target::ToNav;
 
 pub use crate::{
     annotations::{Annotation, AnnotationConfig, AnnotationKind, AnnotationLocation},
-    call_hierarchy::CallItem,
+    call_hierarchy::{CallHierarchyConfig, CallItem},
     expand_macro::ExpandedMacro,
     file_structure::{StructureNode, StructureNodeKind},
     folding_ranges::{Fold, FoldKind},
@@ -218,7 +218,7 @@ impl Default for AnalysisHost {
 /// `Analysis` are canceled (most method return `Err(Canceled)`).
 #[derive(Debug)]
 pub struct Analysis {
-    db: salsa::Snapshot<RootDatabase>,
+    db: ra_salsa::Snapshot<RootDatabase>,
 }
 
 // As a general design guideline, `Analysis` API are intended to be independent
@@ -564,13 +564,21 @@ impl Analysis {
     }
 
     /// Computes incoming calls for the given file position.
-    pub fn incoming_calls(&self, position: FilePosition) -> Cancellable<Option<Vec<CallItem>>> {
-        self.with_db(|db| call_hierarchy::incoming_calls(db, position))
+    pub fn incoming_calls(
+        &self,
+        config: CallHierarchyConfig,
+        position: FilePosition,
+    ) -> Cancellable<Option<Vec<CallItem>>> {
+        self.with_db(|db| call_hierarchy::incoming_calls(db, config, position))
     }
 
     /// Computes outgoing calls for the given file position.
-    pub fn outgoing_calls(&self, position: FilePosition) -> Cancellable<Option<Vec<CallItem>>> {
-        self.with_db(|db| call_hierarchy::outgoing_calls(db, position))
+    pub fn outgoing_calls(
+        &self,
+        config: CallHierarchyConfig,
+        position: FilePosition,
+    ) -> Cancellable<Option<Vec<CallItem>>> {
+        self.with_db(|db| call_hierarchy::outgoing_calls(db, config, position))
     }
 
     /// Returns a `mod name;` declaration which created the current module.
diff --git a/src/tools/rust-analyzer/crates/ide/src/navigation_target.rs b/src/tools/rust-analyzer/crates/ide/src/navigation_target.rs
index 9bc7bf411f0..9259243db85 100644
--- a/src/tools/rust-analyzer/crates/ide/src/navigation_target.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/navigation_target.rs
@@ -792,6 +792,7 @@ pub(crate) fn orig_range_with_focus_r(
             .definition_range(db)
     };
 
+    // FIXME: Also make use of the syntax context to determine which site we are at?
     let value_range = InFile::new(hir_file, value).original_node_file_range_opt(db);
     let ((call_site_range, call_site_focus), def_site) =
         match InFile::new(hir_file, name).original_node_file_range_opt(db) {
diff --git a/src/tools/rust-analyzer/crates/ide/src/rename.rs b/src/tools/rust-analyzer/crates/ide/src/rename.rs
index e46cb5a781f..f17c1fa5c62 100644
--- a/src/tools/rust-analyzer/crates/ide/src/rename.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/rename.rs
@@ -421,19 +421,28 @@ fn text_edit_from_self_param(self_param: &ast::SelfParam, new_name: &str) -> Opt
         None
     }
 
-    let impl_def = self_param.syntax().ancestors().find_map(ast::Impl::cast)?;
-    let type_name = target_type_name(&impl_def)?;
-
-    let mut replacement_text = String::from(new_name);
-    replacement_text.push_str(": ");
-    match (self_param.amp_token(), self_param.mut_token()) {
-        (Some(_), None) => replacement_text.push('&'),
-        (Some(_), Some(_)) => replacement_text.push_str("&mut "),
-        (_, _) => (),
-    };
-    replacement_text.push_str(type_name.as_str());
+    match self_param.syntax().ancestors().find_map(ast::Impl::cast) {
+        Some(impl_def) => {
+            let type_name = target_type_name(&impl_def)?;
+
+            let mut replacement_text = String::from(new_name);
+            replacement_text.push_str(": ");
+            match (self_param.amp_token(), self_param.mut_token()) {
+                (Some(_), None) => replacement_text.push('&'),
+                (Some(_), Some(_)) => replacement_text.push_str("&mut "),
+                (_, _) => (),
+            };
+            replacement_text.push_str(type_name.as_str());
 
-    Some(TextEdit::replace(self_param.syntax().text_range(), replacement_text))
+            Some(TextEdit::replace(self_param.syntax().text_range(), replacement_text))
+        }
+        None => {
+            cov_mark::hit!(rename_self_outside_of_methods);
+            let mut replacement_text = String::from(new_name);
+            replacement_text.push_str(": _");
+            Some(TextEdit::replace(self_param.syntax().text_range(), replacement_text))
+        }
+    }
 }
 
 #[cfg(test)]
@@ -1978,6 +1987,26 @@ impl Foo {
     }
 
     #[test]
+    fn test_self_outside_of_methods() {
+        cov_mark::check!(rename_self_outside_of_methods);
+        check(
+            "foo",
+            r#"
+fn f($0self) -> i32 {
+    use self as _;
+    self.i
+}
+"#,
+            r#"
+fn f(foo: _) -> i32 {
+    use self as _;
+    foo.i
+}
+"#,
+        );
+    }
+
+    #[test]
     fn test_self_in_path_to_parameter() {
         check(
             "foo",
diff --git a/src/tools/rust-analyzer/crates/ide/src/ssr.rs b/src/tools/rust-analyzer/crates/ide/src/ssr.rs
index 41cc9c067d3..6def28e0b74 100644
--- a/src/tools/rust-analyzer/crates/ide/src/ssr.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/ssr.rs
@@ -59,7 +59,7 @@ mod tests {
     use expect_test::expect;
     use ide_assists::{Assist, AssistResolveStrategy};
     use ide_db::{
-        base_db::salsa::Durability, symbol_index::SymbolsDatabase, FileRange, FxHashSet,
+        base_db::ra_salsa::Durability, symbol_index::SymbolsDatabase, FileRange, FxHashSet,
         RootDatabase,
     };
     use test_fixture::WithFixture;
diff --git a/src/tools/rust-analyzer/crates/ide/src/status.rs b/src/tools/rust-analyzer/crates/ide/src/status.rs
index 67d6932da96..9e823daa2be 100644
--- a/src/tools/rust-analyzer/crates/ide/src/status.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/status.rs
@@ -6,7 +6,7 @@ use hir::{
 };
 use ide_db::{
     base_db::{
-        salsa::{
+        ra_salsa::{
             debug::{DebugQueryTable, TableEntry},
             Query, QueryTable,
         },
diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_extern_crate.html b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_extern_crate.html
index 129b287e52f..7820e4e5a5f 100644
--- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_extern_crate.html
+++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_extern_crate.html
@@ -45,7 +45,12 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
 .invalid_escape_sequence { color: #FC5555; text-decoration: wavy underline; }
 .unresolved_reference    { color: #FC5555; text-decoration: wavy underline; }
 </style>
-<pre><code><span class="keyword">extern</span> <span class="keyword">crate</span> <span class="module crate_root default_library library">std</span><span class="semicolon">;</span>
+<pre><code><span class="keyword">extern</span> <span class="keyword">crate</span> <span class="self_keyword crate_root public">self</span> <span class="keyword">as</span> <span class="module crate_root declaration">this</span><span class="semicolon">;</span>
+<span class="keyword">extern</span> <span class="keyword">crate</span> <span class="module crate_root default_library library">std</span><span class="semicolon">;</span>
 <span class="keyword">extern</span> <span class="keyword">crate</span> <span class="module crate_root default_library library">alloc</span> <span class="keyword">as</span> <span class="module crate_root declaration">abc</span><span class="semicolon">;</span>
 <span class="keyword">extern</span> <span class="keyword">crate</span> <span class="unresolved_reference">unresolved</span> <span class="keyword">as</span> <span class="module crate_root declaration">definitely_unresolved</span><span class="semicolon">;</span>
+<span class="keyword">extern</span> <span class="keyword">crate</span> <span class="unresolved_reference">unresolved</span> <span class="keyword">as</span> <span class="punctuation">_</span><span class="semicolon">;</span>
+<span class="keyword">extern</span> <span class="keyword">crate</span> <span class="module crate_root default_library library">test</span> <span class="keyword">as</span> <span class="module crate_root declaration">opt_in_crate</span><span class="semicolon">;</span>
+<span class="keyword">extern</span> <span class="keyword">crate</span> <span class="module crate_root default_library library">test</span> <span class="keyword">as</span> <span class="punctuation">_</span><span class="semicolon">;</span>
+<span class="keyword">extern</span> <span class="keyword">crate</span> <span class="module crate_root default_library library">proc_macro</span><span class="semicolon">;</span>
 </code></pre>
\ No newline at end of file
diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/tests.rs b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/tests.rs
index 94cee4ef43b..a20147add36 100644
--- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/tests.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/tests.rs
@@ -874,14 +874,23 @@ pub fn block_comments2() {}
 fn test_extern_crate() {
     check_highlighting(
         r#"
-//- /main.rs crate:main deps:std,alloc
+//- /main.rs crate:main deps:std,alloc,test,proc_macro extern-prelude:std,alloc
+extern crate self as this;
 extern crate std;
 extern crate alloc as abc;
 extern crate unresolved as definitely_unresolved;
+extern crate unresolved as _;
+extern crate test as opt_in_crate;
+extern crate test as _;
+extern crate proc_macro;
 //- /std/lib.rs crate:std
 pub struct S;
 //- /alloc/lib.rs crate:alloc
-pub struct A
+pub struct A;
+//- /test/lib.rs crate:test
+pub struct T;
+//- /proc_macro/lib.rs crate:proc_macro
+pub struct ProcMacro;
 "#,
         expect_file!["./test_data/highlight_extern_crate.html"],
         false,
diff --git a/src/tools/rust-analyzer/crates/parser/src/grammar/generic_params.rs b/src/tools/rust-analyzer/crates/parser/src/grammar/generic_params.rs
index e0fa753fa70..ecfabca092c 100644
--- a/src/tools/rust-analyzer/crates/parser/src/grammar/generic_params.rs
+++ b/src/tools/rust-analyzer/crates/parser/src/grammar/generic_params.rs
@@ -145,7 +145,7 @@ fn type_bound(p: &mut Parser<'_>) -> bool {
         T![for] => types::for_type(p, false),
         // test precise_capturing
         // fn captures<'a: 'a, 'b: 'b, T>() -> impl Sized + use<'b, T> {}
-        T![use] => {
+        T![use] if p.nth_at(1, T![<]) => {
             p.bump_any();
             generic_param_list(p)
         }
diff --git a/src/tools/rust-analyzer/crates/parser/src/lexed_str.rs b/src/tools/rust-analyzer/crates/parser/src/lexed_str.rs
index 7ea23b4f752..5322463a713 100644
--- a/src/tools/rust-analyzer/crates/parser/src/lexed_str.rs
+++ b/src/tools/rust-analyzer/crates/parser/src/lexed_str.rs
@@ -188,10 +188,11 @@ impl<'a> Converter<'a> {
 
                 rustc_lexer::TokenKind::RawIdent => IDENT,
 
-                rustc_lexer::TokenKind::GuardedStrPrefix => {
+                rustc_lexer::TokenKind::GuardedStrPrefix if self.edition.at_least_2024() => {
                     err = "Invalid string literal (reserved syntax)";
                     ERROR
-                },
+                }
+                rustc_lexer::TokenKind::GuardedStrPrefix => POUND,
 
                 rustc_lexer::TokenKind::Literal { kind, .. } => {
                     self.extend_literal(token_text.len(), kind);
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/err/0055_impl_use.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/err/0055_impl_use.rast
new file mode 100644
index 00000000000..751f007df94
--- /dev/null
+++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/err/0055_impl_use.rast
@@ -0,0 +1,26 @@
+SOURCE_FILE
+  IMPL
+    IMPL_KW "impl"
+    GENERIC_PARAM_LIST
+      L_ANGLE "<"
+      TYPE_PARAM
+        NAME
+          IDENT "T"
+        COLON ":"
+        WHITESPACE "\n"
+        TYPE_BOUND_LIST
+    ERROR
+      USE_KW "use"
+  WHITESPACE " "
+  MACRO_CALL
+    PATH
+      PATH_SEGMENT
+        NAME_REF
+          IDENT "std"
+    SEMICOLON ";"
+  WHITESPACE "\n"
+error 8: expected R_ANGLE
+error 8: expected type
+error 11: expected `{`
+error 15: expected BANG
+error 15: expected `{`, `[`, `(`
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/err/0055_impl_use.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/err/0055_impl_use.rs
new file mode 100644
index 00000000000..571552bda84
--- /dev/null
+++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/err/0055_impl_use.rs
@@ -0,0 +1,2 @@
+impl<T:
+use std;
diff --git a/src/tools/rust-analyzer/crates/project-model/src/workspace.rs b/src/tools/rust-analyzer/crates/project-model/src/workspace.rs
index 71b9b61e205..d1ee579c0d8 100644
--- a/src/tools/rust-analyzer/crates/project-model/src/workspace.rs
+++ b/src/tools/rust-analyzer/crates/project-model/src/workspace.rs
@@ -553,7 +553,7 @@ impl ProjectWorkspace {
             ProjectWorkspaceKind::Json(project) => project
                 .crates()
                 .filter_map(|(_, krate)| krate.build.as_ref().map(|build| build.build_file.clone()))
-                .map(AbsPathBuf::assert)
+                .map(|build_file| self.workspace_root().join(build_file))
                 .collect(),
             _ => vec![],
         }
diff --git a/src/tools/rust-analyzer/crates/salsa/Cargo.toml b/src/tools/rust-analyzer/crates/ra-salsa/Cargo.toml
index 0d3e1197b5c..b81e780337f 100644
--- a/src/tools/rust-analyzer/crates/salsa/Cargo.toml
+++ b/src/tools/rust-analyzer/crates/ra-salsa/Cargo.toml
@@ -10,7 +10,7 @@ description = "A generic framework for on-demand, incrementalized computation (e
 rust-version.workspace = true
 
 [lib]
-name = "salsa"
+name = "ra_salsa"
 
 [dependencies]
 indexmap = "2.1.0"
@@ -23,7 +23,7 @@ oorandom = "11"
 triomphe = "0.1.11"
 itertools.workspace = true
 
-salsa-macros = { version = "0.0.0", path = "salsa-macros" }
+ra-salsa-macros = { version = "0.0.0", path = "ra-salsa-macros", package = "salsa-macros" }
 
 [dev-dependencies]
 linked-hash-map = "0.5.6"
diff --git a/src/tools/rust-analyzer/crates/salsa/FAQ.md b/src/tools/rust-analyzer/crates/ra-salsa/FAQ.md
index 9c9f6f92da9..9c9f6f92da9 100644
--- a/src/tools/rust-analyzer/crates/salsa/FAQ.md
+++ b/src/tools/rust-analyzer/crates/ra-salsa/FAQ.md
diff --git a/src/tools/rust-analyzer/crates/salsa/LICENSE-APACHE b/src/tools/rust-analyzer/crates/ra-salsa/LICENSE-APACHE
index 1b5ec8b78e2..1b5ec8b78e2 100644
--- a/src/tools/rust-analyzer/crates/salsa/LICENSE-APACHE
+++ b/src/tools/rust-analyzer/crates/ra-salsa/LICENSE-APACHE
diff --git a/src/tools/rust-analyzer/crates/salsa/LICENSE-MIT b/src/tools/rust-analyzer/crates/ra-salsa/LICENSE-MIT
index 31aa79387f2..31aa79387f2 100644
--- a/src/tools/rust-analyzer/crates/salsa/LICENSE-MIT
+++ b/src/tools/rust-analyzer/crates/ra-salsa/LICENSE-MIT
diff --git a/src/tools/rust-analyzer/crates/salsa/README.md b/src/tools/rust-analyzer/crates/ra-salsa/README.md
index 4a8d9f8c731..4a8d9f8c731 100644
--- a/src/tools/rust-analyzer/crates/salsa/README.md
+++ b/src/tools/rust-analyzer/crates/ra-salsa/README.md
diff --git a/src/tools/rust-analyzer/crates/salsa/salsa-macros/Cargo.toml b/src/tools/rust-analyzer/crates/ra-salsa/ra-salsa-macros/Cargo.toml
index 791d2f6e9f5..5613d75c752 100644
--- a/src/tools/rust-analyzer/crates/salsa/salsa-macros/Cargo.toml
+++ b/src/tools/rust-analyzer/crates/ra-salsa/ra-salsa-macros/Cargo.toml
@@ -11,7 +11,7 @@ rust-version.workspace = true
 
 [lib]
 proc-macro = true
-name = "salsa_macros"
+name = "ra_salsa_macros"
 
 [dependencies]
 heck = "0.4"
diff --git a/src/tools/rust-analyzer/crates/salsa/salsa-macros/LICENSE-APACHE b/src/tools/rust-analyzer/crates/ra-salsa/ra-salsa-macros/LICENSE-APACHE
index 0bf2cad6488..0bf2cad6488 100644
--- a/src/tools/rust-analyzer/crates/salsa/salsa-macros/LICENSE-APACHE
+++ b/src/tools/rust-analyzer/crates/ra-salsa/ra-salsa-macros/LICENSE-APACHE
diff --git a/src/tools/rust-analyzer/crates/salsa/salsa-macros/LICENSE-MIT b/src/tools/rust-analyzer/crates/ra-salsa/ra-salsa-macros/LICENSE-MIT
index d99cce5f720..d99cce5f720 100644
--- a/src/tools/rust-analyzer/crates/salsa/salsa-macros/LICENSE-MIT
+++ b/src/tools/rust-analyzer/crates/ra-salsa/ra-salsa-macros/LICENSE-MIT
diff --git a/src/tools/rust-analyzer/crates/salsa/salsa-macros/README.md b/src/tools/rust-analyzer/crates/ra-salsa/ra-salsa-macros/README.md
index 94389aee61a..94389aee61a 100644
--- a/src/tools/rust-analyzer/crates/salsa/salsa-macros/README.md
+++ b/src/tools/rust-analyzer/crates/ra-salsa/ra-salsa-macros/README.md
diff --git a/src/tools/rust-analyzer/crates/salsa/salsa-macros/src/database_storage.rs b/src/tools/rust-analyzer/crates/ra-salsa/ra-salsa-macros/src/database_storage.rs
index f16d814b9f0..63ab84a621e 100644
--- a/src/tools/rust-analyzer/crates/salsa/salsa-macros/src/database_storage.rs
+++ b/src/tools/rust-analyzer/crates/ra-salsa/ra-salsa-macros/src/database_storage.rs
@@ -1,4 +1,4 @@
-//! Implementation for `[salsa::database]` decorator.
+//! Implementation for `[ra_salsa::database]` decorator.
 
 use heck::ToSnakeCase;
 use proc_macro::TokenStream;
@@ -32,7 +32,7 @@ pub(crate) fn database(args: TokenStream, input: TokenStream) -> TokenStream {
         .iter()
         .map(|QueryGroup { group_path }| {
             quote! {
-                <#group_path as salsa::plumbing::QueryGroup>::GroupStorage
+                <#group_path as ra_salsa::plumbing::QueryGroup>::GroupStorage
             }
         })
         .collect();
@@ -64,12 +64,12 @@ pub(crate) fn database(args: TokenStream, input: TokenStream) -> TokenStream {
 
         // ANCHOR:HasQueryGroup
         has_group_impls.extend(quote! {
-            impl salsa::plumbing::HasQueryGroup<#group_path> for #database_name {
+            impl ra_salsa::plumbing::HasQueryGroup<#group_path> for #database_name {
                 fn group_storage(&self) -> &#group_storage {
                     &self.#db_storage_field.query_store().#group_name_snake
                 }
 
-                fn group_storage_mut(&mut self) -> (&#group_storage, &mut salsa::Runtime) {
+                fn group_storage_mut(&mut self) -> (&#group_storage, &mut ra_salsa::Runtime) {
                     let (query_store_mut, runtime) = self.#db_storage_field.query_store_mut();
                     (&query_store_mut.#group_name_snake, runtime)
                 }
@@ -98,13 +98,13 @@ pub(crate) fn database(args: TokenStream, input: TokenStream) -> TokenStream {
     let mut database_data = vec![];
     for QueryGroup { group_path } in query_groups {
         database_data.push(quote! {
-            <#group_path as salsa::plumbing::QueryGroup>::GroupData
+            <#group_path as ra_salsa::plumbing::QueryGroup>::GroupData
         });
     }
 
     // ANCHOR:DatabaseStorageTypes
     output.extend(quote! {
-        impl salsa::plumbing::DatabaseStorageTypes for #database_name {
+        impl ra_salsa::plumbing::DatabaseStorageTypes for #database_name {
             type DatabaseStorage = __SalsaDatabaseStorage;
         }
     });
@@ -121,81 +121,81 @@ pub(crate) fn database(args: TokenStream, input: TokenStream) -> TokenStream {
         fmt_ops.extend(quote! {
             #group_index => {
                 let storage: &#group_storage =
-                    <Self as salsa::plumbing::HasQueryGroup<#group_path>>::group_storage(self);
+                    <Self as ra_salsa::plumbing::HasQueryGroup<#group_path>>::group_storage(self);
                 storage.fmt_index(self, input, fmt)
             }
         });
         maybe_changed_ops.extend(quote! {
             #group_index => {
                 let storage: &#group_storage =
-                    <Self as salsa::plumbing::HasQueryGroup<#group_path>>::group_storage(self);
+                    <Self as ra_salsa::plumbing::HasQueryGroup<#group_path>>::group_storage(self);
                 storage.maybe_changed_after(self, input, revision)
             }
         });
         cycle_recovery_strategy_ops.extend(quote! {
             #group_index => {
                 let storage: &#group_storage =
-                    <Self as salsa::plumbing::HasQueryGroup<#group_path>>::group_storage(self);
+                    <Self as ra_salsa::plumbing::HasQueryGroup<#group_path>>::group_storage(self);
                 storage.cycle_recovery_strategy(self, input)
             }
         });
         for_each_ops.extend(quote! {
             let storage: &#group_storage =
-                <Self as salsa::plumbing::HasQueryGroup<#group_path>>::group_storage(self);
+                <Self as ra_salsa::plumbing::HasQueryGroup<#group_path>>::group_storage(self);
             storage.for_each_query(runtime, &mut op);
         });
     }
     output.extend(quote! {
-        impl salsa::plumbing::DatabaseOps for #database_name {
-            fn ops_database(&self) -> &dyn salsa::Database {
+        impl ra_salsa::plumbing::DatabaseOps for #database_name {
+            fn ops_database(&self) -> &dyn ra_salsa::Database {
                 self
             }
 
-            fn ops_salsa_runtime(&self) -> &salsa::Runtime {
+            fn ops_salsa_runtime(&self) -> &ra_salsa::Runtime {
                 self.#db_storage_field.salsa_runtime()
             }
 
-            fn synthetic_write(&mut self, durability: salsa::Durability) {
+            fn synthetic_write(&mut self, durability: ra_salsa::Durability) {
                 self.#db_storage_field.salsa_runtime_mut().synthetic_write(durability)
             }
 
             fn fmt_index(
                 &self,
-                input: salsa::DatabaseKeyIndex,
+                input: ra_salsa::DatabaseKeyIndex,
                 fmt: &mut std::fmt::Formatter<'_>,
             ) -> std::fmt::Result {
                 match input.group_index() {
                     #fmt_ops
-                    i => panic!("salsa: invalid group index {}", i)
+                    i => panic!("ra_salsa: invalid group index {}", i)
                 }
             }
 
             fn maybe_changed_after(
                 &self,
-                input: salsa::DatabaseKeyIndex,
-                revision: salsa::Revision
+                input: ra_salsa::DatabaseKeyIndex,
+                revision: ra_salsa::Revision
             ) -> bool {
                 match input.group_index() {
                     #maybe_changed_ops
-                    i => panic!("salsa: invalid group index {}", i)
+                    i => panic!("ra_salsa: invalid group index {}", i)
                 }
             }
 
             fn cycle_recovery_strategy(
                 &self,
-                input: salsa::DatabaseKeyIndex,
-            ) -> salsa::plumbing::CycleRecoveryStrategy {
+                input: ra_salsa::DatabaseKeyIndex,
+            ) -> ra_salsa::plumbing::CycleRecoveryStrategy {
                 match input.group_index() {
                     #cycle_recovery_strategy_ops
-                    i => panic!("salsa: invalid group index {}", i)
+                    i => panic!("ra_salsa: invalid group index {}", i)
                 }
             }
 
             fn for_each_query(
                 &self,
-                mut op: &mut dyn FnMut(&dyn salsa::plumbing::QueryStorageMassOps),
+                mut op: &mut dyn FnMut(&dyn ra_salsa::plumbing::QueryStorageMassOps),
             ) {
-                let runtime = salsa::Database::salsa_runtime(self);
+                let runtime = ra_salsa::Database::salsa_runtime(self);
                 #for_each_ops
             }
         }
diff --git a/src/tools/rust-analyzer/crates/salsa/salsa-macros/src/lib.rs b/src/tools/rust-analyzer/crates/ra-salsa/ra-salsa-macros/src/lib.rs
index d3e17c5ebf1..d3e17c5ebf1 100644
--- a/src/tools/rust-analyzer/crates/salsa/salsa-macros/src/lib.rs
+++ b/src/tools/rust-analyzer/crates/ra-salsa/ra-salsa-macros/src/lib.rs
diff --git a/src/tools/rust-analyzer/crates/salsa/salsa-macros/src/parenthesized.rs b/src/tools/rust-analyzer/crates/ra-salsa/ra-salsa-macros/src/parenthesized.rs
index 5ecd1b8a058..5ecd1b8a058 100644
--- a/src/tools/rust-analyzer/crates/salsa/salsa-macros/src/parenthesized.rs
+++ b/src/tools/rust-analyzer/crates/ra-salsa/ra-salsa-macros/src/parenthesized.rs
diff --git a/src/tools/rust-analyzer/crates/salsa/salsa-macros/src/query_group.rs b/src/tools/rust-analyzer/crates/ra-salsa/ra-salsa-macros/src/query_group.rs
index eeaf008a15c..88db6093ee0 100644
--- a/src/tools/rust-analyzer/crates/salsa/salsa-macros/src/query_group.rs
+++ b/src/tools/rust-analyzer/crates/ra-salsa/ra-salsa-macros/src/query_group.rs
@@ -1,4 +1,4 @@
-//! Implementation for `[salsa::query_group]` decorator.
+//! Implementation for `[ra_salsa::query_group]` decorator.
 
 use crate::parenthesized::Parenthesized;
 use heck::ToUpperCamelCase;
@@ -10,7 +10,7 @@ use syn::{
     ReturnType, TraitItem, Type,
 };
 
-/// Implementation for `[salsa::query_group]` decorator.
+/// Implementation for `[ra_salsa::query_group]` decorator.
 pub(crate) fn query_group(args: TokenStream, input: TokenStream) -> TokenStream {
     let group_struct = parse_macro_input!(args as Ident);
     let input: ItemTrait = parse_macro_input!(input as ItemTrait);
@@ -82,7 +82,7 @@ pub(crate) fn query_group(args: TokenStream, input: TokenStream) -> TokenStream
                         num_storages += 1;
                     }
                     _ => {
-                        return Error::new(span, format!("unknown salsa attribute `{name}`"))
+                        return Error::new(span, format!("unknown ra_salsa attribute `{name}`"))
                             .to_compile_error()
                             .into();
                     }
@@ -100,7 +100,7 @@ pub(crate) fn query_group(args: TokenStream, input: TokenStream) -> TokenStream
                 Some(invoke) if storage == QueryStorage::Input => {
                     return Error::new(
                         invoke.span(),
-                        "#[salsa::invoke] cannot be set on #[salsa::input] queries",
+                        "#[ra_salsa::invoke] cannot be set on #[ra_salsa::input] queries",
                     )
                     .to_compile_error()
                     .into();
@@ -155,7 +155,7 @@ pub(crate) fn query_group(args: TokenStream, input: TokenStream) -> TokenStream
                 }
             };
 
-            // For `#[salsa::interned]` keys, we create a "lookup key" automatically.
+            // For `#[ra_salsa::interned]` keys, we create a "lookup key" automatically.
             //
             // For a query like:
             //
@@ -257,7 +257,7 @@ pub(crate) fn query_group(args: TokenStream, input: TokenStream) -> TokenStream
                 // difference in total compilation time in rust-analyzer, though
                 // it's not totally obvious why that should be.
                 fn __shim(db: &(dyn #trait_name + '_), #(#key_names: #keys),*) -> #value {
-                    salsa::plumbing::get_query_table::<#qt>(db).get((#(#key_names),*))
+                    ra_salsa::plumbing::get_query_table::<#qt>(db).get((#(#key_names),*))
                 }
                 __shim(self, #(#key_names),*)
 
@@ -302,20 +302,20 @@ pub(crate) fn query_group(args: TokenStream, input: TokenStream) -> TokenStream
 
 
                 # [doc = #set_constant_fn_docs]
-                fn #set_with_durability_fn_name(&mut self, #(#key_names: #keys,)* value__: #value, durability__: salsa::Durability);
+                fn #set_with_durability_fn_name(&mut self, #(#key_names: #keys,)* value__: #value, durability__: ra_salsa::Durability);
             });
 
             query_fn_definitions.extend(quote! {
                 fn #set_fn_name(&mut self, #(#key_names: #keys,)* value__: #value) {
                     fn __shim(db: &mut dyn #trait_name, #(#key_names: #keys,)* value__: #value) {
-                        salsa::plumbing::get_query_table_mut::<#qt>(db).set((#(#key_names),*), value__)
+                        ra_salsa::plumbing::get_query_table_mut::<#qt>(db).set((#(#key_names),*), value__)
                     }
                     __shim(self, #(#key_names,)* value__)
                 }
 
-                fn #set_with_durability_fn_name(&mut self, #(#key_names: #keys,)* value__: #value, durability__: salsa::Durability) {
-                    fn __shim(db: &mut dyn #trait_name, #(#key_names: #keys,)* value__: #value, durability__: salsa::Durability) {
-                        salsa::plumbing::get_query_table_mut::<#qt>(db).set_with_durability((#(#key_names),*), value__, durability__)
+                fn #set_with_durability_fn_name(&mut self, #(#key_names: #keys,)* value__: #value, durability__: ra_salsa::Durability) {
+                    fn __shim(db: &mut dyn #trait_name, #(#key_names: #keys,)* value__: #value, durability__: ra_salsa::Durability) {
+                        ra_salsa::plumbing::get_query_table_mut::<#qt>(db).set_with_durability((#(#key_names),*), value__, durability__)
                     }
                     __shim(self, #(#key_names,)* value__ ,durability__)
                 }
@@ -324,7 +324,7 @@ pub(crate) fn query_group(args: TokenStream, input: TokenStream) -> TokenStream
 
         // A field for the storage struct
         storage_fields.extend(quote! {
-            #fn_name: std::sync::Arc<<#qt as salsa::Query>::Storage>,
+            #fn_name: std::sync::Arc<<#qt as ra_salsa::Query>::Storage>,
         });
     }
 
@@ -334,8 +334,8 @@ pub(crate) fn query_group(args: TokenStream, input: TokenStream) -> TokenStream
         quote! {
             #(#trait_attrs)*
             #trait_vis trait #trait_name :
-            salsa::Database +
-            salsa::plumbing::HasQueryGroup<#group_struct> +
+            ra_salsa::Database +
+            ra_salsa::plumbing::HasQueryGroup<#group_struct> +
             #bounds
             {
                 #query_fn_declarations
@@ -348,7 +348,7 @@ pub(crate) fn query_group(args: TokenStream, input: TokenStream) -> TokenStream
         /// Representative struct for the query group.
         #trait_vis struct #group_struct { }
 
-        impl salsa::plumbing::QueryGroup for #group_struct
+        impl ra_salsa::plumbing::QueryGroup for #group_struct
         {
             type DynDb = #dyn_db;
             type GroupStorage = #group_storage;
@@ -362,8 +362,8 @@ pub(crate) fn query_group(args: TokenStream, input: TokenStream) -> TokenStream
             impl<DB> #trait_name for DB
             where
                 DB: #bounds,
-                DB: salsa::Database,
-                DB: salsa::plumbing::HasQueryGroup<#group_struct>,
+                DB: ra_salsa::Database,
+                DB: ra_salsa::plumbing::HasQueryGroup<#group_struct>,
             {
                 #query_fn_definitions
             }
@@ -379,18 +379,18 @@ pub(crate) fn query_group(args: TokenStream, input: TokenStream) -> TokenStream
         let qt = &query.query_type;
 
         let storage = match &query.storage {
-            QueryStorage::Memoized => quote!(salsa::plumbing::MemoizedStorage<Self>),
-            QueryStorage::LruMemoized => quote!(salsa::plumbing::LruMemoizedStorage<Self>),
+            QueryStorage::Memoized => quote!(ra_salsa::plumbing::MemoizedStorage<Self>),
+            QueryStorage::LruMemoized => quote!(ra_salsa::plumbing::LruMemoizedStorage<Self>),
             QueryStorage::LruDependencies => {
-                quote!(salsa::plumbing::LruDependencyStorage<Self>)
+                quote!(ra_salsa::plumbing::LruDependencyStorage<Self>)
             }
             QueryStorage::Input if query.keys.is_empty() => {
-                quote!(salsa::plumbing::UnitInputStorage<Self>)
+                quote!(ra_salsa::plumbing::UnitInputStorage<Self>)
             }
-            QueryStorage::Input => quote!(salsa::plumbing::InputStorage<Self>),
-            QueryStorage::Interned => quote!(salsa::plumbing::InternedStorage<Self>),
+            QueryStorage::Input => quote!(ra_salsa::plumbing::InputStorage<Self>),
+            QueryStorage::Interned => quote!(ra_salsa::plumbing::InternedStorage<Self>),
             QueryStorage::InternedLookup { intern_query_type } => {
-                quote!(salsa::plumbing::LookupInternedStorage<Self, #intern_query_type>)
+                quote!(ra_salsa::plumbing::LookupInternedStorage<Self, #intern_query_type>)
             }
             QueryStorage::Transparent => panic!("should have been filtered"),
         };
@@ -408,9 +408,9 @@ pub(crate) fn query_group(args: TokenStream, input: TokenStream) -> TokenStream
             impl #qt {
                 /// Get access to extra methods pertaining to this query.
                 /// You can also use it to invoke this query.
-                #trait_vis fn in_db(self, db: &#dyn_db) -> salsa::QueryTable<'_, Self>
+                #trait_vis fn in_db(self, db: &#dyn_db) -> ra_salsa::QueryTable<'_, Self>
                 {
-                    salsa::plumbing::get_query_table::<#qt>(db)
+                    ra_salsa::plumbing::get_query_table::<#qt>(db)
                 }
             }
         });
@@ -439,7 +439,7 @@ pub(crate) fn query_group(args: TokenStream, input: TokenStream) -> TokenStream
                 /// also set a cancellation flag. This will cause any query
                 /// invocations in other threads to unwind with a `Cancelled`
                 /// sentinel value and eventually let the `set` succeed once all
-                /// threads have unwound past the salsa invocation.
+                /// threads have unwound past the ra_salsa invocation.
                 ///
                 /// If your query implementations are performing expensive
                 /// operations without invoking another query, you can also use
@@ -448,13 +448,13 @@ pub(crate) fn query_group(args: TokenStream, input: TokenStream) -> TokenStream
                 /// thus allowing the `set` to succeed. Otherwise, long-running
                 /// computations may lead to "starvation", meaning that the
                 /// thread attempting to `set` has to wait a long, long time. =)
-                #trait_vis fn in_db_mut(self, db: &mut #dyn_db) -> salsa::QueryTableMut<'_, Self>
+                #trait_vis fn in_db_mut(self, db: &mut #dyn_db) -> ra_salsa::QueryTableMut<'_, Self>
                 {
-                    salsa::plumbing::get_query_table_mut::<#qt>(db)
+                    ra_salsa::plumbing::get_query_table_mut::<#qt>(db)
                 }
             }
 
-            impl<'d> salsa::QueryDb<'d> for #qt
+            impl<'d> ra_salsa::QueryDb<'d> for #qt
             {
                 type DynDb = #dyn_db + 'd;
                 type Group = #group_struct;
@@ -462,7 +462,7 @@ pub(crate) fn query_group(args: TokenStream, input: TokenStream) -> TokenStream
             }
 
             // ANCHOR:Query_impl
-            impl salsa::Query for #qt
+            impl ra_salsa::Query for #qt
             {
                 type Key = (#(#keys),*);
                 type Value = #value;
@@ -473,13 +473,13 @@ pub(crate) fn query_group(args: TokenStream, input: TokenStream) -> TokenStream
                 const QUERY_NAME: &'static str = #query_name;
 
                 fn query_storage<'a>(
-                    group_storage: &'a <Self as salsa::QueryDb<'_>>::GroupStorage,
+                    group_storage: &'a <Self as ra_salsa::QueryDb<'_>>::GroupStorage,
                 ) -> &'a std::sync::Arc<Self::Storage> {
                     &group_storage.#fn_name
                 }
 
                 fn query_storage_mut<'a>(
-                    group_storage: &'a <Self as salsa::QueryDb<'_>>::GroupStorage,
+                    group_storage: &'a <Self as ra_salsa::QueryDb<'_>>::GroupStorage,
                 ) -> &'a std::sync::Arc<Self::Storage> {
                     &group_storage.#fn_name
                 }
@@ -501,10 +501,10 @@ pub(crate) fn query_group(args: TokenStream, input: TokenStream) -> TokenStream
 
             let recover = if let Some(cycle_recovery_fn) = &query.cycle {
                 quote! {
-                    const CYCLE_STRATEGY: salsa::plumbing::CycleRecoveryStrategy =
-                        salsa::plumbing::CycleRecoveryStrategy::Fallback;
-                    fn cycle_fallback(db: &<Self as salsa::QueryDb<'_>>::DynDb, cycle: &salsa::Cycle, #key_pattern: &<Self as salsa::Query>::Key)
-                        -> <Self as salsa::Query>::Value {
+                    const CYCLE_STRATEGY: ra_salsa::plumbing::CycleRecoveryStrategy =
+                        ra_salsa::plumbing::CycleRecoveryStrategy::Fallback;
+                    fn cycle_fallback(db: &<Self as ra_salsa::QueryDb<'_>>::DynDb, cycle: &ra_salsa::Cycle, #key_pattern: &<Self as ra_salsa::Query>::Key)
+                        -> <Self as ra_salsa::Query>::Value {
                         #cycle_recovery_fn(
                                 db,
                                 cycle,
@@ -514,17 +514,17 @@ pub(crate) fn query_group(args: TokenStream, input: TokenStream) -> TokenStream
                 }
             } else {
                 quote! {
-                    const CYCLE_STRATEGY: salsa::plumbing::CycleRecoveryStrategy =
-                        salsa::plumbing::CycleRecoveryStrategy::Panic;
+                    const CYCLE_STRATEGY: ra_salsa::plumbing::CycleRecoveryStrategy =
+                        ra_salsa::plumbing::CycleRecoveryStrategy::Panic;
                 }
             };
 
             output.extend(quote_spanned! {span=>
                 // ANCHOR:QueryFunction_impl
-                impl salsa::plumbing::QueryFunction for #qt
+                impl ra_salsa::plumbing::QueryFunction for #qt
                 {
-                    fn execute(db: &<Self as salsa::QueryDb<'_>>::DynDb, #key_pattern: <Self as salsa::Query>::Key)
-                        -> <Self as salsa::Query>::Value {
+                    fn execute(db: &<Self as ra_salsa::QueryDb<'_>>::DynDb, #key_pattern: <Self as ra_salsa::Query>::Key)
+                        -> <Self as ra_salsa::Query>::Value {
                         #invoke(db, #(#key_names),*)
                     }
 
@@ -539,7 +539,7 @@ pub(crate) fn query_group(args: TokenStream, input: TokenStream) -> TokenStream
     for (Query { fn_name, .. }, query_index) in non_transparent_queries().zip(0_u16..) {
         fmt_ops.extend(quote! {
             #query_index => {
-                salsa::plumbing::QueryStorageOps::fmt_index(
+                ra_salsa::plumbing::QueryStorageOps::fmt_index(
                     &*self.#fn_name, db, input.key_index(), fmt,
                 )
             }
@@ -550,7 +550,7 @@ pub(crate) fn query_group(args: TokenStream, input: TokenStream) -> TokenStream
     for (Query { fn_name, .. }, query_index) in non_transparent_queries().zip(0_u16..) {
         maybe_changed_ops.extend(quote! {
             #query_index => {
-                salsa::plumbing::QueryStorageOps::maybe_changed_after(
+                ra_salsa::plumbing::QueryStorageOps::maybe_changed_after(
                     &*self.#fn_name, db, input.key_index(), revision
                 )
             }
@@ -561,7 +561,7 @@ pub(crate) fn query_group(args: TokenStream, input: TokenStream) -> TokenStream
     for (Query { fn_name, .. }, query_index) in non_transparent_queries().zip(0_u16..) {
         cycle_recovery_strategy_ops.extend(quote! {
             #query_index => {
-                salsa::plumbing::QueryStorageOps::cycle_recovery_strategy(
+                ra_salsa::plumbing::QueryStorageOps::cycle_recovery_strategy(
                     &*self.#fn_name
                 )
             }
@@ -587,7 +587,7 @@ pub(crate) fn query_group(args: TokenStream, input: TokenStream) -> TokenStream
                 #group_storage {
                     #(
                         #queries_with_storage:
-                        std::sync::Arc::new(salsa::plumbing::QueryStorageOps::new(group_index)),
+                        std::sync::Arc::new(ra_salsa::plumbing::QueryStorageOps::new(group_index)),
                     )*
                 }
             }
@@ -599,42 +599,42 @@ pub(crate) fn query_group(args: TokenStream, input: TokenStream) -> TokenStream
             #trait_vis fn fmt_index(
                 &self,
                 db: &(#dyn_db + '_),
-                input: salsa::DatabaseKeyIndex,
+                input: ra_salsa::DatabaseKeyIndex,
                 fmt: &mut std::fmt::Formatter<'_>,
             ) -> std::fmt::Result {
                 match input.query_index() {
                     #fmt_ops
-                    i => panic!("salsa: impossible query index {}", i),
+                    i => panic!("ra_salsa: impossible query index {}", i),
                 }
             }
 
             #trait_vis fn maybe_changed_after(
                 &self,
                 db: &(#dyn_db + '_),
-                input: salsa::DatabaseKeyIndex,
-                revision: salsa::Revision,
+                input: ra_salsa::DatabaseKeyIndex,
+                revision: ra_salsa::Revision,
             ) -> bool {
                 match input.query_index() {
                     #maybe_changed_ops
-                    i => panic!("salsa: impossible query index {}", i),
+                    i => panic!("ra_salsa: impossible query index {}", i),
                 }
             }
 
             #trait_vis fn cycle_recovery_strategy(
                 &self,
                 db: &(#dyn_db + '_),
-                input: salsa::DatabaseKeyIndex,
-            ) -> salsa::plumbing::CycleRecoveryStrategy {
+                input: ra_salsa::DatabaseKeyIndex,
+            ) -> ra_salsa::plumbing::CycleRecoveryStrategy {
                 match input.query_index() {
                     #cycle_recovery_strategy_ops
-                    i => panic!("salsa: impossible query index {}", i),
+                    i => panic!("ra_salsa: impossible query index {}", i),
                 }
             }
 
             #trait_vis fn for_each_query(
                 &self,
-                _runtime: &salsa::Runtime,
-                mut op: &mut dyn FnMut(&dyn salsa::plumbing::QueryStorageMassOps),
+                _runtime: &ra_salsa::Runtime,
+                mut op: &mut dyn FnMut(&dyn ra_salsa::plumbing::QueryStorageMassOps),
             ) {
                 #for_each_ops
             }
@@ -684,23 +684,23 @@ impl TryFrom<syn::Attribute> for SalsaAttr {
 }
 
 fn is_not_salsa_attr_path(path: &syn::Path) -> bool {
-    path.segments.first().map(|s| s.ident != "salsa").unwrap_or(true) || path.segments.len() != 2
+    path.segments.first().map(|s| s.ident != "ra_salsa").unwrap_or(true) || path.segments.len() != 2
 }
 
 fn filter_attrs(attrs: Vec<Attribute>) -> (Vec<Attribute>, Vec<SalsaAttr>) {
     let mut other = vec![];
-    let mut salsa = vec![];
-    // Leave non-salsa attributes untouched. These are
-    // attributes that don't start with `salsa::` or don't have
+    let mut ra_salsa = vec![];
+    // Leave non-ra_salsa attributes untouched. These are
+    // attributes that don't start with `ra_salsa::` or don't have
     // exactly two segments in their path.
-    // Keep the salsa attributes around.
+    // Keep the ra_salsa attributes around.
     for attr in attrs {
         match SalsaAttr::try_from(attr) {
-            Ok(it) => salsa.push(it),
+            Ok(it) => ra_salsa.push(it),
             Err(it) => other.push(it),
         }
     }
-    (other, salsa)
+    (other, ra_salsa)
 }
 
 #[derive(Debug)]
diff --git a/src/tools/rust-analyzer/crates/salsa/src/debug.rs b/src/tools/rust-analyzer/crates/ra-salsa/src/debug.rs
index 5f113541f04..5f113541f04 100644
--- a/src/tools/rust-analyzer/crates/salsa/src/debug.rs
+++ b/src/tools/rust-analyzer/crates/ra-salsa/src/debug.rs
diff --git a/src/tools/rust-analyzer/crates/salsa/src/derived.rs b/src/tools/rust-analyzer/crates/ra-salsa/src/derived.rs
index 8b2fdd6b19c..8b2fdd6b19c 100644
--- a/src/tools/rust-analyzer/crates/salsa/src/derived.rs
+++ b/src/tools/rust-analyzer/crates/ra-salsa/src/derived.rs
diff --git a/src/tools/rust-analyzer/crates/salsa/src/derived/slot.rs b/src/tools/rust-analyzer/crates/ra-salsa/src/derived/slot.rs
index de7a3976074..de7a3976074 100644
--- a/src/tools/rust-analyzer/crates/salsa/src/derived/slot.rs
+++ b/src/tools/rust-analyzer/crates/ra-salsa/src/derived/slot.rs
diff --git a/src/tools/rust-analyzer/crates/salsa/src/derived_lru.rs b/src/tools/rust-analyzer/crates/ra-salsa/src/derived_lru.rs
index bdb448e2412..bdb448e2412 100644
--- a/src/tools/rust-analyzer/crates/salsa/src/derived_lru.rs
+++ b/src/tools/rust-analyzer/crates/ra-salsa/src/derived_lru.rs
diff --git a/src/tools/rust-analyzer/crates/salsa/src/derived_lru/slot.rs b/src/tools/rust-analyzer/crates/ra-salsa/src/derived_lru/slot.rs
index d0e4b5422b5..d0e4b5422b5 100644
--- a/src/tools/rust-analyzer/crates/salsa/src/derived_lru/slot.rs
+++ b/src/tools/rust-analyzer/crates/ra-salsa/src/derived_lru/slot.rs
diff --git a/src/tools/rust-analyzer/crates/salsa/src/durability.rs b/src/tools/rust-analyzer/crates/ra-salsa/src/durability.rs
index 7b8e6840fc9..7b8e6840fc9 100644
--- a/src/tools/rust-analyzer/crates/salsa/src/durability.rs
+++ b/src/tools/rust-analyzer/crates/ra-salsa/src/durability.rs
diff --git a/src/tools/rust-analyzer/crates/salsa/src/hash.rs b/src/tools/rust-analyzer/crates/ra-salsa/src/hash.rs
index 3b2d7df3fbe..3b2d7df3fbe 100644
--- a/src/tools/rust-analyzer/crates/salsa/src/hash.rs
+++ b/src/tools/rust-analyzer/crates/ra-salsa/src/hash.rs
diff --git a/src/tools/rust-analyzer/crates/salsa/src/input.rs b/src/tools/rust-analyzer/crates/ra-salsa/src/input.rs
index f04f48e3bab..f04f48e3bab 100644
--- a/src/tools/rust-analyzer/crates/salsa/src/input.rs
+++ b/src/tools/rust-analyzer/crates/ra-salsa/src/input.rs
diff --git a/src/tools/rust-analyzer/crates/salsa/src/intern_id.rs b/src/tools/rust-analyzer/crates/ra-salsa/src/intern_id.rs
index 8e74c100aca..35b495998e1 100644
--- a/src/tools/rust-analyzer/crates/salsa/src/intern_id.rs
+++ b/src/tools/rust-analyzer/crates/ra-salsa/src/intern_id.rs
@@ -12,7 +12,7 @@ use std::num::NonZeroU32;
 /// which are implemented for `u32` and `usize`:
 ///
 /// ```
-/// # use salsa::InternId;
+/// # use ra_salsa::InternId;
 /// let intern_id1 = InternId::from(22_u32);
 /// let intern_id2 = InternId::from(22_usize);
 /// assert_eq!(intern_id1, intern_id2);
@@ -25,7 +25,7 @@ use std::num::NonZeroU32;
 /// `usize` using the `as_u32` or `as_usize` methods or the `From` impls.
 ///
 /// ```
-/// # use salsa::InternId;
+/// # use ra_salsa::InternId;;
 /// let intern_id = InternId::from(22_u32);
 /// let value = u32::from(intern_id);
 /// assert_eq!(value, 22);
@@ -41,7 +41,7 @@ use std::num::NonZeroU32;
 /// word.
 ///
 /// ```should_panic
-/// # use salsa::InternId;
+/// # use ra_salsa::InternId;;
 /// InternId::from(InternId::MAX);
 /// ```
 ///
@@ -70,7 +70,7 @@ impl InternId {
     /// Convert this raw-id into a u32 value.
     ///
     /// ```
-    /// # use salsa::InternId;
+    /// # use ra_salsa::InternId;
     /// let intern_id = InternId::from(22_u32);
     /// let value = intern_id.as_usize();
     /// assert_eq!(value, 22);
@@ -82,7 +82,7 @@ impl InternId {
     /// Convert this raw-id into a usize value.
     ///
     /// ```
-    /// # use salsa::InternId;
+    /// # use ra_salsa::InternId;
     /// let intern_id = InternId::from(22_u32);
     /// let value = intern_id.as_usize();
     /// assert_eq!(value, 22);
diff --git a/src/tools/rust-analyzer/crates/salsa/src/interned.rs b/src/tools/rust-analyzer/crates/ra-salsa/src/interned.rs
index 359662ec6b2..359662ec6b2 100644
--- a/src/tools/rust-analyzer/crates/salsa/src/interned.rs
+++ b/src/tools/rust-analyzer/crates/ra-salsa/src/interned.rs
diff --git a/src/tools/rust-analyzer/crates/salsa/src/lib.rs b/src/tools/rust-analyzer/crates/ra-salsa/src/lib.rs
index 48d6dc2e387..1b327773ec6 100644
--- a/src/tools/rust-analyzer/crates/salsa/src/lib.rs
+++ b/src/tools/rust-analyzer/crates/ra-salsa/src/lib.rs
@@ -745,6 +745,6 @@ impl Cycle {
 // Re-export the procedural macros.
 #[allow(unused_imports)]
 #[macro_use]
-extern crate salsa_macros;
+extern crate ra_salsa_macros;
 use plumbing::HasQueryGroup;
-pub use salsa_macros::*;
+pub use ra_salsa_macros::*;
diff --git a/src/tools/rust-analyzer/crates/salsa/src/lru.rs b/src/tools/rust-analyzer/crates/ra-salsa/src/lru.rs
index a6f96beeab1..a6f96beeab1 100644
--- a/src/tools/rust-analyzer/crates/salsa/src/lru.rs
+++ b/src/tools/rust-analyzer/crates/ra-salsa/src/lru.rs
diff --git a/src/tools/rust-analyzer/crates/salsa/src/plumbing.rs b/src/tools/rust-analyzer/crates/ra-salsa/src/plumbing.rs
index e96b9daa979..e96b9daa979 100644
--- a/src/tools/rust-analyzer/crates/salsa/src/plumbing.rs
+++ b/src/tools/rust-analyzer/crates/ra-salsa/src/plumbing.rs
diff --git a/src/tools/rust-analyzer/crates/salsa/src/revision.rs b/src/tools/rust-analyzer/crates/ra-salsa/src/revision.rs
index 7f4c333fb19..7f4c333fb19 100644
--- a/src/tools/rust-analyzer/crates/salsa/src/revision.rs
+++ b/src/tools/rust-analyzer/crates/ra-salsa/src/revision.rs
diff --git a/src/tools/rust-analyzer/crates/salsa/src/runtime.rs b/src/tools/rust-analyzer/crates/ra-salsa/src/runtime.rs
index 5fe5f4b46d3..5fe5f4b46d3 100644
--- a/src/tools/rust-analyzer/crates/salsa/src/runtime.rs
+++ b/src/tools/rust-analyzer/crates/ra-salsa/src/runtime.rs
diff --git a/src/tools/rust-analyzer/crates/salsa/src/runtime/dependency_graph.rs b/src/tools/rust-analyzer/crates/ra-salsa/src/runtime/dependency_graph.rs
index ed1d499f637..ed1d499f637 100644
--- a/src/tools/rust-analyzer/crates/salsa/src/runtime/dependency_graph.rs
+++ b/src/tools/rust-analyzer/crates/ra-salsa/src/runtime/dependency_graph.rs
diff --git a/src/tools/rust-analyzer/crates/salsa/src/runtime/local_state.rs b/src/tools/rust-analyzer/crates/ra-salsa/src/runtime/local_state.rs
index 73869671886..73869671886 100644
--- a/src/tools/rust-analyzer/crates/salsa/src/runtime/local_state.rs
+++ b/src/tools/rust-analyzer/crates/ra-salsa/src/runtime/local_state.rs
diff --git a/src/tools/rust-analyzer/crates/salsa/src/storage.rs b/src/tools/rust-analyzer/crates/ra-salsa/src/storage.rs
index e0acf44041b..e0acf44041b 100644
--- a/src/tools/rust-analyzer/crates/salsa/src/storage.rs
+++ b/src/tools/rust-analyzer/crates/ra-salsa/src/storage.rs
diff --git a/src/tools/rust-analyzer/crates/salsa/tests/cycles.rs b/src/tools/rust-analyzer/crates/ra-salsa/tests/cycles.rs
index e9bddfc630e..81136626551 100644
--- a/src/tools/rust-analyzer/crates/salsa/tests/cycles.rs
+++ b/src/tools/rust-analyzer/crates/ra-salsa/tests/cycles.rs
@@ -1,7 +1,7 @@
 use std::panic::UnwindSafe;
 
 use expect_test::expect;
-use salsa::{Durability, ParallelDatabase, Snapshot};
+use ra_salsa::{Durability, ParallelDatabase, Snapshot};
 
 // Axes:
 //
@@ -49,13 +49,13 @@ struct Error {
     cycle: Vec<String>,
 }
 
-#[salsa::database(GroupStruct)]
+#[ra_salsa::database(GroupStruct)]
 #[derive(Default)]
 struct DatabaseImpl {
-    storage: salsa::Storage<Self>,
+    storage: ra_salsa::Storage<Self>,
 }
 
-impl salsa::Database for DatabaseImpl {}
+impl ra_salsa::Database for DatabaseImpl {}
 
 impl ParallelDatabase for DatabaseImpl {
     fn snapshot(&self) -> Snapshot<Self> {
@@ -75,37 +75,37 @@ enum CycleQuery {
     AthenC,
 }
 
-#[salsa::query_group(GroupStruct)]
-trait Database: salsa::Database {
+#[ra_salsa::query_group(GroupStruct)]
+trait Database: ra_salsa::Database {
     // `a` and `b` depend on each other and form a cycle
     fn memoized_a(&self) -> ();
     fn memoized_b(&self) -> ();
     fn volatile_a(&self) -> ();
     fn volatile_b(&self) -> ();
 
-    #[salsa::input]
+    #[ra_salsa::input]
     fn a_invokes(&self) -> CycleQuery;
 
-    #[salsa::input]
+    #[ra_salsa::input]
     fn b_invokes(&self) -> CycleQuery;
 
-    #[salsa::input]
+    #[ra_salsa::input]
     fn c_invokes(&self) -> CycleQuery;
 
-    #[salsa::cycle(recover_a)]
+    #[ra_salsa::cycle(recover_a)]
     fn cycle_a(&self) -> Result<(), Error>;
 
-    #[salsa::cycle(recover_b)]
+    #[ra_salsa::cycle(recover_b)]
     fn cycle_b(&self) -> Result<(), Error>;
 
     fn cycle_c(&self) -> Result<(), Error>;
 }
 
-fn recover_a(db: &dyn Database, cycle: &salsa::Cycle) -> Result<(), Error> {
+fn recover_a(db: &dyn Database, cycle: &ra_salsa::Cycle) -> Result<(), Error> {
     Err(Error { cycle: cycle.all_participants(db) })
 }
 
-fn recover_b(db: &dyn Database, cycle: &salsa::Cycle) -> Result<(), Error> {
+fn recover_b(db: &dyn Database, cycle: &ra_salsa::Cycle) -> Result<(), Error> {
     Err(Error { cycle: cycle.all_participants(db) })
 }
 
@@ -155,10 +155,10 @@ fn cycle_c(db: &dyn Database) -> Result<(), Error> {
 }
 
 #[track_caller]
-fn extract_cycle(f: impl FnOnce() + UnwindSafe) -> salsa::Cycle {
+fn extract_cycle(f: impl FnOnce() + UnwindSafe) -> ra_salsa::Cycle {
     let v = std::panic::catch_unwind(f);
     if let Err(d) = &v {
-        if let Some(cycle) = d.downcast_ref::<salsa::Cycle>() {
+        if let Some(cycle) = d.downcast_ref::<ra_salsa::Cycle>() {
             return cycle.clone();
         }
     }
diff --git a/src/tools/rust-analyzer/crates/salsa/tests/dyn_trait.rs b/src/tools/rust-analyzer/crates/ra-salsa/tests/dyn_trait.rs
index 09ebc5c4ce4..6075ae5c11e 100644
--- a/src/tools/rust-analyzer/crates/salsa/tests/dyn_trait.rs
+++ b/src/tools/rust-analyzer/crates/ra-salsa/tests/dyn_trait.rs
@@ -1,16 +1,16 @@
 //! Test that you can implement a query using a `dyn Trait` setup.
 
-#[salsa::database(DynTraitStorage)]
+#[ra_salsa::database(DynTraitStorage)]
 #[derive(Default)]
 struct DynTraitDatabase {
-    storage: salsa::Storage<Self>,
+    storage: ra_salsa::Storage<Self>,
 }
 
-impl salsa::Database for DynTraitDatabase {}
+impl ra_salsa::Database for DynTraitDatabase {}
 
-#[salsa::query_group(DynTraitStorage)]
+#[ra_salsa::query_group(DynTraitStorage)]
 trait DynTrait {
-    #[salsa::input]
+    #[ra_salsa::input]
     fn input(&self, x: u32) -> u32;
 
     fn output(&self, x: u32) -> u32;
diff --git a/src/tools/rust-analyzer/crates/salsa/tests/incremental/constants.rs b/src/tools/rust-analyzer/crates/ra-salsa/tests/incremental/constants.rs
index 32bfbc4564b..6e51545b60a 100644
--- a/src/tools/rust-analyzer/crates/salsa/tests/incremental/constants.rs
+++ b/src/tools/rust-analyzer/crates/ra-salsa/tests/incremental/constants.rs
@@ -1,10 +1,10 @@
 use crate::implementation::{TestContext, TestContextImpl};
-use salsa::debug::DebugQueryTable;
-use salsa::Durability;
+use ra_salsa::debug::DebugQueryTable;
+use ra_salsa::Durability;
 
-#[salsa::query_group(Constants)]
+#[ra_salsa::query_group(Constants)]
 pub(crate) trait ConstantsDatabase: TestContext {
-    #[salsa::input]
+    #[ra_salsa::input]
     fn input(&self, key: char) -> usize;
 
     fn add(&self, key1: char, key2: char) -> usize;
diff --git a/src/tools/rust-analyzer/crates/salsa/tests/incremental/counter.rs b/src/tools/rust-analyzer/crates/ra-salsa/tests/incremental/counter.rs
index c04857e24c9..c04857e24c9 100644
--- a/src/tools/rust-analyzer/crates/salsa/tests/incremental/counter.rs
+++ b/src/tools/rust-analyzer/crates/ra-salsa/tests/incremental/counter.rs
diff --git a/src/tools/rust-analyzer/crates/salsa/tests/incremental/implementation.rs b/src/tools/rust-analyzer/crates/ra-salsa/tests/incremental/implementation.rs
index 84349134415..e9a59c46304 100644
--- a/src/tools/rust-analyzer/crates/salsa/tests/incremental/implementation.rs
+++ b/src/tools/rust-analyzer/crates/ra-salsa/tests/incremental/implementation.rs
@@ -5,12 +5,12 @@ use crate::memoized_dep_inputs;
 use crate::memoized_inputs;
 use crate::memoized_volatile;
 
-pub(crate) trait TestContext: salsa::Database {
+pub(crate) trait TestContext: ra_salsa::Database {
     fn clock(&self) -> &Counter;
     fn log(&self) -> &Log;
 }
 
-#[salsa::database(
+#[ra_salsa::database(
     constants::Constants,
     memoized_dep_inputs::MemoizedDepInputs,
     memoized_inputs::MemoizedInputs,
@@ -18,7 +18,7 @@ pub(crate) trait TestContext: salsa::Database {
 )]
 #[derive(Default)]
 pub(crate) struct TestContextImpl {
-    storage: salsa::Storage<TestContextImpl>,
+    storage: ra_salsa::Storage<TestContextImpl>,
     clock: Counter,
     log: Log,
 }
@@ -56,4 +56,4 @@ impl TestContext for TestContextImpl {
     }
 }
 
-impl salsa::Database for TestContextImpl {}
+impl ra_salsa::Database for TestContextImpl {}
diff --git a/src/tools/rust-analyzer/crates/salsa/tests/incremental/log.rs b/src/tools/rust-analyzer/crates/ra-salsa/tests/incremental/log.rs
index 1ee57fe667d..1ee57fe667d 100644
--- a/src/tools/rust-analyzer/crates/salsa/tests/incremental/log.rs
+++ b/src/tools/rust-analyzer/crates/ra-salsa/tests/incremental/log.rs
diff --git a/src/tools/rust-analyzer/crates/salsa/tests/incremental/main.rs b/src/tools/rust-analyzer/crates/ra-salsa/tests/incremental/main.rs
index bcd13c75f71..bcd13c75f71 100644
--- a/src/tools/rust-analyzer/crates/salsa/tests/incremental/main.rs
+++ b/src/tools/rust-analyzer/crates/ra-salsa/tests/incremental/main.rs
diff --git a/src/tools/rust-analyzer/crates/salsa/tests/incremental/memoized_dep_inputs.rs b/src/tools/rust-analyzer/crates/ra-salsa/tests/incremental/memoized_dep_inputs.rs
index 4ea33e0c1a0..0043bb45745 100644
--- a/src/tools/rust-analyzer/crates/salsa/tests/incremental/memoized_dep_inputs.rs
+++ b/src/tools/rust-analyzer/crates/ra-salsa/tests/incremental/memoized_dep_inputs.rs
@@ -1,14 +1,14 @@
 use crate::implementation::{TestContext, TestContextImpl};
 
-#[salsa::query_group(MemoizedDepInputs)]
+#[ra_salsa::query_group(MemoizedDepInputs)]
 pub(crate) trait MemoizedDepInputsContext: TestContext {
     fn dep_memoized2(&self) -> usize;
     fn dep_memoized1(&self) -> usize;
-    #[salsa::dependencies]
+    #[ra_salsa::dependencies]
     fn dep_derived1(&self) -> usize;
-    #[salsa::input]
+    #[ra_salsa::input]
     fn dep_input1(&self) -> usize;
-    #[salsa::input]
+    #[ra_salsa::input]
     fn dep_input2(&self) -> usize;
 }
 
diff --git a/src/tools/rust-analyzer/crates/salsa/tests/incremental/memoized_inputs.rs b/src/tools/rust-analyzer/crates/ra-salsa/tests/incremental/memoized_inputs.rs
index 53d2ace8871..007dc3db95a 100644
--- a/src/tools/rust-analyzer/crates/salsa/tests/incremental/memoized_inputs.rs
+++ b/src/tools/rust-analyzer/crates/ra-salsa/tests/incremental/memoized_inputs.rs
@@ -1,11 +1,11 @@
 use crate::implementation::{TestContext, TestContextImpl};
 
-#[salsa::query_group(MemoizedInputs)]
+#[ra_salsa::query_group(MemoizedInputs)]
 pub(crate) trait MemoizedInputsContext: TestContext {
     fn max(&self) -> usize;
-    #[salsa::input]
+    #[ra_salsa::input]
     fn input1(&self) -> usize;
-    #[salsa::input]
+    #[ra_salsa::input]
     fn input2(&self) -> usize;
 }
 
diff --git a/src/tools/rust-analyzer/crates/salsa/tests/incremental/memoized_volatile.rs b/src/tools/rust-analyzer/crates/ra-salsa/tests/incremental/memoized_volatile.rs
index 3dcc32eece3..cd00cc2e6cc 100644
--- a/src/tools/rust-analyzer/crates/salsa/tests/incremental/memoized_volatile.rs
+++ b/src/tools/rust-analyzer/crates/ra-salsa/tests/incremental/memoized_volatile.rs
@@ -1,7 +1,7 @@
 use crate::implementation::{TestContext, TestContextImpl};
-use salsa::{Database, Durability};
+use ra_salsa::{Database, Durability};
 
-#[salsa::query_group(MemoizedVolatile)]
+#[ra_salsa::query_group(MemoizedVolatile)]
 pub(crate) trait MemoizedVolatileContext: TestContext {
     // Queries for testing a "volatile" value wrapped by
     // memoization.
diff --git a/src/tools/rust-analyzer/crates/salsa/tests/interned.rs b/src/tools/rust-analyzer/crates/ra-salsa/tests/interned.rs
index d097e41cfd6..108b129fa3f 100644
--- a/src/tools/rust-analyzer/crates/salsa/tests/interned.rs
+++ b/src/tools/rust-analyzer/crates/ra-salsa/tests/interned.rs
@@ -1,37 +1,37 @@
 //! Test that you can implement a query using a `dyn Trait` setup.
 
-use salsa::InternId;
+use ra_salsa::InternId;
 
-#[salsa::database(InternStorage)]
+#[ra_salsa::database(InternStorage)]
 #[derive(Default)]
 struct Database {
-    storage: salsa::Storage<Self>,
+    storage: ra_salsa::Storage<Self>,
 }
 
-impl salsa::Database for Database {}
+impl ra_salsa::Database for Database {}
 
-impl salsa::ParallelDatabase for Database {
-    fn snapshot(&self) -> salsa::Snapshot<Self> {
-        salsa::Snapshot::new(Database { storage: self.storage.snapshot() })
+impl ra_salsa::ParallelDatabase for Database {
+    fn snapshot(&self) -> ra_salsa::Snapshot<Self> {
+        ra_salsa::Snapshot::new(Database { storage: self.storage.snapshot() })
     }
 }
 
-#[salsa::query_group(InternStorage)]
+#[ra_salsa::query_group(InternStorage)]
 trait Intern {
-    #[salsa::interned]
+    #[ra_salsa::interned]
     fn intern1(&self, x: String) -> InternId;
 
-    #[salsa::interned]
+    #[ra_salsa::interned]
     fn intern2(&self, x: String, y: String) -> InternId;
 
-    #[salsa::interned]
+    #[ra_salsa::interned]
     fn intern_key(&self, x: String) -> InternKey;
 }
 
 #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
 pub struct InternKey(InternId);
 
-impl salsa::InternKey for InternKey {
+impl ra_salsa::InternKey for InternKey {
     fn from_intern_id(v: InternId) -> Self {
         InternKey(v)
     }
diff --git a/src/tools/rust-analyzer/crates/salsa/tests/lru.rs b/src/tools/rust-analyzer/crates/ra-salsa/tests/lru.rs
index ef98a2c32b4..f351f242468 100644
--- a/src/tools/rust-analyzer/crates/salsa/tests/lru.rs
+++ b/src/tools/rust-analyzer/crates/ra-salsa/tests/lru.rs
@@ -22,11 +22,11 @@ impl Drop for HotPotato {
     }
 }
 
-#[salsa::query_group(QueryGroupStorage)]
-trait QueryGroup: salsa::Database {
-    #[salsa::lru]
+#[ra_salsa::query_group(QueryGroupStorage)]
+trait QueryGroup: ra_salsa::Database {
+    #[ra_salsa::lru]
     fn get(&self, x: u32) -> Arc<HotPotato>;
-    #[salsa::lru]
+    #[ra_salsa::lru]
     fn get_volatile(&self, x: u32) -> usize;
 }
 
@@ -40,13 +40,13 @@ fn get_volatile(db: &dyn QueryGroup, _x: u32) -> usize {
     COUNTER.fetch_add(1, Ordering::SeqCst)
 }
 
-#[salsa::database(QueryGroupStorage)]
+#[ra_salsa::database(QueryGroupStorage)]
 #[derive(Default)]
 struct Database {
-    storage: salsa::Storage<Self>,
+    storage: ra_salsa::Storage<Self>,
 }
 
-impl salsa::Database for Database {}
+impl ra_salsa::Database for Database {}
 
 #[test]
 fn lru_works() {
diff --git a/src/tools/rust-analyzer/crates/salsa/tests/macros.rs b/src/tools/rust-analyzer/crates/ra-salsa/tests/macros.rs
index 9b07740e7de..7bb6369b500 100644
--- a/src/tools/rust-analyzer/crates/salsa/tests/macros.rs
+++ b/src/tools/rust-analyzer/crates/ra-salsa/tests/macros.rs
@@ -1,6 +1,6 @@
-#[salsa::query_group(MyStruct)]
-trait MyDatabase: salsa::Database {
-    #[salsa::invoke(another_module::another_name)]
+#[ra_salsa::query_group(MyStruct)]
+trait MyDatabase: ra_salsa::Database {
+    #[ra_salsa::invoke(another_module::another_name)]
     fn my_query(&self, key: ()) -> ();
 }
 
diff --git a/src/tools/rust-analyzer/crates/salsa/tests/no_send_sync.rs b/src/tools/rust-analyzer/crates/ra-salsa/tests/no_send_sync.rs
index 2a25c437c3e..56bd3f4a7ed 100644
--- a/src/tools/rust-analyzer/crates/salsa/tests/no_send_sync.rs
+++ b/src/tools/rust-analyzer/crates/ra-salsa/tests/no_send_sync.rs
@@ -1,7 +1,7 @@
 use std::rc::Rc;
 
-#[salsa::query_group(NoSendSyncStorage)]
-trait NoSendSyncDatabase: salsa::Database {
+#[ra_salsa::query_group(NoSendSyncStorage)]
+trait NoSendSyncDatabase: ra_salsa::Database {
     fn no_send_sync_value(&self, key: bool) -> Rc<bool>;
     fn no_send_sync_key(&self, key: Rc<bool>) -> bool;
 }
@@ -14,13 +14,13 @@ fn no_send_sync_key(_db: &dyn NoSendSyncDatabase, key: Rc<bool>) -> bool {
     *key
 }
 
-#[salsa::database(NoSendSyncStorage)]
+#[ra_salsa::database(NoSendSyncStorage)]
 #[derive(Default)]
 struct DatabaseImpl {
-    storage: salsa::Storage<Self>,
+    storage: ra_salsa::Storage<Self>,
 }
 
-impl salsa::Database for DatabaseImpl {}
+impl ra_salsa::Database for DatabaseImpl {}
 
 #[test]
 fn no_send_sync() {
diff --git a/src/tools/rust-analyzer/crates/salsa/tests/on_demand_inputs.rs b/src/tools/rust-analyzer/crates/ra-salsa/tests/on_demand_inputs.rs
index cad594f536f..4d7832f9ba0 100644
--- a/src/tools/rust-analyzer/crates/salsa/tests/on_demand_inputs.rs
+++ b/src/tools/rust-analyzer/crates/ra-salsa/tests/on_demand_inputs.rs
@@ -8,10 +8,10 @@
 
 use std::{cell::RefCell, collections::HashMap, rc::Rc};
 
-use salsa::{Database as _, Durability, EventKind};
+use ra_salsa::{Database as _, Durability, EventKind};
 
-#[salsa::query_group(QueryGroupStorage)]
-trait QueryGroup: salsa::Database + AsRef<HashMap<u32, u32>> {
+#[ra_salsa::query_group(QueryGroupStorage)]
+trait QueryGroup: ra_salsa::Database + AsRef<HashMap<u32, u32>> {
     fn a(&self, x: u32) -> u32;
     fn b(&self, x: u32) -> u32;
     fn c(&self, x: u32) -> u32;
@@ -32,16 +32,16 @@ fn c(db: &dyn QueryGroup, x: u32) -> u32 {
     db.b(x)
 }
 
-#[salsa::database(QueryGroupStorage)]
+#[ra_salsa::database(QueryGroupStorage)]
 #[derive(Default)]
 struct Database {
-    storage: salsa::Storage<Self>,
+    storage: ra_salsa::Storage<Self>,
     external_state: HashMap<u32, u32>,
-    on_event: Option<Box<dyn Fn(&Database, salsa::Event)>>,
+    on_event: Option<Box<dyn Fn(&Database, ra_salsa::Event)>>,
 }
 
-impl salsa::Database for Database {
-    fn salsa_event(&self, event: salsa::Event) {
+impl ra_salsa::Database for Database {
+    fn salsa_event(&self, event: ra_salsa::Event) {
         if let Some(cb) = &self.on_event {
             cb(self, event)
         }
diff --git a/src/tools/rust-analyzer/crates/salsa/tests/panic_safely.rs b/src/tools/rust-analyzer/crates/ra-salsa/tests/panic_safely.rs
index c11ae9c2144..047a50eb4b2 100644
--- a/src/tools/rust-analyzer/crates/salsa/tests/panic_safely.rs
+++ b/src/tools/rust-analyzer/crates/ra-salsa/tests/panic_safely.rs
@@ -1,10 +1,10 @@
-use salsa::{Database, ParallelDatabase, Snapshot};
+use ra_salsa::{Database, ParallelDatabase, Snapshot};
 use std::panic::{self, AssertUnwindSafe};
 use std::sync::atomic::{AtomicU32, Ordering::SeqCst};
 
-#[salsa::query_group(PanicSafelyStruct)]
-trait PanicSafelyDatabase: salsa::Database {
-    #[salsa::input]
+#[ra_salsa::query_group(PanicSafelyStruct)]
+trait PanicSafelyDatabase: ra_salsa::Database {
+    #[ra_salsa::input]
     fn one(&self) -> usize;
 
     fn panic_safely(&self) -> ();
@@ -23,15 +23,15 @@ fn outer(db: &dyn PanicSafelyDatabase) {
     db.panic_safely();
 }
 
-#[salsa::database(PanicSafelyStruct)]
+#[ra_salsa::database(PanicSafelyStruct)]
 #[derive(Default)]
 struct DatabaseStruct {
-    storage: salsa::Storage<Self>,
+    storage: ra_salsa::Storage<Self>,
 }
 
-impl salsa::Database for DatabaseStruct {}
+impl ra_salsa::Database for DatabaseStruct {}
 
-impl salsa::ParallelDatabase for DatabaseStruct {
+impl ra_salsa::ParallelDatabase for DatabaseStruct {
     fn snapshot(&self) -> Snapshot<Self> {
         Snapshot::new(DatabaseStruct { storage: self.storage.snapshot() })
     }
diff --git a/src/tools/rust-analyzer/crates/salsa/tests/parallel/cancellation.rs b/src/tools/rust-analyzer/crates/ra-salsa/tests/parallel/cancellation.rs
index 9a92e5cc1ff..e47a8ef9aa8 100644
--- a/src/tools/rust-analyzer/crates/salsa/tests/parallel/cancellation.rs
+++ b/src/tools/rust-analyzer/crates/ra-salsa/tests/parallel/cancellation.rs
@@ -1,5 +1,5 @@
 use crate::setup::{CancellationFlag, Knobs, ParDatabase, ParDatabaseImpl, WithValue};
-use salsa::{Cancelled, ParallelDatabase};
+use ra_salsa::{Cancelled, ParallelDatabase};
 
 macro_rules! assert_cancelled {
     ($thread:expr) => {
@@ -96,7 +96,7 @@ fn in_par_get_set_cancellation_transitive() {
     assert_eq!(thread2.join().unwrap(), 111);
 }
 
-/// https://github.com/salsa-rs/salsa/issues/66
+/// https://github.com/ra_salsa-rs/ra_salsa/issues/66
 #[test]
 fn no_back_dating_in_cancellation() {
     let mut db = ParDatabaseImpl::default();
diff --git a/src/tools/rust-analyzer/crates/salsa/tests/parallel/frozen.rs b/src/tools/rust-analyzer/crates/ra-salsa/tests/parallel/frozen.rs
index 5359a8820e2..9e42e261517 100644
--- a/src/tools/rust-analyzer/crates/salsa/tests/parallel/frozen.rs
+++ b/src/tools/rust-analyzer/crates/ra-salsa/tests/parallel/frozen.rs
@@ -1,6 +1,6 @@
 use crate::setup::{ParDatabase, ParDatabaseImpl};
 use crate::signal::Signal;
-use salsa::{Database, ParallelDatabase};
+use ra_salsa::{Database, ParallelDatabase};
 use std::{
     panic::{catch_unwind, AssertUnwindSafe},
     sync::Arc,
diff --git a/src/tools/rust-analyzer/crates/salsa/tests/parallel/independent.rs b/src/tools/rust-analyzer/crates/ra-salsa/tests/parallel/independent.rs
index bd6ba3bf931..cbbac0608d1 100644
--- a/src/tools/rust-analyzer/crates/salsa/tests/parallel/independent.rs
+++ b/src/tools/rust-analyzer/crates/ra-salsa/tests/parallel/independent.rs
@@ -1,5 +1,5 @@
 use crate::setup::{ParDatabase, ParDatabaseImpl};
-use salsa::ParallelDatabase;
+use ra_salsa::ParallelDatabase;
 
 /// Test two `sum` queries (on distinct keys) executing in different
 /// threads. Really just a test that `snapshot` etc compiles.
diff --git a/src/tools/rust-analyzer/crates/salsa/tests/parallel/main.rs b/src/tools/rust-analyzer/crates/ra-salsa/tests/parallel/main.rs
index 31c0da18375..31c0da18375 100644
--- a/src/tools/rust-analyzer/crates/salsa/tests/parallel/main.rs
+++ b/src/tools/rust-analyzer/crates/ra-salsa/tests/parallel/main.rs
diff --git a/src/tools/rust-analyzer/crates/salsa/tests/parallel/parallel_cycle_all_recover.rs b/src/tools/rust-analyzer/crates/ra-salsa/tests/parallel/parallel_cycle_all_recover.rs
index a13ae3418f2..dabdb3babc0 100644
--- a/src/tools/rust-analyzer/crates/salsa/tests/parallel/parallel_cycle_all_recover.rs
+++ b/src/tools/rust-analyzer/crates/ra-salsa/tests/parallel/parallel_cycle_all_recover.rs
@@ -3,7 +3,7 @@
 //! both intra and cross thread.
 
 use crate::setup::{Knobs, ParDatabaseImpl};
-use salsa::ParallelDatabase;
+use ra_salsa::ParallelDatabase;
 
 // Recover cycle test:
 //
@@ -46,37 +46,37 @@ fn parallel_cycle_all_recover() {
     assert_eq!(thread_b.join().unwrap(), 21);
 }
 
-#[salsa::query_group(ParallelCycleAllRecover)]
+#[ra_salsa::query_group(ParallelCycleAllRecover)]
 pub(crate) trait TestDatabase: Knobs {
-    #[salsa::cycle(recover_a1)]
+    #[ra_salsa::cycle(recover_a1)]
     fn a1(&self, key: i32) -> i32;
 
-    #[salsa::cycle(recover_a2)]
+    #[ra_salsa::cycle(recover_a2)]
     fn a2(&self, key: i32) -> i32;
 
-    #[salsa::cycle(recover_b1)]
+    #[ra_salsa::cycle(recover_b1)]
     fn b1(&self, key: i32) -> i32;
 
-    #[salsa::cycle(recover_b2)]
+    #[ra_salsa::cycle(recover_b2)]
     fn b2(&self, key: i32) -> i32;
 }
 
-fn recover_a1(_db: &dyn TestDatabase, _cycle: &salsa::Cycle, key: &i32) -> i32 {
+fn recover_a1(_db: &dyn TestDatabase, _cycle: &ra_salsa::Cycle, key: &i32) -> i32 {
     tracing::debug!("recover_a1");
     key * 10 + 1
 }
 
-fn recover_a2(_db: &dyn TestDatabase, _cycle: &salsa::Cycle, key: &i32) -> i32 {
+fn recover_a2(_db: &dyn TestDatabase, _cycle: &ra_salsa::Cycle, key: &i32) -> i32 {
     tracing::debug!("recover_a2");
     key * 10 + 2
 }
 
-fn recover_b1(_db: &dyn TestDatabase, _cycle: &salsa::Cycle, key: &i32) -> i32 {
+fn recover_b1(_db: &dyn TestDatabase, _cycle: &ra_salsa::Cycle, key: &i32) -> i32 {
     tracing::debug!("recover_b1");
     key * 20 + 1
 }
 
-fn recover_b2(_db: &dyn TestDatabase, _cycle: &salsa::Cycle, key: &i32) -> i32 {
+fn recover_b2(_db: &dyn TestDatabase, _cycle: &ra_salsa::Cycle, key: &i32) -> i32 {
     tracing::debug!("recover_b2");
     key * 20 + 2
 }
diff --git a/src/tools/rust-analyzer/crates/salsa/tests/parallel/parallel_cycle_mid_recover.rs b/src/tools/rust-analyzer/crates/ra-salsa/tests/parallel/parallel_cycle_mid_recover.rs
index 971fe7ab120..20c508e0b8b 100644
--- a/src/tools/rust-analyzer/crates/salsa/tests/parallel/parallel_cycle_mid_recover.rs
+++ b/src/tools/rust-analyzer/crates/ra-salsa/tests/parallel/parallel_cycle_mid_recover.rs
@@ -3,7 +3,7 @@
 //! both intra and cross thread.
 
 use crate::setup::{Knobs, ParDatabaseImpl};
-use salsa::ParallelDatabase;
+use ra_salsa::ParallelDatabase;
 
 // Recover cycle test:
 //
@@ -47,27 +47,27 @@ fn parallel_cycle_mid_recovers() {
     assert_eq!(thread_b.join().unwrap(), 22);
 }
 
-#[salsa::query_group(ParallelCycleMidRecovers)]
+#[ra_salsa::query_group(ParallelCycleMidRecovers)]
 pub(crate) trait TestDatabase: Knobs {
     fn a1(&self, key: i32) -> i32;
 
     fn a2(&self, key: i32) -> i32;
 
-    #[salsa::cycle(recover_b1)]
+    #[ra_salsa::cycle(recover_b1)]
     fn b1(&self, key: i32) -> i32;
 
     fn b2(&self, key: i32) -> i32;
 
-    #[salsa::cycle(recover_b3)]
+    #[ra_salsa::cycle(recover_b3)]
     fn b3(&self, key: i32) -> i32;
 }
 
-fn recover_b1(_db: &dyn TestDatabase, _cycle: &salsa::Cycle, key: &i32) -> i32 {
+fn recover_b1(_db: &dyn TestDatabase, _cycle: &ra_salsa::Cycle, key: &i32) -> i32 {
     tracing::debug!("recover_b1");
     key * 20 + 2
 }
 
-fn recover_b3(_db: &dyn TestDatabase, _cycle: &salsa::Cycle, key: &i32) -> i32 {
+fn recover_b3(_db: &dyn TestDatabase, _cycle: &ra_salsa::Cycle, key: &i32) -> i32 {
     tracing::debug!("recover_b1");
     key * 200 + 2
 }
diff --git a/src/tools/rust-analyzer/crates/salsa/tests/parallel/parallel_cycle_none_recover.rs b/src/tools/rust-analyzer/crates/ra-salsa/tests/parallel/parallel_cycle_none_recover.rs
index 3c73852eafb..88d5fee0a22 100644
--- a/src/tools/rust-analyzer/crates/salsa/tests/parallel/parallel_cycle_none_recover.rs
+++ b/src/tools/rust-analyzer/crates/ra-salsa/tests/parallel/parallel_cycle_none_recover.rs
@@ -4,7 +4,7 @@
 
 use crate::setup::{Knobs, ParDatabaseImpl};
 use expect_test::expect;
-use salsa::ParallelDatabase;
+use ra_salsa::ParallelDatabase;
 
 #[test]
 fn parallel_cycle_none_recover() {
@@ -24,7 +24,7 @@ fn parallel_cycle_none_recover() {
     // We expect B to panic because it detects a cycle (it is the one that calls A, ultimately).
     // Right now, it panics with a string.
     let err_b = thread_b.join().unwrap_err();
-    if let Some(c) = err_b.downcast_ref::<salsa::Cycle>() {
+    if let Some(c) = err_b.downcast_ref::<ra_salsa::Cycle>() {
         expect![[r#"
             [
                 "parallel::parallel_cycle_none_recover::AQuery::a(-1)",
@@ -38,10 +38,10 @@ fn parallel_cycle_none_recover() {
 
     // We expect A to propagate a panic, which causes us to use the sentinel
     // type `Canceled`.
-    assert!(thread_a.join().unwrap_err().downcast_ref::<salsa::Cycle>().is_some());
+    assert!(thread_a.join().unwrap_err().downcast_ref::<ra_salsa::Cycle>().is_some());
 }
 
-#[salsa::query_group(ParallelCycleNoneRecover)]
+#[ra_salsa::query_group(ParallelCycleNoneRecover)]
 pub(crate) trait TestDatabase: Knobs {
     fn a(&self, key: i32) -> i32;
     fn b(&self, key: i32) -> i32;
diff --git a/src/tools/rust-analyzer/crates/salsa/tests/parallel/parallel_cycle_one_recovers.rs b/src/tools/rust-analyzer/crates/ra-salsa/tests/parallel/parallel_cycle_one_recovers.rs
index 025fbf37477..074ed1bd349 100644
--- a/src/tools/rust-analyzer/crates/salsa/tests/parallel/parallel_cycle_one_recovers.rs
+++ b/src/tools/rust-analyzer/crates/ra-salsa/tests/parallel/parallel_cycle_one_recovers.rs
@@ -3,7 +3,7 @@
 //! both intra and cross thread.
 
 use crate::setup::{Knobs, ParDatabaseImpl};
-use salsa::ParallelDatabase;
+use ra_salsa::ParallelDatabase;
 
 // Recover cycle test:
 //
@@ -49,11 +49,11 @@ fn parallel_cycle_one_recovers() {
     assert_eq!(thread_b.join().unwrap(), 22);
 }
 
-#[salsa::query_group(ParallelCycleOneRecovers)]
+#[ra_salsa::query_group(ParallelCycleOneRecovers)]
 pub(crate) trait TestDatabase: Knobs {
     fn a1(&self, key: i32) -> i32;
 
-    #[salsa::cycle(recover)]
+    #[ra_salsa::cycle(recover)]
     fn a2(&self, key: i32) -> i32;
 
     fn b1(&self, key: i32) -> i32;
@@ -61,7 +61,7 @@ pub(crate) trait TestDatabase: Knobs {
     fn b2(&self, key: i32) -> i32;
 }
 
-fn recover(_db: &dyn TestDatabase, _cycle: &salsa::Cycle, key: &i32) -> i32 {
+fn recover(_db: &dyn TestDatabase, _cycle: &ra_salsa::Cycle, key: &i32) -> i32 {
     tracing::debug!("recover");
     key * 20 + 2
 }
diff --git a/src/tools/rust-analyzer/crates/salsa/tests/parallel/race.rs b/src/tools/rust-analyzer/crates/ra-salsa/tests/parallel/race.rs
index c53d4b464ea..7aa6d4530b4 100644
--- a/src/tools/rust-analyzer/crates/salsa/tests/parallel/race.rs
+++ b/src/tools/rust-analyzer/crates/ra-salsa/tests/parallel/race.rs
@@ -1,7 +1,7 @@
 use std::panic::AssertUnwindSafe;
 
 use crate::setup::{ParDatabase, ParDatabaseImpl};
-use salsa::{Cancelled, ParallelDatabase};
+use ra_salsa::{Cancelled, ParallelDatabase};
 
 /// Test where a read and a set are racing with one another.
 /// Should be atomic.
diff --git a/src/tools/rust-analyzer/crates/salsa/tests/parallel/setup.rs b/src/tools/rust-analyzer/crates/ra-salsa/tests/parallel/setup.rs
index 0a35902b435..fd1f51326e3 100644
--- a/src/tools/rust-analyzer/crates/salsa/tests/parallel/setup.rs
+++ b/src/tools/rust-analyzer/crates/ra-salsa/tests/parallel/setup.rs
@@ -1,16 +1,16 @@
 use crate::signal::Signal;
-use salsa::Database;
-use salsa::ParallelDatabase;
-use salsa::Snapshot;
+use ra_salsa::Database;
+use ra_salsa::ParallelDatabase;
+use ra_salsa::Snapshot;
 use std::sync::Arc;
 use std::{
     cell::Cell,
     panic::{catch_unwind, resume_unwind, AssertUnwindSafe},
 };
 
-#[salsa::query_group(Par)]
+#[ra_salsa::query_group(Par)]
 pub(crate) trait ParDatabase: Knobs {
-    #[salsa::input]
+    #[ra_salsa::input]
     fn input(&self, key: char) -> usize;
 
     fn sum(&self, key: &'static str) -> usize;
@@ -152,7 +152,7 @@ fn sum3_drop_sum(db: &dyn ParDatabase, key: &'static str) -> usize {
     db.sum2_drop_sum(key)
 }
 
-#[salsa::database(
+#[ra_salsa::database(
     Par,
     crate::parallel_cycle_all_recover::ParallelCycleAllRecover,
     crate::parallel_cycle_none_recover::ParallelCycleNoneRecover,
@@ -161,13 +161,13 @@ fn sum3_drop_sum(db: &dyn ParDatabase, key: &'static str) -> usize {
 )]
 #[derive(Default)]
 pub(crate) struct ParDatabaseImpl {
-    storage: salsa::Storage<Self>,
+    storage: ra_salsa::Storage<Self>,
     knobs: KnobsStruct,
 }
 
 impl Database for ParDatabaseImpl {
-    fn salsa_event(&self, event: salsa::Event) {
-        if let salsa::EventKind::WillBlockOn { .. } = event.kind {
+    fn salsa_event(&self, event: ra_salsa::Event) {
+        if let ra_salsa::EventKind::WillBlockOn { .. } = event.kind {
             self.signal(self.knobs().signal_on_will_block.get());
         }
     }
diff --git a/src/tools/rust-analyzer/crates/salsa/tests/parallel/signal.rs b/src/tools/rust-analyzer/crates/ra-salsa/tests/parallel/signal.rs
index 0af7b66e482..0af7b66e482 100644
--- a/src/tools/rust-analyzer/crates/salsa/tests/parallel/signal.rs
+++ b/src/tools/rust-analyzer/crates/ra-salsa/tests/parallel/signal.rs
diff --git a/src/tools/rust-analyzer/crates/salsa/tests/parallel/stress.rs b/src/tools/rust-analyzer/crates/ra-salsa/tests/parallel/stress.rs
index 2fa317b2b90..f3a435b47f1 100644
--- a/src/tools/rust-analyzer/crates/salsa/tests/parallel/stress.rs
+++ b/src/tools/rust-analyzer/crates/ra-salsa/tests/parallel/stress.rs
@@ -1,17 +1,17 @@
 use rand::seq::SliceRandom;
 use rand::Rng;
 
-use salsa::ParallelDatabase;
-use salsa::Snapshot;
-use salsa::{Cancelled, Database};
+use ra_salsa::ParallelDatabase;
+use ra_salsa::Snapshot;
+use ra_salsa::{Cancelled, Database};
 
 // Number of operations a reader performs
 const N_MUTATOR_OPS: usize = 100;
 const N_READER_OPS: usize = 100;
 
-#[salsa::query_group(Stress)]
-trait StressDatabase: salsa::Database {
-    #[salsa::input]
+#[ra_salsa::query_group(Stress)]
+trait StressDatabase: ra_salsa::Database {
+    #[ra_salsa::input]
     fn a(&self, key: usize) -> usize;
 
     fn b(&self, key: usize) -> usize;
@@ -28,15 +28,15 @@ fn c(db: &dyn StressDatabase, key: usize) -> usize {
     db.b(key)
 }
 
-#[salsa::database(Stress)]
+#[ra_salsa::database(Stress)]
 #[derive(Default)]
 struct StressDatabaseImpl {
-    storage: salsa::Storage<Self>,
+    storage: ra_salsa::Storage<Self>,
 }
 
-impl salsa::Database for StressDatabaseImpl {}
+impl ra_salsa::Database for StressDatabaseImpl {}
 
-impl salsa::ParallelDatabase for StressDatabaseImpl {
+impl ra_salsa::ParallelDatabase for StressDatabaseImpl {
     fn snapshot(&self) -> Snapshot<StressDatabaseImpl> {
         Snapshot::new(StressDatabaseImpl { storage: self.storage.snapshot() })
     }
diff --git a/src/tools/rust-analyzer/crates/salsa/tests/parallel/true_parallel.rs b/src/tools/rust-analyzer/crates/ra-salsa/tests/parallel/true_parallel.rs
index d0e58efd1ac..44db17bd852 100644
--- a/src/tools/rust-analyzer/crates/salsa/tests/parallel/true_parallel.rs
+++ b/src/tools/rust-analyzer/crates/ra-salsa/tests/parallel/true_parallel.rs
@@ -1,5 +1,5 @@
 use crate::setup::{Knobs, ParDatabase, ParDatabaseImpl, WithValue};
-use salsa::ParallelDatabase;
+use ra_salsa::ParallelDatabase;
 use std::panic::{self, AssertUnwindSafe};
 
 /// Test where two threads are executing sum. We show that they can
diff --git a/src/tools/rust-analyzer/crates/salsa/tests/storage_varieties/implementation.rs b/src/tools/rust-analyzer/crates/ra-salsa/tests/storage_varieties/implementation.rs
index 2843660f154..39b2befd15b 100644
--- a/src/tools/rust-analyzer/crates/salsa/tests/storage_varieties/implementation.rs
+++ b/src/tools/rust-analyzer/crates/ra-salsa/tests/storage_varieties/implementation.rs
@@ -1,10 +1,10 @@
 use crate::queries;
 use std::cell::Cell;
 
-#[salsa::database(queries::GroupStruct)]
+#[ra_salsa::database(queries::GroupStruct)]
 #[derive(Default)]
 pub(crate) struct DatabaseImpl {
-    storage: salsa::Storage<Self>,
+    storage: ra_salsa::Storage<Self>,
     counter: Cell<usize>,
 }
 
@@ -16,4 +16,4 @@ impl queries::Counter for DatabaseImpl {
     }
 }
 
-impl salsa::Database for DatabaseImpl {}
+impl ra_salsa::Database for DatabaseImpl {}
diff --git a/src/tools/rust-analyzer/crates/salsa/tests/storage_varieties/main.rs b/src/tools/rust-analyzer/crates/ra-salsa/tests/storage_varieties/main.rs
index e92c61740e0..e92c61740e0 100644
--- a/src/tools/rust-analyzer/crates/salsa/tests/storage_varieties/main.rs
+++ b/src/tools/rust-analyzer/crates/ra-salsa/tests/storage_varieties/main.rs
diff --git a/src/tools/rust-analyzer/crates/salsa/tests/storage_varieties/queries.rs b/src/tools/rust-analyzer/crates/ra-salsa/tests/storage_varieties/queries.rs
index 0847fadefb0..bc9b10ae7bb 100644
--- a/src/tools/rust-analyzer/crates/salsa/tests/storage_varieties/queries.rs
+++ b/src/tools/rust-analyzer/crates/ra-salsa/tests/storage_varieties/queries.rs
@@ -1,8 +1,8 @@
-pub(crate) trait Counter: salsa::Database {
+pub(crate) trait Counter: ra_salsa::Database {
     fn increment(&self) -> usize;
 }
 
-#[salsa::query_group(GroupStruct)]
+#[ra_salsa::query_group(GroupStruct)]
 pub(crate) trait Database: Counter {
     fn memoized(&self) -> usize;
     fn volatile(&self) -> usize;
diff --git a/src/tools/rust-analyzer/crates/salsa/tests/storage_varieties/tests.rs b/src/tools/rust-analyzer/crates/ra-salsa/tests/storage_varieties/tests.rs
index 8e2f9b03cb9..7c33bbfc747 100644
--- a/src/tools/rust-analyzer/crates/salsa/tests/storage_varieties/tests.rs
+++ b/src/tools/rust-analyzer/crates/ra-salsa/tests/storage_varieties/tests.rs
@@ -2,8 +2,8 @@
 
 use crate::implementation::DatabaseImpl;
 use crate::queries::Database;
-use salsa::Database as _Database;
-use salsa::Durability;
+use ra_salsa::Database as _Database;
+use ra_salsa::Durability;
 
 #[test]
 fn memoized_twice() {
diff --git a/src/tools/rust-analyzer/crates/salsa/tests/transparent.rs b/src/tools/rust-analyzer/crates/ra-salsa/tests/transparent.rs
index 2e6dd4267b2..886f4641065 100644
--- a/src/tools/rust-analyzer/crates/salsa/tests/transparent.rs
+++ b/src/tools/rust-analyzer/crates/ra-salsa/tests/transparent.rs
@@ -1,10 +1,10 @@
 //! Test that transparent (uncached) queries work
 
-#[salsa::query_group(QueryGroupStorage)]
+#[ra_salsa::query_group(QueryGroupStorage)]
 trait QueryGroup {
-    #[salsa::input]
+    #[ra_salsa::input]
     fn input(&self, x: u32) -> u32;
-    #[salsa::transparent]
+    #[ra_salsa::transparent]
     fn wrap(&self, x: u32) -> u32;
     fn get(&self, x: u32) -> u32;
 }
@@ -17,13 +17,13 @@ fn get(db: &dyn QueryGroup, x: u32) -> u32 {
     db.wrap(x)
 }
 
-#[salsa::database(QueryGroupStorage)]
+#[ra_salsa::database(QueryGroupStorage)]
 #[derive(Default)]
 struct Database {
-    storage: salsa::Storage<Self>,
+    storage: ra_salsa::Storage<Self>,
 }
 
-impl salsa::Database for Database {}
+impl ra_salsa::Database for Database {}
 
 #[test]
 fn transparent_queries_work() {
diff --git a/src/tools/rust-analyzer/crates/salsa/tests/variadic.rs b/src/tools/rust-analyzer/crates/ra-salsa/tests/variadic.rs
index cb857844eb7..11a6d13ebe2 100644
--- a/src/tools/rust-analyzer/crates/salsa/tests/variadic.rs
+++ b/src/tools/rust-analyzer/crates/ra-salsa/tests/variadic.rs
@@ -1,6 +1,6 @@
-#[salsa::query_group(HelloWorld)]
-trait HelloWorldDatabase: salsa::Database {
-    #[salsa::input]
+#[ra_salsa::query_group(HelloWorld)]
+trait HelloWorldDatabase: ra_salsa::Database {
+    #[ra_salsa::input]
     fn input(&self, a: u32, b: u32) -> u32;
 
     fn none(&self) -> u32;
@@ -28,13 +28,13 @@ fn trailing(_db: &dyn HelloWorldDatabase, a: u32, b: u32) -> u32 {
     a - b
 }
 
-#[salsa::database(HelloWorld)]
+#[ra_salsa::database(HelloWorld)]
 #[derive(Default)]
 struct DatabaseStruct {
-    storage: salsa::Storage<Self>,
+    storage: ra_salsa::Storage<Self>,
 }
 
-impl salsa::Database for DatabaseStruct {}
+impl ra_salsa::Database for DatabaseStruct {}
 
 #[test]
 fn execute() {
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/Cargo.toml b/src/tools/rust-analyzer/crates/rust-analyzer/Cargo.toml
index eb95f42d755..2dd2f2242a0 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/Cargo.toml
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/Cargo.toml
@@ -25,7 +25,7 @@ crossbeam-channel.workspace = true
 dirs = "5.0.1"
 dissimilar.workspace = true
 itertools.workspace = true
-scip = "0.3.3"
+scip = "0.5.1"
 lsp-types = { version = "=0.95.0", features = ["proposed"] }
 parking_lot = "0.12.1"
 xflags = "0.3.0"
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/analysis_stats.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/analysis_stats.rs
index 4844c514ae9..c2164614274 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/analysis_stats.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/analysis_stats.rs
@@ -22,7 +22,7 @@ use ide::{
 };
 use ide_db::{
     base_db::{
-        salsa::{self, debug::DebugQueryTable, ParallelDatabase},
+        ra_salsa::{self, debug::DebugQueryTable, ParallelDatabase},
         SourceDatabase, SourceRootDatabase,
     },
     EditionedFileId, LineIndexDatabase, SnippetCap,
@@ -46,8 +46,8 @@ use crate::cli::{
 
 /// Need to wrap Snapshot to provide `Clone` impl for `map_with`
 struct Snap<DB>(DB);
-impl<DB: ParallelDatabase> Clone for Snap<salsa::Snapshot<DB>> {
-    fn clone(&self) -> Snap<salsa::Snapshot<DB>> {
+impl<DB: ParallelDatabase> Clone for Snap<ra_salsa::Snapshot<DB>> {
+    fn clone(&self) -> Snap<ra_salsa::Snapshot<DB>> {
         Snap(self.0.snapshot())
     }
 }
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs
index ef2e542cf22..518b588cb7d 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs
@@ -12,10 +12,11 @@ use std::{
 use cfg::{CfgAtom, CfgDiff};
 use hir::Symbol;
 use ide::{
-    AssistConfig, CallableSnippets, CompletionConfig, CompletionFieldsToResolve, DiagnosticsConfig,
-    ExprFillDefaultMode, GenericParameterHints, HighlightConfig, HighlightRelatedConfig,
-    HoverConfig, HoverDocFormat, InlayFieldsToResolve, InlayHintsConfig, JoinLinesConfig,
-    MemoryLayoutHoverConfig, MemoryLayoutHoverRenderKind, Snippet, SnippetScope, SourceRootId,
+    AssistConfig, CallHierarchyConfig, CallableSnippets, CompletionConfig,
+    CompletionFieldsToResolve, DiagnosticsConfig, ExprFillDefaultMode, GenericParameterHints,
+    HighlightConfig, HighlightRelatedConfig, HoverConfig, HoverDocFormat, InlayFieldsToResolve,
+    InlayHintsConfig, JoinLinesConfig, MemoryLayoutHoverConfig, MemoryLayoutHoverRenderKind,
+    Snippet, SnippetScope, SourceRootId,
 };
 use ide_db::{
     imports::insert_use::{ImportGranularity, InsertUseConfig, PrefixKind},
@@ -262,7 +263,7 @@ config_data! {
         /// Exclude imports from find-all-references.
         references_excludeImports: bool = false,
 
-        /// Exclude tests from find-all-references.
+        /// Exclude tests from find-all-references and call-hierarchy.
         references_excludeTests: bool = false,
 
         /// Inject additional highlighting into doc comments.
@@ -1392,6 +1393,10 @@ impl Config {
         }
     }
 
+    pub fn call_hierarchy(&self) -> CallHierarchyConfig {
+        CallHierarchyConfig { exclude_tests: self.references_excludeTests().to_owned() }
+    }
+
     pub fn completion(&self, source_root: Option<SourceRootId>) -> CompletionConfig {
         let client_capability_fields = self.completion_resolve_support_properties();
         CompletionConfig {
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/global_state.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/global_state.rs
index c3142c9cfca..7fbeaa4e3ea 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/global_state.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/global_state.rs
@@ -51,6 +51,11 @@ pub(crate) struct FetchWorkspaceResponse {
     pub(crate) force_crate_graph_reload: bool,
 }
 
+pub(crate) struct FetchBuildDataResponse {
+    pub(crate) workspaces: Arc<Vec<ProjectWorkspace>>,
+    pub(crate) build_scripts: Vec<anyhow::Result<WorkspaceBuildScripts>>,
+}
+
 // Enforces drop order
 pub(crate) struct Handle<H, C> {
     pub(crate) handle: H,
@@ -152,8 +157,7 @@ pub(crate) struct GlobalState {
 
     // op queues
     pub(crate) fetch_workspaces_queue: OpQueue<FetchWorkspaceRequest, FetchWorkspaceResponse>,
-    pub(crate) fetch_build_data_queue:
-        OpQueue<(), (Arc<Vec<ProjectWorkspace>>, Vec<anyhow::Result<WorkspaceBuildScripts>>)>,
+    pub(crate) fetch_build_data_queue: OpQueue<(), FetchBuildDataResponse>,
     pub(crate) fetch_proc_macros_queue: OpQueue<Vec<ProcMacroPaths>, bool>,
     pub(crate) prime_caches_queue: OpQueue,
     pub(crate) discover_workspace_queue: OpQueue,
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/request.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/request.rs
index 9773d8dbce0..a9f8ac3a80a 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/request.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/request.rs
@@ -1708,7 +1708,8 @@ pub(crate) fn handle_call_hierarchy_incoming(
     let frange = from_proto::file_range(&snap, &doc, item.selection_range)?;
     let fpos = FilePosition { file_id: frange.file_id, offset: frange.range.start() };
 
-    let call_items = match snap.analysis.incoming_calls(fpos)? {
+    let config = snap.config.call_hierarchy();
+    let call_items = match snap.analysis.incoming_calls(config, fpos)? {
         None => return Ok(None),
         Some(it) => it,
     };
@@ -1745,7 +1746,8 @@ pub(crate) fn handle_call_hierarchy_outgoing(
     let frange = from_proto::file_range(&snap, &doc, item.selection_range)?;
     let fpos = FilePosition { file_id: frange.file_id, offset: frange.range.start() };
 
-    let call_items = match snap.analysis.outgoing_calls(fpos)? {
+    let config = snap.config.call_hierarchy();
+    let call_items = match snap.analysis.outgoing_calls(config, fpos)? {
         None => return Ok(None),
         Some(it) => it,
     };
@@ -2201,14 +2203,14 @@ fn run_rustfmt(
             let cmd = Utf8PathBuf::from(&command);
             let target_spec = TargetSpec::for_file(snap, file_id)?;
             let mut cmd = match target_spec {
-                Some(TargetSpec::Cargo(spec)) => {
-                    // approach: if the command name contains a path separator, join it with the workspace root.
+                Some(TargetSpec::Cargo(_)) => {
+                    // approach: if the command name contains a path separator, join it with the project root.
                     // however, if the path is absolute, joining will result in the absolute path being preserved.
                     // as a fallback, rely on $PATH-based discovery.
                     let cmd_path = if command.contains(std::path::MAIN_SEPARATOR)
                         || (cfg!(windows) && command.contains('/'))
                     {
-                        spec.workspace_root.join(cmd).into()
+                        snap.config.root_path().join(cmd).into()
                     } else {
                         cmd
                     };
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/to_proto.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/to_proto.rs
index 375b7428c2d..d654dc3e7f4 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/to_proto.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/to_proto.rs
@@ -280,14 +280,14 @@ fn completion_item(
     let mut something_to_resolve = false;
 
     let filter_text = if fields_to_resolve.resolve_filter_text {
-        something_to_resolve = !item.lookup().is_empty();
+        something_to_resolve |= !item.lookup().is_empty();
         None
     } else {
         Some(item.lookup().to_owned())
     };
 
     let text_edit = if fields_to_resolve.resolve_text_edit {
-        something_to_resolve = true;
+        something_to_resolve |= true;
         None
     } else {
         // LSP does not allow arbitrary edits in completion, so we have to do a
@@ -319,14 +319,14 @@ fn completion_item(
 
     let insert_text_format = item.is_snippet.then_some(lsp_types::InsertTextFormat::SNIPPET);
     let tags = if fields_to_resolve.resolve_tags {
-        something_to_resolve = item.deprecated;
+        something_to_resolve |= item.deprecated;
         None
     } else {
         item.deprecated.then(|| vec![lsp_types::CompletionItemTag::DEPRECATED])
     };
     let command = if item.trigger_call_info && config.client_commands().trigger_parameter_hints {
         if fields_to_resolve.resolve_command {
-            something_to_resolve = true;
+            something_to_resolve |= true;
             None
         } else {
             Some(command::trigger_parameter_hints())
@@ -336,14 +336,14 @@ fn completion_item(
     };
 
     let detail = if fields_to_resolve.resolve_detail {
-        something_to_resolve = item.detail.is_some();
+        something_to_resolve |= item.detail.is_some();
         None
     } else {
-        item.detail
+        item.detail.clone()
     };
 
     let documentation = if fields_to_resolve.resolve_documentation {
-        something_to_resolve = item.documentation.is_some();
+        something_to_resolve |= item.documentation.is_some();
         None
     } else {
         item.documentation.map(documentation)
@@ -366,11 +366,11 @@ fn completion_item(
 
     if config.completion_label_details_support() {
         if fields_to_resolve.resolve_label_details {
-            something_to_resolve = true;
+            something_to_resolve |= true;
         } else {
             lsp_item.label_details = Some(lsp_types::CompletionItemLabelDetails {
                 detail: item.label_detail.as_ref().map(ToString::to_string),
-                description: lsp_item.detail.clone(),
+                description: item.detail,
             });
         }
     } else if let Some(label_detail) = item.label_detail {
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/main_loop.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/main_loop.rs
index ef289720568..20be38a9e4b 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/main_loop.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/main_loop.rs
@@ -23,7 +23,8 @@ use crate::{
     discover::{DiscoverArgument, DiscoverCommand, DiscoverProjectMessage},
     flycheck::{self, FlycheckMessage},
     global_state::{
-        file_id_to_url, url_to_file_id, FetchWorkspaceRequest, FetchWorkspaceResponse, GlobalState,
+        file_id_to_url, url_to_file_id, FetchBuildDataResponse, FetchWorkspaceRequest,
+        FetchWorkspaceResponse, GlobalState,
     },
     hack_recover_crate_name,
     handlers::dispatch::{NotificationDispatcher, RequestDispatcher},
@@ -738,8 +739,10 @@ impl GlobalState {
                 let (state, msg) = match progress {
                     BuildDataProgress::Begin => (Some(Progress::Begin), None),
                     BuildDataProgress::Report(msg) => (Some(Progress::Report), Some(msg)),
-                    BuildDataProgress::End(build_data_result) => {
-                        self.fetch_build_data_queue.op_completed(build_data_result);
+                    BuildDataProgress::End((workspaces, build_scripts)) => {
+                        let resp = FetchBuildDataResponse { workspaces, build_scripts };
+                        self.fetch_build_data_queue.op_completed(resp);
+
                         if let Err(e) = self.fetch_build_data_error() {
                             error!("FetchBuildDataError: {e}");
                         }
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/reload.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/reload.rs
index 60ee0295a3a..0b24833358d 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/reload.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/reload.rs
@@ -18,7 +18,7 @@ use std::{iter, mem};
 use hir::{db::DefDatabase, ChangeWithProcMacros, ProcMacros, ProcMacrosBuilder};
 use ide::CrateId;
 use ide_db::{
-    base_db::{salsa::Durability, CrateGraph, CrateWorkspaceData, ProcMacroPaths},
+    base_db::{ra_salsa::Durability, CrateGraph, CrateWorkspaceData, ProcMacroPaths},
     FxHashMap,
 };
 use itertools::Itertools;
@@ -33,7 +33,9 @@ use vfs::{AbsPath, AbsPathBuf, ChangeKind};
 use crate::{
     config::{Config, FilesWatcher, LinkedProject},
     flycheck::{FlycheckConfig, FlycheckHandle},
-    global_state::{FetchWorkspaceRequest, FetchWorkspaceResponse, GlobalState},
+    global_state::{
+        FetchBuildDataResponse, FetchWorkspaceRequest, FetchWorkspaceResponse, GlobalState,
+    },
     lsp_ext,
     main_loop::{DiscoverProjectParam, Task},
     op_queue::Cause,
@@ -475,7 +477,9 @@ impl GlobalState {
 
         if same_workspaces {
             let (workspaces, build_scripts) = match self.fetch_build_data_queue.last_op_result() {
-                Some((workspaces, build_scripts)) => (workspaces.clone(), build_scripts.as_slice()),
+                Some(FetchBuildDataResponse { workspaces, build_scripts }) => {
+                    (workspaces.clone(), build_scripts.as_slice())
+                }
                 None => (Default::default(), Default::default()),
             };
 
@@ -769,12 +773,14 @@ impl GlobalState {
     pub(super) fn fetch_build_data_error(&self) -> Result<(), String> {
         let mut buf = String::new();
 
-        let Some((_, ws)) = &self.fetch_build_data_queue.last_op_result() else {
+        let Some(FetchBuildDataResponse { build_scripts, .. }) =
+            &self.fetch_build_data_queue.last_op_result()
+        else {
             return Ok(());
         };
 
-        for ws in ws {
-            match ws {
+        for script in build_scripts {
+            match script {
                 Ok(data) => {
                     if let Some(stderr) = data.error() {
                         stdx::format_to!(buf, "{:#}\n", stderr)
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/tracing/config.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/tracing/config.rs
index b73f6e77514..5ab2dc2b67a 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/tracing/config.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/tracing/config.rs
@@ -7,7 +7,7 @@ use anyhow::Context;
 use tracing::level_filters::LevelFilter;
 use tracing_subscriber::{
     filter::{filter_fn, Targets},
-    fmt::MakeWriter,
+    fmt::{time, MakeWriter},
     layer::SubscriberExt,
     Layer, Registry,
 };
@@ -58,6 +58,10 @@ where
         let writer = self.writer;
 
         let ra_fmt_layer = tracing_subscriber::fmt::layer()
+            .with_timer(
+                time::OffsetTime::local_rfc_3339()
+                    .expect("Could not get local offset, make sure you're on the main thread"),
+            )
             .with_target(false)
             .with_ansi(false)
             .with_writer(writer)
diff --git a/src/tools/rust-analyzer/crates/span/Cargo.toml b/src/tools/rust-analyzer/crates/span/Cargo.toml
index 3381dac0b42..569da8082a8 100644
--- a/src/tools/rust-analyzer/crates/span/Cargo.toml
+++ b/src/tools/rust-analyzer/crates/span/Cargo.toml
@@ -12,7 +12,7 @@ authors.workspace = true
 
 [dependencies]
 la-arena.workspace = true
-salsa.workspace = true
+ra-salsa.workspace = true
 rustc-hash.workspace = true
 hashbrown.workspace = true
 text-size.workspace = true
diff --git a/src/tools/rust-analyzer/crates/span/src/hygiene.rs b/src/tools/rust-analyzer/crates/span/src/hygiene.rs
index 3863b3e809c..67d7bb9a0de 100644
--- a/src/tools/rust-analyzer/crates/span/src/hygiene.rs
+++ b/src/tools/rust-analyzer/crates/span/src/hygiene.rs
@@ -21,7 +21,7 @@
 //! `ExpnData::call_site` in rustc, [`MacroCallLoc::call_site`] in rust-analyzer.
 use std::fmt;
 
-use salsa::{InternId, InternValue};
+use ra_salsa::{InternId, InternValue};
 
 use crate::MacroCallId;
 
@@ -39,11 +39,11 @@ impl fmt::Debug for SyntaxContextId {
     }
 }
 
-impl salsa::InternKey for SyntaxContextId {
-    fn from_intern_id(v: salsa::InternId) -> Self {
+impl ra_salsa::InternKey for SyntaxContextId {
+    fn from_intern_id(v: ra_salsa::InternId) -> Self {
         SyntaxContextId(v)
     }
-    fn as_intern_id(&self) -> salsa::InternId {
+    fn as_intern_id(&self) -> ra_salsa::InternId {
         self.0
     }
 }
diff --git a/src/tools/rust-analyzer/crates/span/src/lib.rs b/src/tools/rust-analyzer/crates/span/src/lib.rs
index 61e4c98128a..bd270bfe2b1 100644
--- a/src/tools/rust-analyzer/crates/span/src/lib.rs
+++ b/src/tools/rust-analyzer/crates/span/src/lib.rs
@@ -1,7 +1,7 @@
 //! File and span related types.
 use std::fmt::{self, Write};
 
-use salsa::InternId;
+use ra_salsa::InternId;
 
 mod ast_id;
 mod hygiene;
@@ -261,13 +261,13 @@ pub struct MacroFileId {
 /// `MacroCallId` identifies a particular macro invocation, like
 /// `println!("Hello, {}", world)`.
 #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
-pub struct MacroCallId(salsa::InternId);
+pub struct MacroCallId(ra_salsa::InternId);
 
-impl salsa::InternKey for MacroCallId {
-    fn from_intern_id(v: salsa::InternId) -> Self {
+impl ra_salsa::InternKey for MacroCallId {
+    fn from_intern_id(v: ra_salsa::InternId) -> Self {
         MacroCallId(v)
     }
-    fn as_intern_id(&self) -> salsa::InternId {
+    fn as_intern_id(&self) -> ra_salsa::InternId {
         self.0
     }
 }
diff --git a/src/tools/rust-analyzer/docs/user/generated_config.adoc b/src/tools/rust-analyzer/docs/user/generated_config.adoc
index 708fc2b7891..babeb4272be 100644
--- a/src/tools/rust-analyzer/docs/user/generated_config.adoc
+++ b/src/tools/rust-analyzer/docs/user/generated_config.adoc
@@ -852,7 +852,7 @@ Exclude imports from find-all-references.
 [[rust-analyzer.references.excludeTests]]rust-analyzer.references.excludeTests (default: `false`)::
 +
 --
-Exclude tests from find-all-references.
+Exclude tests from find-all-references and call-hierarchy.
 --
 [[rust-analyzer.runnables.command]]rust-analyzer.runnables.command (default: `null`)::
 +
diff --git a/src/tools/rust-analyzer/editors/code/package.json b/src/tools/rust-analyzer/editors/code/package.json
index a823e5bb96c..a52b3d1ec5c 100644
--- a/src/tools/rust-analyzer/editors/code/package.json
+++ b/src/tools/rust-analyzer/editors/code/package.json
@@ -2333,7 +2333,7 @@
                 "title": "references",
                 "properties": {
                     "rust-analyzer.references.excludeTests": {
-                        "markdownDescription": "Exclude tests from find-all-references.",
+                        "markdownDescription": "Exclude tests from find-all-references and call-hierarchy.",
                         "default": false,
                         "type": "boolean"
                     }
diff --git a/src/tools/rust-analyzer/editors/code/src/bootstrap.ts b/src/tools/rust-analyzer/editors/code/src/bootstrap.ts
index 35867f710d5..8fc9f09324a 100644
--- a/src/tools/rust-analyzer/editors/code/src/bootstrap.ts
+++ b/src/tools/rust-analyzer/editors/code/src/bootstrap.ts
@@ -1,9 +1,9 @@
 import * as vscode from "vscode";
 import * as os from "os";
 import type { Config } from "./config";
-import { type Env, log } from "./util";
+import { type Env, log, spawnAsync } from "./util";
 import type { PersistentState } from "./persistent_state";
-import { exec, spawnSync } from "child_process";
+import { exec } from "child_process";
 import { TextDecoder } from "node:util";
 
 export async function bootstrap(
@@ -61,13 +61,12 @@ async function getServer(
             // if so, use the rust-analyzer component
             const toolchainUri = vscode.Uri.joinPath(workspaceFolder.uri, "rust-toolchain.toml");
             if (await hasToolchainFileWithRaDeclared(toolchainUri)) {
-                const res = spawnSync("rustup", ["which", "rust-analyzer"], {
-                    encoding: "utf8",
+                const res = await spawnAsync("rustup", ["which", "rust-analyzer"], {
                     env: { ...process.env },
                     cwd: workspaceFolder.uri.fsPath,
                 });
                 if (!res.error && res.status === 0) {
-                    toolchainServerPath = earliestToolchainPath(
+                    toolchainServerPath = await earliestToolchainPath(
                         toolchainServerPath,
                         res.stdout.trim(),
                         raVersionResolver,
@@ -114,10 +113,8 @@ async function getServer(
 }
 
 // Given a path to a rust-analyzer executable, resolve its version and return it.
-function raVersionResolver(path: string): string | undefined {
-    const res = spawnSync(path, ["--version"], {
-        encoding: "utf8",
-    });
+async function raVersionResolver(path: string): Promise<string | undefined> {
+    const res = await spawnAsync(path, ["--version"]);
     if (!res.error && res.status === 0) {
         return res.stdout;
     } else {
@@ -126,13 +123,16 @@ function raVersionResolver(path: string): string | undefined {
 }
 
 // Given a path to two rust-analyzer executables, return the earliest one by date.
-function earliestToolchainPath(
+async function earliestToolchainPath(
     path0: string | undefined,
     path1: string,
-    raVersionResolver: (path: string) => string | undefined,
-): string {
+    raVersionResolver: (path: string) => Promise<string | undefined>,
+): Promise<string> {
     if (path0) {
-        if (orderFromPath(path0, raVersionResolver) < orderFromPath(path1, raVersionResolver)) {
+        if (
+            (await orderFromPath(path0, raVersionResolver)) <
+            (await orderFromPath(path1, raVersionResolver))
+        ) {
             return path0;
         } else {
             return path1;
@@ -150,11 +150,11 @@ function earliestToolchainPath(
 //  nightly   - /Users/myuser/.rustup/toolchains/nightly-2022-11-22-aarch64-apple-darwin/bin/rust-analyzer
 //  versioned - /Users/myuser/.rustup/toolchains/1.72.1-aarch64-apple-darwin/bin/rust-analyzer
 //  stable    - /Users/myuser/.rustup/toolchains/stable-aarch64-apple-darwin/bin/rust-analyzer
-function orderFromPath(
+async function orderFromPath(
     path: string,
-    raVersionResolver: (path: string) => string | undefined,
-): string {
-    const raVersion = raVersionResolver(path);
+    raVersionResolver: (path: string) => Promise<string | undefined>,
+): Promise<string> {
+    const raVersion = await raVersionResolver(path);
     const raDate = raVersion?.match(/^rust-analyzer .*\(.* (\d{4}-\d{2}-\d{2})\)$/);
     if (raDate?.length === 2) {
         const precedence = path.includes("nightly-") ? "0" : "1";
@@ -184,11 +184,10 @@ async function hasToolchainFileWithRaDeclared(uri: vscode.Uri): Promise<boolean>
     }
 }
 
-export function isValidExecutable(path: string, extraEnv: Env): boolean {
+export async function isValidExecutable(path: string, extraEnv: Env): Promise<boolean> {
     log.debug("Checking availability of a binary at", path);
 
-    const res = spawnSync(path, ["--version"], {
-        encoding: "utf8",
+    const res = await spawnAsync(path, ["--version"], {
         env: { ...process.env, ...extraEnv },
     });
 
diff --git a/src/tools/rust-analyzer/editors/code/src/debug.ts b/src/tools/rust-analyzer/editors/code/src/debug.ts
index fb7e340e517..77ab44f24ce 100644
--- a/src/tools/rust-analyzer/editors/code/src/debug.ts
+++ b/src/tools/rust-analyzer/editors/code/src/debug.ts
@@ -6,6 +6,7 @@ import type * as ra from "./lsp_ext";
 import { Cargo } from "./toolchain";
 import type { Ctx } from "./ctx";
 import { createTaskFromRunnable, prepareEnv } from "./run";
+import { execSync } from "node:child_process";
 import { execute, isCargoRunnableArgs, unwrapUndefinable } from "./util";
 import type { Config } from "./config";
 
@@ -105,9 +106,11 @@ async function getDebugConfiguration(
         const commandCCpp: string = createCommandLink("ms-vscode.cpptools");
         const commandCodeLLDB: string = createCommandLink("vadimcn.vscode-lldb");
         const commandNativeDebug: string = createCommandLink("webfreak.debug");
+        const commandLLDBDap: string = createCommandLink("llvm-vs-code-extensions.lldb-dap");
 
         await vscode.window.showErrorMessage(
             `Install [CodeLLDB](command:${commandCodeLLDB} "Open CodeLLDB")` +
+                `, [lldb-dap](command:${commandLLDBDap} "Open lldb-dap")` +
                 `, [C/C++](command:${commandCCpp} "Open C/C++") ` +
                 `or [Native Debug](command:${commandNativeDebug} "Open Native Debug") for debugging.`,
         );
@@ -220,10 +223,30 @@ type DebugConfigProvider<Type extends string, DebugConfig extends BaseDebugConfi
 
 type KnownEnginesType = (typeof knownEngines)[keyof typeof knownEngines];
 const knownEngines: {
+    "llvm-vs-code-extensions.lldb-dap": DebugConfigProvider<"lldb-dap", LldbDapDebugConfig>;
     "vadimcn.vscode-lldb": DebugConfigProvider<"lldb", CodeLldbDebugConfig>;
     "ms-vscode.cpptools": DebugConfigProvider<"cppvsdbg" | "cppdbg", CCppDebugConfig>;
     "webfreak.debug": DebugConfigProvider<"gdb", NativeDebugConfig>;
 } = {
+    "llvm-vs-code-extensions.lldb-dap": {
+        type: "lldb-dap",
+        executableProperty: "program",
+        environmentProperty: (env) => ["env", Object.entries(env).map(([k, v]) => `${k}=${v}`)],
+        runnableArgsProperty: (runnableArgs: ra.CargoRunnableArgs) => [
+            "args",
+            runnableArgs.executableArgs,
+        ],
+        additional: {
+            sourceMap: [
+                [
+                    `/rustc/${/commit-hash:\s(.*)$/m.exec(
+                        execSync("rustc -V -v", {}).toString(),
+                    )?.[1]}/library`,
+                    "${config:rust-analyzer.cargo.sysroot}/lib/rustlib/src/rust/library",
+                ],
+            ],
+        },
+    },
     "vadimcn.vscode-lldb": {
         type: "lldb",
         executableProperty: "program",
@@ -336,6 +359,13 @@ type CCppDebugConfig = {
     };
 } & BaseDebugConfig<"cppvsdbg" | "cppdbg">;
 
+type LldbDapDebugConfig = {
+    program: string;
+    args: string[];
+    env: string[];
+    sourceMap: [string, string][];
+} & BaseDebugConfig<"lldb-dap">;
+
 type CodeLldbDebugConfig = {
     program: string;
     args: string[];
diff --git a/src/tools/rust-analyzer/editors/code/src/util.ts b/src/tools/rust-analyzer/editors/code/src/util.ts
index db64a013fda..d7ca6b3557d 100644
--- a/src/tools/rust-analyzer/editors/code/src/util.ts
+++ b/src/tools/rust-analyzer/editors/code/src/util.ts
@@ -1,6 +1,6 @@
 import * as vscode from "vscode";
 import { strict as nativeAssert } from "assert";
-import { exec, type ExecOptions } from "child_process";
+import { exec, spawn, type SpawnOptionsWithoutStdio, type ExecOptions } from "child_process";
 import { inspect } from "util";
 import type { CargoRunnableArgs, ShellRunnableArgs } from "./lsp_ext";
 
@@ -233,3 +233,55 @@ export function expectNotUndefined<T>(input: Undefinable<T>, msg: string): NotUn
 export function unwrapUndefinable<T>(input: Undefinable<T>): NotUndefined<T> {
     return expectNotUndefined(input, `unwrapping \`undefined\``);
 }
+
+interface SpawnAsyncReturns {
+    stdout: string;
+    stderr: string;
+    status: number | null;
+    error?: Error | undefined;
+}
+
+export async function spawnAsync(
+    path: string,
+    args?: ReadonlyArray<string>,
+    options?: SpawnOptionsWithoutStdio,
+): Promise<SpawnAsyncReturns> {
+    const child = spawn(path, args, options);
+    const stdout: Array<Buffer> = [];
+    const stderr: Array<Buffer> = [];
+    try {
+        const res = await new Promise<{ stdout: string; stderr: string; status: number | null }>(
+            (resolve, reject) => {
+                child.stdout.on("data", (chunk) => stdout.push(Buffer.from(chunk)));
+                child.stderr.on("data", (chunk) => stderr.push(Buffer.from(chunk)));
+                child.on("error", (error) =>
+                    reject({
+                        stdout: Buffer.concat(stdout).toString("utf8"),
+                        stderr: Buffer.concat(stderr).toString("utf8"),
+                        error,
+                    }),
+                );
+                child.on("close", (status) =>
+                    resolve({
+                        stdout: Buffer.concat(stdout).toString("utf8"),
+                        stderr: Buffer.concat(stderr).toString("utf8"),
+                        status,
+                    }),
+                );
+            },
+        );
+
+        return {
+            stdout: res.stdout,
+            stderr: res.stderr,
+            status: res.status,
+        };
+    } catch (e: any) {
+        return {
+            stdout: e.stdout,
+            stderr: e.stderr,
+            status: e.status,
+            error: e.error,
+        };
+    }
+}
diff --git a/src/tools/rust-analyzer/editors/code/tests/unit/bootstrap.test.ts b/src/tools/rust-analyzer/editors/code/tests/unit/bootstrap.test.ts
index 8aeb72180a0..baabf4f8977 100644
--- a/src/tools/rust-analyzer/editors/code/tests/unit/bootstrap.test.ts
+++ b/src/tools/rust-analyzer/editors/code/tests/unit/bootstrap.test.ts
@@ -6,9 +6,9 @@ export async function getTests(ctx: Context) {
     await ctx.suite("Bootstrap/Select toolchain RA", (suite) => {
         suite.addTest("Order of nightly RA", async () => {
             assert.deepStrictEqual(
-                _private.orderFromPath(
+                await _private.orderFromPath(
                     "/Users/myuser/.rustup/toolchains/nightly-2022-11-22-aarch64-apple-darwin/bin/rust-analyzer",
-                    function (path: string) {
+                    async function (path: string) {
                         assert.deepStrictEqual(
                             path,
                             "/Users/myuser/.rustup/toolchains/nightly-2022-11-22-aarch64-apple-darwin/bin/rust-analyzer",
@@ -22,9 +22,9 @@ export async function getTests(ctx: Context) {
 
         suite.addTest("Order of versioned RA", async () => {
             assert.deepStrictEqual(
-                _private.orderFromPath(
+                await _private.orderFromPath(
                     "/Users/myuser/.rustup/toolchains/1.72.1-aarch64-apple-darwin/bin/rust-analyzer",
-                    function (path: string) {
+                    async function (path: string) {
                         assert.deepStrictEqual(
                             path,
                             "/Users/myuser/.rustup/toolchains/1.72.1-aarch64-apple-darwin/bin/rust-analyzer",
@@ -38,9 +38,9 @@ export async function getTests(ctx: Context) {
 
         suite.addTest("Order of versioned RA when unable to obtain version date", async () => {
             assert.deepStrictEqual(
-                _private.orderFromPath(
+                await _private.orderFromPath(
                     "/Users/myuser/.rustup/toolchains/1.72.1-aarch64-apple-darwin/bin/rust-analyzer",
-                    function () {
+                    async function () {
                         return "rust-analyzer 1.72.1";
                     },
                 ),
@@ -50,9 +50,9 @@ export async function getTests(ctx: Context) {
 
         suite.addTest("Order of stable RA", async () => {
             assert.deepStrictEqual(
-                _private.orderFromPath(
+                await _private.orderFromPath(
                     "/Users/myuser/.rustup/toolchains/stable-aarch64-apple-darwin/bin/rust-analyzer",
-                    function (path: string) {
+                    async function (path: string) {
                         assert.deepStrictEqual(
                             path,
                             "/Users/myuser/.rustup/toolchains/stable-aarch64-apple-darwin/bin/rust-analyzer",
@@ -66,7 +66,7 @@ export async function getTests(ctx: Context) {
 
         suite.addTest("Order with invalid path to RA", async () => {
             assert.deepStrictEqual(
-                _private.orderFromPath("some-weird-path", function () {
+                await _private.orderFromPath("some-weird-path", async function () {
                     return undefined;
                 }),
                 "2",
@@ -75,10 +75,10 @@ export async function getTests(ctx: Context) {
 
         suite.addTest("Earliest RA between nightly and stable", async () => {
             assert.deepStrictEqual(
-                _private.earliestToolchainPath(
+                await _private.earliestToolchainPath(
                     "/Users/myuser/.rustup/toolchains/stable-aarch64-apple-darwin/bin/rust-analyzer",
                     "/Users/myuser/.rustup/toolchains/nightly-2022-11-22-aarch64-apple-darwin/bin/rust-analyzer",
-                    function (path: string) {
+                    async function (path: string) {
                         if (
                             path ===
                             "/Users/myuser/.rustup/toolchains/nightly-2022-11-22-aarch64-apple-darwin/bin/rust-analyzer"
diff --git a/src/tools/rust-analyzer/rust-version b/src/tools/rust-analyzer/rust-version
index f217c6a19cb..d0f9fa7ac42 100644
--- a/src/tools/rust-analyzer/rust-version
+++ b/src/tools/rust-analyzer/rust-version
@@ -1 +1 @@
-cf24c73141a77db730f4b7fda69dcd7e8b113b51
+dd5127615ad626741a1116d022cf784637ac05df
diff --git a/src/tools/rust-analyzer/xtask/src/tidy.rs b/src/tools/rust-analyzer/xtask/src/tidy.rs
index ea51d33ed9c..0268e2473c0 100644
--- a/src/tools/rust-analyzer/xtask/src/tidy.rs
+++ b/src/tools/rust-analyzer/xtask/src/tidy.rs
@@ -223,7 +223,7 @@ struct TidyDocs {
 impl TidyDocs {
     fn visit(&mut self, path: &Path, text: &str) {
         // Tests and diagnostic fixes don't need module level comments.
-        if is_exclude_dir(path, &["tests", "test_data", "fixes", "grammar", "salsa"]) {
+        if is_exclude_dir(path, &["tests", "test_data", "fixes", "grammar", "ra-salsa"]) {
             return;
         }
 
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 {