about summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/bootstrap/Cargo.lock16
-rw-r--r--src/bootstrap/Cargo.toml7
-rwxr-xr-xsrc/bootstrap/configure.py14
-rw-r--r--src/bootstrap/defaults/config.tools.toml2
-rw-r--r--src/bootstrap/download-ci-llvm-stamp2
-rw-r--r--src/bootstrap/mk/Makefile.in18
-rw-r--r--src/bootstrap/src/bin/main.rs16
-rw-r--r--src/bootstrap/src/core/build_steps/check.rs2
-rw-r--r--src/bootstrap/src/core/build_steps/compile.rs316
-rw-r--r--src/bootstrap/src/core/build_steps/dist.rs164
-rw-r--r--src/bootstrap/src/core/build_steps/doc.rs9
-rw-r--r--src/bootstrap/src/core/build_steps/llvm.rs83
-rw-r--r--src/bootstrap/src/core/build_steps/perf.rs2
-rw-r--r--src/bootstrap/src/core/build_steps/run.rs37
-rw-r--r--src/bootstrap/src/core/build_steps/setup.rs83
-rw-r--r--src/bootstrap/src/core/build_steps/setup/tests.rs21
-rw-r--r--src/bootstrap/src/core/build_steps/test.rs163
-rw-r--r--src/bootstrap/src/core/build_steps/tool.rs411
-rw-r--r--src/bootstrap/src/core/build_steps/toolstate.rs8
-rw-r--r--src/bootstrap/src/core/build_steps/vendor.rs42
-rw-r--r--src/bootstrap/src/core/builder/cargo.rs45
-rw-r--r--src/bootstrap/src/core/builder/mod.rs91
-rw-r--r--src/bootstrap/src/core/builder/tests.rs161
-rw-r--r--src/bootstrap/src/core/config/config.rs74
-rw-r--r--src/bootstrap/src/core/config/flags.rs8
-rw-r--r--src/bootstrap/src/core/config/tests.rs61
-rw-r--r--src/bootstrap/src/core/metadata.rs7
-rw-r--r--src/bootstrap/src/core/sanity.rs6
-rw-r--r--src/bootstrap/src/lib.rs66
-rw-r--r--src/bootstrap/src/utils/cache.rs46
-rw-r--r--src/bootstrap/src/utils/cache/tests.rs52
-rw-r--r--src/bootstrap/src/utils/cc_detect.rs60
-rw-r--r--src/bootstrap/src/utils/cc_detect/tests.rs254
-rw-r--r--src/bootstrap/src/utils/change_tracker.rs15
-rw-r--r--src/bootstrap/src/utils/exec.rs22
-rw-r--r--src/bootstrap/src/utils/helpers.rs3
-rw-r--r--src/bootstrap/src/utils/proc_macro_deps.rs15
-rw-r--r--src/bootstrap/src/utils/tracing.rs17
-rw-r--r--src/ci/docker/host-x86_64/dist-loongarch64-linux/Dockerfile4
-rw-r--r--src/ci/docker/host-x86_64/dist-loongarch64-linux/loongarch64-unknown-linux-gnu.defconfig2
-rw-r--r--src/ci/docker/host-x86_64/dist-loongarch64-musl/Dockerfile4
-rw-r--r--src/ci/docker/host-x86_64/dist-loongarch64-musl/loongarch64-unknown-linux-musl.defconfig2
-rw-r--r--src/ci/docker/host-x86_64/dist-powerpc64le-linux/Dockerfile4
-rwxr-xr-xsrc/ci/docker/run.sh10
-rwxr-xr-xsrc/ci/docker/scripts/build-clang.sh2
-rw-r--r--src/ci/docker/scripts/crosstool-ng-git.sh17
-rw-r--r--src/ci/docker/scripts/crosstool-ng.sh2
-rw-r--r--src/ci/docker/scripts/musl.sh41
-rwxr-xr-xsrc/ci/docker/scripts/rfl-build.sh4
-rw-r--r--src/ci/docker/scripts/sccache.sh4
-rw-r--r--src/ci/github-actions/jobs.yml60
-rwxr-xr-xsrc/ci/run.sh2
-rwxr-xr-xsrc/ci/scripts/free-disk-space.sh84
-rwxr-xr-xsrc/ci/scripts/install-mingw.sh6
-rwxr-xr-xsrc/ci/scripts/upload-artifacts.sh15
m---------src/doc/book0
m---------src/doc/edition-guide0
m---------src/doc/embedded-book0
m---------src/doc/nomicon0
m---------src/doc/reference0
m---------src/doc/rust-by-example0
-rw-r--r--src/doc/rustc-dev-guide/examples/rustc-driver-interacting-with-the-ast.rs4
-rw-r--r--src/doc/rustc-dev-guide/src/SUMMARY.md1
-rw-r--r--src/doc/rustc-dev-guide/src/building/bootstrapping/debugging-bootstrap.md16
-rw-r--r--src/doc/rustc-dev-guide/src/building/bootstrapping/writing-tools-in-bootstrap.md23
-rw-r--r--src/doc/rustc-dev-guide/src/building/suggested.md11
-rw-r--r--src/doc/rustc-dev-guide/src/implementing_new_features.md8
-rw-r--r--src/doc/rustc-dev-guide/src/mir/index.md4
-rw-r--r--src/doc/rustc-dev-guide/src/parallel-rustc.md5
-rw-r--r--src/doc/rustc-dev-guide/src/tests/directives.md3
-rw-r--r--src/doc/rustc/src/SUMMARY.md2
-rw-r--r--src/doc/rustc/src/check-cfg.md2
-rw-r--r--src/doc/rustc/src/codegen-options/index.md2
-rw-r--r--src/doc/rustc/src/command-line-arguments.md2
-rw-r--r--src/doc/rustc/src/platform-support.md17
-rw-r--r--src/doc/rustc/src/platform-support/amdgcn-amd-amdhsa.md111
-rw-r--r--src/doc/rustc/src/platform-support/avr-none.md81
-rw-r--r--src/doc/rustc/src/platform-support/loongarch-none.md2
-rw-r--r--src/doc/rustc/src/platform-support/nto-qnx.md2
-rw-r--r--src/doc/rustc/src/platform-support/redox.md4
-rw-r--r--src/doc/rustc/src/platform-support/wasm32-unknown-unknown.md2
-rw-r--r--src/doc/rustc/src/platform-support/x86_64-pc-cygwin.md39
-rw-r--r--src/doc/rustdoc/src/command-line-arguments.md3
-rw-r--r--src/doc/rustdoc/src/unstable-features.md19
-rw-r--r--src/doc/rustdoc/src/write-documentation/the-doc-attribute.md5
-rw-r--r--src/doc/unstable-book/src/compiler-flags/autodiff.md9
-rw-r--r--src/doc/unstable-book/src/language-features/asm-experimental-arch.md2
-rw-r--r--src/doc/unstable-book/src/language-features/extended-varargs-abi-support.md10
-rw-r--r--src/doc/unstable-book/src/language-features/intrinsics.md14
-rw-r--r--src/doc/unstable-book/src/language-features/ref-pat-eat-one-layer-2024-structural.md5
-rw-r--r--src/doc/unstable-book/src/language-features/ref-pat-eat-one-layer-2024.md5
-rw-r--r--src/etc/lldb_commands77
-rw-r--r--src/etc/lldb_providers.py500
-rw-r--r--src/etc/rust_analyzer_zed.json52
-rw-r--r--src/librustdoc/Cargo.toml3
-rw-r--r--src/librustdoc/clean/cfg.rs2
-rw-r--r--src/librustdoc/clean/inline.rs4
-rw-r--r--src/librustdoc/clean/mod.rs24
-rw-r--r--src/librustdoc/clean/types.rs19
-rw-r--r--src/librustdoc/clean/utils.rs73
-rw-r--r--src/librustdoc/core.rs13
-rw-r--r--src/librustdoc/display.rs (renamed from src/librustdoc/joined.rs)18
-rw-r--r--src/librustdoc/doctest.rs1
-rw-r--r--src/librustdoc/doctest/make.rs16
-rw-r--r--src/librustdoc/doctest/rust.rs10
-rw-r--r--src/librustdoc/html/escape.rs51
-rw-r--r--src/librustdoc/html/format.rs170
-rw-r--r--src/librustdoc/html/highlight.rs192
-rw-r--r--src/librustdoc/html/highlight/tests.rs31
-rw-r--r--src/librustdoc/html/layout.rs25
-rw-r--r--src/librustdoc/html/markdown.rs83
-rw-r--r--src/librustdoc/html/render/context.rs92
-rw-r--r--src/librustdoc/html/render/mod.rs456
-rw-r--r--src/librustdoc/html/render/print_item.rs674
-rw-r--r--src/librustdoc/html/render/sidebar.rs21
-rw-r--r--src/librustdoc/html/render/span_map.rs16
-rw-r--r--src/librustdoc/html/render/tests.rs6
-rw-r--r--src/librustdoc/html/render/write_shared.rs39
-rw-r--r--src/librustdoc/html/sources.rs42
-rw-r--r--src/librustdoc/html/static/css/rustdoc.css185
-rw-r--r--src/librustdoc/html/static/js/main.js42
-rw-r--r--src/librustdoc/html/static/js/scrape-examples.js22
-rw-r--r--src/librustdoc/html/static/js/search.js5
-rw-r--r--src/librustdoc/html/static/js/settings.js13
-rw-r--r--src/librustdoc/html/static/js/src-script.js8
-rw-r--r--src/librustdoc/html/static/js/storage.js3
-rw-r--r--src/librustdoc/html/templates/scraped_source.html12
-rw-r--r--src/librustdoc/html/templates/source.html10
-rw-r--r--src/librustdoc/json/conversions.rs2
-rw-r--r--src/librustdoc/lib.rs4
-rw-r--r--src/librustdoc/passes/calculate_doc_coverage.rs2
-rw-r--r--src/librustdoc/passes/collect_intra_doc_links.rs2
-rw-r--r--src/librustdoc/passes/lint/check_code_block_syntax.rs7
-rw-r--r--src/librustdoc/passes/strip_aliased_non_local.rs2
-rw-r--r--src/librustdoc/scrape_examples.rs11
-rw-r--r--src/librustdoc/visit_ast.rs18
m---------src/llvm-project0
-rw-r--r--src/rustdoc-json-types/lib.rs6
-rw-r--r--src/stage0926
-rw-r--r--src/tools/build-manifest/src/main.rs3
m---------src/tools/cargo0
-rw-r--r--src/tools/clippy/clippy_lints/src/arbitrary_source_item_ordering.rs6
-rw-r--r--src/tools/clippy/clippy_lints/src/assigning_clones.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/async_yields_async.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/attrs/inline_always.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/attrs/repr_attributes.rs42
-rw-r--r--src/tools/clippy/clippy_lints/src/attrs/unnecessary_clippy_cfg.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/attrs/utils.rs8
-rw-r--r--src/tools/clippy/clippy_lints/src/booleans.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/casts/cast_ptr_alignment.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/collection_is_never_read.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/default_numeric_fallback.rs3
-rw-r--r--src/tools/clippy/clippy_lints/src/default_union_representation.rs17
-rw-r--r--src/tools/clippy/clippy_lints/src/dereference.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/derivable_impls.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/derive.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/disallowed_macros.rs1
-rw-r--r--src/tools/clippy/clippy_lints/src/doc/include_in_doc_without_cfg.rs9
-rw-r--r--src/tools/clippy/clippy_lints/src/doc/missing_headers.rs5
-rw-r--r--src/tools/clippy/clippy_lints/src/doc/mod.rs22
-rw-r--r--src/tools/clippy/clippy_lints/src/doc/suspicious_doc_comments.rs10
-rw-r--r--src/tools/clippy/clippy_lints/src/doc/too_long_first_doc_paragraph.rs9
-rw-r--r--src/tools/clippy/clippy_lints/src/empty_drop.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/enum_clike.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/escape.rs3
-rw-r--r--src/tools/clippy/clippy_lints/src/eta_reduction.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/exit.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/extra_unused_type_parameters.rs6
-rw-r--r--src/tools/clippy/clippy_lints/src/fallible_impl_from.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/format_impl.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/four_forward_slashes.rs3
-rw-r--r--src/tools/clippy/clippy_lints/src/from_over_into.rs8
-rw-r--r--src/tools/clippy/clippy_lints/src/functions/impl_trait_in_params.rs6
-rw-r--r--src/tools/clippy/clippy_lints/src/functions/must_use.rs39
-rw-r--r--src/tools/clippy/clippy_lints/src/functions/not_unsafe_ptr_arg_deref.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/functions/renamed_function_params.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/implicit_hasher.rs8
-rw-r--r--src/tools/clippy/clippy_lints/src/inconsistent_struct_constructor.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/index_refutable_slice.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/indexing_slicing.rs6
-rw-r--r--src/tools/clippy/clippy_lints/src/infinite_iter.rs3
-rw-r--r--src/tools/clippy/clippy_lints/src/inline_fn_without_body.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/items_after_statements.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/items_after_test_module.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/iter_without_into_iter.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/large_include_file.rs59
-rw-r--r--src/tools/clippy/clippy_lints/src/large_stack_arrays.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/len_zero.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/lib.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/lifetimes.rs10
-rw-r--r--src/tools/clippy/clippy_lints/src/lines_filter_map_ok.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/loops/infinite_loop.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/loops/manual_find.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/loops/needless_range_loop.rs6
-rw-r--r--src/tools/clippy/clippy_lints/src/loops/utils.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/loops/while_let_on_iterator.rs10
-rw-r--r--src/tools/clippy/clippy_lints/src/macro_use.rs6
-rw-r--r--src/tools/clippy/clippy_lints/src/manual_async_fn.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/manual_float_methods.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/manual_non_exhaustive.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/manual_option_as_slice.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/manual_retain.rs6
-rw-r--r--src/tools/clippy/clippy_lints/src/map_unit_fn.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/matches/redundant_pattern_match.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/bind_instead_of_map.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/bytecount.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/filter_map.rs6
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/filter_map_bool_then.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/format_collect.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/is_empty.rs3
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/iter_filter.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/iter_kv_map.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/iter_nth_zero.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/iter_overeager_cloned.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/manual_c_str_literals.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/manual_inspect.rs6
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/manual_ok_or.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/map_clone.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/map_err_ignore.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/map_with_unused_argument_over_ranges.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/mod.rs6
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/needless_character_iteration.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/needless_collect.rs6
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/obfuscated_if_else.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/option_as_ref_deref.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/option_map_or_none.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/option_map_unwrap_or.rs11
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/or_fun_call.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/result_map_or_else_none.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/return_and_then.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/search_is_some.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/str_splitn.rs6
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/suspicious_map.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/unnecessary_filter_map.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/unnecessary_fold.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/unnecessary_lazy_eval.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/unnecessary_literal_unwrap.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/unnecessary_map_or.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/unnecessary_result_map_or_else.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/unnecessary_sort_by.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/unnecessary_to_owned.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/unused_enumerate_index.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/useless_asref.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/utils.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/min_ident_chars.rs6
-rw-r--r--src/tools/clippy/clippy_lints/src/missing_const_for_fn.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/missing_const_for_thread_local.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/missing_doc.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/missing_fields_in_debug.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/missing_inline.rs6
-rw-r--r--src/tools/clippy/clippy_lints/src/multiple_unsafe_ops_per_block.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/mutable_debug_assertion.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/needless_borrowed_ref.rs3
-rw-r--r--src/tools/clippy/clippy_lints/src/needless_borrows_for_generic_args.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/needless_for_each.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/needless_late_init.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/needless_pass_by_ref_mut.rs8
-rw-r--r--src/tools/clippy/clippy_lints/src/new_without_default.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/no_effect.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/no_mangle_with_rust_abi.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/non_canonical_impls.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/non_copy_const.rs9
-rw-r--r--src/tools/clippy/clippy_lints/src/non_std_lazy_statics.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/non_zero_suggestions.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/operators/arithmetic_side_effects.rs8
-rw-r--r--src/tools/clippy/clippy_lints/src/operators/assign_op_pattern.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/operators/identity_op.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/operators/numeric_arithmetic.rs10
-rw-r--r--src/tools/clippy/clippy_lints/src/operators/ptr_eq.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/panic_unimplemented.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/pass_by_ref_or_value.rs8
-rw-r--r--src/tools/clippy/clippy_lints/src/ptr.rs7
-rw-r--r--src/tools/clippy/clippy_lints/src/ptr_offset_with_cast.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/redundant_async_block.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/redundant_clone.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/redundant_closure_call.rs8
-rw-r--r--src/tools/clippy/clippy_lints/src/redundant_locals.rs7
-rw-r--r--src/tools/clippy/clippy_lints/src/returns.rs7
-rw-r--r--src/tools/clippy/clippy_lints/src/same_name_method.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/self_named_constructors.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/shadow.rs18
-rw-r--r--src/tools/clippy/clippy_lints/src/significant_drop_tightening.rs3
-rw-r--r--src/tools/clippy/clippy_lints/src/single_call_fn.rs3
-rw-r--r--src/tools/clippy/clippy_lints/src/string_patterns.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/strings.rs9
-rw-r--r--src/tools/clippy/clippy_lints/src/suspicious_trait_impl.rs6
-rw-r--r--src/tools/clippy/clippy_lints/src/swap_ptr_to_ref.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/trait_bounds.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/transmute/eager_transmute.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/transmute/missing_transmute_annotations.rs6
-rw-r--r--src/tools/clippy/clippy_lints/src/types/borrowed_box.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/types/mod.rs5
-rw-r--r--src/tools/clippy/clippy_lints/src/unconditional_recursion.rs21
-rw-r--r--src/tools/clippy/clippy_lints/src/undocumented_unsafe_blocks.rs13
-rw-r--r--src/tools/clippy/clippy_lints/src/unit_return_expecting_ord.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/unit_types/let_unit_value.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/unused_async.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/unused_io_amount.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/unused_peekable.rs6
-rw-r--r--src/tools/clippy/clippy_lints/src/unused_self.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/unwrap.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/unwrap_in_result.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/utils/author.rs7
-rw-r--r--src/tools/clippy/clippy_lints/src/utils/internal_lints/collapsible_calls.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/utils/internal_lints/invalid_paths.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/utils/internal_lints/lint_without_lint_pass.rs6
-rw-r--r--src/tools/clippy/clippy_lints/src/zero_repeat_side_effects.rs3
-rw-r--r--src/tools/clippy/clippy_lints/src/zero_sized_map_values.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/zombie_processes.rs4
-rw-r--r--src/tools/clippy/clippy_utils/src/check_proc_macro.rs2
-rw-r--r--src/tools/clippy/clippy_utils/src/consts.rs11
-rw-r--r--src/tools/clippy/clippy_utils/src/eager_or_lazy.rs2
-rw-r--r--src/tools/clippy/clippy_utils/src/higher.rs2
-rw-r--r--src/tools/clippy/clippy_utils/src/hir_utils.rs13
-rw-r--r--src/tools/clippy/clippy_utils/src/lib.rs90
-rw-r--r--src/tools/clippy/clippy_utils/src/macros.rs2
-rw-r--r--src/tools/clippy/clippy_utils/src/mir/mod.rs6
-rw-r--r--src/tools/clippy/clippy_utils/src/ptr.rs2
-rw-r--r--src/tools/clippy/clippy_utils/src/sugg.rs2
-rw-r--r--src/tools/clippy/clippy_utils/src/ty/mod.rs4
-rw-r--r--src/tools/clippy/clippy_utils/src/usage.rs4
-rw-r--r--src/tools/clippy/clippy_utils/src/visitors.rs26
-rw-r--r--src/tools/clippy/tests/ui-toml/dbg_macro/dbg_macro.stderr31
-rw-r--r--src/tools/clippy/tests/ui-toml/doc_valid_idents_append/doc_markdown.stderr5
-rw-r--r--src/tools/clippy/tests/ui-toml/doc_valid_idents_replace/doc_markdown.stderr15
-rw-r--r--src/tools/clippy/tests/ui-toml/enum_variant_size/enum_variant_size.stderr5
-rw-r--r--src/tools/clippy/tests/ui-toml/large_include_file/large_include_file.stderr18
-rw-r--r--src/tools/clippy/tests/ui-toml/renamed_function_params/renamed_function_params.default.stderr5
-rw-r--r--src/tools/clippy/tests/ui-toml/renamed_function_params/renamed_function_params.extend.stderr5
-rw-r--r--src/tools/clippy/tests/ui-toml/unwrap_used/unwrap_used.stderr75
-rw-r--r--src/tools/clippy/tests/ui/assign_ops2.stderr90
-rw-r--r--src/tools/clippy/tests/ui/async_yields_async.stderr12
-rw-r--r--src/tools/clippy/tests/ui/author/blocks.stdout4
-rw-r--r--src/tools/clippy/tests/ui/author/macro_in_closure.stdout2
-rw-r--r--src/tools/clippy/tests/ui/author/repeat.stdout2
-rw-r--r--src/tools/clippy/tests/ui/bind_instead_of_map_multipart.stderr20
-rw-r--r--src/tools/clippy/tests/ui/borrow_deref_ref_unfixable.stderr7
-rw-r--r--src/tools/clippy/tests/ui/cast.stderr103
-rw-r--r--src/tools/clippy/tests/ui/cast_lossless_bool.stderr75
-rw-r--r--src/tools/clippy/tests/ui/cast_lossless_float.stderr65
-rw-r--r--src/tools/clippy/tests/ui/cast_lossless_integer.stderr198
-rw-r--r--src/tools/clippy/tests/ui/cast_size.64bit.stderr45
-rw-r--r--src/tools/clippy/tests/ui/create_dir.stderr10
-rw-r--r--src/tools/clippy/tests/ui/dbg_macro/dbg_macro.stderr81
-rw-r--r--src/tools/clippy/tests/ui/dbg_macro/dbg_macro_unfixable.stderr25
-rw-r--r--src/tools/clippy/tests/ui/doc/doc-fixable.stderr161
-rw-r--r--src/tools/clippy/tests/ui/doc/doc_markdown-issue_13097.stderr5
-rw-r--r--src/tools/clippy/tests/ui/doc/issue_10262.stderr5
-rw-r--r--src/tools/clippy/tests/ui/doc/issue_12795.stderr20
-rw-r--r--src/tools/clippy/tests/ui/doc/issue_9473.stderr5
-rw-r--r--src/tools/clippy/tests/ui/doc/unbalanced_ticks.stderr15
-rw-r--r--src/tools/clippy/tests/ui/eager_transmute.stderr85
-rw-r--r--src/tools/clippy/tests/ui/empty_line_after/doc_comments.stderr5
-rw-r--r--src/tools/clippy/tests/ui/excessive_precision.stderr80
-rw-r--r--src/tools/clippy/tests/ui/fn_to_numeric_cast_any.stderr34
-rw-r--r--src/tools/clippy/tests/ui/for_kv_map.stderr30
-rw-r--r--src/tools/clippy/tests/ui/four_forward_slashes.stderr8
-rw-r--r--src/tools/clippy/tests/ui/four_forward_slashes_first_line.stderr2
-rw-r--r--src/tools/clippy/tests/ui/get_unwrap.stderr85
-rw-r--r--src/tools/clippy/tests/ui/implicit_hasher.stderr6
-rw-r--r--src/tools/clippy/tests/ui/implicit_return.stderr51
-rw-r--r--src/tools/clippy/tests/ui/iter_nth.stderr40
-rw-r--r--src/tools/clippy/tests/ui/join_absolute_paths.stderr40
-rw-r--r--src/tools/clippy/tests/ui/large_enum_variant.64bit.stderr65
-rw-r--r--src/tools/clippy/tests/ui/legacy_numeric_constants.stderr77
-rw-r--r--src/tools/clippy/tests/ui/literals.stderr32
-rw-r--r--src/tools/clippy/tests/ui/lossy_float_literal.stderr55
-rw-r--r--src/tools/clippy/tests/ui/manual_assert.edition2018.stderr10
-rw-r--r--src/tools/clippy/tests/ui/manual_assert.edition2021.stderr10
-rw-r--r--src/tools/clippy/tests/ui/manual_async_fn.stderr60
-rw-r--r--src/tools/clippy/tests/ui/manual_flatten.stderr8
-rw-r--r--src/tools/clippy/tests/ui/manual_float_methods.stderr45
-rw-r--r--src/tools/clippy/tests/ui/manual_ignore_case_cmp.stderr245
-rw-r--r--src/tools/clippy/tests/ui/manual_is_ascii_check.stderr20
-rw-r--r--src/tools/clippy/tests/ui/map_all_any_identity.stderr10
-rw-r--r--src/tools/clippy/tests/ui/map_with_unused_argument_over_ranges.stderr10
-rw-r--r--src/tools/clippy/tests/ui/map_with_unused_argument_over_ranges_nostd.stderr5
-rw-r--r--src/tools/clippy/tests/ui/match_result_ok.stderr15
-rw-r--r--src/tools/clippy/tests/ui/match_str_case_mismatch.stderr35
-rw-r--r--src/tools/clippy/tests/ui/must_use_unit.fixed5
-rw-r--r--src/tools/clippy/tests/ui/must_use_unit.rs5
-rw-r--r--src/tools/clippy/tests/ui/must_use_unit.stderr18
-rw-r--r--src/tools/clippy/tests/ui/must_use_unit_12320.rs11
-rw-r--r--src/tools/clippy/tests/ui/must_use_unit_12320.stderr28
-rw-r--r--src/tools/clippy/tests/ui/needless_borrow_pat.stderr10
-rw-r--r--src/tools/clippy/tests/ui/needless_for_each_unfixable.stderr5
-rw-r--r--src/tools/clippy/tests/ui/needless_pass_by_value.stderr20
-rw-r--r--src/tools/clippy/tests/ui/needless_range_loop.stderr70
-rw-r--r--src/tools/clippy/tests/ui/needless_range_loop2.stderr40
-rw-r--r--src/tools/clippy/tests/ui/needless_return.stderr50
-rw-r--r--src/tools/clippy/tests/ui/never_loop.stderr10
-rw-r--r--src/tools/clippy/tests/ui/non_canonical_partial_ord_impl.stderr5
-rw-r--r--src/tools/clippy/tests/ui/non_std_lazy_static/non_std_lazy_static_fixable.stderr15
-rw-r--r--src/tools/clippy/tests/ui/non_std_lazy_static/non_std_lazy_static_unfixable.stderr15
-rw-r--r--src/tools/clippy/tests/ui/nonminimal_bool.stderr50
-rw-r--r--src/tools/clippy/tests/ui/octal_escapes.stderr65
-rw-r--r--src/tools/clippy/tests/ui/op_ref.stderr5
-rw-r--r--src/tools/clippy/tests/ui/option_as_ref_cloned.stderr15
-rw-r--r--src/tools/clippy/tests/ui/redundant_guards.stderr5
-rw-r--r--src/tools/clippy/tests/ui/ref_binding_to_reference.stderr10
-rw-r--r--src/tools/clippy/tests/ui/ref_option/ref_option.all.stderr10
-rw-r--r--src/tools/clippy/tests/ui/ref_option/ref_option.private.stderr5
-rw-r--r--src/tools/clippy/tests/ui/repeat_vec_with_capacity.stderr15
-rw-r--r--src/tools/clippy/tests/ui/repeat_vec_with_capacity_nostd.stderr5
-rw-r--r--src/tools/clippy/tests/ui/reversed_empty_ranges_fixable.stderr20
-rw-r--r--src/tools/clippy/tests/ui/reversed_empty_ranges_loops_fixable.stderr30
-rw-r--r--src/tools/clippy/tests/ui/single_range_in_vec_init.stderr90
-rw-r--r--src/tools/clippy/tests/ui/string_lit_chars_any.stderr25
-rw-r--r--src/tools/clippy/tests/ui/suspicious_command_arg_space.stderr10
-rw-r--r--src/tools/clippy/tests/ui/suspicious_doc_comments.stderr15
-rw-r--r--src/tools/clippy/tests/ui/suspicious_to_owned.stderr28
-rw-r--r--src/tools/clippy/tests/ui/suspicious_xor_used_as_pow.stderr35
-rw-r--r--src/tools/clippy/tests/ui/too_long_first_doc_paragraph-fix.stderr2
-rw-r--r--src/tools/clippy/tests/ui/too_long_first_doc_paragraph.stderr4
-rw-r--r--src/tools/clippy/tests/ui/transmute_ptr_to_ptr.stderr55
-rw-r--r--src/tools/clippy/tests/ui/transmutes_expressible_as_ptr_casts.stderr10
-rw-r--r--src/tools/clippy/tests/ui/unit_arg.stderr18
-rw-r--r--src/tools/clippy/tests/ui/unknown_clippy_lints.stderr10
-rw-r--r--src/tools/clippy/tests/ui/unnecessary_lazy_eval.stderr315
-rw-r--r--src/tools/clippy/tests/ui/unnecessary_lazy_eval_unfixable.stderr20
-rw-r--r--src/tools/clippy/tests/ui/unnecessary_literal_unwrap.stderr50
-rw-r--r--src/tools/clippy/tests/ui/unnecessary_map_or.stderr48
-rw-r--r--src/tools/clippy/tests/ui/unnecessary_wraps.stderr44
-rw-r--r--src/tools/clippy/tests/ui/unnested_or_patterns.stderr85
-rw-r--r--src/tools/clippy/tests/ui/unnested_or_patterns2.stderr40
-rw-r--r--src/tools/clippy/tests/ui/unused_enumerate_index.stderr10
-rw-r--r--src/tools/clippy/tests/ui/unused_format_specs.stderr20
-rw-r--r--src/tools/clippy/tests/ui/unused_result_ok.stderr20
-rw-r--r--src/tools/compiletest/src/common.rs23
-rw-r--r--src/tools/compiletest/src/directive-list.rs2
-rw-r--r--src/tools/compiletest/src/header.rs15
-rw-r--r--src/tools/compiletest/src/header/cfg.rs10
-rw-r--r--src/tools/compiletest/src/header/tests.rs28
-rw-r--r--src/tools/compiletest/src/lib.rs79
-rw-r--r--src/tools/compiletest/src/runtest.rs6
-rw-r--r--src/tools/compiletest/src/runtest/debuginfo.rs29
-rw-r--r--src/tools/compiletest/src/runtest/js_doc.rs3
-rw-r--r--src/tools/compiletest/src/runtest/run_make.rs67
-rw-r--r--src/tools/compiletest/src/runtest/rustdoc.rs5
m---------src/tools/enzyme0
-rw-r--r--src/tools/error_index_generator/Cargo.toml1
-rw-r--r--src/tools/generate-copyright/src/cargo_metadata.rs44
-rw-r--r--src/tools/generate-copyright/src/main.rs50
-rw-r--r--src/tools/generate-windows-sys/Cargo.toml2
-rw-r--r--src/tools/generate-windows-sys/src/main.rs3
-rw-r--r--src/tools/llvm-bitcode-linker/src/bin/llvm-bitcode-linker.rs6
-rw-r--r--src/tools/llvm-bitcode-linker/src/linker.rs13
-rw-r--r--src/tools/miri/src/intrinsics/atomic.rs2
-rw-r--r--src/tools/miri/src/intrinsics/mod.rs16
-rw-r--r--src/tools/miri/src/intrinsics/simd.rs4
-rw-r--r--src/tools/miri/src/lib.rs4
-rw-r--r--src/tools/miri/src/operator.rs4
-rw-r--r--src/tools/miri/src/shims/foreign_items.rs8
-rw-r--r--src/tools/miri/tests/fail/intrinsics/ptr_offset_from_unsigned_neg.rs4
-rw-r--r--src/tools/miri/tests/fail/intrinsics/ptr_offset_from_unsigned_neg.stderr4
-rw-r--r--src/tools/miri/tests/fail/panic/panic_abort1.rs2
-rw-r--r--src/tools/miri/tests/fail/panic/panic_abort2.rs2
-rw-r--r--src/tools/miri/tests/fail/panic/panic_abort3.rs2
-rw-r--r--src/tools/miri/tests/fail/panic/panic_abort4.rs2
-rw-r--r--src/tools/miri/tests/fail/ptr_swap_nonoverlapping.rs15
-rw-r--r--src/tools/miri/tests/fail/ptr_swap_nonoverlapping.stderr33
-rw-r--r--src/tools/miri/tests/fail/validity/dyn-transmute-inner-binder.rs30
-rw-r--r--src/tools/miri/tests/fail/validity/dyn-transmute-inner-binder.stderr15
-rw-r--r--src/tools/miri/tests/pass/dyn-upcast.rs30
-rw-r--r--src/tools/miri/tests/pass/float.rs6
-rw-r--r--src/tools/miri/tests/pass/intrinsics/portable-simd.rs27
-rw-r--r--src/tools/miri/tests/pass/ptr_offset.rs7
-rw-r--r--src/tools/miri/tests/pass/shims/x86/intrinsics-x86-pause-without-sse2.stderr5
-rw-r--r--src/tools/run-make-support/src/external_deps/rustc.rs9
-rw-r--r--src/tools/rust-analyzer/.git-blame-ignore-revs1
-rw-r--r--src/tools/rust-analyzer/.github/workflows/autopublish.yaml1
-rw-r--r--src/tools/rust-analyzer/.github/workflows/ci.yaml6
-rw-r--r--src/tools/rust-analyzer/.github/workflows/publish-libs.yaml1
-rw-r--r--src/tools/rust-analyzer/.github/workflows/release.yaml17
-rw-r--r--src/tools/rust-analyzer/.gitignore7
-rw-r--r--src/tools/rust-analyzer/CONTRIBUTING.md2
-rw-r--r--src/tools/rust-analyzer/Cargo.lock60
-rw-r--r--src/tools/rust-analyzer/Cargo.toml12
-rw-r--r--src/tools/rust-analyzer/PRIVACY.md2
-rw-r--r--src/tools/rust-analyzer/README.md13
-rw-r--r--src/tools/rust-analyzer/crates/base-db/src/input.rs47
-rw-r--r--src/tools/rust-analyzer/crates/base-db/src/lib.rs4
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/Cargo.toml2
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/data.rs18
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/data/adt.rs9
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/db.rs2
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/dyn_map.rs7
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/expander.rs9
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/expr_store.rs (renamed from src/tools/rust-analyzer/crates/hir-def/src/body.rs)359
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/expr_store/body.rs175
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/expr_store/lower.rs (renamed from src/tools/rust-analyzer/crates/hir-def/src/body/lower.rs)461
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/expr_store/lower/asm.rs (renamed from src/tools/rust-analyzer/crates/hir-def/src/body/lower/asm.rs)2
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/expr_store/pretty.rs (renamed from src/tools/rust-analyzer/crates/hir-def/src/body/pretty.rs)60
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/expr_store/scope.rs (renamed from src/tools/rust-analyzer/crates/hir-def/src/body/scope.rs)54
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/expr_store/tests.rs (renamed from src/tools/rust-analyzer/crates/hir-def/src/body/tests.rs)46
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/expr_store/tests/block.rs (renamed from src/tools/rust-analyzer/crates/hir-def/src/body/tests/block.rs)0
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/generics.rs2
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/hir.rs21
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/import_map.rs22
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/item_scope.rs18
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/item_tree.rs8
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/lang_item.rs1
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/lib.rs43
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/proc_macros.rs20
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/nameres.rs2
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/nameres/collector.rs37
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/nameres/mod_resolution.rs5
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/path.rs10
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/resolver.rs12
-rw-r--r--src/tools/rust-analyzer/crates/hir-expand/Cargo.toml1
-rw-r--r--src/tools/rust-analyzer/crates/hir-expand/src/builtin/fn_macro.rs2
-rw-r--r--src/tools/rust-analyzer/crates/hir-expand/src/cfg_process.rs56
-rw-r--r--src/tools/rust-analyzer/crates/hir-expand/src/db.rs12
-rw-r--r--src/tools/rust-analyzer/crates/hir-expand/src/name.rs6
-rw-r--r--src/tools/rust-analyzer/crates/hir-expand/src/prettify_macro_expansion_.rs4
-rw-r--r--src/tools/rust-analyzer/crates/hir-expand/src/proc_macro.rs8
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/Cargo.toml2
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/autoderef.rs11
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/consteval.rs3
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/expr.rs6
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/match_check.rs3
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/match_check/pat_analysis.rs12
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/unsafe_check.rs134
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/generics.rs3
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/infer.rs86
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/infer/cast.rs1
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/infer/coerce.rs40
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/infer/diagnostics.rs91
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs286
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/infer/pat.rs136
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/infer/path.rs177
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/layout.rs23
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/lib.rs27
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/lower.rs997
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/lower/diagnostics.rs2
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/lower/path.rs917
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/method_resolution.rs21
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/mir.rs13
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/mir/eval.rs13
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/mir/eval/shim.rs79
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/mir/eval/tests.rs69
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/mir/lower.rs29
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/mir/lower/pattern_matching.rs4
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/mir/pretty.rs2
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/tests.rs4
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/tests/coercion.rs3
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/tests/diagnostics.rs50
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/tests/method_resolution.rs6
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/tests/simple.rs47
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/utils.rs78
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/variance.rs1
-rw-r--r--src/tools/rust-analyzer/crates/hir/src/diagnostics.rs85
-rw-r--r--src/tools/rust-analyzer/crates/hir/src/display.rs4
-rw-r--r--src/tools/rust-analyzer/crates/hir/src/from_id.rs3
-rw-r--r--src/tools/rust-analyzer/crates/hir/src/has_source.rs4
-rw-r--r--src/tools/rust-analyzer/crates/hir/src/lib.rs145
-rw-r--r--src/tools/rust-analyzer/crates/hir/src/semantics.rs8
-rw-r--r--src/tools/rust-analyzer/crates/hir/src/semantics/child_by_source.rs3
-rw-r--r--src/tools/rust-analyzer/crates/hir/src/semantics/source_to_def.rs16
-rw-r--r--src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs118
-rw-r--r--src/tools/rust-analyzer/crates/hir/src/term_search.rs2
-rw-r--r--src/tools/rust-analyzer/crates/hir/src/term_search/tactics.rs13
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/assist_config.rs1
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_missing_match_arms.rs136
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/handlers/apply_demorgan.rs4
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/handlers/auto_import.rs6
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_tuple_struct_to_named_struct.rs102
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/handlers/expand_glob_import.rs365
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_delegate_methods.rs25
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_delegate_trait.rs37
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/lib.rs1
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/tests.rs36
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/tests/generated.rs25
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/utils.rs4
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/completions/dot.rs28
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/completions/flyimport.rs8
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/completions/postfix.rs91
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/completions/postfix/format_like.rs2
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/context.rs4
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/context/analysis.rs41
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/lib.rs8
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/render/function.rs18
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/snippet.rs12
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/tests/expression.rs50
-rw-r--r--src/tools/rust-analyzer/crates/ide-db/Cargo.toml1
-rw-r--r--src/tools/rust-analyzer/crates/ide-db/src/apply_change.rs9
-rw-r--r--src/tools/rust-analyzer/crates/ide-db/src/defs.rs3
-rw-r--r--src/tools/rust-analyzer/crates/ide-db/src/generated/lints.rs4
-rw-r--r--src/tools/rust-analyzer/crates/ide-db/src/imports/import_assets.rs4
-rw-r--r--src/tools/rust-analyzer/crates/ide-db/src/items_locator.rs3
-rw-r--r--src/tools/rust-analyzer/crates/ide-db/src/prime_caches.rs123
-rw-r--r--src/tools/rust-analyzer/crates/ide-db/src/search.rs1
-rw-r--r--src/tools/rust-analyzer/crates/ide-db/src/symbol_index.rs8
-rw-r--r--src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/incorrect_case.rs4
-rw-r--r--src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/invalid_cast.rs35
-rw-r--r--src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/macro_error.rs4
-rw-r--r--src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/mismatched_arg_count.rs2
-rw-r--r--src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_unsafe.rs88
-rw-r--r--src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/mutability_errors.rs3
-rw-r--r--src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/parenthesized_generic_args_without_fn_trait.rs59
-rw-r--r--src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/type_mismatch.rs25
-rw-r--r--src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unresolved_field.rs7
-rw-r--r--src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unresolved_method.rs53
-rw-r--r--src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unused_variables.rs13
-rw-r--r--src/tools/rust-analyzer/crates/ide-diagnostics/src/lib.rs8
-rw-r--r--src/tools/rust-analyzer/crates/ide-ssr/src/lib.rs18
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/annotations.rs69
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/doc_links.rs8
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/expand_macro.rs10
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/extend_selection.rs10
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/fetch_crates.rs10
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/file_structure.rs11
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/goto_definition.rs62
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/goto_implementation.rs10
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/goto_type_definition.rs10
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/highlight_related.rs12
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/hover.rs2
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/hover/render.rs1
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/inlay_hints.rs10
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/inlay_hints/adjustment.rs102
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/inlay_hints/bind_pat.rs33
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/inlay_hints/closure_captures.rs2
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/inlay_hints/extern_block.rs19
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/inlay_hints/implicit_drop.rs38
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/interpret.rs8
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/join_lines.rs14
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/lib.rs4
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/matching_brace.rs10
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/moniker.rs22
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/move_item.rs8
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/parent_module.rs10
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/references.rs10
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/rename.rs32
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/runnables.rs84
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/signature_help.rs4
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/static_index.rs38
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/status.rs11
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/syntax_highlighting.rs190
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/format.rs4
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/highlight.rs46
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_doctest.html9
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_general.html15
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_strings.html4
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/tests.rs31
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/typing.rs11
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/typing/on_enter.rs24
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/view_crate_graph.rs11
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/view_hir.rs9
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/view_item_tree.rs8
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/view_memory_layout.rs8
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/view_mir.rs6
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/view_syntax_tree.rs107
-rw-r--r--src/tools/rust-analyzer/crates/intern/src/symbol/symbols.rs37
-rw-r--r--src/tools/rust-analyzer/crates/limit/Cargo.toml16
-rw-r--r--src/tools/rust-analyzer/crates/limit/src/lib.rs67
-rw-r--r--src/tools/rust-analyzer/crates/load-cargo/src/lib.rs5
-rw-r--r--src/tools/rust-analyzer/crates/parser/Cargo.toml1
-rw-r--r--src/tools/rust-analyzer/crates/parser/src/grammar/expressions.rs18
-rw-r--r--src/tools/rust-analyzer/crates/parser/src/grammar/items/adt.rs5
-rw-r--r--src/tools/rust-analyzer/crates/parser/src/parser.rs5
-rw-r--r--src/tools/rust-analyzer/crates/parser/src/syntax_kind/generated.rs2
-rw-r--r--src/tools/rust-analyzer/crates/parser/test_data/generated/runner.rs12
-rw-r--r--src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/comma_after_default_values_syntax.rast59
-rw-r--r--src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/comma_after_default_values_syntax.rs4
-rw-r--r--src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/record_literal_before_ellipsis_recovery.rast70
-rw-r--r--src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/record_literal_before_ellipsis_recovery.rs2
-rw-r--r--src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/record_field_default_values.rast28
-rw-r--r--src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/record_field_default_values.rs1
-rw-r--r--src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/record_lit.rast47
-rw-r--r--src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/record_lit.rs2
-rw-r--r--src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/struct_initializer_with_defaults.rast39
-rw-r--r--src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/struct_initializer_with_defaults.rs3
-rw-r--r--src/tools/rust-analyzer/crates/proc-macro-srv-cli/src/main_loop.rs10
-rw-r--r--src/tools/rust-analyzer/crates/proc-macro-srv/src/lib.rs39
-rw-r--r--src/tools/rust-analyzer/crates/proc-macro-srv/src/tests/utils.rs2
-rw-r--r--src/tools/rust-analyzer/crates/project-model/src/cargo_workspace.rs3
-rw-r--r--src/tools/rust-analyzer/crates/project-model/src/lib.rs10
-rw-r--r--src/tools/rust-analyzer/crates/project-model/src/project_json.rs7
-rw-r--r--src/tools/rust-analyzer/crates/project-model/src/sysroot.rs160
-rw-r--r--src/tools/rust-analyzer/crates/project-model/src/tests.rs25
-rw-r--r--src/tools/rust-analyzer/crates/project-model/src/workspace.rs422
-rw-r--r--src/tools/rust-analyzer/crates/project-model/test_data/output/cargo_hello_world_project_model.txt25
-rw-r--r--src/tools/rust-analyzer/crates/project-model/test_data/output/cargo_hello_world_project_model_with_selective_overrides.txt25
-rw-r--r--src/tools/rust-analyzer/crates/project-model/test_data/output/cargo_hello_world_project_model_with_wildcard_overrides.txt25
-rw-r--r--src/tools/rust-analyzer/crates/project-model/test_data/output/rust_project_cfg_groups.txt14
-rw-r--r--src/tools/rust-analyzer/crates/project-model/test_data/output/rust_project_hello_world_project_model.txt12
-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.rs3
-rw-r--r--src/tools/rust-analyzer/crates/rust-analyzer/src/cli/diagnostics.rs4
-rw-r--r--src/tools/rust-analyzer/crates/rust-analyzer/src/cli/rustc_tests.rs12
-rw-r--r--src/tools/rust-analyzer/crates/rust-analyzer/src/cli/scip.rs83
-rw-r--r--src/tools/rust-analyzer/crates/rust-analyzer/src/cli/unresolved_references.rs4
-rw-r--r--src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs42
-rw-r--r--src/tools/rust-analyzer/crates/rust-analyzer/src/global_state.rs30
-rw-r--r--src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/dispatch.rs18
-rw-r--r--src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/notification.rs19
-rw-r--r--src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/request.rs194
-rw-r--r--src/tools/rust-analyzer/crates/rust-analyzer/src/integrated_benchmarks.rs14
-rw-r--r--src/tools/rust-analyzer/crates/rust-analyzer/src/lib.rs93
-rw-r--r--src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/from_proto.rs30
-rw-r--r--src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/semantic_tokens.rs5
-rw-r--r--src/tools/rust-analyzer/crates/rust-analyzer/src/main_loop.rs59
-rw-r--r--src/tools/rust-analyzer/crates/rust-analyzer/src/reload.rs9
-rw-r--r--src/tools/rust-analyzer/crates/rust-analyzer/src/task_pool.rs6
-rw-r--r--src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/cli.rs156
-rw-r--r--src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/main.rs116
-rw-r--r--src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/testdir.rs9
-rw-r--r--src/tools/rust-analyzer/crates/stdx/src/panic_context.rs40
-rw-r--r--src/tools/rust-analyzer/crates/stdx/src/thread/pool.rs21
-rw-r--r--src/tools/rust-analyzer/crates/syntax/rust.ungram2
-rw-r--r--src/tools/rust-analyzer/crates/syntax/src/ast/edit_in_place.rs46
-rw-r--r--src/tools/rust-analyzer/crates/syntax/src/ast/generated/nodes.rs6
-rw-r--r--src/tools/rust-analyzer/crates/syntax/src/ast/generated/tokens.rs2
-rw-r--r--src/tools/rust-analyzer/crates/syntax/src/ast/make.rs3
-rw-r--r--src/tools/rust-analyzer/crates/syntax/src/ast/prec.rs117
-rw-r--r--src/tools/rust-analyzer/crates/test-fixture/src/lib.rs67
-rw-r--r--src/tools/rust-analyzer/crates/test-utils/src/lib.rs9
-rw-r--r--src/tools/rust-analyzer/crates/test-utils/src/minicore.rs6
-rw-r--r--src/tools/rust-analyzer/crates/vfs-notify/src/lib.rs5
-rw-r--r--src/tools/rust-analyzer/crates/vfs/src/lib.rs27
-rw-r--r--src/tools/rust-analyzer/docs/book/README.md29
-rw-r--r--src/tools/rust-analyzer/docs/book/book.toml41
-rw-r--r--src/tools/rust-analyzer/docs/book/src/README.md21
-rw-r--r--src/tools/rust-analyzer/docs/book/src/SUMMARY.md24
-rw-r--r--src/tools/rust-analyzer/docs/book/src/assists.md8
-rw-r--r--src/tools/rust-analyzer/docs/book/src/assists_generated.md3846
-rw-r--r--src/tools/rust-analyzer/docs/book/src/configuration.md51
-rw-r--r--src/tools/rust-analyzer/docs/book/src/configuration_generated.md1206
-rw-r--r--src/tools/rust-analyzer/docs/book/src/contributing/README.md (renamed from src/tools/rust-analyzer/docs/dev/README.md)14
-rw-r--r--src/tools/rust-analyzer/docs/book/src/contributing/architecture.md (renamed from src/tools/rust-analyzer/docs/dev/architecture.md)15
-rw-r--r--src/tools/rust-analyzer/docs/book/src/contributing/debugging.md (renamed from src/tools/rust-analyzer/docs/dev/debugging.md)8
-rw-r--r--src/tools/rust-analyzer/docs/book/src/contributing/guide.md (renamed from src/tools/rust-analyzer/docs/dev/guide.md)13
-rw-r--r--src/tools/rust-analyzer/docs/book/src/contributing/lsp-extensions.md (renamed from src/tools/rust-analyzer/docs/dev/lsp-extensions.md)2
-rw-r--r--src/tools/rust-analyzer/docs/book/src/contributing/setup.md (renamed from src/tools/rust-analyzer/docs/dev/setup.md)0
-rw-r--r--src/tools/rust-analyzer/docs/book/src/contributing/style.md (renamed from src/tools/rust-analyzer/docs/dev/style.md)8
-rw-r--r--src/tools/rust-analyzer/docs/book/src/contributing/syntax.md (renamed from src/tools/rust-analyzer/docs/dev/syntax.md)0
-rw-r--r--src/tools/rust-analyzer/docs/book/src/diagnostics.md16
-rw-r--r--src/tools/rust-analyzer/docs/book/src/diagnostics_generated.md516
-rw-r--r--src/tools/rust-analyzer/docs/book/src/editor_features.md203
-rw-r--r--src/tools/rust-analyzer/docs/book/src/features.md3
-rw-r--r--src/tools/rust-analyzer/docs/book/src/features_generated.md940
-rw-r--r--src/tools/rust-analyzer/docs/book/src/installation.md40
-rw-r--r--src/tools/rust-analyzer/docs/book/src/non_cargo_based_projects.md246
-rw-r--r--src/tools/rust-analyzer/docs/book/src/other_editors.md425
-rw-r--r--src/tools/rust-analyzer/docs/book/src/privacy.md15
-rw-r--r--src/tools/rust-analyzer/docs/book/src/rust_analyzer_binary.md74
-rw-r--r--src/tools/rust-analyzer/docs/book/src/security.md19
-rw-r--r--src/tools/rust-analyzer/docs/book/src/troubleshooting.md50
-rw-r--r--src/tools/rust-analyzer/docs/book/src/vs_code.md121
-rw-r--r--src/tools/rust-analyzer/docs/user/.gitignore1
-rw-r--r--src/tools/rust-analyzer/docs/user/generated_config.adoc1197
-rw-r--r--src/tools/rust-analyzer/docs/user/manual.adoc1121
-rw-r--r--src/tools/rust-analyzer/editors/code/package-lock.json307
-rw-r--r--src/tools/rust-analyzer/editors/code/package.json20
-rw-r--r--src/tools/rust-analyzer/editors/code/src/commands.ts14
-rw-r--r--src/tools/rust-analyzer/editors/code/src/ctx.ts4
-rw-r--r--src/tools/rust-analyzer/editors/code/src/syntax_tree_provider.ts140
-rw-r--r--src/tools/rust-analyzer/editors/code/walkthrough-setup-tips.md1
-rw-r--r--src/tools/rust-analyzer/lib/line-index/src/lib.rs12
-rw-r--r--src/tools/rust-analyzer/rust-version2
-rw-r--r--src/tools/rust-analyzer/xtask/Cargo.toml3
-rw-r--r--src/tools/rust-analyzer/xtask/src/codegen.rs11
-rw-r--r--src/tools/rust-analyzer/xtask/src/codegen/assists_doc_tests.rs10
-rw-r--r--src/tools/rust-analyzer/xtask/src/codegen/diagnostics_docs.rs6
-rw-r--r--src/tools/rust-analyzer/xtask/src/codegen/feature_docs.rs16
-rw-r--r--src/tools/rust-analyzer/xtask/src/codegen/grammar.rs3
-rw-r--r--src/tools/rust-analyzer/xtask/src/codegen/parser_inline_tests.rs133
-rw-r--r--src/tools/rust-analyzer/xtask/src/publish/notes.rs6
-rw-r--r--src/tools/rust-analyzer/xtask/src/release.rs21
-rw-r--r--src/tools/rust-analyzer/xtask/src/tidy.rs7
-rw-r--r--src/tools/rust-analyzer/xtask/test_data/expected.md8
-rw-r--r--src/tools/rustbook/Cargo.lock11
-rw-r--r--src/tools/rustbook/Cargo.toml3
-rw-r--r--src/tools/rustfmt/src/patterns.rs87
-rw-r--r--src/tools/rustfmt/src/spanned.rs2
-rw-r--r--src/tools/rustfmt/src/types.rs16
-rw-r--r--src/tools/tidy/src/deps.rs14
-rw-r--r--src/tools/tidy/src/issues.txt2
-rw-r--r--src/version2
779 files changed, 23148 insertions, 10991 deletions
diff --git a/src/bootstrap/Cargo.lock b/src/bootstrap/Cargo.lock
index d2f3c7f36ca..890e64e2bab 100644
--- a/src/bootstrap/Cargo.lock
+++ b/src/bootstrap/Cargo.lock
@@ -59,6 +59,7 @@ dependencies = [
  "termcolor",
  "toml",
  "tracing",
+ "tracing-chrome",
  "tracing-subscriber",
  "tracing-tree",
  "walkdir",
@@ -87,9 +88,9 @@ dependencies = [
 
 [[package]]
 name = "cc"
-version = "1.2.0"
+version = "1.1.22"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1aeb932158bd710538c73702db6945cb68a8fb08c519e6e12706b94263b36db8"
+checksum = "9540e661f81799159abee814118cc139a2004b3a3aa3ea37724a1b66530b90e0"
 dependencies = [
  "shlex",
 ]
@@ -728,6 +729,17 @@ dependencies = [
 ]
 
 [[package]]
+name = "tracing-chrome"
+version = "0.7.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bf0a738ed5d6450a9fb96e86a23ad808de2b727fd1394585da5cdd6788ffe724"
+dependencies = [
+ "serde_json",
+ "tracing-core",
+ "tracing-subscriber",
+]
+
+[[package]]
 name = "tracing-core"
 version = "0.1.33"
 source = "registry+https://github.com/rust-lang/crates.io-index"
diff --git a/src/bootstrap/Cargo.toml b/src/bootstrap/Cargo.toml
index d7afcc7f27d..2c1d85b01e6 100644
--- a/src/bootstrap/Cargo.toml
+++ b/src/bootstrap/Cargo.toml
@@ -7,7 +7,7 @@ default-run = "bootstrap"
 
 [features]
 build-metrics = ["sysinfo"]
-tracing = ["dep:tracing", "dep:tracing-subscriber", "dep:tracing-tree"]
+tracing = ["dep:tracing", "dep:tracing-chrome", "dep:tracing-subscriber", "dep:tracing-tree"]
 
 [lib]
 path = "src/lib.rs"
@@ -37,7 +37,9 @@ test = false
 # Most of the time updating these dependencies requires modifications to the
 # bootstrap codebase(e.g., https://github.com/rust-lang/rust/issues/124565);
 # otherwise, some targets will fail. That's why these dependencies are explicitly pinned.
-cc = "=1.2.0"
+#
+# Do not upgrade this crate unless https://github.com/rust-lang/cc-rs/issues/1317 is fixed.
+cc = "=1.1.22"
 cmake = "=0.1.48"
 
 build_helper = { path = "../build_helper" }
@@ -67,6 +69,7 @@ sysinfo = { version = "0.33.0", default-features = false, optional = true, featu
 
 # Dependencies needed by the `tracing` feature
 tracing = { version = "0.1", optional = true, features = ["attributes"] }
+tracing-chrome = { version = "0.7", optional = true }
 tracing-subscriber = { version = "0.3", optional = true, features = ["env-filter", "fmt", "registry", "std"] }
 tracing-tree = { version = "0.4.0", optional = true }
 
diff --git a/src/bootstrap/configure.py b/src/bootstrap/configure.py
index a86c20d46bd..e3f58d97cbc 100755
--- a/src/bootstrap/configure.py
+++ b/src/bootstrap/configure.py
@@ -44,10 +44,14 @@ o("optimize-tests", "rust.optimize-tests", "build tests with optimizations")
 o("verbose-tests", "rust.verbose-tests", "enable verbose output when running tests")
 o(
     "ccache",
-    "llvm.ccache",
-    "invoke gcc/clang via ccache to reuse object files between builds",
+    "build.ccache",
+    "invoke gcc/clang/rustc via ccache to reuse object files between builds",
+)
+o(
+    "sccache",
+    None,
+    "invoke gcc/clang/rustc via sccache to reuse object files between builds",
 )
-o("sccache", None, "invoke gcc/clang via sccache to reuse object files between builds")
 o("local-rust", None, "use an installed rustc rather than downloading a snapshot")
 v("local-rust-root", None, "set prefix for local rust binary")
 o(
@@ -510,7 +514,7 @@ def apply_args(known_args, option_checking, config):
         build_triple = build(known_args)
 
         if option.name == "sccache":
-            set("llvm.ccache", "sccache", config)
+            set("build.ccache", "sccache", config)
         elif option.name == "local-rust":
             for path in os.environ["PATH"].split(os.pathsep):
                 if os.path.exists(path + "/rustc"):
@@ -772,6 +776,6 @@ if __name__ == "__main__":
         f.write(contents)
 
     p("")
-    p("run `python {}/x.py --help`".format(rust_dir))
+    p("run `{} {}/x.py --help`".format(os.path.basename(sys.executable), rust_dir))
     if "GITHUB_ACTIONS" in os.environ:
         print("::endgroup::")
diff --git a/src/bootstrap/defaults/config.tools.toml b/src/bootstrap/defaults/config.tools.toml
index 64097320cab..57c2706f60a 100644
--- a/src/bootstrap/defaults/config.tools.toml
+++ b/src/bootstrap/defaults/config.tools.toml
@@ -8,6 +8,8 @@ incremental = true
 download-rustc = "if-unchanged"
 
 [build]
+# cargo and clippy tests don't pass on stage 1
+test-stage = 2
 # Document with the in-tree rustdoc by default, since `download-rustc` makes it quick to compile.
 doc-stage = 2
 # Contributors working on tools will probably expect compiler docs to be generated, so they can figure out how to use the API.
diff --git a/src/bootstrap/download-ci-llvm-stamp b/src/bootstrap/download-ci-llvm-stamp
index 42cecbf5df9..d2c0d380fb4 100644
--- a/src/bootstrap/download-ci-llvm-stamp
+++ b/src/bootstrap/download-ci-llvm-stamp
@@ -1,4 +1,4 @@
 Change this file to make users of the `download-ci-llvm` configuration download
 a new version of LLVM from CI, even if the LLVM submodule hasn’t changed.
 
-Last change is for: https://github.com/rust-lang/rust/pull/129788
+Last change is for: https://github.com/rust-lang/rust/pull/134740
diff --git a/src/bootstrap/mk/Makefile.in b/src/bootstrap/mk/Makefile.in
index 7e6a39a236e..88aa70d4f2f 100644
--- a/src/bootstrap/mk/Makefile.in
+++ b/src/bootstrap/mk/Makefile.in
@@ -99,16 +99,18 @@ prepare:
 
 # Set of tests that represent around half of the time of the test suite.
 # Used to split tests across multiple CI runners.
-STAGE_2_TEST_SET1 := test --stage 2 --skip=compiler --skip=src
-STAGE_2_TEST_SET2 := test --stage 2 --skip=tests --skip=coverage-map --skip=coverage-run --skip=library --skip=tidyselftest
+SKIP_COMPILER := --skip=compiler
+SKIP_SRC := --skip=src
+TEST_SET1 := $(SKIP_COMPILER) $(SKIP_SRC)
+TEST_SET2 := --skip=tests --skip=coverage-map --skip=coverage-run --skip=library --skip=tidyselftest
 
 ## MSVC native builders
 
 # this intentionally doesn't use `$(BOOTSTRAP)` so we can test the shebang on Windows
 ci-msvc-py:
-	$(Q)$(CFG_SRC_DIR)/x.py $(STAGE_2_TEST_SET1)
+	$(Q)$(CFG_SRC_DIR)/x.py test --stage 2 $(TEST_SET1)
 ci-msvc-ps1:
-	$(Q)$(CFG_SRC_DIR)/x.ps1 $(STAGE_2_TEST_SET2)
+	$(Q)$(CFG_SRC_DIR)/x.ps1 test --stage 2 $(TEST_SET2)
 ci-msvc: ci-msvc-py ci-msvc-ps1
 
 ## MingW native builders
@@ -116,10 +118,14 @@ ci-msvc: ci-msvc-py ci-msvc-ps1
 # Set of tests that should represent half of the time of the test suite.
 # Used to split tests across multiple CI runners.
 # Test both x and bootstrap entrypoints.
+ci-mingw-x-1:
+	$(Q)$(CFG_SRC_DIR)/x test --stage 2 $(SKIP_COMPILER) $(TEST_SET2)
+ci-mingw-x-2:
+	$(Q)$(CFG_SRC_DIR)/x test --stage 2 $(SKIP_SRC) $(TEST_SET2)
 ci-mingw-x:
-	$(Q)$(CFG_SRC_DIR)/x $(STAGE_2_TEST_SET1)
+	$(Q)$(CFG_SRC_DIR)/x test --stage 2 $(TEST_SET1)
 ci-mingw-bootstrap:
-	$(Q)$(BOOTSTRAP) $(STAGE_2_TEST_SET2)
+	$(Q)$(BOOTSTRAP) test --stage 2 $(TEST_SET2)
 ci-mingw: ci-mingw-x ci-mingw-bootstrap
 
 .PHONY: dist
diff --git a/src/bootstrap/src/bin/main.rs b/src/bootstrap/src/bin/main.rs
index 441674936c6..38b380e3db8 100644
--- a/src/bootstrap/src/bin/main.rs
+++ b/src/bootstrap/src/bin/main.rs
@@ -21,7 +21,7 @@ use tracing::instrument;
 #[cfg_attr(feature = "tracing", instrument(level = "trace", name = "main"))]
 fn main() {
     #[cfg(feature = "tracing")]
-    setup_tracing();
+    let _guard = setup_tracing();
 
     let args = env::args().skip(1).collect::<Vec<_>>();
 
@@ -210,7 +210,7 @@ fn check_version(config: &Config) -> Option<String> {
 // - `tracing`'s `#[instrument(..)]` macro will need to be gated like `#![cfg_attr(feature =
 //   "tracing", instrument(..))]`.
 #[cfg(feature = "tracing")]
-fn setup_tracing() {
+fn setup_tracing() -> impl Drop {
     use tracing_subscriber::EnvFilter;
     use tracing_subscriber::layer::SubscriberExt;
 
@@ -218,7 +218,17 @@ fn setup_tracing() {
     // cf. <https://docs.rs/tracing-tree/latest/tracing_tree/struct.HierarchicalLayer.html>.
     let layer = tracing_tree::HierarchicalLayer::default().with_targets(true).with_indent_amount(2);
 
-    let registry = tracing_subscriber::registry().with(filter).with(layer);
+    let mut chrome_layer = tracing_chrome::ChromeLayerBuilder::new().include_args(true);
+
+    // Writes the Chrome profile to trace-<unix-timestamp>.json if enabled
+    if !env::var("BOOTSTRAP_PROFILE").is_ok_and(|v| v == "1") {
+        chrome_layer = chrome_layer.writer(io::sink());
+    }
+
+    let (chrome_layer, _guard) = chrome_layer.build();
+
+    let registry = tracing_subscriber::registry().with(filter).with(layer).with(chrome_layer);
 
     tracing::subscriber::set_global_default(registry).unwrap();
+    _guard
 }
diff --git a/src/bootstrap/src/core/build_steps/check.rs b/src/bootstrap/src/core/build_steps/check.rs
index 21afb031203..b8bbe1eb5f8 100644
--- a/src/bootstrap/src/core/build_steps/check.rs
+++ b/src/bootstrap/src/core/build_steps/check.rs
@@ -69,7 +69,7 @@ impl Step for Std {
         );
 
         std_cargo(builder, target, compiler.stage, &mut cargo);
-        if matches!(builder.config.cmd, Subcommand::Fix { .. }) {
+        if matches!(builder.config.cmd, Subcommand::Fix) {
             // By default, cargo tries to fix all targets. Tell it not to fix tests until we've added `test` to the sysroot.
             cargo.arg("--lib");
         }
diff --git a/src/bootstrap/src/core/build_steps/compile.rs b/src/bootstrap/src/core/build_steps/compile.rs
index cd3558ac6a4..9d3d07c83d2 100644
--- a/src/bootstrap/src/core/build_steps/compile.rs
+++ b/src/bootstrap/src/core/build_steps/compile.rs
@@ -16,6 +16,8 @@ use std::process::Stdio;
 use std::{env, fs, str};
 
 use serde_derive::Deserialize;
+#[cfg(feature = "tracing")]
+use tracing::{instrument, span};
 
 use crate::core::build_steps::tool::SourceType;
 use crate::core::build_steps::{dist, llvm};
@@ -30,7 +32,7 @@ use crate::utils::exec::command;
 use crate::utils::helpers::{
     exe, get_clang_cl_resource_dir, is_debug_info, is_dylib, symlink_dir, t, up_to_date,
 };
-use crate::{CLang, Compiler, DependencyType, GitRepo, LLVM_TOOLS, Mode};
+use crate::{CLang, Compiler, DependencyType, GitRepo, LLVM_TOOLS, Mode, debug, trace};
 
 #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
 pub struct Std {
@@ -95,9 +97,10 @@ impl Step for Std {
     const DEFAULT: bool = true;
 
     fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
-        run.crate_or_deps("sysroot").path("library").alias("core")
+        run.crate_or_deps("sysroot").path("library")
     }
 
+    #[cfg_attr(feature = "tracing", instrument(level = "trace", name = "Std::make_run", skip_all))]
     fn make_run(run: RunConfig<'_>) {
         let crates = std_crates_for_run_make(&run);
         let builder = run.builder;
@@ -109,6 +112,14 @@ impl Step for Std {
             && builder.download_rustc()
             && builder.config.last_modified_commit(&["library"], "download-rustc", true).is_none();
 
+        trace!("is managed git repo: {}", builder.rust_info().is_managed_git_subrepository());
+        trace!("download_rustc: {}", builder.download_rustc());
+        trace!(
+            "last modified commit: {:?}",
+            builder.config.last_modified_commit(&["library"], "download-rustc", true)
+        );
+        trace!(force_recompile);
+
         run.builder.ensure(Std {
             compiler: run.builder.compiler(run.builder.top_stage, run.build_triple()),
             target: run.target,
@@ -124,13 +135,26 @@ impl Step for Std {
     /// This will build the standard library for a particular stage of the build
     /// using the `compiler` targeting the `target` architecture. The artifacts
     /// created will also be linked into the sysroot directory.
+    #[cfg_attr(
+        feature = "tracing",
+        instrument(
+            level = "debug",
+            name = "Std::run",
+            skip_all,
+            fields(
+                target = ?self.target,
+                compiler = ?self.compiler,
+                force_recompile = self.force_recompile
+            ),
+        ),
+    )]
     fn run(self, builder: &Builder<'_>) {
         let target = self.target;
         let compiler = self.compiler;
 
         // When using `download-rustc`, we already have artifacts for the host available. Don't
         // recompile them.
-        if builder.download_rustc() && target == builder.build.build
+        if builder.download_rustc() && builder.is_builder_target(target)
             // NOTE: the beta compiler may generate different artifacts than the downloaded compiler, so
             // its artifacts can't be reused.
             && compiler.stage != 0
@@ -148,6 +172,9 @@ impl Step for Std {
         if builder.config.keep_stage.contains(&compiler.stage)
             || builder.config.keep_stage_std.contains(&compiler.stage)
         {
+            trace!(keep_stage = ?builder.config.keep_stage);
+            trace!(keep_stage_std = ?builder.config.keep_stage_std);
+
             builder.info("WARNING: Using a potentially old libstd. This may not behave well.");
 
             builder.ensure(StartupObjects { compiler, target });
@@ -163,7 +190,15 @@ impl Step for Std {
         let mut target_deps = builder.ensure(StartupObjects { compiler, target });
 
         let compiler_to_use = builder.compiler_for(compiler.stage, compiler.host, target);
+        trace!(?compiler_to_use);
+
         if compiler_to_use != compiler {
+            trace!(
+                ?compiler_to_use,
+                ?compiler,
+                "compiler != compiler_to_use, handling cross-compile scenario"
+            );
+
             builder.ensure(Std::new(compiler_to_use, target));
             let msg = if compiler_to_use.host == target {
                 format!(
@@ -186,12 +221,21 @@ impl Step for Std {
             return;
         }
 
+        trace!(
+            ?compiler_to_use,
+            ?compiler,
+            "compiler == compiler_to_use, handling not-cross-compile scenario"
+        );
+
         target_deps.extend(self.copy_extra_objects(builder, &compiler, target));
 
         // The LLD wrappers and `rust-lld` are self-contained linking components that can be
         // necessary to link the stdlib on some targets. We'll also need to copy these binaries to
         // the `stage0-sysroot` to ensure the linker is found when bootstrapping on such a target.
-        if compiler.stage == 0 && compiler.host == builder.config.build {
+        if compiler.stage == 0 && builder.is_builder_target(compiler.host) {
+            trace!(
+                "(build == host) copying linking components to `stage0-sysroot` for bootstrapping"
+            );
             // We want to copy the host `bin` folder within the `rustlib` folder in the sysroot.
             let src_sysroot_bin = builder
                 .rustc_snapshot_sysroot()
@@ -210,6 +254,7 @@ impl Step for Std {
         // with -Zalways-encode-mir. This frees us from the need to have a target linker, and the
         // fact that this is a check build integrates nicely with run_cargo.
         let mut cargo = if self.is_for_mir_opt_tests {
+            trace!("building special sysroot for mir-opt tests");
             let mut cargo = builder::Cargo::new_for_mir_opt_tests(
                 builder,
                 compiler,
@@ -222,6 +267,7 @@ impl Step for Std {
             cargo.arg("--manifest-path").arg(builder.src.join("library/sysroot/Cargo.toml"));
             cargo
         } else {
+            trace!("building regular sysroot");
             let mut cargo = builder::Cargo::new(
                 builder,
                 compiler,
@@ -443,8 +489,49 @@ fn compiler_rt_for_profiler(builder: &Builder<'_>) -> PathBuf {
 /// Configure cargo to compile the standard library, adding appropriate env vars
 /// and such.
 pub fn std_cargo(builder: &Builder<'_>, target: TargetSelection, stage: u32, cargo: &mut Cargo) {
-    if let Some(target) = env::var_os("MACOSX_STD_DEPLOYMENT_TARGET") {
-        cargo.env("MACOSX_DEPLOYMENT_TARGET", target);
+    // rustc already ensures that it builds with the minimum deployment
+    // target, so ideally we shouldn't need to do anything here.
+    //
+    // However, `cc` currently defaults to a higher version for backwards
+    // compatibility, which means that compiler-rt, which is built via
+    // compiler-builtins' build script, gets built with a higher deployment
+    // target. This in turn causes warnings while linking, and is generally
+    // a compatibility hazard.
+    //
+    // So, at least until https://github.com/rust-lang/cc-rs/issues/1171, or
+    // perhaps https://github.com/rust-lang/cargo/issues/13115 is resolved, we
+    // explicitly set the deployment target environment variables to avoid
+    // this issue.
+    //
+    // This place also serves as an extension point if we ever wanted to raise
+    // rustc's default deployment target while keeping the prebuilt `std` at
+    // a lower version, so it's kinda nice to have in any case.
+    if target.contains("apple") && !builder.config.dry_run() {
+        // Query rustc for the deployment target, and the associated env var.
+        // The env var is one of the standard `*_DEPLOYMENT_TARGET` vars, i.e.
+        // `MACOSX_DEPLOYMENT_TARGET`, `IPHONEOS_DEPLOYMENT_TARGET`, etc.
+        let mut cmd = command(builder.rustc(cargo.compiler()));
+        cmd.arg("--target").arg(target.rustc_target_arg());
+        cmd.arg("--print=deployment-target");
+        let output = cmd.run_capture_stdout(builder).stdout();
+
+        let (env_var, value) = output.split_once('=').unwrap();
+        // Unconditionally set the env var (if it was set in the environment
+        // already, rustc should've picked that up).
+        cargo.env(env_var.trim(), value.trim());
+
+        // Allow CI to override the deployment target for `std` on macOS.
+        //
+        // This is useful because we might want the host tooling LLVM, `rustc`
+        // and Cargo to have a different deployment target than `std` itself
+        // (currently, these two versions are the same, but in the past, we
+        // supported macOS 10.7 for user code and macOS 10.8 in host tooling).
+        //
+        // It is not necessary on the other platforms, since only macOS has
+        // support for host tooling.
+        if let Some(target) = env::var_os("MACOSX_STD_DEPLOYMENT_TARGET") {
+            cargo.env("MACOSX_DEPLOYMENT_TARGET", target);
+        }
     }
 
     // Paths needed by `library/profiler_builtins/build.rs`.
@@ -624,6 +711,19 @@ impl Step for StdLink {
     /// Note that this assumes that `compiler` has already generated the libstd
     /// libraries for `target`, and this method will find them in the relevant
     /// output directory.
+    #[cfg_attr(
+        feature = "tracing",
+        instrument(
+            level = "trace",
+            name = "StdLink::run",
+            skip_all,
+            fields(
+                compiler = ?self.compiler,
+                target_compiler = ?self.target_compiler,
+                target = ?self.target
+            ),
+        ),
+    )]
     fn run(self, builder: &Builder<'_>) {
         let compiler = self.compiler;
         let target_compiler = self.target_compiler;
@@ -781,6 +881,15 @@ impl Step for StartupObjects {
     /// They don't require any library support as they're just plain old object
     /// files, so we just use the nightly snapshot compiler to always build them (as
     /// no other compilers are guaranteed to be available).
+    #[cfg_attr(
+        feature = "tracing",
+        instrument(
+            level = "trace",
+            name = "StartupObjects::run",
+            skip_all,
+            fields(compiler = ?self.compiler, target = ?self.target),
+        ),
+    )]
     fn run(self, builder: &Builder<'_>) -> Vec<(PathBuf, DependencyType)> {
         let for_compiler = self.compiler;
         let target = self.target;
@@ -895,6 +1004,15 @@ impl Step for Rustc {
     /// This will build the compiler for a particular stage of the build using
     /// the `compiler` targeting the `target` architecture. The artifacts
     /// created will also be linked into the sysroot directory.
+    #[cfg_attr(
+        feature = "tracing",
+        instrument(
+            level = "debug",
+            name = "Rustc::run",
+            skip_all,
+            fields(previous_compiler = ?self.compiler, target = ?self.target),
+        ),
+    )]
     fn run(self, builder: &Builder<'_>) -> u32 {
         let compiler = self.compiler;
         let target = self.target;
@@ -902,6 +1020,8 @@ impl Step for Rustc {
         // NOTE: the ABI of the beta compiler is different from the ABI of the downloaded compiler,
         // so its artifacts can't be reused.
         if builder.download_rustc() && compiler.stage != 0 {
+            trace!(stage = compiler.stage, "`download_rustc` requested");
+
             let sysroot = builder.ensure(Sysroot { compiler, force_recompile: false });
             cp_rustc_component_to_ci_sysroot(
                 builder,
@@ -914,6 +1034,8 @@ impl Step for Rustc {
         builder.ensure(Std::new(compiler, target));
 
         if builder.config.keep_stage.contains(&compiler.stage) {
+            trace!(stage = compiler.stage, "`keep-stage` requested");
+
             builder.info("WARNING: Using a potentially old librustc. This may not behave well.");
             builder.info("WARNING: Use `--keep-stage-std` if you want to rebuild the compiler when it changes");
             builder.ensure(RustcLink::from_rustc(self, compiler));
@@ -1049,12 +1171,19 @@ pub fn rustc_cargo(
     // <https://rust-lang.zulipchat.com/#narrow/stream/131828-t-compiler/topic/Internal.20lint.20for.20raw.20.60print!.60.20and.20.60println!.60.3F>.
     cargo.rustflag("-Zon-broken-pipe=kill");
 
-    // We temporarily disable linking here as part of some refactoring.
-    // This way, people can manually use -Z llvm-plugins and -C passes=enzyme for now.
-    // In a follow-up PR, we will re-enable linking here and load the pass for them.
-    //if builder.config.llvm_enzyme {
-    //    cargo.rustflag("-l").rustflag("Enzyme-19");
-    //}
+    // We want to link against registerEnzyme and in the future we want to use additional
+    // functionality from Enzyme core. For that we need to link against Enzyme.
+    if builder.config.llvm_enzyme {
+        let arch = builder.build.build;
+        let enzyme_dir = builder.build.out.join(arch).join("enzyme").join("lib");
+        cargo.rustflag("-L").rustflag(enzyme_dir.to_str().expect("Invalid path"));
+
+        if !builder.config.dry_run() {
+            let llvm_config = builder.llvm_config(builder.config.build).unwrap();
+            let llvm_version_major = llvm::get_llvm_version_major(builder, &llvm_config);
+            cargo.rustflag("-l").rustflag(&format!("Enzyme-{llvm_version_major}"));
+        }
+    }
 
     // Building with protected visibility reduces the number of dynamic relocations needed, giving
     // us a faster startup time. However GNU ld < 2.40 will error if we try to link a shared object
@@ -1064,9 +1193,7 @@ pub fn rustc_cargo(
         cargo.rustflag("-Zdefault-visibility=protected");
     }
 
-    // We currently don't support cross-crate LTO in stage0. This also isn't hugely necessary
-    // and may just be a time sink.
-    if compiler.stage != 0 {
+    if is_lto_stage(compiler) {
         match builder.config.rust_lto {
             RustcLto::Thin | RustcLto::Fat => {
                 // Since using LTO for optimizing dylibs is currently experimental,
@@ -1220,7 +1347,7 @@ pub fn rustc_cargo_env(
 
     // Build jemalloc on AArch64 with support for page sizes up to 64K
     // See: https://github.com/rust-lang/rust/pull/135081
-    if builder.config.jemalloc
+    if builder.config.jemalloc(target)
         && target.starts_with("aarch64")
         && env::var_os("JEMALLOC_SYS_WITH_LG_PAGE").is_none()
     {
@@ -1234,6 +1361,9 @@ fn rustc_llvm_env(builder: &Builder<'_>, cargo: &mut Cargo, target: TargetSelect
     if builder.is_rust_llvm(target) {
         cargo.env("LLVM_RUSTLLVM", "1");
     }
+    if builder.config.llvm_enzyme {
+        cargo.env("LLVM_ENZYME", "1");
+    }
     let llvm::LlvmResult { llvm_config, .. } = builder.ensure(llvm::Llvm { target });
     cargo.env("LLVM_CONFIG", &llvm_config);
 
@@ -1331,6 +1461,19 @@ impl Step for RustcLink {
     }
 
     /// Same as `std_link`, only for librustc
+    #[cfg_attr(
+        feature = "tracing",
+        instrument(
+            level = "trace",
+            name = "RustcLink::run",
+            skip_all,
+            fields(
+                compiler = ?self.compiler,
+                previous_stage_compiler = ?self.previous_stage_compiler,
+                target = ?self.target,
+            ),
+        ),
+    )]
     fn run(self, builder: &Builder<'_>) {
         let compiler = self.compiler;
         let previous_stage_compiler = self.previous_stage_compiler;
@@ -1419,6 +1562,19 @@ impl Step for CodegenBackend {
         }
     }
 
+    #[cfg_attr(
+        feature = "tracing",
+        instrument(
+            level = "debug",
+            name = "CodegenBackend::run",
+            skip_all,
+            fields(
+                compiler = ?self.compiler,
+                target = ?self.target,
+                backend = ?self.target,
+            ),
+        ),
+    )]
     fn run(self, builder: &Builder<'_>) {
         let compiler = self.compiler;
         let target = self.target;
@@ -1427,6 +1583,7 @@ impl Step for CodegenBackend {
         builder.ensure(Rustc::new(compiler, target));
 
         if builder.config.keep_stage.contains(&compiler.stage) {
+            trace!("`keep-stage` requested");
             builder.info(
                 "WARNING: Using a potentially old codegen backend. \
                 This may not behave well.",
@@ -1544,7 +1701,8 @@ pub fn compiler_file(
         return PathBuf::new();
     }
     let mut cmd = command(compiler);
-    cmd.args(builder.cflags(target, GitRepo::Rustc, c));
+    cmd.args(builder.cc_handled_clags(target, c));
+    cmd.args(builder.cc_unhandled_cflags(target, GitRepo::Rustc, c));
     cmd.arg(format!("-print-file-name={file}"));
     let out = cmd.run_capture_stdout(builder).stdout();
     PathBuf::from(out.trim())
@@ -1573,6 +1731,15 @@ impl Step for Sysroot {
     /// Returns the sysroot that `compiler` is supposed to use.
     /// For the stage0 compiler, this is stage0-sysroot (because of the initial std build).
     /// For all other stages, it's the same stage directory that the compiler lives in.
+    #[cfg_attr(
+        feature = "tracing",
+        instrument(
+            level = "debug",
+            name = "Sysroot::run",
+            skip_all,
+            fields(compiler = ?self.compiler),
+        ),
+    )]
     fn run(self, builder: &Builder<'_>) -> PathBuf {
         let compiler = self.compiler;
         let host_dir = builder.out.join(compiler.host);
@@ -1589,6 +1756,7 @@ impl Step for Sysroot {
             }
         };
         let sysroot = sysroot_dir(compiler.stage);
+        trace!(stage = ?compiler.stage, ?sysroot);
 
         builder
             .verbose(|| println!("Removing sysroot {} to avoid caching bugs", sysroot.display()));
@@ -1743,10 +1911,20 @@ impl Step for Assemble {
     /// This will assemble a compiler in `build/$host/stage$stage`. The compiler
     /// must have been previously produced by the `stage - 1` builder.build
     /// compiler.
+    #[cfg_attr(
+        feature = "tracing",
+        instrument(
+            level = "debug",
+            name = "Assemble::run",
+            skip_all,
+            fields(target_compiler = ?self.target_compiler),
+        ),
+    )]
     fn run(self, builder: &Builder<'_>) -> Compiler {
         let target_compiler = self.target_compiler;
 
         if target_compiler.stage == 0 {
+            trace!("stage 0 build compiler is always available, simply returning");
             assert_eq!(
                 builder.config.build, target_compiler.host,
                 "Cannot obtain compiler for non-native build triple at stage 0"
@@ -1762,9 +1940,13 @@ impl Step for Assemble {
         t!(fs::create_dir_all(&libdir_bin));
 
         if builder.config.llvm_enabled(target_compiler.host) {
+            trace!("target_compiler.host" = ?target_compiler.host, "LLVM enabled");
+
             let llvm::LlvmResult { llvm_config, .. } =
                 builder.ensure(llvm::Llvm { target: target_compiler.host });
             if !builder.config.dry_run() && builder.config.llvm_tools_enabled {
+                trace!("LLVM tools enabled");
+
                 let llvm_bin_dir =
                     command(llvm_config).arg("--bindir").run_capture_stdout(builder).stdout();
                 let llvm_bin_dir = Path::new(llvm_bin_dir.trim());
@@ -1774,7 +1956,13 @@ impl Step for Assemble {
                 // rustup, and lets developers use a locally built toolchain to
                 // build projects that expect llvm tools to be present in the sysroot
                 // (e.g. the `bootimage` crate).
+
+                #[cfg(feature = "tracing")]
+                let _llvm_tools_span =
+                    span!(tracing::Level::TRACE, "installing llvm tools to sysroot", ?libdir_bin)
+                        .entered();
                 for tool in LLVM_TOOLS {
+                    trace!("installing `{tool}`");
                     let tool_exe = exe(tool, target_compiler.host);
                     let src_path = llvm_bin_dir.join(&tool_exe);
                     // When using `download-ci-llvm`, some of the tools
@@ -1794,18 +1982,22 @@ impl Step for Assemble {
 
         let maybe_install_llvm_bitcode_linker = |compiler| {
             if builder.config.llvm_bitcode_linker_enabled {
-                let src_path = builder.ensure(crate::core::build_steps::tool::LlvmBitcodeLinker {
-                    compiler,
-                    target: target_compiler.host,
-                    extra_features: vec![],
-                });
+                trace!("llvm-bitcode-linker enabled, installing");
+                let llvm_bitcode_linker =
+                    builder.ensure(crate::core::build_steps::tool::LlvmBitcodeLinker {
+                        compiler,
+                        target: target_compiler.host,
+                        extra_features: vec![],
+                    });
                 let tool_exe = exe("llvm-bitcode-linker", target_compiler.host);
-                builder.copy_link(&src_path, &libdir_bin.join(tool_exe));
+                builder.copy_link(&llvm_bitcode_linker.tool_path, &libdir_bin.join(tool_exe));
             }
         };
 
         // If we're downloading a compiler from CI, we can use the same compiler for all stages other than 0.
         if builder.download_rustc() {
+            trace!("`download-rustc` requested, reusing CI compiler for stage > 0");
+
             builder.ensure(Std::new(target_compiler, target_compiler.host));
             let sysroot =
                 builder.ensure(Sysroot { compiler: target_compiler, force_recompile: false });
@@ -1835,23 +2027,28 @@ impl Step for Assemble {
         //
         // FIXME: It may be faster if we build just a stage 1 compiler and then
         //        use that to bootstrap this compiler forward.
+        debug!(
+            "ensuring build compiler is available: compiler(stage = {}, host = {:?})",
+            target_compiler.stage - 1,
+            builder.config.build,
+        );
         let mut build_compiler = builder.compiler(target_compiler.stage - 1, builder.config.build);
 
         // Build enzyme
-        let enzyme_install = if builder.config.llvm_enzyme {
-            Some(builder.ensure(llvm::Enzyme { target: build_compiler.host }))
-        } else {
-            None
-        };
-
-        if let Some(enzyme_install) = enzyme_install {
+        if builder.config.llvm_enzyme && !builder.config.dry_run() {
+            debug!("`llvm_enzyme` requested");
+            let enzyme_install = builder.ensure(llvm::Enzyme { target: build_compiler.host });
+            let llvm_config = builder.llvm_config(builder.config.build).unwrap();
+            let llvm_version_major = llvm::get_llvm_version_major(builder, &llvm_config);
             let lib_ext = std::env::consts::DLL_EXTENSION;
-            let src_lib = enzyme_install.join("build/Enzyme/libEnzyme-19").with_extension(lib_ext);
+            let libenzyme = format!("libEnzyme-{llvm_version_major}");
+            let src_lib =
+                enzyme_install.join("build/Enzyme").join(&libenzyme).with_extension(lib_ext);
             let libdir = builder.sysroot_target_libdir(build_compiler, build_compiler.host);
             let target_libdir =
                 builder.sysroot_target_libdir(target_compiler, target_compiler.host);
-            let dst_lib = libdir.join("libEnzyme-19").with_extension(lib_ext);
-            let target_dst_lib = target_libdir.join("libEnzyme-19").with_extension(lib_ext);
+            let dst_lib = libdir.join(&libenzyme).with_extension(lib_ext);
+            let target_dst_lib = target_libdir.join(&libenzyme).with_extension(lib_ext);
             builder.copy_link(&src_lib, &dst_lib);
             builder.copy_link(&src_lib, &target_dst_lib);
         }
@@ -1861,13 +2058,27 @@ impl Step for Assemble {
         // link to these. (FIXME: Is that correct? It seems to be correct most
         // of the time but I think we do link to these for stage2/bin compilers
         // when not performing a full bootstrap).
+        debug!(
+            ?build_compiler,
+            "target_compiler.host" = ?target_compiler.host,
+            "building compiler libraries to link to"
+        );
         let actual_stage = builder.ensure(Rustc::new(build_compiler, target_compiler.host));
         // Current build_compiler.stage might be uplifted instead of being built; so update it
         // to not fail while linking the artifacts.
+        debug!(
+            "(old) build_compiler.stage" = build_compiler.stage,
+            "(adjusted) build_compiler.stage" = actual_stage,
+            "temporarily adjusting `build_compiler.stage` to account for uplifted libraries"
+        );
         build_compiler.stage = actual_stage;
 
+        #[cfg(feature = "tracing")]
+        let _codegen_backend_span =
+            span!(tracing::Level::DEBUG, "building requested codegen backends").entered();
         for backend in builder.config.codegen_backends(target_compiler.host) {
             if backend == "llvm" {
+                debug!("llvm codegen backend is already built as part of rustc");
                 continue; // Already built as part of rustc
             }
 
@@ -1877,6 +2088,8 @@ impl Step for Assemble {
                 backend: backend.clone(),
             });
         }
+        #[cfg(feature = "tracing")]
+        drop(_codegen_backend_span);
 
         let stage = target_compiler.stage;
         let host = target_compiler.host;
@@ -1936,6 +2149,7 @@ impl Step for Assemble {
             }
         }
 
+        debug!("copying codegen backends to sysroot");
         copy_codegen_backends_to_sysroot(builder, build_compiler, target_compiler);
 
         if builder.config.lld_enabled {
@@ -1946,6 +2160,11 @@ impl Step for Assemble {
         }
 
         if builder.config.llvm_enabled(target_compiler.host) && builder.config.llvm_tools_enabled {
+            debug!(
+                "llvm and llvm tools enabled; copying `llvm-objcopy` as `rust-objcopy` to \
+                workaround faulty homebrew `strip`s"
+            );
+
             // `llvm-strip` is used by rustc, which is actually just a symlink to `llvm-objcopy`, so
             // copy and rename `llvm-objcopy`.
             //
@@ -1963,14 +2182,13 @@ impl Step for Assemble {
         // logic to create the final binary. This is used by the
         // `wasm32-wasip2` target of Rust.
         if builder.tool_enabled("wasm-component-ld") {
-            let wasm_component_ld_exe =
-                builder.ensure(crate::core::build_steps::tool::WasmComponentLd {
-                    compiler: build_compiler,
-                    target: target_compiler.host,
-                });
+            let wasm_component = builder.ensure(crate::core::build_steps::tool::WasmComponentLd {
+                compiler: build_compiler,
+                target: target_compiler.host,
+            });
             builder.copy_link(
-                &wasm_component_ld_exe,
-                &libdir_bin.join(wasm_component_ld_exe.file_name().unwrap()),
+                &wasm_component.tool_path,
+                &libdir_bin.join(wasm_component.tool_path.file_name().unwrap()),
             );
         }
 
@@ -1978,6 +2196,11 @@ impl Step for Assemble {
 
         // Ensure that `libLLVM.so` ends up in the newly build compiler directory,
         // so that it can be found when the newly built `rustc` is run.
+        debug!(
+            "target_compiler.host" = ?target_compiler.host,
+            ?sysroot,
+            "ensuring availability of `libLLVM.so` in compiler directory"
+        );
         dist::maybe_install_llvm_runtime(builder, target_compiler.host, &sysroot);
         dist::maybe_install_llvm_target(builder, target_compiler.host, &sysroot);
 
@@ -1987,6 +2210,7 @@ impl Step for Assemble {
         let bindir = sysroot.join("bin");
         t!(fs::create_dir_all(bindir));
         let compiler = builder.rustc(target_compiler);
+        debug!(src = ?rustc, dst = ?compiler, "linking compiler binary itself");
         builder.copy_link(&rustc, &compiler);
 
         target_compiler
@@ -2191,6 +2415,10 @@ pub fn stream_cargo(
     cb: &mut dyn FnMut(CargoMessage<'_>),
 ) -> bool {
     let mut cmd = cargo.into_cmd();
+
+    #[cfg(feature = "tracing")]
+    let _run_span = crate::trace_cmd!(cmd);
+
     let cargo = cmd.as_command_mut();
     // Instruct Cargo to give us json messages on stdout, critically leaving
     // stderr as piped so we can get those pretty colors.
@@ -2267,7 +2495,8 @@ pub fn strip_debug(builder: &Builder<'_>, target: TargetSelection, path: &Path)
     // FIXME: to make things simpler for now, limit this to the host and target where we know
     // `strip -g` is both available and will fix the issue, i.e. on a x64 linux host that is not
     // cross-compiling. Expand this to other appropriate targets in the future.
-    if target != "x86_64-unknown-linux-gnu" || target != builder.config.build || !path.exists() {
+    if target != "x86_64-unknown-linux-gnu" || !builder.is_builder_target(target) || !path.exists()
+    {
         return;
     }
 
@@ -2290,3 +2519,8 @@ pub fn strip_debug(builder: &Builder<'_>, target: TargetSelection, path: &Path)
     // everything else (standard library, future stages...) to be rebuilt.
     t!(file.set_modified(previous_mtime));
 }
+
+/// We only use LTO for stage 2+, to speed up build time of intermediate stages.
+pub fn is_lto_stage(build_compiler: &Compiler) -> bool {
+    build_compiler.stage != 0
+}
diff --git a/src/bootstrap/src/core/build_steps/dist.rs b/src/bootstrap/src/core/build_steps/dist.rs
index 18f920b85ee..26b3e0b701c 100644
--- a/src/bootstrap/src/core/build_steps/dist.rs
+++ b/src/bootstrap/src/core/build_steps/dist.rs
@@ -16,10 +16,12 @@ use std::{env, fs};
 
 use object::BinaryFormat;
 use object::read::archive::ArchiveFile;
+#[cfg(feature = "tracing")]
+use tracing::instrument;
 
 use crate::core::build_steps::doc::DocumentationFormat;
 use crate::core::build_steps::tool::{self, Tool};
-use crate::core::build_steps::vendor::default_paths_to_vendor;
+use crate::core::build_steps::vendor::{VENDOR_DIR, Vendor};
 use crate::core::build_steps::{compile, llvm};
 use crate::core::builder::{Builder, Kind, RunConfig, ShouldRun, Step};
 use crate::core::config::TargetSelection;
@@ -30,7 +32,7 @@ use crate::utils::helpers::{
     exe, is_dylib, move_file, t, target_supports_cranelift_backend, timeit,
 };
 use crate::utils::tarball::{GeneratedTarball, OverlayKind, Tarball};
-use crate::{Compiler, DependencyType, LLVM_TOOLS, Mode};
+use crate::{Compiler, DependencyType, LLVM_TOOLS, Mode, trace};
 
 pub fn pkgname(builder: &Builder<'_>, component: &str) -> String {
     format!("{}-{}", component, builder.rust_package_vers())
@@ -419,16 +421,12 @@ impl Step for Rustc {
 
             if let Some(ra_proc_macro_srv) = builder.ensure_if_default(
                 tool::RustAnalyzerProcMacroSrv {
-                    compiler: builder.compiler_for(
-                        compiler.stage,
-                        builder.config.build,
-                        compiler.host,
-                    ),
+                    compiler: builder.compiler(compiler.stage, builder.config.build),
                     target: compiler.host,
                 },
                 builder.kind,
             ) {
-                builder.install(&ra_proc_macro_srv, &image.join("libexec"), 0o755);
+                builder.install(&ra_proc_macro_srv.tool_path, &image.join("libexec"), 0o755);
             }
 
             let libdir_relative = builder.libdir_relative(compiler);
@@ -582,7 +580,7 @@ impl Step for DebuggerScripts {
 fn skip_host_target_lib(builder: &Builder<'_>, compiler: Compiler) -> bool {
     // The only true set of target libraries came from the build triple, so
     // let's reduce redundant work by only producing archives from that host.
-    if compiler.host != builder.config.build {
+    if !builder.is_builder_target(compiler.host) {
         builder.info("\tskipping, not a build host");
         true
     } else {
@@ -637,7 +635,7 @@ fn copy_target_libs(
     for (path, dependency_type) in builder.read_stamp_file(stamp) {
         if dependency_type == DependencyType::TargetSelfContained {
             builder.copy_link(&path, &self_contained_dst.join(path.file_name().unwrap()));
-        } else if dependency_type == DependencyType::Target || builder.config.build == target {
+        } else if dependency_type == DependencyType::Target || builder.is_builder_target(target) {
             builder.copy_link(&path, &dst.join(path.file_name().unwrap()));
         }
     }
@@ -773,11 +771,7 @@ impl Step for Analysis {
             // Find the actual compiler (handling the full bootstrap option) which
             // produced the save-analysis data because that data isn't copied
             // through the sysroot uplifting.
-            compiler: run.builder.compiler_for(
-                run.builder.top_stage,
-                run.builder.config.build,
-                run.target,
-            ),
+            compiler: run.builder.compiler(run.builder.top_stage, run.builder.config.build),
             target: run.target,
         });
     }
@@ -786,7 +780,7 @@ impl Step for Analysis {
     fn run(self, builder: &Builder<'_>) -> Option<GeneratedTarball> {
         let compiler = self.compiler;
         let target = self.target;
-        if compiler.host != builder.config.build {
+        if !builder.is_builder_target(compiler.host) {
             return None;
         }
 
@@ -1027,6 +1021,17 @@ impl Step for PlainSourceTarball {
             ],
             plain_dst_src,
         );
+        // We keep something in src/gcc because it is a registered submodule,
+        // and if it misses completely it can cause issues elsewhere
+        // (see https://github.com/rust-lang/rust/issues/137332).
+        // We can also let others know why is the source code missing.
+        if !builder.config.dry_run() {
+            builder.create_dir(&plain_dst_src.join("src/gcc"));
+            t!(std::fs::write(
+                plain_dst_src.join("src/gcc/notice.txt"),
+                "The GCC source code is not included due to unclear licensing implications\n"
+            ));
+        }
 
         // Copy the files normally
         for item in &src_files {
@@ -1050,19 +1055,6 @@ impl Step for PlainSourceTarball {
         if builder.config.dist_vendor {
             builder.require_and_update_all_submodules();
 
-            // Vendor all Cargo dependencies
-            let mut cmd = command(&builder.initial_cargo);
-            cmd.arg("vendor").arg("--versioned-dirs");
-
-            for (p, _) in default_paths_to_vendor(builder) {
-                cmd.arg("--sync").arg(p);
-            }
-
-            cmd
-                // Will read the libstd Cargo.toml which uses the unstable `public-dependency` feature.
-                .env("RUSTC_BOOTSTRAP", "1")
-                .current_dir(plain_dst_src);
-
             // Vendor packages that are required by opt-dist to collect PGO profiles.
             let pkgs_for_pgo_training = build_helper::LLVM_PGO_CRATES
                 .iter()
@@ -1074,15 +1066,18 @@ impl Step for PlainSourceTarball {
                     manifest_path.push("Cargo.toml");
                     manifest_path
                 });
-            for manifest_path in pkgs_for_pgo_training {
-                cmd.arg("--sync").arg(manifest_path);
-            }
 
-            let config = cmd.run_capture(builder).stdout();
+            // Vendor all Cargo dependencies
+            let vendor = builder.ensure(Vendor {
+                sync_args: pkgs_for_pgo_training.collect(),
+                versioned_dirs: true,
+                root_dir: plain_dst_src.into(),
+                output_dir: VENDOR_DIR.into(),
+            });
 
             let cargo_config_dir = plain_dst_src.join(".cargo");
             builder.create_dir(&cargo_config_dir);
-            builder.create(&cargo_config_dir.join("config.toml"), &config);
+            builder.create(&cargo_config_dir.join("config.toml"), &vendor.config);
         }
 
         // Delete extraneous directories
@@ -1121,11 +1116,7 @@ impl Step for Cargo {
 
     fn make_run(run: RunConfig<'_>) {
         run.builder.ensure(Cargo {
-            compiler: run.builder.compiler_for(
-                run.builder.top_stage,
-                run.builder.config.build,
-                run.target,
-            ),
+            compiler: run.builder.compiler(run.builder.top_stage, run.builder.config.build),
             target: run.target,
         });
     }
@@ -1142,7 +1133,7 @@ impl Step for Cargo {
         let mut tarball = Tarball::new(builder, "cargo", &target.triple);
         tarball.set_overlay(OverlayKind::Cargo);
 
-        tarball.add_file(cargo, "bin", 0o755);
+        tarball.add_file(cargo.tool_path, "bin", 0o755);
         tarball.add_file(etc.join("_cargo"), "share/zsh/site-functions", 0o644);
         tarball.add_renamed_file(etc.join("cargo.bashcomp.sh"), "etc/bash_completion.d", "cargo");
         tarball.add_dir(etc.join("man"), "share/man/man1");
@@ -1170,11 +1161,7 @@ impl Step for Rls {
 
     fn make_run(run: RunConfig<'_>) {
         run.builder.ensure(Rls {
-            compiler: run.builder.compiler_for(
-                run.builder.top_stage,
-                run.builder.config.build,
-                run.target,
-            ),
+            compiler: run.builder.compiler(run.builder.top_stage, run.builder.config.build),
             target: run.target,
         });
     }
@@ -1188,7 +1175,7 @@ impl Step for Rls {
         let mut tarball = Tarball::new(builder, "rls", &target.triple);
         tarball.set_overlay(OverlayKind::Rls);
         tarball.is_preview(true);
-        tarball.add_file(rls, "bin", 0o755);
+        tarball.add_file(rls.tool_path, "bin", 0o755);
         tarball.add_legal_and_readme_to("share/doc/rls");
         Some(tarball.generate())
     }
@@ -1212,11 +1199,7 @@ impl Step for RustAnalyzer {
 
     fn make_run(run: RunConfig<'_>) {
         run.builder.ensure(RustAnalyzer {
-            compiler: run.builder.compiler_for(
-                run.builder.top_stage,
-                run.builder.config.build,
-                run.target,
-            ),
+            compiler: run.builder.compiler(run.builder.top_stage, run.builder.config.build),
             target: run.target,
         });
     }
@@ -1230,7 +1213,7 @@ impl Step for RustAnalyzer {
         let mut tarball = Tarball::new(builder, "rust-analyzer", &target.triple);
         tarball.set_overlay(OverlayKind::RustAnalyzer);
         tarball.is_preview(true);
-        tarball.add_file(rust_analyzer, "bin", 0o755);
+        tarball.add_file(rust_analyzer.tool_path, "bin", 0o755);
         tarball.add_legal_and_readme_to("share/doc/rust-analyzer");
         Some(tarball.generate())
     }
@@ -1254,11 +1237,7 @@ impl Step for Clippy {
 
     fn make_run(run: RunConfig<'_>) {
         run.builder.ensure(Clippy {
-            compiler: run.builder.compiler_for(
-                run.builder.top_stage,
-                run.builder.config.build,
-                run.target,
-            ),
+            compiler: run.builder.compiler(run.builder.top_stage, run.builder.config.build),
             target: run.target,
         });
     }
@@ -1276,8 +1255,8 @@ impl Step for Clippy {
         let mut tarball = Tarball::new(builder, "clippy", &target.triple);
         tarball.set_overlay(OverlayKind::Clippy);
         tarball.is_preview(true);
-        tarball.add_file(clippy, "bin", 0o755);
-        tarball.add_file(cargoclippy, "bin", 0o755);
+        tarball.add_file(clippy.tool_path, "bin", 0o755);
+        tarball.add_file(cargoclippy.tool_path, "bin", 0o755);
         tarball.add_legal_and_readme_to("share/doc/clippy");
         Some(tarball.generate())
     }
@@ -1301,11 +1280,7 @@ impl Step for Miri {
 
     fn make_run(run: RunConfig<'_>) {
         run.builder.ensure(Miri {
-            compiler: run.builder.compiler_for(
-                run.builder.top_stage,
-                run.builder.config.build,
-                run.target,
-            ),
+            compiler: run.builder.compiler(run.builder.top_stage, run.builder.config.build),
             target: run.target,
         });
     }
@@ -1326,8 +1301,8 @@ impl Step for Miri {
         let mut tarball = Tarball::new(builder, "miri", &target.triple);
         tarball.set_overlay(OverlayKind::Miri);
         tarball.is_preview(true);
-        tarball.add_file(miri, "bin", 0o755);
-        tarball.add_file(cargomiri, "bin", 0o755);
+        tarball.add_file(miri.tool_path, "bin", 0o755);
+        tarball.add_file(cargomiri.tool_path, "bin", 0o755);
         tarball.add_legal_and_readme_to("share/doc/miri");
         Some(tarball.generate())
     }
@@ -1439,11 +1414,7 @@ impl Step for Rustfmt {
 
     fn make_run(run: RunConfig<'_>) {
         run.builder.ensure(Rustfmt {
-            compiler: run.builder.compiler_for(
-                run.builder.top_stage,
-                run.builder.config.build,
-                run.target,
-            ),
+            compiler: run.builder.compiler(run.builder.top_stage, run.builder.config.build),
             target: run.target,
         });
     }
@@ -1457,8 +1428,8 @@ impl Step for Rustfmt {
         let mut tarball = Tarball::new(builder, "rustfmt", &target.triple);
         tarball.set_overlay(OverlayKind::Rustfmt);
         tarball.is_preview(true);
-        tarball.add_file(rustfmt, "bin", 0o755);
-        tarball.add_file(cargofmt, "bin", 0o755);
+        tarball.add_file(rustfmt.tool_path, "bin", 0o755);
+        tarball.add_file(cargofmt.tool_path, "bin", 0o755);
         tarball.add_legal_and_readme_to("share/doc/rustfmt");
         Some(tarball.generate())
     }
@@ -1493,7 +1464,7 @@ impl Step for Extended {
     fn run(self, builder: &Builder<'_>) {
         let target = self.target;
         let stage = self.stage;
-        let compiler = builder.compiler_for(self.stage, self.host, self.target);
+        let compiler = builder.compiler(self.stage, self.host);
 
         builder.info(&format!("Dist extended stage{} ({})", compiler.stage, target));
 
@@ -2029,6 +2000,15 @@ fn install_llvm_file(
 /// Maybe add LLVM object files to the given destination lib-dir. Allows either static or dynamic linking.
 ///
 /// Returns whether the files were actually copied.
+#[cfg_attr(
+    feature = "tracing",
+    instrument(
+        level = "trace",
+        name = "maybe_install_llvm",
+        skip_all,
+        fields(target = ?target, dst_libdir = ?dst_libdir, install_symlink = install_symlink),
+    ),
+)]
 fn maybe_install_llvm(
     builder: &Builder<'_>,
     target: TargetSelection,
@@ -2052,6 +2032,7 @@ fn maybe_install_llvm(
     // If the LLVM is coming from ourselves (just from CI) though, we
     // still want to install it, as it otherwise won't be available.
     if builder.is_system_llvm(target) {
+        trace!("system LLVM requested, no install");
         return false;
     }
 
@@ -2070,6 +2051,7 @@ fn maybe_install_llvm(
     } else if let llvm::LlvmBuildStatus::AlreadyBuilt(llvm::LlvmResult { llvm_config, .. }) =
         llvm::prebuilt_llvm_config(builder, target, true)
     {
+        trace!("LLVM already built, installing LLVM files");
         let mut cmd = command(llvm_config);
         cmd.arg("--libfiles");
         builder.verbose(|| println!("running {cmd:?}"));
@@ -2092,6 +2074,19 @@ fn maybe_install_llvm(
 }
 
 /// Maybe add libLLVM.so to the target lib-dir for linking.
+#[cfg_attr(
+    feature = "tracing",
+    instrument(
+        level = "trace",
+        name = "maybe_install_llvm_target",
+        skip_all,
+        fields(
+            llvm_link_shared = ?builder.llvm_link_shared(),
+            target = ?target,
+            sysroot = ?sysroot,
+        ),
+    ),
+)]
 pub fn maybe_install_llvm_target(builder: &Builder<'_>, target: TargetSelection, sysroot: &Path) {
     let dst_libdir = sysroot.join("lib/rustlib").join(target).join("lib");
     // We do not need to copy LLVM files into the sysroot if it is not
@@ -2103,6 +2098,19 @@ pub fn maybe_install_llvm_target(builder: &Builder<'_>, target: TargetSelection,
 }
 
 /// Maybe add libLLVM.so to the runtime lib-dir for rustc itself.
+#[cfg_attr(
+    feature = "tracing",
+    instrument(
+        level = "trace",
+        name = "maybe_install_llvm_runtime",
+        skip_all,
+        fields(
+            llvm_link_shared = ?builder.llvm_link_shared(),
+            target = ?target,
+            sysroot = ?sysroot,
+        ),
+    ),
+)]
 pub fn maybe_install_llvm_runtime(builder: &Builder<'_>, target: TargetSelection, sysroot: &Path) {
     let dst_libdir =
         sysroot.join(builder.sysroot_libdir_relative(Compiler { stage: 1, host: target }));
@@ -2220,11 +2228,7 @@ impl Step for LlvmBitcodeLinker {
 
     fn make_run(run: RunConfig<'_>) {
         run.builder.ensure(LlvmBitcodeLinker {
-            compiler: run.builder.compiler_for(
-                run.builder.top_stage,
-                run.builder.config.build,
-                run.target,
-            ),
+            compiler: run.builder.compiler(run.builder.top_stage, run.builder.config.build),
             target: run.target,
         });
     }
@@ -2243,7 +2247,7 @@ impl Step for LlvmBitcodeLinker {
         tarball.set_overlay(OverlayKind::LlvmBitcodeLinker);
         tarball.is_preview(true);
 
-        tarball.add_file(llbc_linker, self_contained_bin_dir, 0o755);
+        tarball.add_file(llbc_linker.tool_path, self_contained_bin_dir, 0o755);
 
         Some(tarball.generate())
     }
diff --git a/src/bootstrap/src/core/build_steps/doc.rs b/src/bootstrap/src/core/build_steps/doc.rs
index dedcc139ae1..aee56fe78e2 100644
--- a/src/bootstrap/src/core/build_steps/doc.rs
+++ b/src/bootstrap/src/core/build_steps/doc.rs
@@ -572,14 +572,15 @@ impl Step for Std {
 
     fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
         let builder = run.builder;
-        run.crate_or_deps("sysroot")
-            .path("library")
-            .alias("core")
-            .default_condition(builder.config.docs)
+        run.crate_or_deps("sysroot").path("library").default_condition(builder.config.docs)
     }
 
     fn make_run(run: RunConfig<'_>) {
         let crates = compile::std_crates_for_run_make(&run);
+        let target_is_no_std = run.builder.no_std(run.target).unwrap_or(false);
+        if crates.is_empty() && target_is_no_std {
+            return;
+        }
         run.builder.ensure(Std {
             stage: run.builder.top_stage,
             target: run.target,
diff --git a/src/bootstrap/src/core/build_steps/llvm.rs b/src/bootstrap/src/core/build_steps/llvm.rs
index 25e5e9e6eb9..40d701f22c1 100644
--- a/src/bootstrap/src/core/build_steps/llvm.rs
+++ b/src/bootstrap/src/core/build_steps/llvm.rs
@@ -16,6 +16,8 @@ use std::{env, fs};
 
 use build_helper::ci::CiEnv;
 use build_helper::git::get_closest_merge_commit;
+#[cfg(feature = "tracing")]
+use tracing::instrument;
 
 use crate::core::builder::{Builder, RunConfig, ShouldRun, Step};
 use crate::core::config::{Config, TargetSelection};
@@ -24,7 +26,7 @@ use crate::utils::exec::command;
 use crate::utils::helpers::{
     self, exe, get_clang_cl_resource_dir, t, unhashed_basename, up_to_date,
 };
-use crate::{CLang, GitRepo, Kind};
+use crate::{CLang, GitRepo, Kind, trace};
 
 #[derive(Clone)]
 pub struct LlvmResult {
@@ -54,6 +56,14 @@ impl LlvmBuildStatus {
             LlvmBuildStatus::ShouldBuild(_) => true,
         }
     }
+
+    #[cfg(test)]
+    pub fn llvm_result(&self) -> &LlvmResult {
+        match self {
+            LlvmBuildStatus::AlreadyBuilt(res) => res,
+            LlvmBuildStatus::ShouldBuild(meta) => &meta.res,
+        }
+    }
 }
 
 /// Linker flags to pass to LLVM's CMake invocation.
@@ -120,9 +130,19 @@ pub fn prebuilt_llvm_config(
     let root = "src/llvm-project/llvm";
     let out_dir = builder.llvm_out(target);
 
-    let mut llvm_config_ret_dir = builder.llvm_out(builder.config.build);
-    llvm_config_ret_dir.push("bin");
-    let build_llvm_config = llvm_config_ret_dir.join(exe("llvm-config", builder.config.build));
+    let build_llvm_config = if let Some(build_llvm_config) = builder
+        .config
+        .target_config
+        .get(&builder.config.build)
+        .and_then(|config| config.llvm_config.clone())
+    {
+        build_llvm_config
+    } else {
+        let mut llvm_config_ret_dir = builder.llvm_out(builder.config.build);
+        llvm_config_ret_dir.push("bin");
+        llvm_config_ret_dir.join(exe("llvm-config", builder.config.build))
+    };
+
     let llvm_cmake_dir = out_dir.join("lib/cmake/llvm");
     let res = LlvmResult { llvm_config: build_llvm_config, llvm_cmake_dir };
 
@@ -335,7 +355,7 @@ impl Step for Llvm {
         let llvm_targets = match &builder.config.llvm_targets {
             Some(s) => s,
             None => {
-                "AArch64;ARM;BPF;Hexagon;LoongArch;MSP430;Mips;NVPTX;PowerPC;RISCV;\
+                "AArch64;AMDGPU;ARM;BPF;Hexagon;LoongArch;MSP430;Mips;NVPTX;PowerPC;RISCV;\
                      Sparc;SystemZ;WebAssembly;X86"
             }
         };
@@ -498,7 +518,7 @@ impl Step for Llvm {
         }
 
         // https://llvm.org/docs/HowToCrossCompileLLVM.html
-        if target != builder.config.build {
+        if !builder.is_builder_target(target) {
             let LlvmResult { llvm_config, .. } =
                 builder.ensure(Llvm { target: builder.config.build });
             if !builder.config.dry_run() {
@@ -553,10 +573,7 @@ impl Step for Llvm {
 
         // Helper to find the name of LLVM's shared library on darwin and linux.
         let find_llvm_lib_name = |extension| {
-            let version =
-                command(&res.llvm_config).arg("--version").run_capture_stdout(builder).stdout();
-            let major = version.split('.').next().unwrap();
-
+            let major = get_llvm_version_major(builder, &res.llvm_config);
             match &llvm_version_suffix {
                 Some(version_suffix) => format!("libLLVM-{major}{version_suffix}.{extension}"),
                 None => format!("libLLVM-{major}.{extension}"),
@@ -606,12 +623,22 @@ impl Step for Llvm {
     }
 }
 
+pub fn get_llvm_version(builder: &Builder<'_>, llvm_config: &Path) -> String {
+    command(llvm_config).arg("--version").run_capture_stdout(builder).stdout().trim().to_owned()
+}
+
+pub fn get_llvm_version_major(builder: &Builder<'_>, llvm_config: &Path) -> u8 {
+    let version = get_llvm_version(builder, llvm_config);
+    let major_str = version.split_once('.').expect("Failed to parse LLVM version").0;
+    major_str.parse().unwrap()
+}
+
 fn check_llvm_version(builder: &Builder<'_>, llvm_config: &Path) {
     if builder.config.dry_run() {
         return;
     }
 
-    let version = command(llvm_config).arg("--version").run_capture_stdout(builder).stdout();
+    let version = get_llvm_version(builder, llvm_config);
     let mut parts = version.split('.').take(2).filter_map(|s| s.parse::<u32>().ok());
     if let (Some(major), Some(_minor)) = (parts.next(), parts.next()) {
         if major >= 18 {
@@ -643,7 +670,7 @@ fn configure_cmake(
     }
     cfg.target(&target.triple).host(&builder.config.build.triple);
 
-    if target != builder.config.build {
+    if !builder.is_builder_target(target) {
         cfg.define("CMAKE_CROSSCOMPILING", "True");
 
         if target.contains("netbsd") {
@@ -761,9 +788,15 @@ fn configure_cmake(
     }
 
     cfg.build_arg("-j").build_arg(builder.jobs().to_string());
+    // FIXME(madsmtm): Allow `cmake-rs` to select flags by itself by passing
+    // our flags via `.cflag`/`.cxxflag` instead.
+    //
+    // Needs `suppressed_compiler_flag_prefixes` to be gone, and hence
+    // https://github.com/llvm/llvm-project/issues/88780 to be fixed.
     let mut cflags: OsString = builder
-        .cflags(target, GitRepo::Llvm, CLang::C)
+        .cc_handled_clags(target, CLang::C)
         .into_iter()
+        .chain(builder.cc_unhandled_cflags(target, GitRepo::Llvm, CLang::C))
         .filter(|flag| {
             !suppressed_compiler_flag_prefixes
                 .iter()
@@ -782,8 +815,9 @@ fn configure_cmake(
     }
     cfg.define("CMAKE_C_FLAGS", cflags);
     let mut cxxflags: OsString = builder
-        .cflags(target, GitRepo::Llvm, CLang::Cxx)
+        .cc_handled_clags(target, CLang::Cxx)
         .into_iter()
+        .chain(builder.cc_unhandled_cflags(target, GitRepo::Llvm, CLang::Cxx))
         .filter(|flag| {
             !suppressed_compiler_flag_prefixes
                 .iter()
@@ -902,6 +936,15 @@ impl Step for Enzyme {
     }
 
     /// Compile Enzyme for `target`.
+    #[cfg_attr(
+        feature = "tracing",
+        instrument(
+            level = "debug",
+            name = "Enzyme::run",
+            skip_all,
+            fields(target = ?self.target),
+        ),
+    )]
     fn run(self, builder: &Builder<'_>) -> PathBuf {
         builder.require_submodule(
             "src/tools/enzyme",
@@ -927,7 +970,9 @@ impl Step for Enzyme {
         let out_dir = builder.enzyme_out(target);
         let stamp = BuildStamp::new(&out_dir).with_prefix("enzyme").add_stamp(smart_stamp_hash);
 
+        trace!("checking build stamp to see if we need to rebuild enzyme artifacts");
         if stamp.is_up_to_date() {
+            trace!(?out_dir, "enzyme build artifacts are up to date");
             if stamp.stamp().is_empty() {
                 builder.info(
                     "Could not determine the Enzyme submodule commit hash. \
@@ -941,6 +986,7 @@ impl Step for Enzyme {
             return out_dir;
         }
 
+        trace!(?target, "(re)building enzyme artifacts");
         builder.info(&format!("Building Enzyme for {}", target));
         t!(stamp.remove());
         let _time = helpers::timeit(builder);
@@ -950,18 +996,17 @@ impl Step for Enzyme {
             .config
             .update_submodule(Path::new("src").join("tools").join("enzyme").to_str().unwrap());
         let mut cfg = cmake::Config::new(builder.src.join("src/tools/enzyme/enzyme/"));
-        // FIXME(ZuseZ4): Find a nicer way to use Enzyme Debug builds
-        //cfg.profile("Debug");
-        //cfg.define("CMAKE_BUILD_TYPE", "Debug");
         configure_cmake(builder, target, &mut cfg, true, LdFlags::default(), &[]);
 
         // Re-use the same flags as llvm to control the level of debug information
-        // generated for lld.
+        // generated by Enzyme.
+        // FIXME(ZuseZ4): Find a nicer way to use Enzyme Debug builds.
         let profile = match (builder.config.llvm_optimize, builder.config.llvm_release_debuginfo) {
             (false, _) => "Debug",
             (true, false) => "Release",
             (true, true) => "RelWithDebInfo",
         };
+        trace!(?profile);
 
         cfg.out_dir(&out_dir)
             .profile(profile)
@@ -1085,7 +1130,7 @@ impl Step for Lld {
             .define("LLVM_CMAKE_DIR", llvm_cmake_dir)
             .define("LLVM_INCLUDE_TESTS", "OFF");
 
-        if target != builder.config.build {
+        if !builder.is_builder_target(target) {
             // Use the host llvm-tblgen binary.
             cfg.define(
                 "LLVM_TABLEGEN_EXE",
diff --git a/src/bootstrap/src/core/build_steps/perf.rs b/src/bootstrap/src/core/build_steps/perf.rs
index 98c63a41e76..6962001fdc2 100644
--- a/src/bootstrap/src/core/build_steps/perf.rs
+++ b/src/bootstrap/src/core/build_steps/perf.rs
@@ -166,7 +166,7 @@ Consider setting `rust.debuginfo-level = 1` in `config.toml`."#);
     let results_dir = rustc_perf_dir.join("results");
     builder.create_dir(&results_dir);
 
-    let mut cmd = command(collector);
+    let mut cmd = command(collector.tool_path);
 
     // We need to set the working directory to `src/tools/rustc-perf`, so that it can find the directory
     // with compile-time benchmarks.
diff --git a/src/bootstrap/src/core/build_steps/run.rs b/src/bootstrap/src/core/build_steps/run.rs
index 167b8a5b168..fea8232296e 100644
--- a/src/bootstrap/src/core/build_steps/run.rs
+++ b/src/bootstrap/src/core/build_steps/run.rs
@@ -9,6 +9,7 @@ use crate::Mode;
 use crate::core::build_steps::dist::distdir;
 use crate::core::build_steps::test;
 use crate::core::build_steps::tool::{self, SourceType, Tool};
+use crate::core::build_steps::vendor::{Vendor, default_paths_to_vendor};
 use crate::core::builder::{Builder, Kind, RunConfig, ShouldRun, Step};
 use crate::core::config::TargetSelection;
 use crate::core::config::flags::get_completion;
@@ -125,11 +126,7 @@ impl Step for Miri {
 
         // This compiler runs on the host, we'll just use it for the target.
         let target_compiler = builder.compiler(stage, host);
-        // Similar to `compile::Assemble`, build with the previous stage's compiler. Otherwise
-        // we'd have stageN/bin/rustc and stageN/bin/rustdoc be effectively different stage
-        // compilers, which isn't what we want. Rustdoc should be linked in the same way as the
-        // rustc compiler it's paired with, so it must be built with the previous stage compiler.
-        let host_compiler = builder.compiler(stage - 1, host);
+        let host_compiler = tool::get_tool_rustc_compiler(builder, target_compiler);
 
         // Get a target sysroot for Miri.
         let miri_sysroot = test::Miri::build_miri_sysroot(builder, target_compiler, target);
@@ -212,11 +209,39 @@ impl Step for GenerateCopyright {
         let dest = builder.out.join("COPYRIGHT.html");
         let dest_libstd = builder.out.join("COPYRIGHT-library.html");
 
+        let paths_to_vendor = default_paths_to_vendor(builder);
+        for (_, submodules) in &paths_to_vendor {
+            for submodule in submodules {
+                builder.build.require_submodule(submodule, None);
+            }
+        }
+        let cargo_manifests = paths_to_vendor
+            .into_iter()
+            .map(|(path, _submodules)| path.to_str().unwrap().to_string())
+            .inspect(|path| assert!(!path.contains(','), "{path} contains a comma in its name"))
+            .collect::<Vec<_>>()
+            .join(",");
+
+        let vendored_sources = if let Some(path) = builder.vendored_crates_path() {
+            path
+        } else {
+            let cache_dir = builder.out.join("tmp").join("generate-copyright-vendor");
+            builder.ensure(Vendor {
+                sync_args: Vec::new(),
+                versioned_dirs: true,
+                root_dir: builder.src.clone(),
+                output_dir: cache_dir.clone(),
+            });
+            cache_dir
+        };
+
         let mut cmd = builder.tool_cmd(Tool::GenerateCopyright);
+        cmd.env("CARGO_MANIFESTS", &cargo_manifests);
         cmd.env("LICENSE_METADATA", &license_metadata);
         cmd.env("DEST", &dest);
         cmd.env("DEST_LIBSTD", &dest_libstd);
-        cmd.env("OUT_DIR", &builder.out);
+        cmd.env("SRC_DIR", &builder.src);
+        cmd.env("VENDOR_DIR", &vendored_sources);
         cmd.env("CARGO", &builder.initial_cargo);
         // it is important that generate-copyright runs from the root of the
         // source tree, because it uses relative paths
diff --git a/src/bootstrap/src/core/build_steps/setup.rs b/src/bootstrap/src/core/build_steps/setup.rs
index fbd0dc3ec30..f25dfaab0f1 100644
--- a/src/bootstrap/src/core/build_steps/setup.rs
+++ b/src/bootstrap/src/core/build_steps/setup.rs
@@ -523,19 +523,31 @@ undesirable, simply delete the `pre-push` file from .git/hooks."
 /// Handles editor-specific setup differences
 #[derive(Clone, Debug, Eq, PartialEq)]
 enum EditorKind {
-    Vscode,
-    Vim,
     Emacs,
     Helix,
+    Vim,
+    VsCode,
+    Zed,
 }
 
 impl EditorKind {
+    // Used in `./tests.rs`.
+    #[allow(dead_code)]
+    pub const ALL: &[EditorKind] = &[
+        EditorKind::Emacs,
+        EditorKind::Helix,
+        EditorKind::Vim,
+        EditorKind::VsCode,
+        EditorKind::Zed,
+    ];
+
     fn prompt_user() -> io::Result<Option<EditorKind>> {
         let prompt_str = "Available editors:
-1. vscode
-2. vim
-3. emacs
-4. helix
+1. Emacs
+2. Helix
+3. Vim
+4. VS Code
+5. Zed
 
 Select which editor you would like to set up [default: None]: ";
 
@@ -543,28 +555,41 @@ Select which editor you would like to set up [default: None]: ";
         loop {
             print!("{}", prompt_str);
             io::stdout().flush()?;
-            input.clear();
             io::stdin().read_line(&mut input)?;
-            match input.trim().to_lowercase().as_str() {
-                "1" | "vscode" => return Ok(Some(EditorKind::Vscode)),
-                "2" | "vim" => return Ok(Some(EditorKind::Vim)),
-                "3" | "emacs" => return Ok(Some(EditorKind::Emacs)),
-                "4" | "helix" => return Ok(Some(EditorKind::Helix)),
-                "" => return Ok(None),
+
+            let mut modified_input = input.to_lowercase();
+            modified_input.retain(|ch| !ch.is_whitespace());
+            match modified_input.as_str() {
+                "1" | "emacs" => return Ok(Some(EditorKind::Emacs)),
+                "2" | "helix" => return Ok(Some(EditorKind::Helix)),
+                "3" | "vim" => return Ok(Some(EditorKind::Vim)),
+                "4" | "vscode" => return Ok(Some(EditorKind::VsCode)),
+                "5" | "zed" => return Ok(Some(EditorKind::Zed)),
+                "" | "none" => return Ok(None),
                 _ => {
                     eprintln!("ERROR: unrecognized option '{}'", input.trim());
                     eprintln!("NOTE: press Ctrl+C to exit");
                 }
-            };
+            }
+
+            input.clear();
         }
     }
 
     /// A list of historical hashes of each LSP settings file
     /// New entries should be appended whenever this is updated so we can detect
     /// outdated vs. user-modified settings files.
-    fn hashes(&self) -> Vec<&str> {
+    fn hashes(&self) -> &'static [&'static str] {
         match self {
-            EditorKind::Vscode | EditorKind::Vim => vec![
+            EditorKind::Emacs => &[
+                "51068d4747a13732440d1a8b8f432603badb1864fa431d83d0fd4f8fa57039e0",
+                "d29af4d949bbe2371eac928a3c31cf9496b1701aa1c45f11cd6c759865ad5c45",
+            ],
+            EditorKind::Helix => &[
+                "2d3069b8cf1b977e5d4023965eb6199597755e6c96c185ed5f2854f98b83d233",
+                "6736d61409fbebba0933afd2e4c44ff2f97c1cb36cf0299a7f4a7819b8775040",
+            ],
+            EditorKind::Vim | EditorKind::VsCode => &[
                 "ea67e259dedf60d4429b6c349a564ffcd1563cf41c920a856d1f5b16b4701ac8",
                 "56e7bf011c71c5d81e0bf42e84938111847a810eee69d906bba494ea90b51922",
                 "af1b5efe196aed007577899db9dae15d6dbc923d6fa42fa0934e68617ba9bbe0",
@@ -576,12 +601,8 @@ Select which editor you would like to set up [default: None]: ";
                 "4eecb58a2168b252077369da446c30ed0e658301efe69691979d1ef0443928f4",
                 "c394386e6133bbf29ffd32c8af0bb3d4aac354cba9ee051f29612aa9350f8f8d",
             ],
-            EditorKind::Emacs => vec![
-                "51068d4747a13732440d1a8b8f432603badb1864fa431d83d0fd4f8fa57039e0",
-                "d29af4d949bbe2371eac928a3c31cf9496b1701aa1c45f11cd6c759865ad5c45",
-            ],
-            EditorKind::Helix => {
-                vec!["2d3069b8cf1b977e5d4023965eb6199597755e6c96c185ed5f2854f98b83d233"]
+            EditorKind::Zed => {
+                &["bbce727c269d1bd0c98afef4d612eb4ce27aea3c3a8968c5f10b31affbc40b6c"]
             }
         }
     }
@@ -592,29 +613,31 @@ Select which editor you would like to set up [default: None]: ";
 
     fn settings_short_path(&self) -> PathBuf {
         self.settings_folder().join(match self {
-            EditorKind::Vscode => "settings.json",
-            EditorKind::Vim => "coc-settings.json",
             EditorKind::Emacs => ".dir-locals.el",
             EditorKind::Helix => "languages.toml",
+            EditorKind::Vim => "coc-settings.json",
+            EditorKind::VsCode | EditorKind::Zed => "settings.json",
         })
     }
 
     fn settings_folder(&self) -> PathBuf {
         match self {
-            EditorKind::Vscode => PathBuf::from(".vscode"),
-            EditorKind::Vim => PathBuf::from(".vim"),
             EditorKind::Emacs => PathBuf::new(),
             EditorKind::Helix => PathBuf::from(".helix"),
+            EditorKind::Vim => PathBuf::from(".vim"),
+            EditorKind::VsCode => PathBuf::from(".vscode"),
+            EditorKind::Zed => PathBuf::from(".zed"),
         }
     }
 
-    fn settings_template(&self) -> &str {
+    fn settings_template(&self) -> &'static str {
         match self {
-            EditorKind::Vscode | EditorKind::Vim => {
-                include_str!("../../../../etc/rust_analyzer_settings.json")
-            }
             EditorKind::Emacs => include_str!("../../../../etc/rust_analyzer_eglot.el"),
             EditorKind::Helix => include_str!("../../../../etc/rust_analyzer_helix.toml"),
+            EditorKind::Vim | EditorKind::VsCode => {
+                include_str!("../../../../etc/rust_analyzer_settings.json")
+            }
+            EditorKind::Zed => include_str!("../../../../etc/rust_analyzer_zed.json"),
         }
     }
 
diff --git a/src/bootstrap/src/core/build_steps/setup/tests.rs b/src/bootstrap/src/core/build_steps/setup/tests.rs
index f3d4b6aa4db..e8f83ff75e4 100644
--- a/src/bootstrap/src/core/build_steps/setup/tests.rs
+++ b/src/bootstrap/src/core/build_steps/setup/tests.rs
@@ -5,13 +5,16 @@ use crate::utils::helpers::hex_encode;
 
 #[test]
 fn check_matching_settings_hash() {
-    let editor = EditorKind::Vscode;
-    let mut hasher = sha2::Sha256::new();
-    hasher.update(&editor.settings_template());
-    let hash = hex_encode(hasher.finalize().as_slice());
-    assert_eq!(
-        &hash,
-        editor.hashes().last().unwrap(),
-        "Update `EditorKind::hashes()` with the new hash of `src/etc/rust_analyzer_settings.json`"
-    );
+    for editor in EditorKind::ALL {
+        let mut hasher = sha2::Sha256::new();
+        hasher.update(&editor.settings_template());
+        let hash = hex_encode(hasher.finalize().as_slice());
+        assert_eq!(
+            &hash,
+            editor.hashes().last().unwrap(),
+            "Update `EditorKind::hashes()` with the new hash of `{}` for `EditorKind::{:?}`",
+            editor.settings_template(),
+            editor,
+        );
+    }
 }
diff --git a/src/bootstrap/src/core/build_steps/test.rs b/src/bootstrap/src/core/build_steps/test.rs
index 825e5452f0e..02d6f205d80 100644
--- a/src/bootstrap/src/core/build_steps/test.rs
+++ b/src/bootstrap/src/core/build_steps/test.rs
@@ -12,6 +12,7 @@ use clap_complete::shells;
 
 use crate::core::build_steps::compile::run_cargo;
 use crate::core::build_steps::doc::DocumentationFormat;
+use crate::core::build_steps::llvm::get_llvm_version;
 use crate::core::build_steps::synthetic_targets::MirOptPanicAbortSyntheticTarget;
 use crate::core::build_steps::tool::{self, SourceType, Tool};
 use crate::core::build_steps::toolstate::ToolState;
@@ -262,7 +263,7 @@ impl Step for Cargotest {
 
         let _time = helpers::timeit(builder);
         let mut cmd = builder.tool_cmd(Tool::CargoTest);
-        cmd.arg(&cargo)
+        cmd.arg(&cargo.tool_path)
             .arg(&out_dir)
             .args(builder.config.test_args())
             .env("RUSTC", builder.rustc(compiler))
@@ -292,14 +293,31 @@ impl Step for Cargo {
     }
 
     fn make_run(run: RunConfig<'_>) {
-        run.builder.ensure(Cargo { stage: run.builder.top_stage, host: run.target });
+        // If stage is explicitly set or not lower than 2, keep it. Otherwise, make sure it's at least 2
+        // as tests for this step don't work with a lower stage.
+        let stage = if run.builder.config.is_explicit_stage() || run.builder.top_stage >= 2 {
+            run.builder.top_stage
+        } else {
+            2
+        };
+
+        run.builder.ensure(Cargo { stage, host: run.target });
     }
 
     /// Runs `cargo test` for `cargo` packaged with Rust.
     fn run(self, builder: &Builder<'_>) {
-        let compiler = builder.compiler(self.stage, self.host);
+        let stage = self.stage;
+
+        if stage < 2 {
+            eprintln!("WARNING: cargo tests on stage {stage} may not behave well.");
+            eprintln!("HELP: consider using stage 2");
+        }
+
+        let compiler = builder.compiler(stage, self.host);
+
+        let cargo = builder.ensure(tool::Cargo { compiler, target: self.host });
+        let compiler = cargo.build_compiler;
 
-        builder.ensure(tool::Cargo { compiler, target: self.host });
         let cargo = tool::prepare_tool_cargo(
             builder,
             compiler,
@@ -332,7 +350,7 @@ impl Step for Cargo {
                 crates: vec!["cargo".into()],
                 target: self.host.triple.to_string(),
                 host: self.host.triple.to_string(),
-                stage: self.stage,
+                stage,
             },
             builder,
         );
@@ -366,6 +384,7 @@ impl Step for RustAnalyzer {
         let stage = self.stage;
         let host = self.host;
         let compiler = builder.compiler(stage, host);
+        let compiler = tool::get_tool_rustc_compiler(builder, compiler);
 
         // We don't need to build the whole Rust Analyzer for the proc-macro-srv test suite,
         // but we do need the standard library to be present.
@@ -426,7 +445,8 @@ impl Step for Rustfmt {
         let host = self.host;
         let compiler = builder.compiler(stage, host);
 
-        builder.ensure(tool::Rustfmt { compiler, target: self.host });
+        let tool_result = builder.ensure(tool::Rustfmt { compiler, target: self.host });
+        let compiler = tool_result.build_compiler;
 
         let mut cargo = tool::prepare_tool_cargo(
             builder,
@@ -521,16 +541,11 @@ impl Step for Miri {
 
         // This compiler runs on the host, we'll just use it for the target.
         let target_compiler = builder.compiler(stage, host);
-        // Similar to `compile::Assemble`, build with the previous stage's compiler. Otherwise
-        // we'd have stageN/bin/rustc and stageN/bin/rustdoc be effectively different stage
-        // compilers, which isn't what we want. Rustdoc should be linked in the same way as the
-        // rustc compiler it's paired with, so it must be built with the previous stage compiler.
-        let host_compiler = builder.compiler(stage - 1, host);
 
         // Build our tools.
-        let miri = builder.ensure(tool::Miri { compiler: host_compiler, target: host });
+        let miri = builder.ensure(tool::Miri { compiler: target_compiler, target: host });
         // the ui tests also assume cargo-miri has been built
-        builder.ensure(tool::CargoMiri { compiler: host_compiler, target: host });
+        builder.ensure(tool::CargoMiri { compiler: target_compiler, target: host });
 
         // We also need sysroots, for Miri and for the host (the latter for build scripts).
         // This is for the tests so everything is done with the target compiler.
@@ -541,7 +556,8 @@ impl Step for Miri {
         // Miri has its own "target dir" for ui test dependencies. Make sure it gets cleared when
         // the sysroot gets rebuilt, to avoid "found possibly newer version of crate `std`" errors.
         if !builder.config.dry_run() {
-            let ui_test_dep_dir = builder.stage_out(host_compiler, Mode::ToolStd).join("miri_ui");
+            let ui_test_dep_dir =
+                builder.stage_out(miri.build_compiler, Mode::ToolStd).join("miri_ui");
             // The mtime of `miri_sysroot` changes when the sysroot gets rebuilt (also see
             // <https://github.com/RalfJung/rustc-build-sysroot/commit/10ebcf60b80fe2c3dc765af0ff19fdc0da4b7466>).
             // We can hence use that directly as a signal to clear the ui test dir.
@@ -552,7 +568,7 @@ impl Step for Miri {
         // This is with the Miri crate, so it uses the host compiler.
         let mut cargo = tool::prepare_tool_cargo(
             builder,
-            host_compiler,
+            miri.build_compiler,
             Mode::ToolRustc,
             host,
             Kind::Test,
@@ -570,7 +586,7 @@ impl Step for Miri {
         // miri tests need to know about the stage sysroot
         cargo.env("MIRI_SYSROOT", &miri_sysroot);
         cargo.env("MIRI_HOST_SYSROOT", &host_sysroot);
-        cargo.env("MIRI", &miri);
+        cargo.env("MIRI", &miri.tool_path);
 
         // Set the target.
         cargo.env("MIRI_TEST_TARGET", target.rustc_target_arg());
@@ -733,7 +749,15 @@ impl Step for Clippy {
     }
 
     fn make_run(run: RunConfig<'_>) {
-        run.builder.ensure(Clippy { stage: run.builder.top_stage, host: run.target });
+        // If stage is explicitly set or not lower than 2, keep it. Otherwise, make sure it's at least 2
+        // as tests for this step don't work with a lower stage.
+        let stage = if run.builder.config.is_explicit_stage() || run.builder.top_stage >= 2 {
+            run.builder.top_stage
+        } else {
+            2
+        };
+
+        run.builder.ensure(Clippy { stage, host: run.target });
     }
 
     /// Runs `cargo test` for clippy.
@@ -742,7 +766,13 @@ impl Step for Clippy {
         let host = self.host;
         let compiler = builder.compiler(stage, host);
 
-        builder.ensure(tool::Clippy { compiler, target: self.host });
+        if stage < 2 {
+            eprintln!("WARNING: clippy tests on stage {stage} may not behave well.");
+            eprintln!("HELP: consider using stage 2");
+        }
+
+        let tool_result = builder.ensure(tool::Clippy { compiler, target: self.host });
+        let compiler = tool_result.build_compiler;
         let mut cargo = tool::prepare_tool_cargo(
             builder,
             compiler,
@@ -1233,6 +1263,14 @@ impl Step for RunMakeSupport {
             &[],
         );
 
+        let _guard = builder.msg_tool(
+            Kind::Build,
+            Mode::ToolStd,
+            "run-make-support",
+            self.compiler.stage,
+            &self.compiler.host,
+            &self.target,
+        );
         cargo.into_cmd().run(builder);
 
         let lib_name = "librun_make_support.rlib";
@@ -1648,16 +1686,17 @@ NOTE: if you're sure you want to do this, please open an issue as to why. In the
         // bootstrap compiler.
         // NOTE: Only stage 1 is special cased because we need the rustc_private artifacts to match the
         // running compiler in stage 2 when plugins run.
-        let stage_id = if suite == "ui-fulldeps" && compiler.stage == 1 {
-            // At stage 0 (stage - 1) we are using the beta compiler. Using `self.target` can lead finding
-            // an incorrect compiler path on cross-targets, as the stage 0 beta compiler is always equal
-            // to `build.build` in the configuration.
+        let (stage, stage_id) = if suite == "ui-fulldeps" && compiler.stage == 1 {
+            // At stage 0 (stage - 1) we are using the beta compiler. Using `self.target` can lead
+            // finding an incorrect compiler path on cross-targets, as the stage 0 beta compiler is
+            // always equal to `build.build` in the configuration.
             let build = builder.build.build;
-
             compiler = builder.compiler(compiler.stage - 1, build);
-            format!("stage{}-{}", compiler.stage + 1, build)
+            let test_stage = compiler.stage + 1;
+            (test_stage, format!("stage{}-{}", test_stage, build))
         } else {
-            format!("stage{}-{}", compiler.stage, target)
+            let stage = compiler.stage;
+            (stage, format!("stage{}-{}", stage, target))
         };
 
         if suite.ends_with("fulldeps") {
@@ -1699,6 +1738,9 @@ NOTE: if you're sure you want to do this, please open an issue as to why. In the
         // compiletest currently has... a lot of arguments, so let's just pass all
         // of them!
 
+        cmd.arg("--stage").arg(stage.to_string());
+        cmd.arg("--stage-id").arg(stage_id);
+
         cmd.arg("--compile-lib-path").arg(builder.rustc_libdir(compiler));
         cmd.arg("--run-lib-path").arg(builder.sysroot_target_libdir(compiler, target));
         cmd.arg("--rustc-path").arg(builder.rustc(compiler));
@@ -1715,18 +1757,7 @@ NOTE: if you're sure you want to do this, please open an issue as to why. In the
                 // If we're using `--stage 0`, we should provide the bootstrap cargo.
                 builder.initial_cargo.clone()
             } else {
-                // We need to properly build cargo using the suitable stage compiler.
-
-                let compiler = builder.download_rustc().then_some(compiler).unwrap_or_else(||
-                    // HACK: currently tool stages are off-by-one compared to compiler stages, i.e. if
-                    // you give `tool::Cargo` a stage 1 rustc, it will cause stage 2 rustc to be built
-                    // and produce a cargo built with stage 2 rustc. To fix this, we need to chop off
-                    // the compiler stage by 1 to align with expected `./x test run-make --stage N`
-                    // behavior, i.e. we need to pass `N - 1` compiler stage to cargo. See also Miri
-                    // which does a similar hack.
-                    builder.compiler(builder.top_stage - 1, compiler.host));
-
-                builder.ensure(tool::Cargo { compiler, target: compiler.host })
+                builder.ensure(tool::Cargo { compiler, target: compiler.host }).tool_path
             };
 
             cmd.arg("--cargo-path").arg(cargo_path);
@@ -1747,9 +1778,10 @@ NOTE: if you're sure you want to do this, please open an issue as to why. In the
             // Use the beta compiler for jsondocck
             let json_compiler = compiler.with_stage(0);
             cmd.arg("--jsondocck-path")
-                .arg(builder.ensure(tool::JsonDocCk { compiler: json_compiler, target }));
-            cmd.arg("--jsondoclint-path")
-                .arg(builder.ensure(tool::JsonDocLint { compiler: json_compiler, target }));
+                .arg(builder.ensure(tool::JsonDocCk { compiler: json_compiler, target }).tool_path);
+            cmd.arg("--jsondoclint-path").arg(
+                builder.ensure(tool::JsonDocLint { compiler: json_compiler, target }).tool_path,
+            );
         }
 
         if matches!(mode, "coverage-map" | "coverage-run") {
@@ -1757,7 +1789,9 @@ NOTE: if you're sure you want to do this, please open an issue as to why. In the
             cmd.arg("--coverage-dump-path").arg(coverage_dump);
         }
 
-        cmd.arg("--src-base").arg(builder.src.join("tests").join(suite));
+        cmd.arg("--src-root").arg(&builder.src);
+        cmd.arg("--src-test-suite-root").arg(builder.src.join("tests").join(suite));
+
         cmd.arg("--build-base").arg(testdir(builder, compiler.host).join(suite));
 
         // When top stage is 0, that means that we're testing an externally provided compiler.
@@ -1767,8 +1801,9 @@ NOTE: if you're sure you want to do this, please open an issue as to why. In the
         } else {
             builder.sysroot(compiler).to_path_buf()
         };
+
         cmd.arg("--sysroot-base").arg(sysroot);
-        cmd.arg("--stage-id").arg(stage_id);
+
         cmd.arg("--suite").arg(suite);
         cmd.arg("--mode").arg(mode);
         cmd.arg("--target").arg(target.rustc_target_arg());
@@ -1840,6 +1875,11 @@ NOTE: if you're sure you want to do this, please open an issue as to why. In the
             }
         }
 
+        // FIXME(136096): on macOS, we get linker warnings about duplicate `-lm` flags.
+        if suite == "ui-fulldeps" && target.ends_with("darwin") {
+            flags.push("-Alinker_messages".into());
+        }
+
         let mut hostflags = flags.clone();
         hostflags.push(format!("-Lnative={}", builder.test_helpers_out(compiler.host).display()));
         hostflags.extend(linker_flags(builder, compiler.host, LldThreads::No));
@@ -1847,12 +1887,6 @@ NOTE: if you're sure you want to do this, please open an issue as to why. In the
         let mut targetflags = flags;
         targetflags.push(format!("-Lnative={}", builder.test_helpers_out(target).display()));
 
-        // FIXME: on macOS, we get linker warnings about duplicate `-lm` flags. We should investigate why this happens.
-        if suite == "ui-fulldeps" && target.ends_with("darwin") {
-            hostflags.push("-Alinker_messages".into());
-            targetflags.push("-Alinker_messages".into());
-        }
-
         for flag in hostflags {
             cmd.arg("--host-rustcflags").arg(flag);
         }
@@ -1940,8 +1974,7 @@ NOTE: if you're sure you want to do this, please open an issue as to why. In the
             let llvm::LlvmResult { llvm_config, .. } =
                 builder.ensure(llvm::Llvm { target: builder.config.build });
             if !builder.config.dry_run() {
-                let llvm_version =
-                    command(&llvm_config).arg("--version").run_capture_stdout(builder).stdout();
+                let llvm_version = get_llvm_version(builder, &llvm_config);
                 let llvm_components =
                     command(&llvm_config).arg("--components").run_capture_stdout(builder).stdout();
                 // Remove trailing newline from llvm-config output.
@@ -1964,13 +1997,12 @@ NOTE: if you're sure you want to do this, please open an issue as to why. In the
             if !builder.config.dry_run() && suite.ends_with("fulldeps") {
                 let llvm_libdir =
                     command(&llvm_config).arg("--libdir").run_capture_stdout(builder).stdout();
-                let mut rustflags = env::var("RUSTFLAGS").unwrap_or_default();
-                if target.is_msvc() {
-                    rustflags.push_str(&format!("-Clink-arg=-LIBPATH:{llvm_libdir}"));
+                let link_llvm = if target.is_msvc() {
+                    format!("-Clink-arg=-LIBPATH:{llvm_libdir}")
                 } else {
-                    rustflags.push_str(&format!("-Clink-arg=-L{llvm_libdir}"));
-                }
-                cmd.env("RUSTFLAGS", rustflags);
+                    format!("-Clink-arg=-L{llvm_libdir}")
+                };
+                cmd.arg("--host-rustcflags").arg(link_llvm);
             }
 
             if !builder.config.dry_run() && matches!(mode, "run-make" | "coverage-run") {
@@ -2006,14 +2038,18 @@ NOTE: if you're sure you want to do this, please open an issue as to why. In the
         // Only pass correct values for these flags for the `run-make` suite as it
         // requires that a C++ compiler was configured which isn't always the case.
         if !builder.config.dry_run() && mode == "run-make" {
+            let mut cflags = builder.cc_handled_clags(target, CLang::C);
+            cflags.extend(builder.cc_unhandled_cflags(target, GitRepo::Rustc, CLang::C));
+            let mut cxxflags = builder.cc_handled_clags(target, CLang::Cxx);
+            cxxflags.extend(builder.cc_unhandled_cflags(target, GitRepo::Rustc, CLang::Cxx));
             cmd.arg("--cc")
                 .arg(builder.cc(target))
                 .arg("--cxx")
                 .arg(builder.cxx(target).unwrap())
                 .arg("--cflags")
-                .arg(builder.cflags(target, GitRepo::Rustc, CLang::C).join(" "))
+                .arg(cflags.join(" "))
                 .arg("--cxxflags")
-                .arg(builder.cflags(target, GitRepo::Rustc, CLang::Cxx).join(" "));
+                .arg(cxxflags.join(" "));
             copts_passed = true;
             if let Some(ar) = builder.ar(target) {
                 cmd.arg("--ar").arg(ar);
@@ -2733,7 +2769,7 @@ impl Step for Crate {
             cargo
         } else {
             // Also prepare a sysroot for the target.
-            if builder.config.build != target {
+            if !builder.is_builder_target(target) {
                 builder.ensure(compile::Std::new(compiler, target).force_recompile(true));
                 builder.ensure(RemoteCopyLibs { compiler, target });
             }
@@ -2979,12 +3015,15 @@ impl Step for RemoteCopyLibs {
 
         builder.info(&format!("REMOTE copy libs to emulator ({target})"));
 
-        let server = builder.ensure(tool::RemoteTestServer { compiler, target });
+        let remote_test_server = builder.ensure(tool::RemoteTestServer { compiler, target });
 
         // Spawn the emulator and wait for it to come online
         let tool = builder.tool_exe(Tool::RemoteTestClient);
         let mut cmd = command(&tool);
-        cmd.arg("spawn-emulator").arg(target.triple).arg(&server).arg(builder.tempdir());
+        cmd.arg("spawn-emulator")
+            .arg(target.triple)
+            .arg(&remote_test_server.tool_path)
+            .arg(builder.tempdir());
         if let Some(rootfs) = builder.qemu_rootfs(target) {
             cmd.arg(rootfs);
         }
@@ -3612,7 +3651,7 @@ impl Step for TestFloatParse {
         let bootstrap_host = builder.config.build;
         let compiler = builder.compiler(builder.top_stage, bootstrap_host);
         let path = self.path.to_str().unwrap();
-        let crate_name = self.path.components().last().unwrap().as_os_str().to_str().unwrap();
+        let crate_name = self.path.iter().next_back().unwrap().to_str().unwrap();
 
         builder.ensure(tool::TestFloatParse { host: self.host });
 
diff --git a/src/bootstrap/src/core/build_steps/tool.rs b/src/bootstrap/src/core/build_steps/tool.rs
index 793fa24991b..39acb646dff 100644
--- a/src/bootstrap/src/core/build_steps/tool.rs
+++ b/src/bootstrap/src/core/build_steps/tool.rs
@@ -1,6 +1,21 @@
+//! This module handles building and managing various tools in bootstrap
+//! build system.
+//!
+//! **What It Does**
+//! - Defines how tools are built, configured and installed.
+//! - Manages tool dependencies and build steps.
+//! - Copies built tool binaries to the correct locations.
+//!
+//! Each Rust tool **MUST** utilize `ToolBuild` inside their `Step` logic,
+//! return `ToolBuildResult` and should never prepare `cargo` invocations manually.
+
 use std::path::PathBuf;
 use std::{env, fs};
 
+#[cfg(feature = "tracing")]
+use tracing::instrument;
+
+use crate::core::build_steps::compile::is_lto_stage;
 use crate::core::build_steps::toolstate::ToolState;
 use crate::core::build_steps::{compile, llvm};
 use crate::core::builder;
@@ -60,8 +75,21 @@ impl Builder<'_> {
     }
 }
 
+/// Result of the tool build process. Each `Step` in this module is responsible
+/// for using this type as `type Output = ToolBuildResult;`
+#[derive(Clone)]
+pub struct ToolBuildResult {
+    /// Executable path of the corresponding tool that was built.
+    pub tool_path: PathBuf,
+    /// Compiler used to build the tool. For non-`ToolRustc` tools this is equal to `target_compiler`.
+    /// For `ToolRustc` this is one stage before of the `target_compiler`.
+    pub build_compiler: Compiler,
+    /// Target compiler passed to `Step`.
+    pub target_compiler: Compiler,
+}
+
 impl Step for ToolBuild {
-    type Output = PathBuf;
+    type Output = ToolBuildResult;
 
     fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
         run.never()
@@ -71,25 +99,31 @@ impl Step for ToolBuild {
     ///
     /// This will build the specified tool with the specified `host` compiler in
     /// `stage` into the normal cargo output directory.
-    fn run(self, builder: &Builder<'_>) -> PathBuf {
-        let compiler = self.compiler;
+    fn run(mut self, builder: &Builder<'_>) -> ToolBuildResult {
         let target = self.target;
         let mut tool = self.tool;
         let path = self.path;
 
+        let target_compiler = self.compiler;
+        self.compiler = if self.mode == Mode::ToolRustc {
+            get_tool_rustc_compiler(builder, self.compiler)
+        } else {
+            self.compiler
+        };
+
         match self.mode {
             Mode::ToolRustc => {
-                builder.ensure(compile::Std::new(compiler, compiler.host));
-                builder.ensure(compile::Rustc::new(compiler, target));
+                builder.ensure(compile::Std::new(self.compiler, self.compiler.host));
+                builder.ensure(compile::Rustc::new(self.compiler, target));
             }
-            Mode::ToolStd => builder.ensure(compile::Std::new(compiler, target)),
+            Mode::ToolStd => builder.ensure(compile::Std::new(self.compiler, target)),
             Mode::ToolBootstrap => {} // uses downloaded stage0 compiler libs
             _ => panic!("unexpected Mode for tool build"),
         }
 
         let mut cargo = prepare_tool_cargo(
             builder,
-            compiler,
+            self.compiler,
             self.mode,
             target,
             Kind::Build,
@@ -97,10 +131,28 @@ impl Step for ToolBuild {
             self.source_type,
             &self.extra_features,
         );
+
+        if path.ends_with("/rustdoc") &&
+            // rustdoc is performance sensitive, so apply LTO to it.
+            is_lto_stage(&self.compiler)
+        {
+            let lto = match builder.config.rust_lto {
+                RustcLto::Off => Some("off"),
+                RustcLto::Thin => Some("thin"),
+                RustcLto::Fat => Some("fat"),
+                RustcLto::ThinLocal => None,
+            };
+            if let Some(lto) = lto {
+                cargo.env(cargo_profile_var("LTO", &builder.config), lto);
+            }
+        }
+
         if !self.allow_features.is_empty() {
             cargo.allow_features(self.allow_features);
         }
+
         cargo.args(self.cargo_args);
+
         let _guard = builder.msg_tool(
             Kind::Build,
             self.mode,
@@ -127,7 +179,10 @@ impl Step for ToolBuild {
             if tool == "tidy" {
                 tool = "rust-tidy";
             }
-            copy_link_tool_bin(builder, self.compiler, self.target, self.mode, tool)
+            let tool_path =
+                copy_link_tool_bin(builder, self.compiler, self.target, self.mode, tool);
+
+            ToolBuildResult { tool_path, build_compiler: self.compiler, target_compiler }
         }
     }
 }
@@ -236,6 +291,23 @@ pub fn prepare_tool_cargo(
     cargo
 }
 
+/// Handle stage-off logic for `ToolRustc` tools when necessary.
+pub(crate) fn get_tool_rustc_compiler(
+    builder: &Builder<'_>,
+    target_compiler: Compiler,
+) -> Compiler {
+    if builder.download_rustc() && target_compiler.stage == 1 {
+        // We already have the stage 1 compiler, we don't need to cut the stage.
+        builder.compiler(target_compiler.stage, builder.config.build)
+    } else {
+        // Similar to `compile::Assemble`, build with the previous stage's compiler. Otherwise
+        // we'd have stageN/bin/rustc and stageN/bin/$rustc_tool be effectively different stage
+        // compilers, which isn't what we want. Rustc tools should be linked in the same way as the
+        // compiler it's paired with, so it must be built with the previous stage compiler.
+        builder.compiler(target_compiler.stage.saturating_sub(1), builder.config.build)
+    }
+}
+
 /// Links a built tool binary with the given `name` from the build directory to the
 /// tools directory.
 fn copy_link_tool_bin(
@@ -275,7 +347,7 @@ macro_rules! bootstrap_tool {
                         self.ensure($name {
                             compiler: self.compiler(0, self.config.build),
                             target: self.config.build,
-                        }),
+                        }).tool_path,
                     )+
                 }
             }
@@ -289,7 +361,7 @@ macro_rules! bootstrap_tool {
         }
 
         impl Step for $name {
-            type Output = PathBuf;
+            type Output = ToolBuildResult;
 
             fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
                 run.path($path)
@@ -303,7 +375,15 @@ macro_rules! bootstrap_tool {
                 });
             }
 
-            fn run(self, builder: &Builder<'_>) -> PathBuf {
+            #[cfg_attr(
+                feature = "tracing",
+                instrument(
+                    level = "debug",
+                    name = $tool_name,
+                    skip_all,
+                ),
+            )]
+            fn run(self, builder: &Builder<'_>) -> ToolBuildResult {
                 $(
                     for submodule in $submodules {
                         builder.require_submodule(submodule, None);
@@ -378,7 +458,7 @@ pub struct OptimizedDist {
 }
 
 impl Step for OptimizedDist {
-    type Output = PathBuf;
+    type Output = ToolBuildResult;
 
     fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
         run.path("src/tools/opt-dist")
@@ -391,7 +471,7 @@ impl Step for OptimizedDist {
         });
     }
 
-    fn run(self, builder: &Builder<'_>) -> PathBuf {
+    fn run(self, builder: &Builder<'_>) -> ToolBuildResult {
         // We need to ensure the rustc-perf submodule is initialized when building opt-dist since
         // the tool requires it to be in place to run.
         builder.require_submodule("src/tools/rustc-perf", None);
@@ -420,7 +500,7 @@ pub struct RustcPerf {
 
 impl Step for RustcPerf {
     /// Path to the built `collector` binary.
-    type Output = PathBuf;
+    type Output = ToolBuildResult;
 
     fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
         run.path("src/tools/rustc-perf")
@@ -433,7 +513,7 @@ impl Step for RustcPerf {
         });
     }
 
-    fn run(self, builder: &Builder<'_>) -> PathBuf {
+    fn run(self, builder: &Builder<'_>) -> ToolBuildResult {
         // We need to ensure the rustc-perf submodule is initialized.
         builder.require_submodule("src/tools/rustc-perf", None);
 
@@ -450,12 +530,12 @@ impl Step for RustcPerf {
             // a CLI.
             cargo_args: vec!["-p".to_string(), "collector".to_string()],
         };
-        let collector_bin = builder.ensure(tool.clone());
+        let res = builder.ensure(tool.clone());
         // We also need to symlink the `rustc-fake` binary to the corresponding directory,
         // because `collector` expects it in the same directory.
         copy_link_tool_bin(builder, tool.compiler, tool.target, tool.mode, "rustc-fake");
 
-        collector_bin
+        res
     }
 }
 
@@ -470,7 +550,7 @@ impl ErrorIndex {
         // for rustc_private and libLLVM.so, and `sysroot_lib` for libstd, etc.
         let host = builder.config.build;
         let compiler = builder.compiler_for(builder.top_stage, host, host);
-        let mut cmd = command(builder.ensure(ErrorIndex { compiler }));
+        let mut cmd = command(builder.ensure(ErrorIndex { compiler }).tool_path);
         let mut dylib_paths = builder.rustc_lib_paths(compiler);
         dylib_paths.push(PathBuf::from(&builder.sysroot_target_libdir(compiler, compiler.host)));
         add_dylib_path(dylib_paths, &mut cmd);
@@ -479,27 +559,23 @@ impl ErrorIndex {
 }
 
 impl Step for ErrorIndex {
-    type Output = PathBuf;
+    type Output = ToolBuildResult;
 
     fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
         run.path("src/tools/error_index_generator")
     }
 
     fn make_run(run: RunConfig<'_>) {
-        // Compile the error-index in the same stage as rustdoc to avoid
-        // recompiling rustdoc twice if we can.
-        //
         // NOTE: This `make_run` isn't used in normal situations, only if you
         // manually build the tool with `x.py build
         // src/tools/error-index-generator` which almost nobody does.
         // Normally, `x.py test` or `x.py doc` will use the
         // `ErrorIndex::command` function instead.
-        let compiler =
-            run.builder.compiler(run.builder.top_stage.saturating_sub(1), run.builder.config.build);
+        let compiler = run.builder.compiler(run.builder.top_stage, run.builder.config.build);
         run.builder.ensure(ErrorIndex { compiler });
     }
 
-    fn run(self, builder: &Builder<'_>) -> PathBuf {
+    fn run(self, builder: &Builder<'_>) -> ToolBuildResult {
         builder.ensure(ToolBuild {
             compiler: self.compiler,
             target: self.compiler.host,
@@ -521,7 +597,7 @@ pub struct RemoteTestServer {
 }
 
 impl Step for RemoteTestServer {
-    type Output = PathBuf;
+    type Output = ToolBuildResult;
 
     fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
         run.path("src/tools/remote-test-server")
@@ -534,7 +610,7 @@ impl Step for RemoteTestServer {
         });
     }
 
-    fn run(self, builder: &Builder<'_>) -> PathBuf {
+    fn run(self, builder: &Builder<'_>) -> ToolBuildResult {
         builder.ensure(ToolBuild {
             compiler: self.compiler,
             target: self.target,
@@ -557,7 +633,7 @@ pub struct Rustdoc {
 }
 
 impl Step for Rustdoc {
-    type Output = PathBuf;
+    type Output = ToolBuildResult;
     const DEFAULT: bool = true;
     const ONLY_HOSTS: bool = true;
 
@@ -566,24 +642,25 @@ impl Step for Rustdoc {
     }
 
     fn make_run(run: RunConfig<'_>) {
-        run.builder.ensure(Rustdoc {
-            // NOTE: this is somewhat unique in that we actually want a *target*
-            // compiler here, because rustdoc *is* a compiler. We won't be using
-            // this as the compiler to build with, but rather this is "what
-            // compiler are we producing"?
-            compiler: run.builder.compiler(run.builder.top_stage, run.target),
-        });
+        run.builder
+            .ensure(Rustdoc { compiler: run.builder.compiler(run.builder.top_stage, run.target) });
     }
 
-    fn run(self, builder: &Builder<'_>) -> PathBuf {
+    fn run(self, builder: &Builder<'_>) -> ToolBuildResult {
         let target_compiler = self.compiler;
+        let target = target_compiler.host;
+
         if target_compiler.stage == 0 {
             if !target_compiler.is_snapshot(builder) {
                 panic!("rustdoc in stage 0 must be snapshot rustdoc");
             }
-            return builder.initial_rustdoc.clone();
+
+            return ToolBuildResult {
+                tool_path: builder.initial_rustdoc.clone(),
+                build_compiler: target_compiler,
+                target_compiler,
+            };
         }
-        let target = target_compiler.host;
 
         let bin_rustdoc = || {
             let sysroot = builder.sysroot(target_compiler);
@@ -613,27 +690,15 @@ impl Step for Rustdoc {
 
                 let bin_rustdoc = bin_rustdoc();
                 builder.copy_link(&precompiled_rustdoc, &bin_rustdoc);
-                return bin_rustdoc;
+
+                return ToolBuildResult {
+                    tool_path: bin_rustdoc,
+                    build_compiler: target_compiler,
+                    target_compiler,
+                };
             }
         }
 
-        let build_compiler = if builder.download_rustc() && target_compiler.stage == 1 {
-            // We already have the stage 1 compiler, we don't need to cut the stage.
-            builder.compiler(target_compiler.stage, builder.config.build)
-        } else {
-            // Similar to `compile::Assemble`, build with the previous stage's compiler. Otherwise
-            // we'd have stageN/bin/rustc and stageN/bin/rustdoc be effectively different stage
-            // compilers, which isn't what we want. Rustdoc should be linked in the same way as the
-            // rustc compiler it's paired with, so it must be built with the previous stage compiler.
-            builder.compiler(target_compiler.stage - 1, builder.config.build)
-        };
-
-        // When using `download-rustc` and a stage0 build_compiler, copying rustc doesn't actually
-        // build stage0 libstd (because the libstd in sysroot has the wrong ABI). Explicitly build
-        // it.
-        builder.ensure(compile::Std::new(build_compiler, target_compiler.host));
-        builder.ensure(compile::Rustc::new(build_compiler, target_compiler.host));
-
         // The presence of `target_compiler` ensures that the necessary libraries (codegen backends,
         // compiler libraries, ...) are built. Rustdoc does not require the presence of any
         // libraries within sysroot_libdir (i.e., rustlib), though doctests may want it (since
@@ -641,63 +706,39 @@ impl Step for Rustdoc {
         // libraries here. The intuition here is that If we've built a compiler, we should be able
         // to build rustdoc.
         //
-        let mut features = Vec::new();
-        if builder.config.jemalloc {
-            features.push("jemalloc".to_string());
+        let mut extra_features = Vec::new();
+        if builder.config.jemalloc(target) {
+            extra_features.push("jemalloc".to_string());
         }
 
-        // NOTE: Never modify the rustflags here, it breaks the build cache for other tools!
-        let mut cargo = prepare_tool_cargo(
-            builder,
-            build_compiler,
-            Mode::ToolRustc,
-            target,
-            Kind::Build,
-            "src/tools/rustdoc",
-            SourceType::InTree,
-            features.as_slice(),
-        );
-
-        // rustdoc is performance sensitive, so apply LTO to it.
-        let lto = match builder.config.rust_lto {
-            RustcLto::Off => Some("off"),
-            RustcLto::Thin => Some("thin"),
-            RustcLto::Fat => Some("fat"),
-            RustcLto::ThinLocal => None,
-        };
-        if let Some(lto) = lto {
-            cargo.env(cargo_profile_var("LTO", &builder.config), lto);
-        }
-
-        let _guard = builder.msg_tool(
-            Kind::Build,
-            Mode::ToolRustc,
-            "rustdoc",
-            build_compiler.stage,
-            &self.compiler.host,
-            &target,
-        );
-        cargo.into_cmd().run(builder);
-
-        // Cargo adds a number of paths to the dylib search path on windows, which results in
-        // the wrong rustdoc being executed. To avoid the conflicting rustdocs, we name the "tool"
-        // rustdoc a different name.
-        let tool_rustdoc = builder
-            .cargo_out(build_compiler, Mode::ToolRustc, target)
-            .join(exe("rustdoc_tool_binary", target_compiler.host));
+        let ToolBuildResult { tool_path, build_compiler, target_compiler } =
+            builder.ensure(ToolBuild {
+                compiler: target_compiler,
+                target,
+                // Cargo adds a number of paths to the dylib search path on windows, which results in
+                // the wrong rustdoc being executed. To avoid the conflicting rustdocs, we name the "tool"
+                // rustdoc a different name.
+                tool: "rustdoc_tool_binary",
+                mode: Mode::ToolRustc,
+                path: "src/tools/rustdoc",
+                source_type: SourceType::InTree,
+                extra_features,
+                allow_features: "",
+                cargo_args: Vec::new(),
+            });
 
         // don't create a stage0-sysroot/bin directory.
         if target_compiler.stage > 0 {
             if builder.config.rust_debuginfo_level_tools == DebuginfoLevel::None {
                 // Due to LTO a lot of debug info from C++ dependencies such as jemalloc can make it into
                 // our final binaries
-                compile::strip_debug(builder, target, &tool_rustdoc);
+                compile::strip_debug(builder, target, &tool_path);
             }
             let bin_rustdoc = bin_rustdoc();
-            builder.copy_link(&tool_rustdoc, &bin_rustdoc);
-            bin_rustdoc
+            builder.copy_link(&tool_path, &bin_rustdoc);
+            ToolBuildResult { tool_path: bin_rustdoc, build_compiler, target_compiler }
         } else {
-            tool_rustdoc
+            ToolBuildResult { tool_path, build_compiler, target_compiler }
         }
     }
 }
@@ -709,7 +750,7 @@ pub struct Cargo {
 }
 
 impl Step for Cargo {
-    type Output = PathBuf;
+    type Output = ToolBuildResult;
     const DEFAULT: bool = true;
     const ONLY_HOSTS: bool = true;
 
@@ -725,7 +766,7 @@ impl Step for Cargo {
         });
     }
 
-    fn run(self, builder: &Builder<'_>) -> PathBuf {
+    fn run(self, builder: &Builder<'_>) -> ToolBuildResult {
         builder.build.require_submodule("src/tools/cargo", None);
 
         builder.ensure(ToolBuild {
@@ -749,20 +790,34 @@ pub struct LldWrapper {
 }
 
 impl Step for LldWrapper {
-    type Output = ();
+    type Output = ToolBuildResult;
 
     fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
         run.never()
     }
 
-    fn run(self, builder: &Builder<'_>) {
+    #[cfg_attr(
+        feature = "tracing",
+        instrument(
+            level = "debug",
+            name = "LldWrapper::run",
+            skip_all,
+            fields(build_compiler = ?self.build_compiler, target_compiler = ?self.target_compiler),
+        ),
+    )]
+
+    fn run(self, builder: &Builder<'_>) -> ToolBuildResult {
         if builder.config.dry_run() {
-            return;
+            return ToolBuildResult {
+                tool_path: Default::default(),
+                build_compiler: self.build_compiler,
+                target_compiler: self.target_compiler,
+            };
         }
 
         let target = self.target_compiler.host;
 
-        let executable = builder.ensure(ToolBuild {
+        let tool_result = builder.ensure(ToolBuild {
             compiler: self.build_compiler,
             target,
             tool: "lld-wrapper",
@@ -786,8 +841,11 @@ impl Step for LldWrapper {
         t!(fs::create_dir_all(&self_contained_lld_dir));
 
         for name in crate::LLD_FILE_NAMES {
-            builder.copy_link(&executable, &self_contained_lld_dir.join(exe(name, target)));
+            builder
+                .copy_link(&tool_result.tool_path, &self_contained_lld_dir.join(exe(name, target)));
         }
+
+        tool_result
     }
 }
 
@@ -802,7 +860,7 @@ impl RustAnalyzer {
 }
 
 impl Step for RustAnalyzer {
-    type Output = PathBuf;
+    type Output = ToolBuildResult;
     const DEFAULT: bool = true;
     const ONLY_HOSTS: bool = true;
 
@@ -818,7 +876,7 @@ impl Step for RustAnalyzer {
         });
     }
 
-    fn run(self, builder: &Builder<'_>) -> PathBuf {
+    fn run(self, builder: &Builder<'_>) -> ToolBuildResult {
         builder.ensure(ToolBuild {
             compiler: self.compiler,
             target: self.target,
@@ -840,7 +898,7 @@ pub struct RustAnalyzerProcMacroSrv {
 }
 
 impl Step for RustAnalyzerProcMacroSrv {
-    type Output = Option<PathBuf>;
+    type Output = Option<ToolBuildResult>;
     const DEFAULT: bool = true;
     const ONLY_HOSTS: bool = true;
 
@@ -862,8 +920,8 @@ impl Step for RustAnalyzerProcMacroSrv {
         });
     }
 
-    fn run(self, builder: &Builder<'_>) -> Option<PathBuf> {
-        let path = builder.ensure(ToolBuild {
+    fn run(self, builder: &Builder<'_>) -> Option<ToolBuildResult> {
+        let tool_result = builder.ensure(ToolBuild {
             compiler: self.compiler,
             target: self.target,
             tool: "rust-analyzer-proc-macro-srv",
@@ -879,9 +937,10 @@ impl Step for RustAnalyzerProcMacroSrv {
         // so that r-a can use it.
         let libexec_path = builder.sysroot(self.compiler).join("libexec");
         t!(fs::create_dir_all(&libexec_path));
-        builder.copy_link(&path, &libexec_path.join("rust-analyzer-proc-macro-srv"));
+        builder
+            .copy_link(&tool_result.tool_path, &libexec_path.join("rust-analyzer-proc-macro-srv"));
 
-        Some(path)
+        Some(tool_result)
     }
 }
 
@@ -893,7 +952,7 @@ pub struct LlvmBitcodeLinker {
 }
 
 impl Step for LlvmBitcodeLinker {
-    type Output = PathBuf;
+    type Output = ToolBuildResult;
     const DEFAULT: bool = true;
     const ONLY_HOSTS: bool = true;
 
@@ -911,51 +970,38 @@ impl Step for LlvmBitcodeLinker {
         });
     }
 
-    fn run(self, builder: &Builder<'_>) -> PathBuf {
-        let bin_name = "llvm-bitcode-linker";
-
-        // If enabled, use ci-rustc and skip building the in-tree compiler.
-        if !builder.download_rustc() {
-            builder.ensure(compile::Std::new(self.compiler, self.compiler.host));
-            builder.ensure(compile::Rustc::new(self.compiler, self.target));
-        }
-
-        let cargo = prepare_tool_cargo(
-            builder,
-            self.compiler,
-            Mode::ToolRustc,
-            self.target,
-            Kind::Build,
-            "src/tools/llvm-bitcode-linker",
-            SourceType::InTree,
-            &self.extra_features,
-        );
-
-        let _guard = builder.msg_tool(
-            Kind::Build,
-            Mode::ToolRustc,
-            bin_name,
-            self.compiler.stage,
-            &self.compiler.host,
-            &self.target,
-        );
-
-        cargo.into_cmd().run(builder);
-
-        let tool_out = builder
-            .cargo_out(self.compiler, Mode::ToolRustc, self.target)
-            .join(exe(bin_name, self.compiler.host));
+    #[cfg_attr(
+        feature = "tracing",
+        instrument(level = "debug", name = "LlvmBitcodeLinker::run", skip_all)
+    )]
+    fn run(self, builder: &Builder<'_>) -> ToolBuildResult {
+        let tool_result = builder.ensure(ToolBuild {
+            compiler: self.compiler,
+            target: self.target,
+            tool: "llvm-bitcode-linker",
+            mode: Mode::ToolRustc,
+            path: "src/tools/llvm-bitcode-linker",
+            source_type: SourceType::InTree,
+            extra_features: self.extra_features,
+            allow_features: "",
+            cargo_args: Vec::new(),
+        });
 
-        if self.compiler.stage > 0 {
+        if tool_result.target_compiler.stage > 0 {
             let bindir_self_contained = builder
-                .sysroot(self.compiler)
+                .sysroot(tool_result.target_compiler)
                 .join(format!("lib/rustlib/{}/bin/self-contained", self.target.triple));
             t!(fs::create_dir_all(&bindir_self_contained));
-            let bin_destination = bindir_self_contained.join(exe(bin_name, self.compiler.host));
-            builder.copy_link(&tool_out, &bin_destination);
-            bin_destination
+            let bin_destination = bindir_self_contained
+                .join(exe("llvm-bitcode-linker", tool_result.target_compiler.host));
+            builder.copy_link(&tool_result.tool_path, &bin_destination);
+            ToolBuildResult {
+                tool_path: bin_destination,
+                build_compiler: tool_result.build_compiler,
+                target_compiler: tool_result.target_compiler,
+            }
         } else {
-            tool_out
+            tool_result
         }
     }
 }
@@ -1040,7 +1086,7 @@ macro_rules! tool_extended {
         }
 
         impl Step for $name {
-            type Output = PathBuf;
+            type Output = ToolBuildResult;
             const DEFAULT: bool = true; // Overridden by `should_run_tool_build_step`
             const ONLY_HOSTS: bool = true;
 
@@ -1060,7 +1106,7 @@ macro_rules! tool_extended {
                 });
             }
 
-            fn run(self, builder: &Builder<'_>) -> PathBuf {
+            fn run(self, builder: &Builder<'_>) -> ToolBuildResult {
                 let Self { compiler, target } = self;
                 run_tool_build_step(
                     builder,
@@ -1106,38 +1152,37 @@ fn run_tool_build_step(
     tool_name: &'static str,
     path: &'static str,
     add_bins_to_sysroot: Option<&[&str]>,
-) -> PathBuf {
-    let tool = builder.ensure(ToolBuild {
-        compiler,
-        target,
-        tool: tool_name,
-        mode: Mode::ToolRustc,
-        path,
-        extra_features: vec![],
-        source_type: SourceType::InTree,
-        allow_features: "",
-        cargo_args: vec![],
-    });
+) -> ToolBuildResult {
+    let ToolBuildResult { tool_path, build_compiler, target_compiler } =
+        builder.ensure(ToolBuild {
+            compiler,
+            target,
+            tool: tool_name,
+            mode: Mode::ToolRustc,
+            path,
+            extra_features: vec![],
+            source_type: SourceType::InTree,
+            allow_features: "",
+            cargo_args: vec![],
+        });
 
     // FIXME: This should just be an if-let-chain, but those are unstable.
     if let Some(add_bins_to_sysroot) =
-        add_bins_to_sysroot.filter(|bins| !bins.is_empty() && compiler.stage > 0)
+        add_bins_to_sysroot.filter(|bins| !bins.is_empty() && target_compiler.stage > 0)
     {
-        let bindir = builder.sysroot(compiler).join("bin");
+        let bindir = builder.sysroot(target_compiler).join("bin");
         t!(fs::create_dir_all(&bindir));
 
-        let tools_out = builder.cargo_out(compiler, Mode::ToolRustc, target);
-
         for add_bin in add_bins_to_sysroot {
-            let bin_source = tools_out.join(exe(add_bin, target));
-            let bin_destination = bindir.join(exe(add_bin, compiler.host));
-            builder.copy_link(&bin_source, &bin_destination);
+            let bin_destination = bindir.join(exe(add_bin, target_compiler.host));
+            builder.copy_link(&tool_path, &bin_destination);
         }
 
         // Return a path into the bin dir.
-        bindir.join(exe(tool_name, compiler.host))
+        let path = bindir.join(exe(tool_name, target_compiler.host));
+        ToolBuildResult { tool_path: path, build_compiler, target_compiler }
     } else {
-        tool
+        ToolBuildResult { tool_path, build_compiler, target_compiler }
     }
 }
 
@@ -1175,7 +1220,7 @@ pub struct TestFloatParse {
 }
 
 impl Step for TestFloatParse {
-    type Output = ();
+    type Output = ToolBuildResult;
     const ONLY_HOSTS: bool = true;
     const DEFAULT: bool = false;
 
@@ -1183,7 +1228,7 @@ impl Step for TestFloatParse {
         run.path("src/etc/test-float-parse")
     }
 
-    fn run(self, builder: &Builder<'_>) {
+    fn run(self, builder: &Builder<'_>) -> ToolBuildResult {
         let bootstrap_host = builder.config.build;
         let compiler = builder.compiler(builder.top_stage, bootstrap_host);
 
@@ -1197,7 +1242,7 @@ impl Step for TestFloatParse {
             extra_features: Vec::new(),
             allow_features: "",
             cargo_args: Vec::new(),
-        });
+        })
     }
 }
 
diff --git a/src/bootstrap/src/core/build_steps/toolstate.rs b/src/bootstrap/src/core/build_steps/toolstate.rs
index 668133f05cb..a65623de95a 100644
--- a/src/bootstrap/src/core/build_steps/toolstate.rs
+++ b/src/bootstrap/src/core/build_steps/toolstate.rs
@@ -354,12 +354,12 @@ fn read_old_toolstate() -> Vec<RepoState> {
 ///   1. Generate a new Personal access token:
 ///
 ///       * Login to the bot account, and go to Settings -> Developer settings ->
-///           Personal access tokens
+///         Personal access tokens
 ///       * Click "Generate new token"
 ///       * Enable the "public_repo" permission, then click "Generate token"
 ///       * Copy the generated token (should be a 40-digit hexadecimal number).
-///           Save it somewhere secure, as the token would be gone once you leave
-///           the page.
+///         Save it somewhere secure, as the token would be gone once you leave
+///         the page.
 ///
 ///   2. Update the variable group in Azure Pipelines
 ///
@@ -368,7 +368,7 @@ fn read_old_toolstate() -> Vec<RepoState> {
 ///   4. Replace the email address below if the bot account identity is changed
 ///
 ///       * See <https://help.github.com/articles/about-commit-email-addresses/>
-///           if a private email by GitHub is wanted.
+///         if a private email by GitHub is wanted.
 fn commit_toolstate_change(builder: &Builder<'_>, current_toolstate: &ToolstateData) {
     let message = format!("({} CI update)", OS.expect("linux/windows only"));
     let mut success = false;
diff --git a/src/bootstrap/src/core/build_steps/vendor.rs b/src/bootstrap/src/core/build_steps/vendor.rs
index 26d0f100ffd..984c70955d7 100644
--- a/src/bootstrap/src/core/build_steps/vendor.rs
+++ b/src/bootstrap/src/core/build_steps/vendor.rs
@@ -1,9 +1,16 @@
+//! Handles the vendoring process for the bootstrap system.
+//!
+//! This module ensures that all required Cargo dependencies are gathered
+//! and stored in the `<src>/<VENDOR_DIR>` directory.
 use std::path::PathBuf;
 
 use crate::core::build_steps::tool::SUBMODULES_FOR_RUSTBOOK;
 use crate::core::builder::{Builder, RunConfig, ShouldRun, Step};
 use crate::utils::exec::command;
 
+/// The name of the directory where vendored dependencies are stored.
+pub const VENDOR_DIR: &str = "vendor";
+
 /// Returns the cargo workspaces to vendor for `x vendor` and dist tarballs.
 ///
 /// Returns a `Vec` of `(path_to_manifest, submodules_required)` where
@@ -27,15 +34,24 @@ pub fn default_paths_to_vendor(builder: &Builder<'_>) -> Vec<(PathBuf, Vec<&'sta
     .collect()
 }
 
+/// Defines the vendoring step in the bootstrap process.
+///
+/// This step executes `cargo vendor` to collect all dependencies
+/// and store them in the `<src>/<VENDOR_DIR>` directory.
 #[derive(Debug, Clone, Hash, PartialEq, Eq)]
 pub(crate) struct Vendor {
-    sync_args: Vec<PathBuf>,
-    versioned_dirs: bool,
-    root_dir: PathBuf,
+    /// Additional paths to synchronize during vendoring.
+    pub(crate) sync_args: Vec<PathBuf>,
+    /// Determines whether vendored dependencies use versioned directories.
+    pub(crate) versioned_dirs: bool,
+    /// The root directory of the source code.
+    pub(crate) root_dir: PathBuf,
+    /// The target directory for storing vendored dependencies.
+    pub(crate) output_dir: PathBuf,
 }
 
 impl Step for Vendor {
-    type Output = ();
+    type Output = VendorOutput;
     const DEFAULT: bool = true;
     const ONLY_HOSTS: bool = true;
 
@@ -48,10 +64,17 @@ impl Step for Vendor {
             sync_args: run.builder.config.cmd.vendor_sync_args(),
             versioned_dirs: run.builder.config.cmd.vendor_versioned_dirs(),
             root_dir: run.builder.src.clone(),
+            output_dir: run.builder.src.join(VENDOR_DIR),
         });
     }
 
+    /// Executes the vendoring process.
+    ///
+    /// This function runs `cargo vendor` and ensures all required submodules
+    /// are initialized before vendoring begins.
     fn run(self, builder: &Builder<'_>) -> Self::Output {
+        builder.info(&format!("Vendoring sources to {:?}", self.root_dir));
+
         let mut cmd = command(&builder.initial_cargo);
         cmd.arg("vendor");
 
@@ -81,8 +104,15 @@ impl Step for Vendor {
         // which uses the unstable `public-dependency` feature.
         cmd.env("RUSTC_BOOTSTRAP", "1");
 
-        cmd.current_dir(self.root_dir);
+        cmd.current_dir(self.root_dir).arg(&self.output_dir);
 
-        cmd.run(builder);
+        let config = cmd.run_capture_stdout(builder);
+        VendorOutput { config: config.stdout() }
     }
 }
+
+/// Stores the result of the vendoring step.
+#[derive(Debug, Clone)]
+pub(crate) struct VendorOutput {
+    pub(crate) config: String,
+}
diff --git a/src/bootstrap/src/core/builder/cargo.rs b/src/bootstrap/src/core/builder/cargo.rs
index 2ecab262413..f08d229c76d 100644
--- a/src/bootstrap/src/core/builder/cargo.rs
+++ b/src/bootstrap/src/core/builder/cargo.rs
@@ -245,7 +245,11 @@ impl Cargo {
                 // flesh out rpath support more fully in the future.
                 self.rustflags.arg("-Zosx-rpath-install-name");
                 Some(format!("-Wl,-rpath,@loader_path/../{libdir}"))
-            } else if !target.is_windows() && !target.contains("aix") && !target.contains("xous") {
+            } else if !target.is_windows()
+                && !target.contains("cygwin")
+                && !target.contains("aix")
+                && !target.contains("xous")
+            {
                 self.rustflags.arg("-Clink-args=-Wl,-z,origin");
                 Some(format!("-Wl,-rpath,$ORIGIN/../{libdir}"))
             } else {
@@ -281,10 +285,7 @@ impl Cargo {
 
         // Ignore linker warnings for now. These are complicated to fix and don't affect the build.
         // FIXME: we should really investigate these...
-        // cfg(bootstrap)
-        if compiler.stage != 0 {
-            self.rustflags.arg("-Alinker-messages");
-        }
+        self.rustflags.arg("-Alinker-messages");
 
         // Throughout the build Cargo can execute a number of build scripts
         // compiling C/C++ code and we need to pass compilers, archivers, flags, etc
@@ -323,8 +324,15 @@ impl Cargo {
             let cc = ccacheify(&builder.cc(target));
             self.command.env(format!("CC_{triple_underscored}"), &cc);
 
-            let cflags = builder.cflags(target, GitRepo::Rustc, CLang::C).join(" ");
-            self.command.env(format!("CFLAGS_{triple_underscored}"), &cflags);
+            // Extend `CXXFLAGS_$TARGET` with our extra flags.
+            let env = format!("CFLAGS_{triple_underscored}");
+            let mut cflags =
+                builder.cc_unhandled_cflags(target, GitRepo::Rustc, CLang::C).join(" ");
+            if let Ok(var) = std::env::var(&env) {
+                cflags.push(' ');
+                cflags.push_str(&var);
+            }
+            self.command.env(env, &cflags);
 
             if let Some(ar) = builder.ar(target) {
                 let ranlib = format!("{} s", ar.display());
@@ -335,10 +343,17 @@ impl Cargo {
 
             if let Ok(cxx) = builder.cxx(target) {
                 let cxx = ccacheify(&cxx);
-                let cxxflags = builder.cflags(target, GitRepo::Rustc, CLang::Cxx).join(" ");
-                self.command
-                    .env(format!("CXX_{triple_underscored}"), &cxx)
-                    .env(format!("CXXFLAGS_{triple_underscored}"), cxxflags);
+                self.command.env(format!("CXX_{triple_underscored}"), &cxx);
+
+                // Extend `CXXFLAGS_$TARGET` with our extra flags.
+                let env = format!("CXXFLAGS_{triple_underscored}");
+                let mut cxxflags =
+                    builder.cc_unhandled_cflags(target, GitRepo::Rustc, CLang::Cxx).join(" ");
+                if let Ok(var) = std::env::var(&env) {
+                    cxxflags.push(' ');
+                    cxxflags.push_str(&var);
+                }
+                self.command.env(&env, cxxflags);
             }
         }
 
@@ -906,8 +921,7 @@ impl Builder<'_> {
 
         if self.config.rust_remap_debuginfo {
             let mut env_var = OsString::new();
-            if self.config.vendor {
-                let vendor = self.build.src.join("vendor");
+            if let Some(vendor) = self.build.vendored_crates_path() {
                 env_var.push(vendor);
                 env_var.push("=/rust/deps");
             } else {
@@ -1054,10 +1068,7 @@ impl Builder<'_> {
 
         if mode == Mode::Rustc {
             rustflags.arg("-Wrustc::internal");
-            // cfg(bootstrap) - remove this check when lint is in bootstrap compiler
-            if stage != 0 {
-                rustflags.arg("-Drustc::symbol_intern_string_literal");
-            }
+            rustflags.arg("-Drustc::symbol_intern_string_literal");
             // FIXME(edition_2024): Change this to `-Wrust_2024_idioms` when all
             // of the individual lints are satisfied.
             rustflags.arg("-Wkeyword_idents_2024");
diff --git a/src/bootstrap/src/core/builder/mod.rs b/src/bootstrap/src/core/builder/mod.rs
index 8e767a8dcc6..9c04f097bee 100644
--- a/src/bootstrap/src/core/builder/mod.rs
+++ b/src/bootstrap/src/core/builder/mod.rs
@@ -10,6 +10,8 @@ use std::time::{Duration, Instant};
 use std::{env, fs};
 
 use clap::ValueEnum;
+#[cfg(feature = "tracing")]
+use tracing::instrument;
 
 pub use self::cargo::{Cargo, cargo_profile_var};
 pub use crate::Compiler;
@@ -21,7 +23,7 @@ use crate::core::config::{DryRun, TargetSelection};
 use crate::utils::cache::Cache;
 use crate::utils::exec::{BootstrapCommand, command};
 use crate::utils::helpers::{self, LldThreads, add_dylib_path, exe, libdir, linker_args, t};
-use crate::{Build, Crate};
+use crate::{Build, Crate, trace};
 
 mod cargo;
 
@@ -127,10 +129,14 @@ impl RunConfig<'_> {
     pub fn cargo_crates_in_set(&self) -> Vec<String> {
         let mut crates = Vec::new();
         for krate in &self.paths {
-            let path = krate.assert_single_path();
-            let Some(crate_name) = self.builder.crate_paths.get(&path.path) else {
-                panic!("missing crate for path {}", path.path.display())
-            };
+            let path = &krate.assert_single_path().path;
+
+            let crate_name = self
+                .builder
+                .crate_paths
+                .get(path)
+                .unwrap_or_else(|| panic!("missing crate for path {}", path.display()));
+
             crates.push(crate_name.to_string());
         }
         crates
@@ -1214,6 +1220,19 @@ impl<'a> Builder<'a> {
     /// compiler will run on, *not* the target it will build code for). Explicitly does not take
     /// `Compiler` since all `Compiler` instances are meant to be obtained through this function,
     /// since it ensures that they are valid (i.e., built and assembled).
+    #[cfg_attr(
+        feature = "tracing",
+        instrument(
+            level = "trace",
+            name = "Builder::compiler",
+            target = "COMPILER",
+            skip_all,
+            fields(
+                stage = stage,
+                host = ?host,
+            ),
+        ),
+    )]
     pub fn compiler(&self, stage: u32, host: TargetSelection) -> Compiler {
         self.ensure(compile::Assemble { target_compiler: Compiler { stage, host } })
     }
@@ -1229,19 +1248,42 @@ impl<'a> Builder<'a> {
     /// sysroot.
     ///
     /// See `force_use_stage1` and `force_use_stage2` for documentation on what each argument is.
+    #[cfg_attr(
+        feature = "tracing",
+        instrument(
+            level = "trace",
+            name = "Builder::compiler_for",
+            target = "COMPILER_FOR",
+            skip_all,
+            fields(
+                stage = stage,
+                host = ?host,
+                target = ?target,
+            ),
+        ),
+    )]
+
+    /// FIXME: This function is unnecessary (and dangerous, see <https://github.com/rust-lang/rust/issues/137469>).
+    /// We already have uplifting logic for the compiler, so remove this.
     pub fn compiler_for(
         &self,
         stage: u32,
         host: TargetSelection,
         target: TargetSelection,
     ) -> Compiler {
-        if self.build.force_use_stage2(stage) {
+        #![allow(clippy::let_and_return)]
+        let resolved_compiler = if self.build.force_use_stage2(stage) {
+            trace!(target: "COMPILER_FOR", ?stage, "force_use_stage2");
             self.compiler(2, self.config.build)
         } else if self.build.force_use_stage1(stage, target) {
+            trace!(target: "COMPILER_FOR", ?stage, "force_use_stage1");
             self.compiler(1, self.config.build)
         } else {
+            trace!(target: "COMPILER_FOR", ?stage, ?host, "no force, fallback to `compiler()`");
             self.compiler(stage, host)
-        }
+        };
+        trace!(target: "COMPILER_FOR", ?resolved_compiler);
+        resolved_compiler
     }
 
     pub fn sysroot(&self, compiler: Compiler) -> PathBuf {
@@ -1353,7 +1395,7 @@ impl<'a> Builder<'a> {
     }
 
     pub fn rustdoc(&self, compiler: Compiler) -> PathBuf {
-        self.ensure(tool::Rustdoc { compiler })
+        self.ensure(tool::Rustdoc { compiler }).tool_path
     }
 
     pub fn cargo_clippy_cmd(&self, run_compiler: Compiler) -> BootstrapCommand {
@@ -1369,14 +1411,13 @@ impl<'a> Builder<'a> {
             return cmd;
         }
 
-        let build_compiler = self.compiler(run_compiler.stage - 1, self.build.build);
-        self.ensure(tool::Clippy { compiler: build_compiler, target: self.build.build });
+        let _ = self.ensure(tool::Clippy { compiler: run_compiler, target: self.build.build });
         let cargo_clippy =
-            self.ensure(tool::CargoClippy { compiler: build_compiler, target: self.build.build });
+            self.ensure(tool::CargoClippy { compiler: run_compiler, target: self.build.build });
         let mut dylib_path = helpers::dylib_path();
         dylib_path.insert(0, self.sysroot(run_compiler).join("lib"));
 
-        let mut cmd = command(cargo_clippy);
+        let mut cmd = command(cargo_clippy.tool_path);
         cmd.env(helpers::dylib_path_var(), env::join_paths(&dylib_path).unwrap());
         cmd.env("CARGO", &self.initial_cargo);
         cmd
@@ -1384,23 +1425,21 @@ impl<'a> Builder<'a> {
 
     pub fn cargo_miri_cmd(&self, run_compiler: Compiler) -> BootstrapCommand {
         assert!(run_compiler.stage > 0, "miri can not be invoked at stage 0");
-        let build_compiler = self.compiler(run_compiler.stage - 1, self.build.build);
-
         // Prepare the tools
-        let miri = self.ensure(tool::Miri { compiler: build_compiler, target: self.build.build });
+        let miri = self.ensure(tool::Miri { compiler: run_compiler, target: self.build.build });
         let cargo_miri =
-            self.ensure(tool::CargoMiri { compiler: build_compiler, target: self.build.build });
+            self.ensure(tool::CargoMiri { compiler: run_compiler, target: self.build.build });
         // Invoke cargo-miri, make sure it can find miri and cargo.
-        let mut cmd = command(cargo_miri);
-        cmd.env("MIRI", &miri);
+        let mut cmd = command(cargo_miri.tool_path);
+        cmd.env("MIRI", &miri.tool_path);
         cmd.env("CARGO", &self.initial_cargo);
-        // Need to add the `run_compiler` libs. Those are the libs produces *by* `build_compiler`,
-        // so they match the Miri we just built. However this means they are actually living one
-        // stage up, i.e. we are running `stage0-tools-bin/miri` with the libraries in `stage1/lib`.
-        // This is an unfortunate off-by-1 caused (possibly) by the fact that Miri doesn't have an
-        // "assemble" step like rustc does that would cross the stage boundary. We can't use
-        // `add_rustc_lib_path` as that's a NOP on Windows but we do need these libraries added to
-        // the PATH due to the stage mismatch.
+        // Need to add the `run_compiler` libs. Those are the libs produces *by* `build_compiler`
+        // in `tool::ToolBuild` step, so they match the Miri we just built. However this means they
+        // are actually living one stage up, i.e. we are running `stage0-tools-bin/miri` with the
+        // libraries in `stage1/lib`. This is an unfortunate off-by-1 caused (possibly) by the fact
+        // that Miri doesn't have an "assemble" step like rustc does that would cross the stage boundary.
+        // We can't use `add_rustc_lib_path` as that's a NOP on Windows but we do need these libraries
+        // added to the PATH due to the stage mismatch.
         // Also see https://github.com/rust-lang/rust/pull/123192#issuecomment-2028901503.
         add_dylib_path(self.rustc_lib_paths(run_compiler), &mut cmd);
         cmd
@@ -1431,7 +1470,7 @@ impl<'a> Builder<'a> {
     ///
     /// Note that this returns `None` if LLVM is disabled, or if we're in a
     /// check build or dry-run, where there's no need to build all of LLVM.
-    fn llvm_config(&self, target: TargetSelection) -> Option<PathBuf> {
+    pub fn llvm_config(&self, target: TargetSelection) -> Option<PathBuf> {
         if self.config.llvm_enabled(target) && self.kind != Kind::Check && !self.config.dry_run() {
             let llvm::LlvmResult { llvm_config, .. } = self.ensure(llvm::Llvm { target });
             if llvm_config.is_file() {
diff --git a/src/bootstrap/src/core/builder/tests.rs b/src/bootstrap/src/core/builder/tests.rs
index 74e1ed5c637..0eaa89792bd 100644
--- a/src/bootstrap/src/core/builder/tests.rs
+++ b/src/bootstrap/src/core/builder/tests.rs
@@ -1,5 +1,7 @@
 use std::thread;
 
+use llvm::prebuilt_llvm_config;
+
 use super::*;
 use crate::Flags;
 use crate::core::build_steps::doc::DocumentationFormat;
@@ -523,6 +525,7 @@ mod dist {
             first(cache.all::<compile::Rustc>()),
             &[
                 rustc!(TEST_TRIPLE_1 => TEST_TRIPLE_1, stage = 0),
+                rustc!(TEST_TRIPLE_1 => TEST_TRIPLE_1, stage = 1),
                 rustc!(TEST_TRIPLE_1 => TEST_TRIPLE_2, stage = 1),
             ]
         );
@@ -954,3 +957,161 @@ fn test_test_coverage() {
         assert_eq!(modes, expected);
     }
 }
+
+#[test]
+fn test_prebuilt_llvm_config_path_resolution() {
+    fn configure(config: &str) -> Config {
+        Config::parse_inner(
+            Flags::parse(&[
+                "build".to_string(),
+                "--dry-run".to_string(),
+                "--config=/does/not/exist".to_string(),
+            ]),
+            |&_| toml::from_str(&config),
+        )
+    }
+
+    // Removes Windows disk prefix if present
+    fn drop_win_disk_prefix_if_present(path: PathBuf) -> PathBuf {
+        let path_str = path.to_str().unwrap();
+        if let Some((_, without_prefix)) = path_str.split_once(":/") {
+            return PathBuf::from(format!("/{}", without_prefix));
+        }
+
+        path
+    }
+
+    let config = configure(
+        r#"
+            [llvm]
+            download-ci-llvm = false
+
+            [build]
+            build = "x86_64-unknown-linux-gnu"
+            host = ["arm-unknown-linux-gnueabihf"]
+            target = ["arm-unknown-linux-gnueabihf"]
+
+            [target.x86_64-unknown-linux-gnu]
+            llvm-config = "/some/path/to/llvm-config"
+
+            [target.arm-unknown-linux-gnueabihf]
+            cc = "arm-linux-gnueabihf-gcc"
+            cxx = "arm-linux-gnueabihf-g++"
+        "#,
+    );
+
+    let build = Build::new(config);
+    let builder = Builder::new(&build);
+
+    let expected = PathBuf::from("/some/path/to/llvm-config");
+
+    let actual = prebuilt_llvm_config(
+        &builder,
+        TargetSelection::from_user("arm-unknown-linux-gnueabihf"),
+        false,
+    )
+    .llvm_result()
+    .llvm_config
+    .clone();
+    let actual = drop_win_disk_prefix_if_present(actual);
+    assert_eq!(expected, actual);
+
+    let actual = prebuilt_llvm_config(&builder, builder.config.build, false)
+        .llvm_result()
+        .llvm_config
+        .clone();
+    let actual = drop_win_disk_prefix_if_present(actual);
+    assert_eq!(expected, actual);
+    assert_eq!(expected, actual);
+
+    let config = configure(
+        r#"
+            [llvm]
+            download-ci-llvm = false
+        "#,
+    );
+
+    let build = Build::new(config.clone());
+    let builder = Builder::new(&build);
+
+    let actual = prebuilt_llvm_config(&builder, builder.config.build, false)
+        .llvm_result()
+        .llvm_config
+        .clone();
+    let expected = builder
+        .out
+        .join(builder.config.build)
+        .join("llvm/bin")
+        .join(exe("llvm-config", builder.config.build));
+    assert_eq!(expected, actual);
+
+    let config = configure(
+        r#"
+            [llvm]
+            download-ci-llvm = true
+        "#,
+    );
+
+    // CI-LLVM isn't always available; check if it's enabled before testing.
+    if config.llvm_from_ci {
+        let build = Build::new(config.clone());
+        let builder = Builder::new(&build);
+
+        let actual = prebuilt_llvm_config(&builder, builder.config.build, false)
+            .llvm_result()
+            .llvm_config
+            .clone();
+        let expected = builder
+            .out
+            .join(builder.config.build)
+            .join("ci-llvm/bin")
+            .join(exe("llvm-config", builder.config.build));
+        assert_eq!(expected, actual);
+    }
+}
+
+#[test]
+fn test_is_builder_target() {
+    let target1 = TargetSelection::from_user(TEST_TRIPLE_1);
+    let target2 = TargetSelection::from_user(TEST_TRIPLE_2);
+
+    for (target1, target2) in [(target1, target2), (target2, target1)] {
+        let mut config = configure("build", &[], &[]);
+        config.build = target1;
+        let build = Build::new(config);
+        let builder = Builder::new(&build);
+
+        assert!(builder.is_builder_target(target1));
+        assert!(!builder.is_builder_target(target2));
+    }
+}
+
+#[test]
+fn test_get_tool_rustc_compiler() {
+    let mut config = configure("build", &[], &[]);
+    config.download_rustc_commit = None;
+    let build = Build::new(config);
+    let builder = Builder::new(&build);
+
+    let target_triple_1 = TargetSelection::from_user(TEST_TRIPLE_1);
+
+    let compiler = Compiler { stage: 2, host: target_triple_1 };
+    let expected = Compiler { stage: 1, host: target_triple_1 };
+    let actual = tool::get_tool_rustc_compiler(&builder, compiler);
+    assert_eq!(expected, actual);
+
+    let compiler = Compiler { stage: 1, host: target_triple_1 };
+    let expected = Compiler { stage: 0, host: target_triple_1 };
+    let actual = tool::get_tool_rustc_compiler(&builder, compiler);
+    assert_eq!(expected, actual);
+
+    let mut config = configure("build", &[], &[]);
+    config.download_rustc_commit = Some("".to_owned());
+    let build = Build::new(config);
+    let builder = Builder::new(&build);
+
+    let compiler = Compiler { stage: 1, host: target_triple_1 };
+    let expected = Compiler { stage: 1, host: target_triple_1 };
+    let actual = tool::get_tool_rustc_compiler(&builder, compiler);
+    assert_eq!(expected, actual);
+}
diff --git a/src/bootstrap/src/core/config/config.rs b/src/bootstrap/src/core/config/config.rs
index d3ed7ecddd3..2be42f16e2a 100644
--- a/src/bootstrap/src/core/config/config.rs
+++ b/src/bootstrap/src/core/config/config.rs
@@ -218,6 +218,8 @@ pub struct Config {
     pub stderr_is_tty: bool,
 
     pub on_fail: Option<String>,
+    pub explicit_stage_from_cli: bool,
+    pub explicit_stage_from_config: bool,
     pub stage: u32,
     pub keep_stage: Vec<u32>,
     pub keep_stage_std: Vec<u32>,
@@ -325,6 +327,9 @@ pub struct Config {
     pub hosts: Vec<TargetSelection>,
     pub targets: Vec<TargetSelection>,
     pub local_rebuild: bool,
+    #[cfg(not(test))]
+    jemalloc: bool,
+    #[cfg(test)]
     pub jemalloc: bool,
     pub control_flow_guard: bool,
     pub ehcont_guard: bool,
@@ -643,6 +648,7 @@ pub struct Target {
     pub no_std: bool,
     pub codegen_backends: Option<Vec<String>>,
     pub optimized_compiler_builtins: Option<bool>,
+    pub jemalloc: Option<bool>,
 }
 
 impl Target {
@@ -935,6 +941,7 @@ define_config! {
         optimized_compiler_builtins: Option<bool> = "optimized-compiler-builtins",
         jobs: Option<u32> = "jobs",
         compiletest_diff_tool: Option<String> = "compiletest-diff-tool",
+        ccache: Option<StringOrBool> = "ccache",
     }
 }
 
@@ -961,6 +968,7 @@ define_config! {
         tests: Option<bool> = "tests",
         enzyme: Option<bool> = "enzyme",
         plugins: Option<bool> = "plugins",
+        // FIXME: Remove this field at Q2 2025, it has been replaced by build.ccache
         ccache: Option<StringOrBool> = "ccache",
         static_libstdcpp: Option<bool> = "static-libstdcpp",
         libzstd: Option<bool> = "libzstd",
@@ -1232,6 +1240,7 @@ define_config! {
         codegen_backends: Option<Vec<String>> = "codegen-backends",
         runner: Option<String> = "runner",
         optimized_compiler_builtins: Option<bool> = "optimized-compiler-builtins",
+        jemalloc: Option<bool> = "jemalloc",
     }
 }
 
@@ -1622,6 +1631,7 @@ impl Config {
             optimized_compiler_builtins,
             jobs,
             compiletest_diff_tool,
+            mut ccache,
         } = toml.build.unwrap_or_default();
 
         config.jobs = Some(threads_from_config(flags.jobs.unwrap_or(jobs.unwrap_or(0))));
@@ -2006,7 +2016,7 @@ impl Config {
                 tests,
                 enzyme,
                 plugins,
-                ccache,
+                ccache: llvm_ccache,
                 static_libstdcpp,
                 libzstd,
                 ninja,
@@ -2029,13 +2039,11 @@ impl Config {
                 download_ci_llvm,
                 build_config,
             } = llvm;
-            match ccache {
-                Some(StringOrBool::String(ref s)) => config.ccache = Some(s.to_string()),
-                Some(StringOrBool::Bool(true)) => {
-                    config.ccache = Some("ccache".to_string());
-                }
-                Some(StringOrBool::Bool(false)) | None => {}
+            if llvm_ccache.is_some() {
+                eprintln!("Warning: llvm.ccache is deprecated. Use build.ccache instead.");
             }
+
+            ccache = ccache.or(llvm_ccache);
             set(&mut config.ninja_in_file, ninja);
             llvm_tests = tests;
             llvm_enzyme = enzyme;
@@ -2160,6 +2168,7 @@ impl Config {
                 target.profiler = cfg.profiler;
                 target.rpath = cfg.rpath;
                 target.optimized_compiler_builtins = cfg.optimized_compiler_builtins;
+                target.jemalloc = cfg.jemalloc;
 
                 if let Some(ref backends) = cfg.codegen_backends {
                     let available_backends = ["llvm", "cranelift", "gcc"];
@@ -2189,6 +2198,14 @@ impl Config {
             }
         }
 
+        match ccache {
+            Some(StringOrBool::String(ref s)) => config.ccache = Some(s.to_string()),
+            Some(StringOrBool::Bool(true)) => {
+                config.ccache = Some("ccache".to_string());
+            }
+            Some(StringOrBool::Bool(false)) | None => {}
+        }
+
         if config.llvm_from_ci {
             let triple = &config.build.triple;
             let ci_llvm_bin = config.ci_llvm_root().join("bin");
@@ -2308,6 +2325,14 @@ impl Config {
         config.compiletest_diff_tool = compiletest_diff_tool;
 
         let download_rustc = config.download_rustc_commit.is_some();
+        config.explicit_stage_from_cli = flags.stage.is_some();
+        config.explicit_stage_from_config = test_stage.is_some()
+            || build_stage.is_some()
+            || doc_stage.is_some()
+            || dist_stage.is_some()
+            || install_stage.is_some()
+            || check_stage.is_some()
+            || bench_stage.is_some();
         // See https://github.com/rust-lang/compiler-team/issues/326
         config.stage = match config.cmd {
             Subcommand::Check { .. } => flags.stage.or(check_stage).unwrap_or(0),
@@ -2315,21 +2340,21 @@ impl Config {
             Subcommand::Doc { .. } => {
                 flags.stage.or(doc_stage).unwrap_or(if download_rustc { 2 } else { 0 })
             }
-            Subcommand::Build { .. } => {
+            Subcommand::Build => {
                 flags.stage.or(build_stage).unwrap_or(if download_rustc { 2 } else { 1 })
             }
             Subcommand::Test { .. } | Subcommand::Miri { .. } => {
                 flags.stage.or(test_stage).unwrap_or(if download_rustc { 2 } else { 1 })
             }
             Subcommand::Bench { .. } => flags.stage.or(bench_stage).unwrap_or(2),
-            Subcommand::Dist { .. } => flags.stage.or(dist_stage).unwrap_or(2),
-            Subcommand::Install { .. } => flags.stage.or(install_stage).unwrap_or(2),
+            Subcommand::Dist => flags.stage.or(dist_stage).unwrap_or(2),
+            Subcommand::Install => flags.stage.or(install_stage).unwrap_or(2),
             Subcommand::Perf { .. } => flags.stage.unwrap_or(1),
             // These are all bootstrap tools, which don't depend on the compiler.
             // The stage we pass shouldn't matter, but use 0 just in case.
             Subcommand::Clean { .. }
             | Subcommand::Clippy { .. }
-            | Subcommand::Fix { .. }
+            | Subcommand::Fix
             | Subcommand::Run { .. }
             | Subcommand::Setup { .. }
             | Subcommand::Format { .. }
@@ -2344,10 +2369,10 @@ impl Config {
                 Subcommand::Test { .. }
                 | Subcommand::Miri { .. }
                 | Subcommand::Doc { .. }
-                | Subcommand::Build { .. }
+                | Subcommand::Build
                 | Subcommand::Bench { .. }
-                | Subcommand::Dist { .. }
-                | Subcommand::Install { .. } => {
+                | Subcommand::Dist
+                | Subcommand::Install => {
                     assert_eq!(
                         config.stage, 2,
                         "x.py should be run with `--stage 2` on CI, but was run with `--stage {}`",
@@ -2357,7 +2382,7 @@ impl Config {
                 Subcommand::Clean { .. }
                 | Subcommand::Check { .. }
                 | Subcommand::Clippy { .. }
-                | Subcommand::Fix { .. }
+                | Subcommand::Fix
                 | Subcommand::Run { .. }
                 | Subcommand::Setup { .. }
                 | Subcommand::Format { .. }
@@ -2377,6 +2402,10 @@ impl Config {
         }
     }
 
+    pub fn is_explicit_stage(&self) -> bool {
+        self.explicit_stage_from_cli || self.explicit_stage_from_config
+    }
+
     /// Runs a command, printing out nice contextual information if it fails.
     /// Exits if the command failed to execute at all, otherwise returns its
     /// `status.success()`.
@@ -2717,6 +2746,10 @@ impl Config {
             .unwrap_or(&self.rust_codegen_backends)
     }
 
+    pub fn jemalloc(&self, target: TargetSelection) -> bool {
+        self.target_config.get(&target).and_then(|cfg| cfg.jemalloc).unwrap_or(self.jemalloc)
+    }
+
     pub fn default_codegen_backend(&self, target: TargetSelection) -> Option<String> {
         self.codegen_backends(target).first().cloned()
     }
@@ -2738,8 +2771,17 @@ impl Config {
     /// tarball). Typically [`crate::Build::require_submodule`] should be
     /// used instead to provide a nice error to the user if the submodule is
     /// missing.
+    #[cfg_attr(
+        feature = "tracing",
+        instrument(
+            level = "trace",
+            name = "Config::update_submodule",
+            skip_all,
+            fields(relative_path = ?relative_path),
+        ),
+    )]
     pub(crate) fn update_submodule(&self, relative_path: &str) {
-        if !self.submodules() {
+        if self.rust_info.is_from_tarball() || !self.submodules() {
             return;
         }
 
diff --git a/src/bootstrap/src/core/config/flags.rs b/src/bootstrap/src/core/config/flags.rs
index c08a041ebcd..3bb62bbe380 100644
--- a/src/bootstrap/src/core/config/flags.rs
+++ b/src/bootstrap/src/core/config/flags.rs
@@ -490,17 +490,17 @@ impl Subcommand {
     pub fn kind(&self) -> Kind {
         match self {
             Subcommand::Bench { .. } => Kind::Bench,
-            Subcommand::Build { .. } => Kind::Build,
+            Subcommand::Build => Kind::Build,
             Subcommand::Check { .. } => Kind::Check,
             Subcommand::Clippy { .. } => Kind::Clippy,
             Subcommand::Doc { .. } => Kind::Doc,
-            Subcommand::Fix { .. } => Kind::Fix,
+            Subcommand::Fix => Kind::Fix,
             Subcommand::Format { .. } => Kind::Format,
             Subcommand::Test { .. } => Kind::Test,
             Subcommand::Miri { .. } => Kind::Miri,
             Subcommand::Clean { .. } => Kind::Clean,
-            Subcommand::Dist { .. } => Kind::Dist,
-            Subcommand::Install { .. } => Kind::Install,
+            Subcommand::Dist => Kind::Dist,
+            Subcommand::Install => Kind::Install,
             Subcommand::Run { .. } => Kind::Run,
             Subcommand::Setup { .. } => Kind::Setup,
             Subcommand::Suggest { .. } => Kind::Suggest,
diff --git a/src/bootstrap/src/core/config/tests.rs b/src/bootstrap/src/core/config/tests.rs
index f0a185ee3a7..eff5e033742 100644
--- a/src/bootstrap/src/core/config/tests.rs
+++ b/src/bootstrap/src/core/config/tests.rs
@@ -454,3 +454,64 @@ fn check_rustc_if_unchanged_paths() {
         assert!(config.src.join(p).exists(), "{p} doesn't exist.");
     }
 }
+
+#[test]
+fn test_explicit_stage() {
+    let config = Config::parse_inner(
+        Flags::parse(&["check".to_owned(), "--config=/does/not/exist".to_owned()]),
+        |&_| {
+            toml::from_str(
+                r#"
+            [build]
+            test-stage = 1
+        "#,
+            )
+        },
+    );
+
+    assert!(!config.explicit_stage_from_cli);
+    assert!(config.explicit_stage_from_config);
+    assert!(config.is_explicit_stage());
+
+    let config = Config::parse_inner(
+        Flags::parse(&[
+            "check".to_owned(),
+            "--stage=2".to_owned(),
+            "--config=/does/not/exist".to_owned(),
+        ]),
+        |&_| toml::from_str(""),
+    );
+
+    assert!(config.explicit_stage_from_cli);
+    assert!(!config.explicit_stage_from_config);
+    assert!(config.is_explicit_stage());
+
+    let config = Config::parse_inner(
+        Flags::parse(&[
+            "check".to_owned(),
+            "--stage=2".to_owned(),
+            "--config=/does/not/exist".to_owned(),
+        ]),
+        |&_| {
+            toml::from_str(
+                r#"
+            [build]
+            test-stage = 1
+        "#,
+            )
+        },
+    );
+
+    assert!(config.explicit_stage_from_cli);
+    assert!(config.explicit_stage_from_config);
+    assert!(config.is_explicit_stage());
+
+    let config = Config::parse_inner(
+        Flags::parse(&["check".to_owned(), "--config=/does/not/exist".to_owned()]),
+        |&_| toml::from_str(""),
+    );
+
+    assert!(!config.explicit_stage_from_cli);
+    assert!(!config.explicit_stage_from_config);
+    assert!(!config.is_explicit_stage());
+}
diff --git a/src/bootstrap/src/core/metadata.rs b/src/bootstrap/src/core/metadata.rs
index 983674d2c68..01cbf662940 100644
--- a/src/bootstrap/src/core/metadata.rs
+++ b/src/bootstrap/src/core/metadata.rs
@@ -1,3 +1,10 @@
+//! This module interacts with Cargo metadata to collect and store information about
+//! the packages in the Rust workspace.
+//!
+//! It runs `cargo metadata` to gather details about each package, including its name,
+//! source, dependencies, targets, and available features. The collected metadata is then
+//! used to update the `Build` structure, ensuring proper dependency resolution and
+//! compilation flow.
 use std::collections::BTreeMap;
 use std::path::PathBuf;
 
diff --git a/src/bootstrap/src/core/sanity.rs b/src/bootstrap/src/core/sanity.rs
index 6c8cda18548..fdac7f3cb17 100644
--- a/src/bootstrap/src/core/sanity.rs
+++ b/src/bootstrap/src/core/sanity.rs
@@ -34,10 +34,6 @@ pub struct Finder {
 // Targets can be removed from this list once they are present in the stage0 compiler (usually by updating the beta compiler of the bootstrap).
 const STAGE0_MISSING_TARGETS: &[&str] = &[
     // just a dummy comment so the list doesn't get onelined
-    "aarch64-unknown-nto-qnx710_iosock",
-    "x86_64-pc-nto-qnx710_iosock",
-    "x86_64-pc-nto-qnx800",
-    "aarch64-unknown-nto-qnx800",
 ];
 
 /// Minimum version threshold for libstdc++ required when using prebuilt LLVM
@@ -329,7 +325,7 @@ than building it.
         if target.contains("musl") && !target.contains("unikraft") {
             // If this is a native target (host is also musl) and no musl-root is given,
             // fall back to the system toolchain in /usr before giving up
-            if build.musl_root(*target).is_none() && build.config.build == *target {
+            if build.musl_root(*target).is_none() && build.is_builder_target(*target) {
                 let target = build.config.target_config.entry(*target).or_default();
                 target.musl_root = Some("/usr".into());
             }
diff --git a/src/bootstrap/src/lib.rs b/src/bootstrap/src/lib.rs
index bc146eda1d5..351e67f6702 100644
--- a/src/bootstrap/src/lib.rs
+++ b/src/bootstrap/src/lib.rs
@@ -50,6 +50,8 @@ pub use utils::change_tracker::{
     CONFIG_CHANGE_HISTORY, find_recent_config_change_ids, human_readable_changes,
 };
 
+use crate::core::build_steps::vendor::VENDOR_DIR;
+
 const LLVM_TOOLS: &[&str] = &[
     "llvm-cov",      // used to generate coverage report
     "llvm-nm",       // used to inspect binaries; it shows symbol names, their sizes and visibility
@@ -249,6 +251,7 @@ impl Mode {
     }
 }
 
+#[derive(Debug, Hash, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
 pub enum CLang {
     C,
     Cxx,
@@ -468,7 +471,20 @@ impl Build {
     ///
     /// The given `err_hint` will be shown to the user if the submodule is not
     /// checked out and submodule management is disabled.
+    #[cfg_attr(
+        feature = "tracing",
+        instrument(
+            level = "trace",
+            name = "Build::require_submodule",
+            skip_all,
+            fields(submodule = submodule),
+        ),
+    )]
     pub fn require_submodule(&self, submodule: &str, err_hint: Option<&str>) {
+        if self.rust_info().is_from_tarball() {
+            return;
+        }
+
         // When testing bootstrap itself, it is much faster to ignore
         // submodules. Almost all Steps work fine without their submodules.
         if cfg!(test) && !self.config.submodules() {
@@ -476,7 +492,7 @@ impl Build {
         }
         self.config.update_submodule(submodule);
         let absolute_path = self.config.src.join(submodule);
-        if dir_is_empty(&absolute_path) {
+        if !absolute_path.exists() || dir_is_empty(&absolute_path) {
             let maybe_enable = if !self.config.submodules()
                 && self.config.rust_info.is_managed_git_subrepository()
             {
@@ -621,7 +637,7 @@ impl Build {
 
         // Check for postponed failures from `test --no-fail-fast`.
         let failures = self.delayed_failures.borrow();
-        if failures.len() > 0 {
+        if !failures.is_empty() {
             eprintln!("\n{} command(s) did not execute successfully:\n", failures.len());
             for failure in failures.iter() {
                 eprintln!("  - {failure}\n");
@@ -676,7 +692,7 @@ impl Build {
             crates.is_empty() || possible_features_by_crates.contains(feature)
         };
         let mut features = vec![];
-        if self.config.jemalloc && check("jemalloc") {
+        if self.config.jemalloc(target) && check("jemalloc") {
             features.push("jemalloc");
         }
         if (self.config.llvm_enabled(target) || kind == Kind::Check) && check("llvm") {
@@ -738,7 +754,7 @@ impl Build {
     /// Note that if LLVM is configured externally then the directory returned
     /// will likely be empty.
     fn llvm_out(&self, target: TargetSelection) -> PathBuf {
-        if self.config.llvm_from_ci && self.config.build == target {
+        if self.config.llvm_from_ci && self.is_builder_target(target) {
             self.config.ci_llvm_root()
         } else {
             self.out.join(target).join("llvm")
@@ -781,6 +797,11 @@ impl Build {
         self.out.join(target).join("md-doc")
     }
 
+    /// Path to the vendored Rust crates.
+    fn vendored_crates_path(&self) -> Option<PathBuf> {
+        if self.config.vendor { Some(self.src.join(VENDOR_DIR)) } else { None }
+    }
+
     /// Returns `true` if this is an external version of LLVM not managed by bootstrap.
     /// In particular, we expect llvm sources to be available when this is false.
     ///
@@ -788,7 +809,7 @@ impl Build {
     fn is_system_llvm(&self, target: TargetSelection) -> bool {
         match self.config.target_config.get(&target) {
             Some(Target { llvm_config: Some(_), .. }) => {
-                let ci_llvm = self.config.llvm_from_ci && target == self.config.build;
+                let ci_llvm = self.config.llvm_from_ci && self.is_builder_target(target);
                 !ci_llvm
             }
             // We're building from the in-tree src/llvm-project sources.
@@ -904,6 +925,9 @@ impl Build {
             return CommandOutput::default();
         }
 
+        #[cfg(feature = "tracing")]
+        let _run_span = trace_cmd!(command);
+
         let created_at = command.get_created_location();
         let executed_at = std::panic::Location::caller();
 
@@ -1178,9 +1202,9 @@ Executed at: {executed_at}"#,
         self.cc.borrow()[&target].path().into()
     }
 
-    /// Returns a list of flags to pass to the C compiler for the target
-    /// specified.
-    fn cflags(&self, target: TargetSelection, which: GitRepo, c: CLang) -> Vec<String> {
+    /// Returns C flags that `cc-rs` thinks should be enabled for the
+    /// specified target by default.
+    fn cc_handled_clags(&self, target: TargetSelection, c: CLang) -> Vec<String> {
         if self.config.dry_run() {
             return Vec::new();
         }
@@ -1189,14 +1213,23 @@ Executed at: {executed_at}"#,
             CLang::Cxx => self.cxx.borrow()[&target].clone(),
         };
 
-        // Filter out -O and /O (the optimization flags) that we picked up from
-        // cc-rs because the build scripts will determine that for themselves.
-        let mut base = base
-            .args()
+        // Filter out -O and /O (the optimization flags) that we picked up
+        // from cc-rs, that's up to the caller to figure out.
+        base.args()
             .iter()
             .map(|s| s.to_string_lossy().into_owned())
             .filter(|s| !s.starts_with("-O") && !s.starts_with("/O"))
-            .collect::<Vec<String>>();
+            .collect::<Vec<String>>()
+    }
+
+    /// Returns extra C flags that `cc-rs` doesn't handle.
+    fn cc_unhandled_cflags(
+        &self,
+        target: TargetSelection,
+        which: GitRepo,
+        c: CLang,
+    ) -> Vec<String> {
+        let mut base = Vec::new();
 
         // If we're compiling C++ on macOS then we add a flag indicating that
         // we want libc++ (more filled out than libstdc++), ensuring that
@@ -1264,7 +1297,7 @@ Executed at: {executed_at}"#,
             // need to use CXX compiler as linker to resolve the exception functions
             // that are only existed in CXX libraries
             Some(self.cxx.borrow()[&target].path().into())
-        } else if target != self.config.build
+        } else if !self.is_builder_target(target)
             && helpers::use_host_linker(target)
             && !target.is_msvc()
         {
@@ -1915,6 +1948,11 @@ to download LLVM rather than building it.
         stream.reset().unwrap();
         result
     }
+
+    /// Checks if the given target is the same as the builder target.
+    fn is_builder_target(&self, target: TargetSelection) -> bool {
+        self.config.build == target
+    }
 }
 
 #[cfg(unix)]
diff --git a/src/bootstrap/src/utils/cache.rs b/src/bootstrap/src/utils/cache.rs
index 29342cc5a2c..1c8cc4025df 100644
--- a/src/bootstrap/src/utils/cache.rs
+++ b/src/bootstrap/src/utils/cache.rs
@@ -1,3 +1,17 @@
+//! This module helps you efficiently store and retrieve values using interning.
+//!
+//! Interning is a neat trick that keeps only one copy of identical values, saving memory
+//! and making comparisons super fast. Here, we provide the `Interned<T>` struct and the `Internable` trait
+//! to make interning easy for different data types.
+//!
+//! The `Interner` struct handles caching for common types like `String`, `PathBuf`, and `Vec<String>`,
+//! while the `Cache` struct acts as a write-once storage for linking computation steps with their results.
+//!
+//! # Thread Safety
+//!
+//! We use `Mutex` to make sure interning and retrieval are thread-safe. But keep in mind—once a value is
+//! interned, it sticks around for the entire lifetime of the program.
+
 use std::any::{Any, TypeId};
 use std::borrow::Borrow;
 use std::cell::RefCell;
@@ -12,6 +26,9 @@ use std::{fmt, mem};
 
 use crate::core::builder::Step;
 
+/// Represents an interned value of type `T`, allowing for efficient comparisons and retrieval.
+///
+/// This struct stores a unique index referencing the interned value within an internal cache.
 pub struct Interned<T>(usize, PhantomData<*const T>);
 
 impl<T: Internable + Default> Default for Interned<T> {
@@ -111,6 +128,10 @@ impl<T: Internable + Ord> Ord for Interned<T> {
     }
 }
 
+/// A structure for managing the interning of values of type `T`.
+///
+/// `TyIntern<T>` maintains a mapping between values and their interned representations,
+/// ensuring that duplicate values are not stored multiple times.
 struct TyIntern<T: Clone + Eq> {
     items: Vec<T>,
     set: HashMap<T, Interned<T>>,
@@ -123,6 +144,9 @@ impl<T: Hash + Clone + Eq> Default for TyIntern<T> {
 }
 
 impl<T: Hash + Clone + Eq> TyIntern<T> {
+    /// Interns a borrowed value, ensuring it is stored uniquely.
+    ///
+    /// If the value has been previously interned, the same `Interned<T>` instance is returned.
     fn intern_borrow<B>(&mut self, item: &B) -> Interned<T>
     where
         B: Eq + Hash + ToOwned<Owned = T> + ?Sized,
@@ -138,6 +162,9 @@ impl<T: Hash + Clone + Eq> TyIntern<T> {
         interned
     }
 
+    /// Interns an owned value, storing it uniquely.
+    ///
+    /// If the value has been previously interned, the existing `Interned<T>` is returned.
     fn intern(&mut self, item: T) -> Interned<T> {
         if let Some(i) = self.set.get(&item) {
             return *i;
@@ -148,11 +175,16 @@ impl<T: Hash + Clone + Eq> TyIntern<T> {
         interned
     }
 
+    /// Retrieves a reference to the interned value associated with the given `Interned<T>` instance.
     fn get(&self, i: Interned<T>) -> &T {
         &self.items[i.0]
     }
 }
 
+/// A global interner for managing interned values of common types.
+///
+/// This structure maintains caches for `String`, `PathBuf`, and `Vec<String>`, ensuring efficient storage
+/// and retrieval of frequently used values.
 #[derive(Default)]
 pub struct Interner {
     strs: Mutex<TyIntern<String>>,
@@ -160,6 +192,10 @@ pub struct Interner {
     lists: Mutex<TyIntern<Vec<String>>>,
 }
 
+/// Defines the behavior required for a type to be internable.
+///
+/// Types implementing this trait must provide access to a static cache and define an `intern` method
+/// that ensures values are stored uniquely.
 trait Internable: Clone + Eq + Hash + 'static {
     fn intern_cache() -> &'static Mutex<TyIntern<Self>>;
 
@@ -187,11 +223,15 @@ impl Internable for Vec<String> {
 }
 
 impl Interner {
+    /// Interns a string reference, ensuring it is stored uniquely.
+    ///
+    /// If the string has been previously interned, the same `Interned<String>` instance is returned.
     pub fn intern_str(&self, s: &str) -> Interned<String> {
         self.strs.lock().unwrap().intern_borrow(s)
     }
 }
 
+/// A global instance of `Interner` that caches common interned values.
 pub static INTERNER: LazyLock<Interner> = LazyLock::new(Interner::default);
 
 /// This is essentially a `HashMap` which allows storing any type in its input and
@@ -209,10 +249,12 @@ pub struct Cache(
 );
 
 impl Cache {
+    /// Creates a new empty cache.
     pub fn new() -> Cache {
         Cache(RefCell::new(HashMap::new()))
     }
 
+    /// Stores the result of a computation step in the cache.
     pub fn put<S: Step>(&self, step: S, value: S::Output) {
         let mut cache = self.0.borrow_mut();
         let type_id = TypeId::of::<S>();
@@ -225,6 +267,7 @@ impl Cache {
         stepcache.insert(step, value);
     }
 
+    /// Retrieves a cached result for the given step, if available.
     pub fn get<S: Step>(&self, step: &S) -> Option<S::Output> {
         let mut cache = self.0.borrow_mut();
         let type_id = TypeId::of::<S>();
@@ -255,3 +298,6 @@ impl Cache {
         self.0.borrow().contains_key(&TypeId::of::<S>())
     }
 }
+
+#[cfg(test)]
+mod tests;
diff --git a/src/bootstrap/src/utils/cache/tests.rs b/src/bootstrap/src/utils/cache/tests.rs
new file mode 100644
index 00000000000..28f5563a589
--- /dev/null
+++ b/src/bootstrap/src/utils/cache/tests.rs
@@ -0,0 +1,52 @@
+use std::path::PathBuf;
+
+use crate::utils::cache::{INTERNER, Internable, TyIntern};
+
+#[test]
+fn test_string_interning() {
+    let s1 = INTERNER.intern_str("Hello");
+    let s2 = INTERNER.intern_str("Hello");
+    let s3 = INTERNER.intern_str("world");
+
+    assert_eq!(s1, s2, "Same strings should be interned to the same instance");
+    assert_ne!(s1, s3, "Different strings should have different interned values");
+}
+
+#[test]
+fn test_path_interning() {
+    let p1 = PathBuf::from("/tmp/file").intern();
+    let p2 = PathBuf::from("/tmp/file").intern();
+    let p3 = PathBuf::from("/tmp/other").intern();
+
+    assert_eq!(p1, p2);
+    assert_ne!(p1, p3);
+}
+
+#[test]
+fn test_vec_interning() {
+    let v1 = vec!["a".to_string(), "b".to_string()].intern();
+    let v2 = vec!["a".to_string(), "b".to_string()].intern();
+    let v3 = vec!["c".to_string()].intern();
+
+    assert_eq!(v1, v2);
+    assert_ne!(v1, v3);
+}
+
+#[test]
+fn test_interned_equality() {
+    let s1 = INTERNER.intern_str("test");
+    let s2 = INTERNER.intern_str("test");
+
+    assert_eq!(s1, s2);
+    assert_eq!(s1, "test");
+}
+
+#[test]
+fn test_ty_intern_intern_borrow() {
+    let mut interner = TyIntern::default();
+    let s1 = interner.intern_borrow("borrowed");
+    let s2 = interner.intern("borrowed".to_string());
+
+    assert_eq!(s1, s2);
+    assert_eq!(interner.get(s1), "borrowed");
+}
diff --git a/src/bootstrap/src/utils/cc_detect.rs b/src/bootstrap/src/utils/cc_detect.rs
index 4aec554b432..1e84a7deff1 100644
--- a/src/bootstrap/src/utils/cc_detect.rs
+++ b/src/bootstrap/src/utils/cc_detect.rs
@@ -29,11 +29,9 @@ use crate::core::config::TargetSelection;
 use crate::utils::exec::{BootstrapCommand, command};
 use crate::{Build, CLang, GitRepo};
 
-// The `cc` crate doesn't provide a way to obtain a path to the detected archiver,
-// so use some simplified logic here. First we respect the environment variable `AR`, then
-// try to infer the archiver path from the C compiler path.
-// In the future this logic should be replaced by calling into the `cc` crate.
-fn cc2ar(cc: &Path, target: TargetSelection) -> Option<PathBuf> {
+/// Finds archiver tool for the given target if possible.
+/// FIXME(onur-ozkan): This logic should be replaced by calling into the `cc` crate.
+fn cc2ar(cc: &Path, target: TargetSelection, default_ar: PathBuf) -> Option<PathBuf> {
     if let Some(ar) = env::var_os(format!("AR_{}", target.triple.replace('-', "_"))) {
         Some(PathBuf::from(ar))
     } else if let Some(ar) = env::var_os("AR") {
@@ -57,19 +55,11 @@ fn cc2ar(cc: &Path, target: TargetSelection) -> Option<PathBuf> {
     } else if target.contains("android") || target.contains("-wasi") {
         Some(cc.parent().unwrap().join(PathBuf::from("llvm-ar")))
     } else {
-        let parent = cc.parent().unwrap();
-        let file = cc.file_name().unwrap().to_str().unwrap();
-        for suffix in &["gcc", "cc", "clang"] {
-            if let Some(idx) = file.rfind(suffix) {
-                let mut file = file[..idx].to_owned();
-                file.push_str("ar");
-                return Some(parent.join(&file));
-            }
-        }
-        Some(parent.join(file))
+        Some(default_ar)
     }
 }
 
+/// Creates and configures a new [`cc::Build`] instance for the given target.
 fn new_cc_build(build: &Build, target: TargetSelection) -> cc::Build {
     let mut cfg = cc::Build::new();
     cfg.cargo_metadata(false)
@@ -96,6 +86,12 @@ fn new_cc_build(build: &Build, target: TargetSelection) -> cc::Build {
     cfg
 }
 
+/// Probes for C and C++ compilers and configures the corresponding entries in the [`Build`]
+/// structure.
+///
+/// This function determines which targets need a C compiler (and, if needed, a C++ compiler)
+/// by combining the primary build target, host targets, and any additional targets. For
+/// each target, it calls [`find_target`] to configure the necessary compiler tools.
 pub fn find(build: &Build) {
     let targets: HashSet<_> = match build.config.cmd {
         // We don't need to check cross targets for these commands.
@@ -124,6 +120,11 @@ pub fn find(build: &Build) {
     }
 }
 
+/// Probes and configures the C and C++ compilers for a single target.
+///
+/// This function uses both user-specified configuration (from `config.toml`) and auto-detection
+/// logic to determine the correct C/C++ compilers for the target. It also determines the appropriate
+/// archiver (`ar`) and sets up additional compilation flags (both handled and unhandled).
 pub fn find_target(build: &Build, target: TargetSelection) {
     let mut cfg = new_cc_build(build, target);
     let config = build.config.target_config.get(&target);
@@ -138,11 +139,12 @@ pub fn find_target(build: &Build, target: TargetSelection) {
     let ar = if let ar @ Some(..) = config.and_then(|c| c.ar.clone()) {
         ar
     } else {
-        cc2ar(compiler.path(), target)
+        cc2ar(compiler.path(), target, PathBuf::from(cfg.get_archiver().get_program()))
     };
 
     build.cc.borrow_mut().insert(target, compiler.clone());
-    let cflags = build.cflags(target, GitRepo::Rustc, CLang::C);
+    let mut cflags = build.cc_handled_clags(target, CLang::C);
+    cflags.extend(build.cc_unhandled_cflags(target, GitRepo::Rustc, CLang::C));
 
     // If we use llvm-libunwind, we will need a C++ compiler as well for all targets
     // We'll need one anyways if the target triple is also a host triple
@@ -168,7 +170,8 @@ pub fn find_target(build: &Build, target: TargetSelection) {
     build.verbose(|| println!("CC_{} = {:?}", target.triple, build.cc(target)));
     build.verbose(|| println!("CFLAGS_{} = {cflags:?}", target.triple));
     if let Ok(cxx) = build.cxx(target) {
-        let cxxflags = build.cflags(target, GitRepo::Rustc, CLang::Cxx);
+        let mut cxxflags = build.cc_handled_clags(target, CLang::Cxx);
+        cxxflags.extend(build.cc_unhandled_cflags(target, GitRepo::Rustc, CLang::Cxx));
         build.verbose(|| println!("CXX_{} = {cxx:?}", target.triple));
         build.verbose(|| println!("CXXFLAGS_{} = {cxxflags:?}", target.triple));
     }
@@ -182,6 +185,8 @@ pub fn find_target(build: &Build, target: TargetSelection) {
     }
 }
 
+/// Determines the default compiler for a given target and language when not explicitly
+/// configured in `config.toml`.
 fn default_compiler(
     cfg: &mut cc::Build,
     compiler: Language,
@@ -258,6 +263,12 @@ fn default_compiler(
     }
 }
 
+/// Constructs the path to the Android NDK compiler for the given target triple and language.
+///
+/// This helper function transform the target triple by converting certain architecture names
+/// (for example, translating "arm" to "arm7a"), appends the minimum API level (hardcoded as "21"
+/// for NDK r26d), and then constructs the full path based on the provided NDK directory and host
+/// platform.
 pub(crate) fn ndk_compiler(compiler: Language, triple: &str, ndk: &Path) -> PathBuf {
     let mut triple_iter = triple.split('-');
     let triple_translated = if let Some(arch) = triple_iter.next() {
@@ -287,7 +298,11 @@ pub(crate) fn ndk_compiler(compiler: Language, triple: &str, ndk: &Path) -> Path
     ndk.join("toolchains").join("llvm").join("prebuilt").join(host_tag).join("bin").join(compiler)
 }
 
-/// The target programming language for a native compiler.
+/// Representing the target programming language for a native compiler.
+///
+/// This enum is used to indicate whether a particular compiler is intended for C or C++.
+/// It also provides helper methods for obtaining the standard executable names for GCC and
+/// clang-based compilers.
 #[derive(PartialEq)]
 pub(crate) enum Language {
     /// The compiler is targeting C.
@@ -297,7 +312,7 @@ pub(crate) enum Language {
 }
 
 impl Language {
-    /// Obtains the name of a compiler in the GCC collection.
+    /// Returns the executable name for a GCC compiler corresponding to this language.
     fn gcc(self) -> &'static str {
         match self {
             Language::C => "gcc",
@@ -305,7 +320,7 @@ impl Language {
         }
     }
 
-    /// Obtains the name of a compiler in the clang suite.
+    /// Returns the executable name for a clang-based compiler corresponding to this language.
     fn clang(self) -> &'static str {
         match self {
             Language::C => "clang",
@@ -313,3 +328,6 @@ impl Language {
         }
     }
 }
+
+#[cfg(test)]
+mod tests;
diff --git a/src/bootstrap/src/utils/cc_detect/tests.rs b/src/bootstrap/src/utils/cc_detect/tests.rs
new file mode 100644
index 00000000000..006dfe7e5d7
--- /dev/null
+++ b/src/bootstrap/src/utils/cc_detect/tests.rs
@@ -0,0 +1,254 @@
+use std::path::{Path, PathBuf};
+use std::{env, iter};
+
+use super::*;
+use crate::core::config::{Target, TargetSelection};
+use crate::{Build, Config, Flags};
+
+#[test]
+fn test_cc2ar_env_specific() {
+    let triple = "x86_64-unknown-linux-gnu";
+    let key = "AR_x86_64_unknown_linux_gnu";
+    env::set_var(key, "custom-ar");
+    let target = TargetSelection::from_user(triple);
+    let cc = Path::new("/usr/bin/clang");
+    let default_ar = PathBuf::from("default-ar");
+    let result = cc2ar(cc, target, default_ar);
+    env::remove_var(key);
+    assert_eq!(result, Some(PathBuf::from("custom-ar")));
+}
+
+#[test]
+fn test_cc2ar_musl() {
+    let triple = "x86_64-unknown-linux-musl";
+    env::remove_var("AR_x86_64_unknown_linux_musl");
+    env::remove_var("AR");
+    let target = TargetSelection::from_user(triple);
+    let cc = Path::new("/usr/bin/clang");
+    let default_ar = PathBuf::from("default-ar");
+    let result = cc2ar(cc, target, default_ar);
+    assert_eq!(result, Some(PathBuf::from("ar")));
+}
+
+#[test]
+fn test_cc2ar_openbsd() {
+    let triple = "x86_64-unknown-openbsd";
+    env::remove_var("AR_x86_64_unknown_openbsd");
+    env::remove_var("AR");
+    let target = TargetSelection::from_user(triple);
+    let cc = Path::new("/usr/bin/cc");
+    let default_ar = PathBuf::from("default-ar");
+    let result = cc2ar(cc, target, default_ar);
+    assert_eq!(result, Some(PathBuf::from("ar")));
+}
+
+#[test]
+fn test_cc2ar_vxworks() {
+    let triple = "armv7-wrs-vxworks";
+    env::remove_var("AR_armv7_wrs_vxworks");
+    env::remove_var("AR");
+    let target = TargetSelection::from_user(triple);
+    let cc = Path::new("/usr/bin/clang");
+    let default_ar = PathBuf::from("default-ar");
+    let result = cc2ar(cc, target, default_ar);
+    assert_eq!(result, Some(PathBuf::from("wr-ar")));
+}
+
+#[test]
+fn test_cc2ar_nto_i586() {
+    let triple = "i586-unknown-nto-something";
+    env::remove_var("AR_i586_unknown_nto_something");
+    env::remove_var("AR");
+    let target = TargetSelection::from_user(triple);
+    let cc = Path::new("/usr/bin/clang");
+    let default_ar = PathBuf::from("default-ar");
+    let result = cc2ar(cc, target, default_ar);
+    assert_eq!(result, Some(PathBuf::from("ntox86-ar")));
+}
+
+#[test]
+fn test_cc2ar_nto_aarch64() {
+    let triple = "aarch64-unknown-nto-something";
+    env::remove_var("AR_aarch64_unknown_nto_something");
+    env::remove_var("AR");
+    let target = TargetSelection::from_user(triple);
+    let cc = Path::new("/usr/bin/clang");
+    let default_ar = PathBuf::from("default-ar");
+    let result = cc2ar(cc, target, default_ar);
+    assert_eq!(result, Some(PathBuf::from("ntoaarch64-ar")));
+}
+
+#[test]
+fn test_cc2ar_nto_x86_64() {
+    let triple = "x86_64-unknown-nto-something";
+    env::remove_var("AR_x86_64_unknown_nto_something");
+    env::remove_var("AR");
+    let target = TargetSelection::from_user(triple);
+    let cc = Path::new("/usr/bin/clang");
+    let default_ar = PathBuf::from("default-ar");
+    let result = cc2ar(cc, target, default_ar);
+    assert_eq!(result, Some(PathBuf::from("ntox86_64-ar")));
+}
+
+#[test]
+#[should_panic(expected = "Unknown architecture, cannot determine archiver for Neutrino QNX")]
+fn test_cc2ar_nto_unknown() {
+    let triple = "powerpc-unknown-nto-something";
+    env::remove_var("AR_powerpc_unknown_nto_something");
+    env::remove_var("AR");
+    let target = TargetSelection::from_user(triple);
+    let cc = Path::new("/usr/bin/clang");
+    let default_ar = PathBuf::from("default-ar");
+    let _ = cc2ar(cc, target, default_ar);
+}
+
+#[test]
+fn test_ndk_compiler_c() {
+    let ndk_path = PathBuf::from("/ndk");
+    let target_triple = "arm-unknown-linux-android";
+    let expected_triple_translated = "armv7a-unknown-linux-android";
+    let expected_compiler = format!("{}21-{}", expected_triple_translated, Language::C.clang());
+    let host_tag = if cfg!(target_os = "macos") {
+        "darwin-x86_64"
+    } else if cfg!(target_os = "windows") {
+        "windows-x86_64"
+    } else {
+        "linux-x86_64"
+    };
+    let expected_path = ndk_path
+        .join("toolchains")
+        .join("llvm")
+        .join("prebuilt")
+        .join(host_tag)
+        .join("bin")
+        .join(&expected_compiler);
+    let result = ndk_compiler(Language::C, target_triple, &ndk_path);
+    assert_eq!(result, expected_path);
+}
+
+#[test]
+fn test_ndk_compiler_cpp() {
+    let ndk_path = PathBuf::from("/ndk");
+    let target_triple = "arm-unknown-linux-android";
+    let expected_triple_translated = "armv7a-unknown-linux-android";
+    let expected_compiler =
+        format!("{}21-{}", expected_triple_translated, Language::CPlusPlus.clang());
+    let host_tag = if cfg!(target_os = "macos") {
+        "darwin-x86_64"
+    } else if cfg!(target_os = "windows") {
+        "windows-x86_64"
+    } else {
+        "linux-x86_64"
+    };
+    let expected_path = ndk_path
+        .join("toolchains")
+        .join("llvm")
+        .join("prebuilt")
+        .join(host_tag)
+        .join("bin")
+        .join(&expected_compiler);
+    let result = ndk_compiler(Language::CPlusPlus, target_triple, &ndk_path);
+    assert_eq!(result, expected_path);
+}
+
+#[test]
+fn test_language_gcc() {
+    assert_eq!(Language::C.gcc(), "gcc");
+    assert_eq!(Language::CPlusPlus.gcc(), "g++");
+}
+
+#[test]
+fn test_language_clang() {
+    assert_eq!(Language::C.clang(), "clang");
+    assert_eq!(Language::CPlusPlus.clang(), "clang++");
+}
+
+#[test]
+fn test_new_cc_build() {
+    let build = Build::new(Config { ..Config::parse(Flags::parse(&["check".to_owned()])) });
+    let target = TargetSelection::from_user("x86_64-unknown-linux-gnu");
+    let cfg = new_cc_build(&build, target.clone());
+    let compiler = cfg.get_compiler();
+    assert!(!compiler.path().to_str().unwrap().is_empty(), "Compiler path should not be empty");
+}
+
+#[test]
+fn test_default_compiler_wasi() {
+    let build = Build::new(Config { ..Config::parse(Flags::parse(&["check".to_owned()])) });
+    let target = TargetSelection::from_user("wasm32-wasi");
+    let wasi_sdk = PathBuf::from("/wasi-sdk");
+    env::set_var("WASI_SDK_PATH", &wasi_sdk);
+    let mut cfg = cc::Build::new();
+    if let Some(result) = default_compiler(&mut cfg, Language::C, target.clone(), &build) {
+        let expected = {
+            let compiler = format!("{}-clang", target.triple);
+            wasi_sdk.join("bin").join(compiler)
+        };
+        assert_eq!(result, expected);
+    } else {
+        panic!(
+            "default_compiler should return a compiler path for wasi target when WASI_SDK_PATH is set"
+        );
+    }
+    env::remove_var("WASI_SDK_PATH");
+}
+
+#[test]
+fn test_default_compiler_fallback() {
+    let build = Build::new(Config { ..Config::parse(Flags::parse(&["check".to_owned()])) });
+    let target = TargetSelection::from_user("x86_64-unknown-linux-gnu");
+    let mut cfg = cc::Build::new();
+    let result = default_compiler(&mut cfg, Language::C, target, &build);
+    assert!(result.is_none(), "default_compiler should return None for generic targets");
+}
+
+#[test]
+fn test_find_target_with_config() {
+    let mut build = Build::new(Config { ..Config::parse(Flags::parse(&["check".to_owned()])) });
+    let target = TargetSelection::from_user("x86_64-unknown-linux-gnu");
+    let mut target_config = Target::default();
+    target_config.cc = Some(PathBuf::from("dummy-cc"));
+    target_config.cxx = Some(PathBuf::from("dummy-cxx"));
+    target_config.ar = Some(PathBuf::from("dummy-ar"));
+    target_config.ranlib = Some(PathBuf::from("dummy-ranlib"));
+    build.config.target_config.insert(target.clone(), target_config);
+    find_target(&build, target.clone());
+    let binding = build.cc.borrow();
+    let cc_tool = binding.get(&target).unwrap();
+    assert_eq!(cc_tool.path(), &PathBuf::from("dummy-cc"));
+    let binding = build.cxx.borrow();
+    let cxx_tool = binding.get(&target).unwrap();
+    assert_eq!(cxx_tool.path(), &PathBuf::from("dummy-cxx"));
+    let binding = build.ar.borrow();
+    let ar = binding.get(&target).unwrap();
+    assert_eq!(ar, &PathBuf::from("dummy-ar"));
+    let binding = build.ranlib.borrow();
+    let ranlib = binding.get(&target).unwrap();
+    assert_eq!(ranlib, &PathBuf::from("dummy-ranlib"));
+}
+
+#[test]
+fn test_find_target_without_config() {
+    let mut build = Build::new(Config { ..Config::parse(Flags::parse(&["check".to_owned()])) });
+    let target = TargetSelection::from_user("x86_64-unknown-linux-gnu");
+    build.config.target_config.clear();
+    find_target(&build, target.clone());
+    assert!(build.cc.borrow().contains_key(&target));
+    if !target.triple.contains("vxworks") {
+        assert!(build.cxx.borrow().contains_key(&target));
+    }
+    assert!(build.ar.borrow().contains_key(&target));
+}
+
+#[test]
+fn test_find() {
+    let mut build = Build::new(Config { ..Config::parse(Flags::parse(&["check".to_owned()])) });
+    let target1 = TargetSelection::from_user("x86_64-unknown-linux-gnu");
+    let target2 = TargetSelection::from_user("arm-linux-androideabi");
+    build.targets.push(target1.clone());
+    build.hosts.push(target2.clone());
+    find(&build);
+    for t in build.hosts.iter().chain(build.targets.iter()).chain(iter::once(&build.build)) {
+        assert!(build.cc.borrow().contains_key(t), "CC not set for target {}", t.triple);
+    }
+}
diff --git a/src/bootstrap/src/utils/change_tracker.rs b/src/bootstrap/src/utils/change_tracker.rs
index 6f62df28e49..8dfe0d3a35e 100644
--- a/src/bootstrap/src/utils/change_tracker.rs
+++ b/src/bootstrap/src/utils/change_tracker.rs
@@ -345,4 +345,19 @@ pub const CONFIG_CHANGE_HISTORY: &[ChangeInfo] = &[
         severity: ChangeSeverity::Info,
         summary: "Rustdoc now respects the value of rust.lto.",
     },
+    ChangeInfo {
+        change_id: 136941,
+        severity: ChangeSeverity::Info,
+        summary: "The llvm.ccache option has moved to build.ccache. llvm.ccache is now deprecated.",
+    },
+    ChangeInfo {
+        change_id: 137170,
+        severity: ChangeSeverity::Info,
+        summary: "It is now possible to configure `jemalloc` for each target",
+    },
+    ChangeInfo {
+        change_id: 137215,
+        severity: ChangeSeverity::Info,
+        summary: "Added `build.test-stage = 2` to 'tools' profile defaults",
+    },
 ];
diff --git a/src/bootstrap/src/utils/exec.rs b/src/bootstrap/src/utils/exec.rs
index 1902dcd3962..7eb9ab96c8a 100644
--- a/src/bootstrap/src/utils/exec.rs
+++ b/src/bootstrap/src/utils/exec.rs
@@ -329,3 +329,25 @@ impl Default for CommandOutput {
         }
     }
 }
+
+/// Helper trait to format both Command and BootstrapCommand as a short execution line,
+/// without all the other details (e.g. environment variables).
+#[allow(unused)]
+pub trait FormatShortCmd {
+    fn format_short_cmd(&self) -> String;
+}
+
+impl FormatShortCmd for BootstrapCommand {
+    fn format_short_cmd(&self) -> String {
+        self.command.format_short_cmd()
+    }
+}
+
+impl FormatShortCmd for Command {
+    fn format_short_cmd(&self) -> String {
+        let program = Path::new(self.get_program());
+        let mut line = vec![program.file_name().unwrap().to_str().unwrap()];
+        line.extend(self.get_args().map(|arg| arg.to_str().unwrap()));
+        line.join(" ")
+    }
+}
diff --git a/src/bootstrap/src/utils/helpers.rs b/src/bootstrap/src/utils/helpers.rs
index a1b1748c85b..3fee397da09 100644
--- a/src/bootstrap/src/utils/helpers.rs
+++ b/src/bootstrap/src/utils/helpers.rs
@@ -265,6 +265,9 @@ pub fn make(host: &str) -> PathBuf {
 
 #[track_caller]
 pub fn output(cmd: &mut Command) -> String {
+    #[cfg(feature = "tracing")]
+    let _run_span = crate::trace_cmd!(cmd);
+
     let output = match cmd.stderr(Stdio::inherit()).output() {
         Ok(status) => status,
         Err(e) => fail(&format!("failed to execute command: {cmd:?}\nERROR: {e}")),
diff --git a/src/bootstrap/src/utils/proc_macro_deps.rs b/src/bootstrap/src/utils/proc_macro_deps.rs
index dbfd6f47dc6..34bf6bb7013 100644
--- a/src/bootstrap/src/utils/proc_macro_deps.rs
+++ b/src/bootstrap/src/utils/proc_macro_deps.rs
@@ -6,25 +6,18 @@ pub static CRATES: &[&str] = &[
     "annotate-snippets",
     "anstyle",
     "basic-toml",
-    "block-buffer",
     "bumpalo",
-    "cfg-if",
-    "cpufeatures",
-    "crypto-common",
     "darling",
     "darling_core",
     "derive_builder_core",
-    "digest",
     "fluent-bundle",
     "fluent-langneg",
     "fluent-syntax",
     "fnv",
-    "generic-array",
     "heck",
     "ident_case",
     "intl-memoizer",
     "intl_pluralrules",
-    "libc",
     "log",
     "memchr",
     "mime",
@@ -32,17 +25,12 @@ pub static CRATES: &[&str] = &[
     "minimal-lexical",
     "nom",
     "num-conv",
-    "once_cell",
-    "pest",
-    "pest_generator",
-    "pest_meta",
     "proc-macro2",
     "quote",
     "rinja_parser",
     "rustc-hash",
     "self_cell",
     "serde",
-    "sha2",
     "smallvec",
     "stable_deref_trait",
     "strsim",
@@ -52,15 +40,12 @@ pub static CRATES: &[&str] = &[
     "time-core",
     "tinystr",
     "type-map",
-    "typenum",
-    "ucd-trie",
     "unic-langid",
     "unic-langid-impl",
     "unic-langid-macros",
     "unicase",
     "unicode-ident",
     "unicode-width",
-    "version_check",
     "wasm-bindgen-backend",
     "wasm-bindgen-macro-support",
     "wasm-bindgen-shared",
diff --git a/src/bootstrap/src/utils/tracing.rs b/src/bootstrap/src/utils/tracing.rs
index e89decf9e55..99849341dc3 100644
--- a/src/bootstrap/src/utils/tracing.rs
+++ b/src/bootstrap/src/utils/tracing.rs
@@ -47,3 +47,20 @@ macro_rules! error {
         ::tracing::error!($($tokens)*)
     }
 }
+
+#[macro_export]
+macro_rules! trace_cmd {
+    ($cmd:expr) => {
+        {
+            use $crate::utils::exec::FormatShortCmd;
+
+            ::tracing::span!(
+                target: "COMMAND",
+                ::tracing::Level::TRACE,
+                "executing command",
+                cmd = $cmd.format_short_cmd(),
+                full_cmd = ?$cmd
+            ).entered()
+        }
+    };
+}
diff --git a/src/ci/docker/host-x86_64/dist-loongarch64-linux/Dockerfile b/src/ci/docker/host-x86_64/dist-loongarch64-linux/Dockerfile
index 71eb72686b0..e3f23149284 100644
--- a/src/ci/docker/host-x86_64/dist-loongarch64-linux/Dockerfile
+++ b/src/ci/docker/host-x86_64/dist-loongarch64-linux/Dockerfile
@@ -3,8 +3,8 @@ FROM ubuntu:22.04
 COPY scripts/cross-apt-packages.sh /scripts/
 RUN sh /scripts/cross-apt-packages.sh
 
-COPY scripts/crosstool-ng-git.sh /scripts/
-RUN sh /scripts/crosstool-ng-git.sh
+COPY scripts/crosstool-ng.sh /scripts/
+RUN sh /scripts/crosstool-ng.sh
 
 COPY scripts/rustbuild-setup.sh /scripts/
 RUN sh /scripts/rustbuild-setup.sh
diff --git a/src/ci/docker/host-x86_64/dist-loongarch64-linux/loongarch64-unknown-linux-gnu.defconfig b/src/ci/docker/host-x86_64/dist-loongarch64-linux/loongarch64-unknown-linux-gnu.defconfig
index 576f3631cd5..aa33f72268e 100644
--- a/src/ci/docker/host-x86_64/dist-loongarch64-linux/loongarch64-unknown-linux-gnu.defconfig
+++ b/src/ci/docker/host-x86_64/dist-loongarch64-linux/loongarch64-unknown-linux-gnu.defconfig
@@ -7,6 +7,8 @@ CT_ARCH_LOONGARCH=y
 # CT_DEMULTILIB is not set
 CT_ARCH_USE_MMU=y
 CT_ARCH_ARCH="loongarch64"
+CT_TARGET_CFLAGS="-mcmodel=medium"
+CT_TARGET_LDFLAGS="-mcmodel=medium"
 CT_KERNEL_LINUX=y
 CT_LINUX_V_5_19=y
 CT_GLIBC_V_2_36=y
diff --git a/src/ci/docker/host-x86_64/dist-loongarch64-musl/Dockerfile b/src/ci/docker/host-x86_64/dist-loongarch64-musl/Dockerfile
index 5081f25e567..2c33b5526ee 100644
--- a/src/ci/docker/host-x86_64/dist-loongarch64-musl/Dockerfile
+++ b/src/ci/docker/host-x86_64/dist-loongarch64-musl/Dockerfile
@@ -3,8 +3,8 @@ FROM ubuntu:22.04
 COPY scripts/cross-apt-packages.sh /scripts/
 RUN sh /scripts/cross-apt-packages.sh
 
-COPY scripts/crosstool-ng-git.sh /scripts/
-RUN sh /scripts/crosstool-ng-git.sh
+COPY scripts/crosstool-ng.sh /scripts/
+RUN sh /scripts/crosstool-ng.sh
 
 COPY scripts/rustbuild-setup.sh /scripts/
 RUN sh /scripts/rustbuild-setup.sh
diff --git a/src/ci/docker/host-x86_64/dist-loongarch64-musl/loongarch64-unknown-linux-musl.defconfig b/src/ci/docker/host-x86_64/dist-loongarch64-musl/loongarch64-unknown-linux-musl.defconfig
index 3ab676ed971..3ccbc583c1b 100644
--- a/src/ci/docker/host-x86_64/dist-loongarch64-musl/loongarch64-unknown-linux-musl.defconfig
+++ b/src/ci/docker/host-x86_64/dist-loongarch64-musl/loongarch64-unknown-linux-musl.defconfig
@@ -7,6 +7,8 @@ CT_ARCH_LOONGARCH=y
 # CT_DEMULTILIB is not set
 CT_ARCH_USE_MMU=y
 CT_ARCH_ARCH="loongarch64"
+CT_TARGET_CFLAGS="-mcmodel=medium"
+CT_TARGET_LDFLAGS="-mcmodel=medium"
 CT_KERNEL_LINUX=y
 CT_LINUX_V_5_19=y
 CT_LIBC_MUSL=y
diff --git a/src/ci/docker/host-x86_64/dist-powerpc64le-linux/Dockerfile b/src/ci/docker/host-x86_64/dist-powerpc64le-linux/Dockerfile
index 9d3be51d037..cb20f43cff7 100644
--- a/src/ci/docker/host-x86_64/dist-powerpc64le-linux/Dockerfile
+++ b/src/ci/docker/host-x86_64/dist-powerpc64le-linux/Dockerfile
@@ -3,8 +3,8 @@ FROM ubuntu:22.04
 COPY scripts/cross-apt-packages.sh /scripts/
 RUN sh /scripts/cross-apt-packages.sh
 
-COPY scripts/crosstool-ng-git.sh /scripts/
-RUN sh /scripts/crosstool-ng-git.sh
+COPY scripts/crosstool-ng.sh /scripts/
+RUN sh /scripts/crosstool-ng.sh
 
 COPY scripts/rustbuild-setup.sh /scripts/
 RUN sh /scripts/rustbuild-setup.sh
diff --git a/src/ci/docker/run.sh b/src/ci/docker/run.sh
index d2697ac27ab..6658b83efc8 100755
--- a/src/ci/docker/run.sh
+++ b/src/ci/docker/run.sh
@@ -236,9 +236,15 @@ args=
 if [ "$SCCACHE_BUCKET" != "" ]; then
     args="$args --env SCCACHE_BUCKET"
     args="$args --env SCCACHE_REGION"
-    args="$args --env AWS_ACCESS_KEY_ID"
-    args="$args --env AWS_SECRET_ACCESS_KEY"
     args="$args --env AWS_REGION"
+
+    # Disable S3 authentication for PR builds, because the access keys are missing
+    if [ "$PR_CI_JOB" != "" ]; then
+      args="$args --env SCCACHE_S3_NO_CREDENTIALS=1"
+    else
+      args="$args --env AWS_ACCESS_KEY_ID"
+      args="$args --env AWS_SECRET_ACCESS_KEY"
+    fi
 else
     mkdir -p $HOME/.cache/sccache
     args="$args --env SCCACHE_DIR=/sccache --volume $HOME/.cache/sccache:/sccache"
diff --git a/src/ci/docker/scripts/build-clang.sh b/src/ci/docker/scripts/build-clang.sh
index 841a0adb2ab..536991cc06b 100755
--- a/src/ci/docker/scripts/build-clang.sh
+++ b/src/ci/docker/scripts/build-clang.sh
@@ -5,7 +5,7 @@ set -ex
 source shared.sh
 
 # Try to keep the LLVM version here in sync with src/ci/scripts/install-clang.sh
-LLVM=llvmorg-19.1.0-rc3
+LLVM=llvmorg-20.1.0-rc2
 
 mkdir llvm-project
 cd llvm-project
diff --git a/src/ci/docker/scripts/crosstool-ng-git.sh b/src/ci/docker/scripts/crosstool-ng-git.sh
deleted file mode 100644
index e86810ae613..00000000000
--- a/src/ci/docker/scripts/crosstool-ng-git.sh
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/bin/sh
-set -ex
-
-URL=https://github.com/crosstool-ng/crosstool-ng
-REV=ed12fa68402f58e171a6f79500f73f4781fdc9e5
-
-mkdir crosstool-ng
-cd crosstool-ng
-git init
-git fetch --depth=1 ${URL} ${REV}
-git reset --hard FETCH_HEAD
-./bootstrap
-./configure --prefix=/usr/local
-make -j$(nproc)
-make install
-cd ..
-rm -rf crosstool-ng
diff --git a/src/ci/docker/scripts/crosstool-ng.sh b/src/ci/docker/scripts/crosstool-ng.sh
index c3ee19b8d2c..eee912b0d75 100644
--- a/src/ci/docker/scripts/crosstool-ng.sh
+++ b/src/ci/docker/scripts/crosstool-ng.sh
@@ -1,7 +1,7 @@
 #!/bin/sh
 set -ex
 
-CT_NG=1.26.0
+CT_NG=1.27.0
 
 url="https://github.com/crosstool-ng/crosstool-ng/archive/crosstool-ng-$CT_NG.tar.gz"
 curl -Lf $url | tar xzf -
diff --git a/src/ci/docker/scripts/musl.sh b/src/ci/docker/scripts/musl.sh
index ece8e6c15c0..9878bec6fbe 100644
--- a/src/ci/docker/scripts/musl.sh
+++ b/src/ci/docker/scripts/musl.sh
@@ -30,6 +30,47 @@ MUSL=musl-1.2.3
 # may have been downloaded in a previous run
 if [ ! -d $MUSL ]; then
   curl https://www.musl-libc.org/releases/$MUSL.tar.gz | tar xzf -
+
+  # Apply patches for CVE-2025-26519. At the time of adding these patches no release containing them
+  # has been published by the musl project, so we just apply them directly on top of the version we
+  # were distributing already. The patches should be removed once we upgrade to musl >= 1.2.6.
+  #
+  # Advisory: https://www.openwall.com/lists/musl/2025/02/13/1
+  #
+  # Patches applied:
+  # - https://www.openwall.com/lists/musl/2025/02/13/1/1
+  # - https://www.openwall.com/lists/musl/2025/02/13/1/2
+  #
+  # ignore-tidy-tab
+  # ignore-tidy-linelength
+  patch -p1 -d $MUSL <<EOF
+--- a/src/locale/iconv.c
++++ b/src/locale/iconv.c
+@@ -502,7 +502,7 @@ size_t iconv(iconv_t cd, char **restrict in, size_t *restrict inb, char **restri
+ 			if (c >= 93 || d >= 94) {
+ 				c += (0xa1-0x81);
+ 				d += 0xa1;
+-				if (c >= 93 || c>=0xc6-0x81 && d>0x52)
++				if (c > 0xc6-0x81 || c==0xc6-0x81 && d>0x52)
+ 					goto ilseq;
+ 				if (d-'A'<26) d = d-'A';
+ 				else if (d-'a'<26) d = d-'a'+26;
+EOF
+  patch -p1 -d $MUSL <<EOF
+--- a/src/locale/iconv.c
++++ b/src/locale/iconv.c
+@@ -545,6 +545,10 @@ size_t iconv(iconv_t cd, char **restrict in, size_t *restrict inb, char **restri
+ 				if (*outb < k) goto toobig;
+ 				memcpy(*out, tmp, k);
+ 			} else k = wctomb_utf8(*out, c);
++			/* This failure condition should be unreachable, but
++			 * is included to prevent decoder bugs from translating
++			 * into advancement outside the output buffer range. */
++			if (k>4) goto ilseq;
+ 			*out += k;
+ 			*outb -= k;
+ 			break;
+EOF
 fi
 
 cd $MUSL
diff --git a/src/ci/docker/scripts/rfl-build.sh b/src/ci/docker/scripts/rfl-build.sh
index 3eb85ab215e..573821c3e59 100755
--- a/src/ci/docker/scripts/rfl-build.sh
+++ b/src/ci/docker/scripts/rfl-build.sh
@@ -2,7 +2,7 @@
 
 set -euo pipefail
 
-LINUX_VERSION=50e57739141b41f731ab31f8380821c7969f9dc4
+LINUX_VERSION=v6.14-rc3
 
 # Build rustc, rustdoc, cargo, clippy-driver and rustfmt
 ../x.py build --stage 2 library rustdoc clippy rustfmt
@@ -28,7 +28,7 @@ rm -rf linux || true
 # Download Linux at a specific commit
 mkdir -p linux
 git -C linux init
-git -C linux remote add origin https://github.com/Darksonn/linux.git
+git -C linux remote add origin https://github.com/Rust-for-Linux/linux.git
 git -C linux fetch --depth 1 origin ${LINUX_VERSION}
 git -C linux checkout FETCH_HEAD
 
diff --git a/src/ci/docker/scripts/sccache.sh b/src/ci/docker/scripts/sccache.sh
index 6c713e1f861..f66671c64d2 100644
--- a/src/ci/docker/scripts/sccache.sh
+++ b/src/ci/docker/scripts/sccache.sh
@@ -6,10 +6,10 @@ set -ex
 
 case "$(uname -m)" in
     x86_64)
-        url="https://ci-mirrors.rust-lang.org/rustc/2021-08-24-sccache-v0.2.15-x86_64-unknown-linux-musl"
+        url="https://ci-mirrors.rust-lang.org/rustc/2025-01-07-sccache-v0.9.1-x86_64-unknown-linux-musl"
         ;;
     aarch64)
-        url="https://ci-mirrors.rust-lang.org/rustc/2021-08-25-sccache-v0.2.15-aarch64-unknown-linux-musl"
+        url="https://ci-mirrors.rust-lang.org/rustc/2025-01-07-sccache-v0.9.1-aarch64-unknown-linux-musl"
         ;;
     *)
         echo "unsupported architecture: $(uname -m)"
diff --git a/src/ci/github-actions/jobs.yml b/src/ci/github-actions/jobs.yml
index 341d82599fd..64e64867de2 100644
--- a/src/ci/github-actions/jobs.yml
+++ b/src/ci/github-actions/jobs.yml
@@ -12,15 +12,15 @@ runners:
 
   # Large runner used mainly for its bigger disk capacity
   - &job-linux-4c-largedisk
-    os: ubuntu-22.04-4core-16gb
+    os: ubuntu-24.04-4core-16gb
     <<: *base-job
 
   - &job-linux-8c
-    os: ubuntu-22.04-8core-32gb
+    os: ubuntu-24.04-8core-32gb
     <<: *base-job
 
   - &job-linux-16c
-    os: ubuntu-22.04-16core-64gb
+    os: ubuntu-24.04-16core-64gb
     <<: *base-job
 
   - &job-macos-xl
@@ -50,7 +50,7 @@ runners:
   - &job-aarch64-linux
     # Free some disk space to avoid running out of space during the build.
     free_disk: true
-    os: ubuntu-22.04-arm
+    os: ubuntu-24.04-arm
 
   - &job-aarch64-linux-8c
     os: ubuntu-22.04-arm64-8core-32gb
@@ -59,6 +59,7 @@ envs:
     SCRIPT: ./x.py --stage 2 test --skip tests/ui --skip tests/rustdoc -- --exact
     RUST_CONFIGURE_ARGS: --build=x86_64-apple-darwin --enable-sanitizers --enable-profiler --set rust.jemalloc
     RUSTC_RETRY_LINKER_ON_SEGFAULT: 1
+    # Ensure that host tooling is tested on our minimum supported macOS version.
     MACOSX_DEPLOYMENT_TARGET: 10.12
     MACOSX_STD_DEPLOYMENT_TARGET: 10.12
     SELECT_XCODE: /Applications/Xcode_15.2.app
@@ -167,10 +168,10 @@ auto:
     <<: *job-linux-4c
 
   - name: dist-loongarch64-linux
-    <<: *job-linux-4c-largedisk
+    <<: *job-linux-4c
 
   - name: dist-loongarch64-musl
-    <<: *job-linux-4c-largedisk
+    <<: *job-linux-4c
 
   - name: dist-ohos
     <<: *job-linux-4c
@@ -293,9 +294,7 @@ auto:
     <<: *job-linux-4c
 
   - name: x86_64-gnu-debug
-    # This seems to be needed because a full stage 2 build + run-make tests
-    # overwhelms the storage capacity of the standard 4c runner.
-    <<: *job-linux-4c-largedisk
+    <<: *job-linux-4c
 
   - name: x86_64-gnu-distcheck
     <<: *job-linux-8c
@@ -370,7 +369,9 @@ auto:
       SCRIPT: ./x.py dist bootstrap --include-default-paths --host=x86_64-apple-darwin --target=x86_64-apple-darwin
       RUST_CONFIGURE_ARGS: --enable-full-tools --enable-sanitizers --enable-profiler --set rust.jemalloc --set rust.lto=thin --set rust.codegen-units=1
       RUSTC_RETRY_LINKER_ON_SEGFAULT: 1
+      # Ensure that host tooling is built to support our minimum support macOS version.
       MACOSX_DEPLOYMENT_TARGET: 10.12
+      MACOSX_STD_DEPLOYMENT_TARGET: 10.12
       SELECT_XCODE: /Applications/Xcode_15.2.app
       NO_LLVM_ASSERTIONS: 1
       NO_DEBUG_ASSERTIONS: 1
@@ -386,7 +387,10 @@ auto:
       # https://github.com/rust-lang/rust/issues/129069
       RUST_CONFIGURE_ARGS: --enable-sanitizers --enable-profiler --set rust.jemalloc --set target.aarch64-apple-ios-macabi.sanitizers=false --set target.x86_64-apple-ios-macabi.sanitizers=false
       RUSTC_RETRY_LINKER_ON_SEGFAULT: 1
+      # Ensure that host tooling is built to support our minimum support macOS version.
+      # FIXME(madsmtm): This might be redundant, as we're not building host tooling here (?)
       MACOSX_DEPLOYMENT_TARGET: 10.12
+      MACOSX_STD_DEPLOYMENT_TARGET: 10.12
       SELECT_XCODE: /Applications/Xcode_15.2.app
       NO_LLVM_ASSERTIONS: 1
       NO_DEBUG_ASSERTIONS: 1
@@ -404,7 +408,6 @@ auto:
       <<: *env-x86_64-apple-tests
     <<: *job-macos-xl
 
-  # This target only needs to support 11.0 and up as nothing else supports the hardware
   - name: dist-aarch64-apple
     env:
       SCRIPT: ./x.py dist bootstrap --include-default-paths --host=aarch64-apple-darwin --target=aarch64-apple-darwin
@@ -419,6 +422,8 @@ auto:
       RUSTC_RETRY_LINKER_ON_SEGFAULT: 1
       SELECT_XCODE: /Applications/Xcode_15.4.app
       USE_XCODE_CLANG: 1
+      # Aarch64 tooling only needs to support macOS 11.0 and up as nothing else
+      # supports the hardware.
       MACOSX_DEPLOYMENT_TARGET: 11.0
       MACOSX_STD_DEPLOYMENT_TARGET: 11.0
       NO_LLVM_ASSERTIONS: 1
@@ -428,7 +433,6 @@ auto:
       CODEGEN_BACKENDS: llvm,cranelift
     <<: *job-macos-m1
 
-  # This target only needs to support 11.0 and up as nothing else supports the hardware
   - name: aarch64-apple
     env:
       SCRIPT: ./x.py --stage 2 test --host=aarch64-apple-darwin --target=aarch64-apple-darwin
@@ -439,6 +443,8 @@ auto:
       RUSTC_RETRY_LINKER_ON_SEGFAULT: 1
       SELECT_XCODE: /Applications/Xcode_15.4.app
       USE_XCODE_CLANG: 1
+      # Aarch64 tooling only needs to support macOS 11.0 and up as nothing else
+      # supports the hardware, so only need to test it there.
       MACOSX_DEPLOYMENT_TARGET: 11.0
       MACOSX_STD_DEPLOYMENT_TARGET: 11.0
       NO_LLVM_ASSERTIONS: 1
@@ -450,28 +456,29 @@ auto:
   #  Windows Builders  #
   ######################
 
+  # x86_64-msvc is split into two jobs to run tests in parallel.
   - name: x86_64-msvc-1
     env:
-      RUST_CONFIGURE_ARGS: --build=x86_64-pc-windows-msvc --enable-profiler
+      RUST_CONFIGURE_ARGS: --build=x86_64-pc-windows-msvc --enable-sanitizers --enable-profiler
       SCRIPT: make ci-msvc-py
     <<: *job-windows-25
 
   - name: x86_64-msvc-2
     env:
-      RUST_CONFIGURE_ARGS: --build=x86_64-pc-windows-msvc --enable-profiler
+      RUST_CONFIGURE_ARGS: --build=x86_64-pc-windows-msvc --enable-sanitizers --enable-profiler
       SCRIPT: make ci-msvc-ps1
     <<: *job-windows-25
 
   # i686-msvc is split into two jobs to run tests in parallel.
   - name: i686-msvc-1
     env:
-      RUST_CONFIGURE_ARGS: --build=i686-pc-windows-msvc
+      RUST_CONFIGURE_ARGS: --build=i686-pc-windows-msvc  --enable-sanitizers
       SCRIPT: make ci-msvc-py
     <<: *job-windows
 
   - name: i686-msvc-2
     env:
-      RUST_CONFIGURE_ARGS: --build=i686-pc-windows-msvc
+      RUST_CONFIGURE_ARGS: --build=i686-pc-windows-msvc --enable-sanitizers
       SCRIPT: make ci-msvc-ps1
     <<: *job-windows
 
@@ -521,13 +528,30 @@ auto:
   # came from the mingw-w64 SourceForge download site. Unfortunately
   # SourceForge is notoriously flaky, so we mirror it on our own infrastructure.
 
-  - name: i686-mingw
+  # i686-mingw is split into three jobs to run tests in parallel.
+  - name: i686-mingw-1
     env:
       RUST_CONFIGURE_ARGS: --build=i686-pc-windows-gnu
-      SCRIPT: make ci-mingw
+      SCRIPT: make ci-mingw-x-1
       # There is no dist-i686-mingw-alt, so there is no prebuilt LLVM with assertions
       NO_DOWNLOAD_CI_LLVM: 1
-    <<: *job-windows-25-8c
+    <<: *job-windows-25
+
+  - name: i686-mingw-2
+    env:
+      RUST_CONFIGURE_ARGS: --build=i686-pc-windows-gnu
+      SCRIPT: make ci-mingw-x-2
+      # There is no dist-i686-mingw-alt, so there is no prebuilt LLVM with assertions
+      NO_DOWNLOAD_CI_LLVM: 1
+    <<: *job-windows-25
+
+  - name: i686-mingw-3
+    env:
+      RUST_CONFIGURE_ARGS: --build=i686-pc-windows-gnu
+      SCRIPT: make ci-mingw-bootstrap
+      # There is no dist-i686-mingw-alt, so there is no prebuilt LLVM with assertions
+      NO_DOWNLOAD_CI_LLVM: 1
+    <<: *job-windows-25
 
   # x86_64-mingw is split into two jobs to run tests in parallel.
   - name: x86_64-mingw-1
diff --git a/src/ci/run.sh b/src/ci/run.sh
index b874f71832d..536754f12bc 100755
--- a/src/ci/run.sh
+++ b/src/ci/run.sh
@@ -279,5 +279,5 @@ if [ "$RUN_CHECK_WITH_PARALLEL_QUERIES" != "" ]; then
 fi
 
 echo "::group::sccache stats"
-sccache --show-stats || true
+sccache --show-adv-stats || true
 echo "::endgroup::"
diff --git a/src/ci/scripts/free-disk-space.sh b/src/ci/scripts/free-disk-space.sh
index 8850e168145..055a6ac2211 100755
--- a/src/ci/scripts/free-disk-space.sh
+++ b/src/ci/scripts/free-disk-space.sh
@@ -69,31 +69,71 @@ printDF() {
     printSeparationLine "="
 }
 
-removeDir() {
-    dir=${1}
-
-    local before
-    if [ ! -d "$dir" ]; then
-        echo "::warning::Directory $dir does not exist, skipping."
-    else
-        before=$(getAvailableSpace)
-        sudo rm -rf "$dir"
-        printSavedSpace "$before" "Removed $dir"
-    fi
-}
-
-removeUnusedDirectories() {
-    local dirs_to_remove=(
+removeUnusedFilesAndDirs() {
+    local to_remove=(
+        "/usr/local/aws-sam-cli"
+        "/usr/local/doc/cmake"
+        "/usr/local/julia"*
         "/usr/local/lib/android"
-        "/usr/share/dotnet"
+        "/usr/local/share/chromedriver-"*
+        "/usr/local/share/chromium"
+        "/usr/local/share/cmake-"*
+        "/usr/local/share/edge_driver"
+        "/usr/local/share/gecko_driver"
+        "/usr/local/share/icons"
+        "/usr/local/share/vim"
+        "/usr/local/share/emacs"
+        "/usr/local/share/powershell"
+        "/usr/local/share/vcpkg"
+        "/usr/share/apache-maven-"*
+        "/usr/share/gradle-"*
+        "/usr/share/java"
+        "/usr/share/kotlinc"
+        "/usr/share/miniconda"
+        "/usr/share/php"
+        "/usr/share/ri"
+        "/usr/share/swift"
+
+        # binaries
+        "/usr/local/bin/azcopy"
+        "/usr/local/bin/bicep"
+        "/usr/local/bin/ccmake"
+        "/usr/local/bin/cmake-"*
+        "/usr/local/bin/cmake"
+        "/usr/local/bin/cpack"
+        "/usr/local/bin/ctest"
+        "/usr/local/bin/helm"
+        "/usr/local/bin/kind"
+        "/usr/local/bin/kustomize"
+        "/usr/local/bin/minikube"
+        "/usr/local/bin/packer"
+        "/usr/local/bin/phpunit"
+        "/usr/local/bin/pulumi-"*
+        "/usr/local/bin/pulumi"
+        "/usr/local/bin/stack"
 
         # Haskell runtime
         "/usr/local/.ghcup"
+
+        # Azure
+        "/opt/az"
+        "/usr/share/az_"*
+
+        # Environment variable set by GitHub Actions
+        "$AGENT_TOOLSDIRECTORY"
     )
 
-    for dir in "${dirs_to_remove[@]}"; do
-        removeDir "$dir"
+    for element in "${to_remove[@]}"; do
+        if [ ! -e "$element" ]; then
+            # The file or directory doesn't exist.
+            # Maybe it was removed in a newer version of the runner or it's not present in a
+            # specific architecture (e.g. ARM).
+            echo "::warning::Directory or file $element does not exist, skipping."
+        fi
     done
+
+    # Remove all files and directories at once to save time.
+    sudo rm -rf "${to_remove[@]}"
 }
 
 execAndMeasureSpaceChange() {
@@ -115,7 +155,6 @@ cleanPackages() {
         '^dotnet-.*'
         '^llvm-.*'
         '^mongodb-.*'
-        '^mysql-.*'
         'azure-cli'
         'firefox'
         'libgl1-mesa-dri'
@@ -138,7 +177,9 @@ cleanPackages() {
     sudo apt-get clean || echo "::warning::The command [sudo apt-get clean] failed failed"
 }
 
-# Remove Docker images
+# Remove Docker images.
+# Ubuntu 22 runners have docker images already installed.
+# They aren't present in ubuntu 24 runners.
 cleanDocker() {
     echo "=> Removing the following docker images:"
     sudo docker image ls
@@ -163,8 +204,7 @@ echo ""
 execAndMeasureSpaceChange cleanPackages "Unused packages"
 execAndMeasureSpaceChange cleanDocker "Docker images"
 execAndMeasureSpaceChange cleanSwap "Swap storage"
-
-removeUnusedDirectories
+execAndMeasureSpaceChange removeUnusedFilesAndDirs "Unused files and directories"
 
 # Output saved space statistic
 echo ""
diff --git a/src/ci/scripts/install-mingw.sh b/src/ci/scripts/install-mingw.sh
index 91eab2e7a08..c8c501e646a 100755
--- a/src/ci/scripts/install-mingw.sh
+++ b/src/ci/scripts/install-mingw.sh
@@ -32,6 +32,12 @@ if isWindows && isKnownToBeMingwBuild; then
             ;;
     esac
 
+    # Stop /msys64/bin from being prepended to PATH by adding the bin directory manually.
+    # Note that this intentionally uses a Windows style path instead of the msys2 path to
+    # avoid being auto-translated into `/usr/bin`, which will not have the desired effect.
+    msys2Path="c:/msys64"
+    ciCommandAddPath "${msys2Path}/usr/bin"
+
     mingw_dir="mingw${bits}"
 
     curl -o mingw.7z "${MIRRORS_BASE}/${mingw_archive}"
diff --git a/src/ci/scripts/upload-artifacts.sh b/src/ci/scripts/upload-artifacts.sh
index 0bc91f6ba71..975b4c52726 100755
--- a/src/ci/scripts/upload-artifacts.sh
+++ b/src/ci/scripts/upload-artifacts.sh
@@ -52,10 +52,15 @@ access_url="https://ci-artifacts.rust-lang.org/${deploy_dir}/$(ciCommit)"
 # to make them easily accessible.
 if [ -n "${GITHUB_STEP_SUMMARY}" ]
 then
-  echo "# CI artifacts" >> "${GITHUB_STEP_SUMMARY}"
+  archives=($(find "${upload_dir}" -maxdepth 1 -name "*.xz"))
 
-  for filename in "${upload_dir}"/*.xz; do
-    filename=$(basename "${filename}")
-    echo "- [${filename}](${access_url}/${filename})" >> "${GITHUB_STEP_SUMMARY}"
-  done
+  # Avoid generating an invalid "*.xz" file if there are no archives
+  if [ ${#archives[@]} -gt 0 ]; then
+    echo "# CI artifacts" >> "${GITHUB_STEP_SUMMARY}"
+
+    for filename in "${upload_dir}"/*.xz; do
+      filename=$(basename "${filename}")
+      echo "- [${filename}](${access_url}/${filename})" >> "${GITHUB_STEP_SUMMARY}"
+    done
+  fi
 fi
diff --git a/src/doc/book b/src/doc/book
-Subproject e2fa4316c5a7c0d2499c5d6b799adcfad6ef7a4
+Subproject 4a01a9182496f807aaa5f72d93a25ce18bcbe10
diff --git a/src/doc/edition-guide b/src/doc/edition-guide
-Subproject f56aecc3b036dff16404b525a83b00f911b9bbe
+Subproject daa4b763cd848f986813b5cf8069e1649f7147a
diff --git a/src/doc/embedded-book b/src/doc/embedded-book
-Subproject ddbf1b4e2858fedb71b7c42eb15c4576517dc12
+Subproject 0b8219ac23a3e09464e4e0166c768cf1c4bba0d
diff --git a/src/doc/nomicon b/src/doc/nomicon
-Subproject 336f75835a6c0514852cc65aba9a698b699b13c
+Subproject 8f5c7322b65d079aa5b242eb10d89a98e12471e
diff --git a/src/doc/reference b/src/doc/reference
-Subproject 4249fb411dd27f945e2881eb0378044b94cee06
+Subproject 615b4cec60c269cfc105d511c93287620032d5b
diff --git a/src/doc/rust-by-example b/src/doc/rust-by-example
-Subproject 743766929f1e53e72fab74394ae259bbfb4a761
+Subproject 66543bbc5b7dbd4e679092c07ae06ba6c73fd91
diff --git a/src/doc/rustc-dev-guide/examples/rustc-driver-interacting-with-the-ast.rs b/src/doc/rustc-dev-guide/examples/rustc-driver-interacting-with-the-ast.rs
index 98c6041d0be..3270c722e07 100644
--- a/src/doc/rustc-dev-guide/examples/rustc-driver-interacting-with-the-ast.rs
+++ b/src/doc/rustc-dev-guide/examples/rustc-driver-interacting-with-the-ast.rs
@@ -20,7 +20,7 @@ use std::path::Path;
 use std::sync::Arc;
 
 use rustc_ast_pretty::pprust::item_to_string;
-use rustc_driver::{Compilation, run_compiler};
+use rustc_driver::{run_compiler, Compilation};
 use rustc_interface::interface::{Compiler, Config};
 use rustc_middle::ty::TyCtxt;
 
@@ -77,7 +77,7 @@ impl rustc_driver::Callbacks for MyCallbacks {
             let item = hir_krate.item(id);
             // Use pattern-matching to find a specific node inside the main function.
             if let rustc_hir::ItemKind::Fn { body, .. } = item.kind {
-                let expr = &tcx.hir().body(body).value;
+                let expr = &tcx.hir_body(body).value;
                 if let rustc_hir::ExprKind::Block(block, _) = expr.kind {
                     if let rustc_hir::StmtKind::Let(let_stmt) = block.stmts[0].kind {
                         if let Some(expr) = let_stmt.init {
diff --git a/src/doc/rustc-dev-guide/src/SUMMARY.md b/src/doc/rustc-dev-guide/src/SUMMARY.md
index 91c4aeacbd7..106db508ebb 100644
--- a/src/doc/rustc-dev-guide/src/SUMMARY.md
+++ b/src/doc/rustc-dev-guide/src/SUMMARY.md
@@ -75,6 +75,7 @@
 - [Prologue](./building/bootstrapping/intro.md)
 - [What Bootstrapping does](./building/bootstrapping/what-bootstrapping-does.md)
 - [How Bootstrap does it](./building/bootstrapping/how-bootstrap-does-it.md)
+- [Writing tools in Bootstrap](./building/bootstrapping/writing-tools-in-bootstrap.md)
 - [Debugging bootstrap](./building/bootstrapping/debugging-bootstrap.md)
 
 # High-level Compiler Architecture
diff --git a/src/doc/rustc-dev-guide/src/building/bootstrapping/debugging-bootstrap.md b/src/doc/rustc-dev-guide/src/building/bootstrapping/debugging-bootstrap.md
index 75d789569de..24b9783ddf0 100644
--- a/src/doc/rustc-dev-guide/src/building/bootstrapping/debugging-bootstrap.md
+++ b/src/doc/rustc-dev-guide/src/building/bootstrapping/debugging-bootstrap.md
@@ -115,6 +115,14 @@ $ BOOTSTRAP_TRACING=CONFIG_HANDLING=TRACE ./x build library --stage 1
 
 [tracing-env-filter]: https://docs.rs/tracing-subscriber/0.3.19/tracing_subscriber/filter/struct.EnvFilter.html
 
+##### FIXME(#96176): specific tracing for `compiler()` vs `compiler_for()`
+
+The additional targets `COMPILER` and `COMPILER_FOR` are used to help trace what
+`builder.compiler()` and `builder.compiler_for()` does. They should be removed
+if [#96176][cleanup-compiler-for] is resolved.
+
+[cleanup-compiler-for]: https://github.com/rust-lang/rust/issues/96176
+
 ### Using `tracing` in bootstrap
 
 Both `tracing::*` macros and the `tracing::instrument` proc-macro attribute need to be gated behind `tracing` feature. Examples:
@@ -160,6 +168,14 @@ For `#[instrument]`, it's recommended to:
 - Explicitly pick an instrumentation name via `name = ".."` to distinguish between e.g. `run` of different steps.
 - Take care to not cause diverging behavior via tracing, e.g. building extra things only when tracing infra is enabled.
 
+### Profiling bootstrap
+
+You can use the `COMMAND` tracing target to trace execution of most commands spawned by bootstrap. If you also use the `BOOTSTRAP_PROFILE=1` environment variable, bootstrap will generate a Chrome JSON trace file, which can be visualized in Chrome's `chrome://tracing` page or on https://ui.perfetto.dev.
+
+```bash
+$ BOOTSTRAP_TRACING=COMMAND=trace BOOTSTRAP_PROFILE=1 ./x build library
+```
+
 ### rust-analyzer integration?
 
 Unfortunately, because bootstrap is a `rust-analyzer.linkedProjects`, you can't ask r-a to check/build bootstrap itself with `tracing` feature enabled to get relevant completions, due to lack of support as described in <https://github.com/rust-lang/rust-analyzer/issues/8521>.
diff --git a/src/doc/rustc-dev-guide/src/building/bootstrapping/writing-tools-in-bootstrap.md b/src/doc/rustc-dev-guide/src/building/bootstrapping/writing-tools-in-bootstrap.md
new file mode 100644
index 00000000000..6046d5b133d
--- /dev/null
+++ b/src/doc/rustc-dev-guide/src/building/bootstrapping/writing-tools-in-bootstrap.md
@@ -0,0 +1,23 @@
+# Writing tools in Bootstrap
+
+There are three types of tools you can write in bootstrap:
+
+- **`Mode::ToolBootstrap`**
+  Use this for tools that don’t need anything from the in-tree compiler and can run with the stage0 `rustc`.
+  The output is placed in the "stage0-bootstrap-tools" directory. This mode is for general-purpose tools built
+  entirely with the stage0 compiler, including target libraries and only works for stage 0.
+
+- **`Mode::ToolStd`**
+  Use this for tools that rely on the locally built std. The output goes into the "stageN-tools" directory.
+  This mode is rarely used, mainly for `compiletest` which requires `libtest`.
+
+- **`Mode::ToolRustc`**
+  Use this for tools that depend on both the locally built `rustc` and the target `std`. This is more complex than
+  the other modes because the tool must be built with the same compiler used for `rustc` and placed in the "stageN-tools"
+  directory. When you choose `Mode::ToolRustc`, `ToolBuild` implementation takes care of this automatically.
+  If you need to use the builder’s compiler for something specific, you can get it from `ToolBuildResult`, which is
+  returned by the tool's [`Step`].
+
+Regardless of the tool type you must return `ToolBuildResult` from the tool’s [`Step`] implementation and use `ToolBuild` inside it.
+
+[`Step`]: https://doc.rust-lang.org/nightly/nightly-rustc/bootstrap/core/builder/trait.Step.html
diff --git a/src/doc/rustc-dev-guide/src/building/suggested.md b/src/doc/rustc-dev-guide/src/building/suggested.md
index e2d50b31d04..2498838144e 100644
--- a/src/doc/rustc-dev-guide/src/building/suggested.md
+++ b/src/doc/rustc-dev-guide/src/building/suggested.md
@@ -179,6 +179,16 @@ You can run `./x setup editor` and select `helix`, which will prompt you to
 create `languages.toml` with the recommended configuration for Helix. The
 recommended settings live at [`src/etc/rust_analyzer_helix.toml`].
 
+### Zed
+
+Zed comes with built-in LSP and rust-analyzer support.
+It can be configured through `.zed/settings.json`, as described
+[here](https://zed.dev/docs/configuring-languages). Selecting `zed`
+in `./x setup editor` will prompt you to create a `.zed/settings.json`
+file which will configure Zed with the recommended configuration. The
+recommended `rust-analyzer` settings live
+at [`src/etc/rust_analyzer_zed.json`].
+
 ## Check, check, and check again
 
 When doing simple refactoring, it can be useful to run `./x check`
@@ -406,4 +416,5 @@ load this completion.
 [`src/etc/rust_analyzer_settings.json`]: https://github.com/rust-lang/rust/blob/master/src/etc/rust_analyzer_settings.json
 [`src/etc/rust_analyzer_eglot.el`]: https://github.com/rust-lang/rust/blob/master/src/etc/rust_analyzer_eglot.el
 [`src/etc/rust_analyzer_helix.toml`]: https://github.com/rust-lang/rust/blob/master/src/etc/rust_analyzer_helix.toml
+[`src/etc/rust_analyzer_zed.json`]: https://github.com/rust-lang/rust/blob/master/src/etc/rust_analyzer_zed.json
 [`src/etc/pre-push.sh`]: https://github.com/rust-lang/rust/blob/master/src/etc/pre-push.sh
diff --git a/src/doc/rustc-dev-guide/src/implementing_new_features.md b/src/doc/rustc-dev-guide/src/implementing_new_features.md
index d87afeaedce..fda38ef4fc0 100644
--- a/src/doc/rustc-dev-guide/src/implementing_new_features.md
+++ b/src/doc/rustc-dev-guide/src/implementing_new_features.md
@@ -9,7 +9,11 @@ smoothly.
 **NOTE: this section is for *language* features, not *library* features,
 which use [a different process].**
 
+See also [the Rust Language Design Team's procedures][lang-propose] for
+proposing changes to the language.
+
 [a different process]: ./stability.md
+[lang-propose]: https://lang-team.rust-lang.org/how_to/propose.html
 
 ## The @rfcbot FCP process
 
@@ -163,9 +167,7 @@ a new unstable feature:
 
 1. Prevent usage of the new feature unless the feature gate is set.
    You can check it in most places in the compiler using the
-   expression `tcx.features().$feature_name` (or
-   `sess.features_untracked().$feature_name` if the
-   tcx is unavailable)
+   expression `tcx.features().$feature_name()`
 
     If the feature gate is not set, you should either maintain
     the pre-feature behavior or raise an error, depending on
diff --git a/src/doc/rustc-dev-guide/src/mir/index.md b/src/doc/rustc-dev-guide/src/mir/index.md
index 778c583919b..f355875aa15 100644
--- a/src/doc/rustc-dev-guide/src/mir/index.md
+++ b/src/doc/rustc-dev-guide/src/mir/index.md
@@ -304,9 +304,9 @@ The most important rule for
 this representation is that every value must be uniquely represented. In other
 words: a specific value must only be representable in one specific way. For example: there is only
 one way to represent an array of two integers as a `ValTree`:
-`ValTree::Branch(&[ValTree::Leaf(first_int), ValTree::Leaf(second_int)])`.
+`Branch([Leaf(first_int), Leaf(second_int)])`.
 Even though theoretically a `[u32; 2]` could be encoded in a `u64` and thus just be a
-`ValTree::Leaf(bits_of_two_u32)`, that is not a legal construction of `ValTree`
+`Leaf(bits_of_two_u32)`, that is not a legal construction of `ValTree`
 (and is very complex to do, so it is unlikely anyone is tempted to do so).
 
 These rules also mean that some values are not representable. There can be no `union`s in type
diff --git a/src/doc/rustc-dev-guide/src/parallel-rustc.md b/src/doc/rustc-dev-guide/src/parallel-rustc.md
index 44c78a125f4..c5b70706a81 100644
--- a/src/doc/rustc-dev-guide/src/parallel-rustc.md
+++ b/src/doc/rustc-dev-guide/src/parallel-rustc.md
@@ -46,8 +46,6 @@ are implemented differently depending on whether `parallel-compiler` is true.
 
 | data structure                   | parallel                                            | non-parallel |
 | -------------------------------- | --------------------------------------------------- | ------------ |
-| Weak                             | std::sync::Weak                                     | std::rc::Weak |
-| Atomic{Bool}/{Usize}/{U32}/{U64} | std::sync::atomic::Atomic{Bool}/{Usize}/{U32}/{U64} | (std::cell::Cell<bool/usize/u32/u64>) |
 | OnceCell                         | std::sync::OnceLock                                 | std::cell::OnceCell |
 | Lock\<T> | (parking_lot::Mutex\<T>) | (std::cell::RefCell) |
 | RwLock\<T> | (parking_lot::RwLock\<T>) | (std::cell::RefCell) |
@@ -58,7 +56,6 @@ are implemented differently depending on whether `parallel-compiler` is true.
 | WriteGuard | parking_lot::RwLockWriteGuard | std::cell::RefMut |
 | MappedWriteGuard | parking_lot::MappedRwLockWriteGuard | std::cell::RefMut |
 | LockGuard | parking_lot::MutexGuard | std::cell::RefMut |
-| MappedLockGuard | parking_lot::MappedMutexGuard | std::cell::RefMut |
 
 - These thread-safe data structures are interspersed during compilation which
   can cause lock contention resulting in degraded performance as the number of
@@ -173,12 +170,10 @@ Here are some resources that can be used to learn more:
 - [This list of interior mutability in the compiler by nikomatsakis][imlist]
 
 [`rayon`]: https://crates.io/crates/rayon
-[Arc]: https://doc.rust-lang.org/std/sync/struct.Arc.html
 [imlist]: https://github.com/nikomatsakis/rustc-parallelization/blob/master/interior-mutability-list.md
 [irlo0]: https://internals.rust-lang.org/t/parallelizing-rustc-using-rayon/6606
 [irlo1]: https://internals.rust-lang.org/t/help-test-parallel-rustc/11503
 [monomorphization]: backend/monomorph.md
 [parallel-rustdoc]: https://github.com/rust-lang/rust/issues/82741
-[Rc]: https://doc.rust-lang.org/std/rc/struct.Rc.html
 [rustc-rayon]: https://github.com/rust-lang/rustc-rayon
 [tracking]: https://github.com/rust-lang/rust/issues/48685
diff --git a/src/doc/rustc-dev-guide/src/tests/directives.md b/src/doc/rustc-dev-guide/src/tests/directives.md
index 9e0f8f9c279..00bb2bc4dbb 100644
--- a/src/doc/rustc-dev-guide/src/tests/directives.md
+++ b/src/doc/rustc-dev-guide/src/tests/directives.md
@@ -154,6 +154,7 @@ Some examples of `X` in `ignore-X` or `only-X`:
   `ignore-coverage-map`, `ignore-coverage-run`
 - When testing a dist toolchain: `dist`
   - This needs to be enabled with `COMPILETEST_ENABLE_DIST_TESTS=1`
+- The `rustc_abi` of the target: e.g. `rustc_abi-x86_64-sse2`
 
 The following directives will check rustc build settings and target
 settings:
@@ -192,6 +193,8 @@ settings:
   specified atomic widths, e.g. the test with `//@ needs-target-has-atomic: 8,
   16, ptr` will only run if it supports the comma-separated list of atomic
   widths.
+- `needs-dynamic-linking` - ignores if target does not support dynamic linking
+  (which is orthogonal to it being unable to create `dylib` and `cdylib` crate types)
 
 The following directives will check LLVM support:
 
diff --git a/src/doc/rustc/src/SUMMARY.md b/src/doc/rustc/src/SUMMARY.md
index 670e4bd1be6..6c7cdec3480 100644
--- a/src/doc/rustc/src/SUMMARY.md
+++ b/src/doc/rustc/src/SUMMARY.md
@@ -29,6 +29,7 @@
     - [\*-apple-watchos](platform-support/apple-watchos.md)
     - [\*-apple-visionos](platform-support/apple-visionos.md)
     - [aarch64-nintendo-switch-freestanding](platform-support/aarch64-nintendo-switch-freestanding.md)
+    - [amdgcn-amd-amdhsa](platform-support/amdgcn-amd-amdhsa.md)
     - [armeb-unknown-linux-gnueabi](platform-support/armeb-unknown-linux-gnueabi.md)
     - [arm-none-eabi](platform-support/arm-none-eabi.md)
       - [armv4t-none-eabi](platform-support/armv4t-none-eabi.md)
@@ -101,6 +102,7 @@
     - [\*-win7-windows-gnu](platform-support/win7-windows-gnu.md)
     - [\*-win7-windows-msvc](platform-support/win7-windows-msvc.md)
     - [x86_64-fortanix-unknown-sgx](platform-support/x86_64-fortanix-unknown-sgx.md)
+    - [x86_64-pc-cygwin](platform-support/x86_64-pc-cygwin.md)
     - [x86_64-pc-solaris](platform-support/solaris.md)
     - [x86_64-unknown-linux-none.md](platform-support/x86_64-unknown-linux-none.md)
     - [x86_64-unknown-none](platform-support/x86_64-unknown-none.md)
diff --git a/src/doc/rustc/src/check-cfg.md b/src/doc/rustc/src/check-cfg.md
index c62ca9fd9ad..00add2651ae 100644
--- a/src/doc/rustc/src/check-cfg.md
+++ b/src/doc/rustc/src/check-cfg.md
@@ -135,7 +135,7 @@ As of `2025-01-02T`, the list of known names is as follows:
  - `windows`
 
 > Starting with 1.85.0, the `test` cfg is consider to be a "userspace" config
-> despite being also set by `rustc` and should be managed by the build-system it-self.
+> despite being also set by `rustc` and should be managed by the build system itself.
 
 Like with `values(any())`, well known names checking can be disabled by passing `cfg(any())`
 as argument to `--check-cfg`.
diff --git a/src/doc/rustc/src/codegen-options/index.md b/src/doc/rustc/src/codegen-options/index.md
index f45217c69ff..8c1769a8c77 100644
--- a/src/doc/rustc/src/codegen-options/index.md
+++ b/src/doc/rustc/src/codegen-options/index.md
@@ -368,7 +368,7 @@ This flag controls the optimization level.
 * `s`: optimize for binary size.
 * `z`: optimize for binary size, but also turn off loop vectorization.
 
-Note: The [`-O` flag][option-o-optimize] is an alias for `-C opt-level=2`.
+Note: The [`-O` flag][option-o-optimize] is an alias for `-C opt-level=3`.
 
 The default is `0`.
 
diff --git a/src/doc/rustc/src/command-line-arguments.md b/src/doc/rustc/src/command-line-arguments.md
index 0942b5ebfee..9dd2e7de1b3 100644
--- a/src/doc/rustc/src/command-line-arguments.md
+++ b/src/doc/rustc/src/command-line-arguments.md
@@ -308,7 +308,7 @@ A synonym for [`-C debuginfo=2`](codegen-options/index.md#debuginfo).
 <a id="option-o-optimize"></a>
 ## `-O`: optimize your code
 
-A synonym for [`-C opt-level=2`](codegen-options/index.md#opt-level).
+A synonym for [`-C opt-level=3`](codegen-options/index.md#opt-level).
 
 <a id="option-o-output"></a>
 ## `-o`: filename of the output
diff --git a/src/doc/rustc/src/platform-support.md b/src/doc/rustc/src/platform-support.md
index e1e17c5917e..c4e5c1aac2f 100644
--- a/src/doc/rustc/src/platform-support.md
+++ b/src/doc/rustc/src/platform-support.md
@@ -101,7 +101,7 @@ target | notes
 [`riscv64gc-unknown-linux-gnu`](platform-support/riscv64gc-unknown-linux-gnu.md) | RISC-V Linux (kernel 4.20, glibc 2.29)
 [`riscv64gc-unknown-linux-musl`](platform-support/riscv64gc-unknown-linux-musl.md) | RISC-V Linux (kernel 4.20, musl 1.2.3)
 [`s390x-unknown-linux-gnu`](platform-support/s390x-unknown-linux-gnu.md) | S390x Linux (kernel 3.2, glibc 2.17)
-[`x86_64-unknown-freebsd`](platform-support/freebsd.md) | 64-bit amd64 FreeBSD
+[`x86_64-unknown-freebsd`](platform-support/freebsd.md) | 64-bit x86 FreeBSD
 [`x86_64-unknown-illumos`](platform-support/illumos.md) | illumos
 `x86_64-unknown-linux-musl` | 64-bit Linux with musl 1.2.3
 [`x86_64-unknown-netbsd`](platform-support/netbsd.md) | NetBSD/amd64
@@ -165,11 +165,11 @@ target | std | notes
 `i586-pc-windows-msvc` | * | 32-bit Windows (original Pentium) [^x86_32-floats-x87]
 `i586-unknown-linux-gnu` | ✓ | 32-bit Linux (kernel 3.2, glibc 2.17, original Pentium) [^x86_32-floats-x87]
 `i586-unknown-linux-musl` | ✓ | 32-bit Linux (musl 1.2.3, original Pentium) [^x86_32-floats-x87]
-[`i686-linux-android`](platform-support/android.md) | ✓ | 32-bit x86 Android ([PentiumPro with SSE](https://developer.android.com/ndk/guides/abis.html#x86)) [^x86_32-floats-return-ABI]
+[`i686-linux-android`](platform-support/android.md) | ✓ | 32-bit x86 Android ([Pentium 4 plus various extensions](https://developer.android.com/ndk/guides/abis.html#x86)) [^x86_32-floats-return-ABI]
 [`i686-pc-windows-gnullvm`](platform-support/pc-windows-gnullvm.md) | ✓ | 32-bit x86 MinGW (Windows 10+, Pentium 4), LLVM ABI [^x86_32-floats-return-ABI]
 [`i686-unknown-freebsd`](platform-support/freebsd.md) | ✓ | 32-bit x86 FreeBSD (Pentium 4) [^x86_32-floats-return-ABI]
 `i686-unknown-linux-musl` | ✓ | 32-bit Linux with musl 1.2.3 (Pentium 4) [^x86_32-floats-return-ABI]
-[`i686-unknown-uefi`](platform-support/unknown-uefi.md) | ? (Pentium 4, softfloat) | 32-bit UEFI
+[`i686-unknown-uefi`](platform-support/unknown-uefi.md) | ? | 32-bit UEFI (Pentium 4, softfloat)
 [`loongarch64-unknown-none`](platform-support/loongarch-none.md) | * | LoongArch64 Bare-metal (LP64D ABI)
 [`loongarch64-unknown-none-softfloat`](platform-support/loongarch-none.md) | * | LoongArch64 Bare-metal (LP64S ABI)
 [`nvptx64-nvidia-cuda`](platform-support/nvptx64-nvidia-cuda.md) | * | --emit=asm generates PTX code that [runs on NVIDIA GPUs]
@@ -272,6 +272,7 @@ target | std | host | notes
 `aarch64_be-unknown-linux-gnu` | ✓ | ✓ | ARM64 Linux (big-endian)
 `aarch64_be-unknown-linux-gnu_ilp32` | ✓ | ✓ | ARM64 Linux (big-endian, ILP32 ABI)
 [`aarch64_be-unknown-netbsd`](platform-support/netbsd.md) | ✓ | ✓ | ARM64 NetBSD (big-endian)
+[`amdgcn-amd-amdhsa`](platform-support/amdgcn-amd-amdhsa.md) | * |  | `-Ctarget-cpu=gfx...` to specify [the AMD GPU] to compile for
 [`arm64_32-apple-watchos`](platform-support/apple-watchos.md) | ✓ |  | Arm Apple WatchOS 64-bit with 32-bit pointers
 [`arm64e-apple-darwin`](platform-support/arm64e-apple-darwin.md)  | ✓ | ✓ | ARM64e Apple Darwin
 [`arm64e-apple-ios`](platform-support/arm64e-apple-ios.md) | ✓ | | ARM64e Apple iOS
@@ -300,7 +301,7 @@ target | std | host | notes
 [`armv8r-none-eabihf`](platform-support/armv8r-none-eabihf.md) | * |  | Bare Armv8-R, hardfloat
 [`armv7a-nuttx-eabi`](platform-support/nuttx.md) | ✓ |  | ARMv7-A with NuttX
 [`armv7a-nuttx-eabihf`](platform-support/nuttx.md) | ✓ |  | ARMv7-A with NuttX, hardfloat
-`avr-unknown-gnu-atmega328` | * |  | AVR. Requires `-Z build-std=core`
+`avr-none` | * |  | AVR; requires `-Zbuild-std=core` and `-Ctarget-cpu=...`
 `bpfeb-unknown-none` | * |  | BPF (big endian)
 `bpfel-unknown-none` | * |  | BPF (little endian)
 `csky-unknown-linux-gnuabiv2` | ✓ |  | C-SKY abiv2 Linux (little endian)
@@ -308,14 +309,14 @@ target | std | host | notes
 [`hexagon-unknown-linux-musl`](platform-support/hexagon-unknown-linux-musl.md) | ✓ | | Hexagon Linux with musl 1.2.3
 [`hexagon-unknown-none-elf`](platform-support/hexagon-unknown-none-elf.md)| * | | Bare Hexagon (v60+, HVX)
 [`i386-apple-ios`](platform-support/apple-ios.md) | ✓ |  | 32-bit x86 iOS (Penryn) [^x86_32-floats-return-ABI]
-[`i586-pc-nto-qnx700`](platform-support/nto-qnx.md) | * |  | 32-bit x86 QNX Neutrino 7.0 RTOS (Pentium 4) [^x86_32-floats-return-ABI]
 [`i586-unknown-netbsd`](platform-support/netbsd.md) | ✓ |  | 32-bit x86 (original Pentium) [^x86_32-floats-x87]
+[`i586-unknown-redox`](platform-support/redox.md) | ✓ |  | 32-bit x86 Redox OS (PentiumPro) [^x86_32-floats-x87]
 [`i686-apple-darwin`](platform-support/apple-darwin.md) | ✓ | ✓ | 32-bit macOS (10.12+, Sierra+, Penryn) [^x86_32-floats-return-ABI]
+[`i686-pc-nto-qnx700`](platform-support/nto-qnx.md) | * |  | 32-bit x86 QNX Neutrino 7.0 RTOS (Pentium 4) [^x86_32-floats-return-ABI]
 `i686-unknown-haiku` | ✓ | ✓ | 32-bit Haiku (Pentium 4) [^x86_32-floats-return-ABI]
-[`i686-unknown-hurd-gnu`](platform-support/hurd.md) | ✓ | ✓ | 32-bit GNU/Hurd (Pentium 4) [^x86_32-floats-x87]
+[`i686-unknown-hurd-gnu`](platform-support/hurd.md) | ✓ | ✓ | 32-bit GNU/Hurd (Pentium 4) [^x86_32-floats-return-ABI]
 [`i686-unknown-netbsd`](platform-support/netbsd.md) | ✓ | ✓ | NetBSD/i386 (Pentium 4) [^x86_32-floats-return-ABI]
 [`i686-unknown-openbsd`](platform-support/openbsd.md) | ✓ | ✓ | 32-bit OpenBSD (Pentium 4) [^x86_32-floats-return-ABI]
-[`i686-unknown-redox`](platform-support/redox.md) | ✓ |  | i686 Redox OS (PentiumPro) [^x86_32-floats-x87]
 `i686-uwp-windows-gnu` | ✓ |  | [^x86_32-floats-return-ABI]
 [`i686-uwp-windows-msvc`](platform-support/uwp-windows-msvc.md) | ✓ |  | [^x86_32-floats-return-ABI]
 [`i686-win7-windows-gnu`](platform-support/win7-windows-gnu.md) | ✓ |   | 32-bit Windows 7 support [^x86_32-floats-return-ABI]
@@ -406,6 +407,7 @@ target | std | host | notes
 [`wasm64-unknown-unknown`](platform-support/wasm64-unknown-unknown.md) | ? |  | WebAssembly
 [`x86_64-apple-tvos`](platform-support/apple-tvos.md) | ✓ |  | x86 64-bit tvOS
 [`x86_64-apple-watchos-sim`](platform-support/apple-watchos.md) | ✓ |  | x86 64-bit Apple WatchOS simulator
+[`x86_64-pc-cygwin`](platform-support/x86_64-pc-cygwin.md) | ? |  | 64-bit x86 Cygwin |
 [`x86_64-pc-nto-qnx710`](platform-support/nto-qnx.md) | ✓ |  | x86 64-bit QNX Neutrino 7.1 RTOS with default network stack (io-pkt) |
 [`x86_64-pc-nto-qnx710_iosock`](platform-support/nto-qnx.md) | ✓ |  | x86 64-bit QNX Neutrino 7.1 RTOS with new network stack (io-sock) |
 [`x86_64-pc-nto-qnx800`](platform-support/nto-qnx.md) | ✓ |  | x86 64-bit QNX Neutrino 8.0 RTOS |
@@ -432,3 +434,4 @@ target | std | host | notes
 [`xtensa-esp32s3-none-elf`](platform-support/xtensa.md) | * |  | Xtensa ESP32-S3
 
 [runs on NVIDIA GPUs]: https://github.com/japaric-archived/nvptx#targets
+[the AMD GPU]: https://llvm.org/docs/AMDGPUUsage.html#processors
diff --git a/src/doc/rustc/src/platform-support/amdgcn-amd-amdhsa.md b/src/doc/rustc/src/platform-support/amdgcn-amd-amdhsa.md
new file mode 100644
index 00000000000..0b2f798e66d
--- /dev/null
+++ b/src/doc/rustc/src/platform-support/amdgcn-amd-amdhsa.md
@@ -0,0 +1,111 @@
+# `amdgcn-amd-amdhsa`
+
+**Tier: 3**
+
+AMD GPU target for compute/HSA (Heterogeneous System Architecture).
+
+## Target maintainers
+
+- [@Flakebi](https://github.com/Flakebi)
+
+## Requirements
+
+AMD GPUs can be targeted via cross-compilation.
+Supported GPUs depend on the LLVM version that is used by Rust.
+In general, most GPUs starting from gfx7 (Sea Islands/CI) are supported as compilation targets, though older GPUs are not supported by the latest host runtime.
+Details about supported GPUs can be found in [LLVM’s documentation] and [ROCm documentation].
+
+Binaries can be loaded by [HIP] or by the HSA runtime implemented in [ROCR-Runtime].
+The format of binaries is a linked ELF.
+
+Binaries must be built with no-std.
+They can use `core` and `alloc` (`alloc` only if an allocator is supplied).
+At least one function needs to use the `"gpu-kernel"` calling convention and should be marked with `no_mangle` for simplicity.
+Functions using the `"gpu-kernel"` calling convention are kernel entrypoints and can be used from the host runtime.
+
+## Building the target
+
+The target is included in rustc.
+
+## Building Rust programs
+
+The amdgpu target supports many hardware generations, which need different binaries.
+The generations are exposed as different target-cpus in the backend.
+As there are many, Rust does not ship pre-compiled libraries for this target.
+Therefore, you have to build your own copy of `core` by using `cargo -Zbuild-std=core` or similar.
+
+To build a binary, create a no-std library:
+```rust,ignore (platform-specific)
+// src/lib.rs
+#![feature(abi_gpu_kernel)]
+#![no_std]
+
+#[panic_handler]
+fn panic(_: &core::panic::PanicInfo) -> ! {
+    loop {}
+}
+
+#[no_mangle]
+pub extern "gpu-kernel" fn kernel(/* Arguments */) {
+    // Code
+}
+```
+
+Build the library as `cdylib`:
+```toml
+# Cargo.toml
+[lib]
+crate-type = ["cdylib"]
+
+[profile.dev]
+lto = true # LTO must be explicitly enabled for now
+[profile.release]
+lto = true
+```
+
+The target-cpu must be from the list [supported by LLVM] (or printed with `rustc --target amdgcn-amd-amdhsa --print target-cpus`).
+The GPU version on the current system can be found e.g. with [`rocminfo`].
+
+Example `.cargo/config.toml` file to set the target and GPU generation:
+```toml
+# .cargo/config.toml
+[build]
+target = "amdgcn-amd-amdhsa"
+rustflags = ["-Ctarget-cpu=gfx1100"]
+
+[unstable]
+build-std = ["core"] # Optional: "alloc"
+```
+
+## Running Rust programs
+
+To run a binary on an AMD GPU, a host runtime is needed.
+On Linux and Windows, [HIP] can be used to load and run binaries.
+Example code on how to load a compiled binary and run it is available in [ROCm examples].
+
+On Linux, binaries can also run through the HSA runtime as implemented in [ROCR-Runtime].
+
+<!-- Mention an allocator once a suitable one exists for amdgpu -->
+
+<!--
+## Testing
+
+Does the target support running binaries, or do binaries have varying
+expectations that prevent having a standard way to run them? If users can run
+binaries, can they do so in some common emulator, or do they need native
+hardware? Does the target support running the Rust testsuite?
+
+-->
+
+## Additional information
+
+More information can be found on the [LLVM page for amdgpu].
+
+[LLVM’s documentation]: https://llvm.org/docs/AMDGPUUsage.html#processors
+[ROCm documentation]: https://rocmdocs.amd.com
+[HIP]: https://rocm.docs.amd.com/projects/HIP/
+[ROCR-Runtime]: https://github.com/ROCm/ROCR-Runtime
+[supported by LLVM]: https://llvm.org/docs/AMDGPUUsage.html#processors
+[LLVM page for amdgpu]: https://llvm.org/docs/AMDGPUUsage.html
+[`rocminfo`]: https://github.com/ROCm/rocminfo
+[ROCm examples]: https://github.com/ROCm/rocm-examples/tree/ca8ef5b6f1390176616cd1c18fbc98785cbc73f6/HIP-Basic/module_api
diff --git a/src/doc/rustc/src/platform-support/avr-none.md b/src/doc/rustc/src/platform-support/avr-none.md
new file mode 100644
index 00000000000..9c1836222c1
--- /dev/null
+++ b/src/doc/rustc/src/platform-support/avr-none.md
@@ -0,0 +1,81 @@
+# `avr-none`
+
+Series of microcontrollers from Atmel: ATmega8, ATmega328p etc.
+
+**Tier: 3**
+
+## Target maintainers
+
+- [Patryk Wychowaniec](https://github.com/Patryk27) <pwychowaniec@pm.me>
+
+## Requirements
+
+This target is only cross-compiled; x86-64 Linux, x86-64 macOS and aarch64 macOS
+hosts are confirmed to work, but in principle any machine able to run rustc and
+avr-gcc should be good.
+
+Compiling for this target requires `avr-gcc` installed, because a couple of
+intrinsics (like 32-bit multiplication) rely on [`libgcc`](https://github.com/gcc-mirror/gcc/blob/3269a722b7a03613e9c4e2862bc5088c4a17cc11/libgcc/config/avr/lib1funcs.S)
+and can't be provided through `compiler-builtins` yet. This is a limitation that
+[we hope to lift in the future](https://github.com/rust-lang/compiler-builtins/issues/711).
+
+You'll also need to setup the `.cargo/config` file - see below for details.
+
+## Building the target
+
+Rust comes with AVR support enabled, you don't have to rebuild the compiler.
+
+## Building Rust programs
+
+Install `avr-gcc`:
+
+```console
+# Ubuntu:
+$ sudo apt-get install gcc-avr
+
+# Mac:
+$ brew tap osx-cross/avr && brew install avr-gcc
+
+# NixOS (takes a couple of minutes, since it's not provided through Hydra):
+$ nix shell nixpkgs#pkgsCross.avr.buildPackages.gcc11
+```
+
+... setup `.cargo/config` for your project:
+
+```toml
+[build]
+target = "avr-none"
+rustflags = ["-C", "target-cpu=atmega328p"]
+
+[unstable]
+build-std = ["core"]
+```
+
+... and then simply run:
+
+```console
+$ cargo build --release
+```
+
+The final binary will be placed into
+`./target/avr-none/release/your-project.elf`.
+
+Note that since AVRs have rather small amounts of registers, ROM and RAM, it's
+recommended to always use `--release` to avoid running out of space.
+
+Also, please note that specifying `-C target-cpu` is required - here's a list of
+the possible variants:
+
+https://github.com/llvm/llvm-project/blob/093d4db2f3c874d4683fb01194b00dbb20e5c713/clang/lib/Basic/Targets/AVR.cpp#L32
+
+## Testing
+
+You can use [`simavr`](https://github.com/buserror/simavr) to emulate the
+resulting firmware on your machine:
+
+```console
+$ simavr -m atmega328p ./target/avr-none/release/your-project.elf
+```
+
+Alternatively, if you want to write a couple of actual `#[test]`s, you can use
+[`avr-tester`](https://github.com/Patryk27/avr-tester).
diff --git a/src/doc/rustc/src/platform-support/loongarch-none.md b/src/doc/rustc/src/platform-support/loongarch-none.md
index 110a7cc3424..bafa85c26e2 100644
--- a/src/doc/rustc/src/platform-support/loongarch-none.md
+++ b/src/doc/rustc/src/platform-support/loongarch-none.md
@@ -32,7 +32,7 @@ By default, code generated with the soft-float target should run on any
 LoongArch64 hardware, with the hard-float target additionally requiring an FPU;
 enabling additional target features may raise this baseline.
 
-Code generated with the targets will use the `small` code model by default.
+Code generated with the targets will use the `medium` code model by default.
 You can change this using the `-C code-model=` option to rustc.
 
 On `loongarch64-unknown-none*`, `extern "C"` uses the [architecture's standard calling convention][lapcs].
diff --git a/src/doc/rustc/src/platform-support/nto-qnx.md b/src/doc/rustc/src/platform-support/nto-qnx.md
index 339741f1472..77e8caaee4c 100644
--- a/src/doc/rustc/src/platform-support/nto-qnx.md
+++ b/src/doc/rustc/src/platform-support/nto-qnx.md
@@ -31,7 +31,7 @@ Currently, the following QNX versions and compilation targets are supported:
 | `aarch64-unknown-nto-qnx710_iosock` | QNX Neutrino 7.1 with io-sock | AArch64             |      ?       |        ✓         |
 | `x86_64-pc-nto-qnx710_iosock`       | QNX Neutrino 7.1 with io-sock | x86_64              |      ?       |        ✓         |
 | `aarch64-unknown-nto-qnx700`        | QNX Neutrino 7.0              | AArch64             |      ?       |        ✓         |
-| `i586-pc-nto-qnx700`                | QNX Neutrino 7.0              | x86                 |              |        ✓         |
+| `i686-pc-nto-qnx700`                | QNX Neutrino 7.0              | x86                 |              |        ✓         |
 
 On QNX Neutrino 7.0 and 7.1, `io-pkt` is used as network stack by default.
 QNX Neutrino 7.1 includes the optional network stack `io-sock`.
diff --git a/src/doc/rustc/src/platform-support/redox.md b/src/doc/rustc/src/platform-support/redox.md
index 1b3321956ef..2bba92d504c 100644
--- a/src/doc/rustc/src/platform-support/redox.md
+++ b/src/doc/rustc/src/platform-support/redox.md
@@ -9,7 +9,7 @@ Target triplets available so far:
 
 - `x86_64-unknown-redox` (tier 2)
 - `aarch64-unknown-redox` (tier 3)
-- `i686-unknown-redox` (tier 3)
+- `i586-unknown-redox` (tier 3)
 
 ## Target maintainers
 
@@ -36,7 +36,7 @@ target = [
     "<HOST_TARGET>",
     "x86_64-unknown-redox",
     "aarch64-unknown-redox",
-    "i686-unknown-redox",
+    "i586-unknown-redox",
 ]
 ```
 
diff --git a/src/doc/rustc/src/platform-support/wasm32-unknown-unknown.md b/src/doc/rustc/src/platform-support/wasm32-unknown-unknown.md
index 73264aba858..ba95ab7af6d 100644
--- a/src/doc/rustc/src/platform-support/wasm32-unknown-unknown.md
+++ b/src/doc/rustc/src/platform-support/wasm32-unknown-unknown.md
@@ -129,6 +129,8 @@ As of the time of this writing the proposals that are enabled by default (the
 * `mutable-globals`
 * `reference-types`
 * `sign-ext`
+* `nontrapping-fptoint` (Rust 1.87.0+, LLVM 20+)
+* `bulk-memory` (Rust 1.87.0+, LLVM 20+)
 
 If you're compiling WebAssembly code for an engine that does not support a
 feature in LLVM's default feature set then the feature must be disabled at
diff --git a/src/doc/rustc/src/platform-support/x86_64-pc-cygwin.md b/src/doc/rustc/src/platform-support/x86_64-pc-cygwin.md
new file mode 100644
index 00000000000..a8fc4f181d8
--- /dev/null
+++ b/src/doc/rustc/src/platform-support/x86_64-pc-cygwin.md
@@ -0,0 +1,39 @@
+# `x86_64-pc-cygwin`
+
+**Tier: 3**
+
+Windows targets supporting Cygwin.
+The `*-cygwin` targets are **not** intended as native target for applications,
+a developer writing Windows applications should use the `*-pc-windows-*` targets instead, which are *native* Windows.
+
+Cygwin is only intended as an emulation layer for Unix-only programs which do not support the native Windows targets.
+
+## Target maintainers
+
+- [Berrysoft](https://github.com/Berrysoft)
+
+## Requirements
+
+This target is cross compiled. It needs `x86_64-pc-cygwin-gcc` as linker.
+
+The `target_os` of the target is `cygwin`, and it is `unix`.
+
+## Building the target
+
+For cross-compilation you want LLVM with [llvm/llvm-project#121439 (merged)](https://github.com/llvm/llvm-project/pull/121439) applied to fix the LLVM codegen on importing external global variables from DLLs.
+No native builds on Cygwin now. It should be possible theoretically though, but might need a lot of patches.
+
+## Building Rust programs
+
+Rust does not yet ship pre-compiled artifacts for this target. To compile for
+this target, you will either need to build Rust with the target enabled (see
+"Building the target" above), or build your own copy of `core` by using
+`build-std` or similar.
+
+## Testing
+
+Created binaries work fine on Windows with Cygwin.
+
+## Cross-compilation toolchains and C code
+
+Compatible C code can be built with GCC shipped with Cygwin. Clang is untested.
diff --git a/src/doc/rustdoc/src/command-line-arguments.md b/src/doc/rustdoc/src/command-line-arguments.md
index 1af5e2b0e1d..872592d669d 100644
--- a/src/doc/rustdoc/src/command-line-arguments.md
+++ b/src/doc/rustdoc/src/command-line-arguments.md
@@ -100,7 +100,8 @@ mod private { // this item is private and will not be documented
 }
 ```
 
-`--document-private-items` documents all items, even if they're not public.
+`--document-private-items` includes all non-public items in the generated documentation except for `#[doc(hidden)]` items.  Private items will be shown with a 🔒 icon.
+
 
 ## `-L`/`--library-path`: where to look for dependencies
 
diff --git a/src/doc/rustdoc/src/unstable-features.md b/src/doc/rustdoc/src/unstable-features.md
index 3abd538d372..d4ff69a9933 100644
--- a/src/doc/rustdoc/src/unstable-features.md
+++ b/src/doc/rustdoc/src/unstable-features.md
@@ -262,6 +262,25 @@ themselves marked as unstable. To use any of these options, pass `-Z unstable-op
 the flag in question to Rustdoc on the command-line. To do this from Cargo, you can either use the
 `RUSTDOCFLAGS` environment variable or the `cargo rustdoc` command.
 
+### `--document-hidden-items`: Show items that are `#[doc(hidden)]`
+<span id="document-hidden-items"></span>
+
+By default, `rustdoc` does not document items that are annotated with
+[`#[doc(hidden)]`](write-documentation/the-doc-attribute.html#hidden).
+
+`--document-hidden-items` causes all items to be documented as if they did not have `#[doc(hidden)]`, except that hidden items will be shown with a 👻 icon.
+
+Here is a table that fully describes which items are documented with each combination of `--document-hidden-items` and `--document-private-items`:
+
+
+| rustdoc flags                   | items that will be documented         |
+|---------------------------------|---------------------------------------|
+| neither flag                    | only public items that are not hidden |
+| only `--document-hidden-items`  | all public items                      |
+| only `--document-private-items` | all items that are not hidden         |
+| both flags                      | all items                             |
+
+
 ### `--markdown-before-content`: include rendered Markdown before the content
 
  * Tracking issue: [#44027](https://github.com/rust-lang/rust/issues/44027)
diff --git a/src/doc/rustdoc/src/write-documentation/the-doc-attribute.md b/src/doc/rustdoc/src/write-documentation/the-doc-attribute.md
index ff033aa14b8..45146993371 100644
--- a/src/doc/rustdoc/src/write-documentation/the-doc-attribute.md
+++ b/src/doc/rustdoc/src/write-documentation/the-doc-attribute.md
@@ -230,9 +230,8 @@ If you want to know more about inlining rules, take a look at the
 
 <span id="dochidden"></span>
 
-Any item annotated with `#[doc(hidden)]` will not appear in the documentation, unless
-the `strip-hidden` pass is removed. Re-exported items where one of its ancestors has
-`#[doc(hidden)]` will be considered the same as private.
+Any item annotated with `#[doc(hidden)]` will not appear in the documentation,
+unless the [`--document-hidden-items`](../unstable-features.md#document-hidden-items) flag is used.
 
 You can find more information in the [`re-exports` chapter](./re-exports.md).
 
diff --git a/src/doc/unstable-book/src/compiler-flags/autodiff.md b/src/doc/unstable-book/src/compiler-flags/autodiff.md
index 4e55be0ad95..95c188d1f3b 100644
--- a/src/doc/unstable-book/src/compiler-flags/autodiff.md
+++ b/src/doc/unstable-book/src/compiler-flags/autodiff.md
@@ -8,16 +8,13 @@ This feature allows you to differentiate functions using automatic differentiati
 Set the `-Zautodiff=<options>` compiler flag to adjust the behaviour of the autodiff feature.
 Multiple options can be separated with a comma. Valid options are:
 
+`Enable` - Required flag to enable autodiff
 `PrintTA` - print Type Analysis Information
 `PrintAA` - print Activity Analysis Information
 `PrintPerf` - print Performance Warnings from Enzyme
-`Print` - prints all intermediate transformations
+`PrintSteps` - prints all intermediate transformations
 `PrintModBefore` - print the whole module, before running opts
-`PrintModAfterOpts` - print the whole module just before we pass it to Enzyme
-`PrintModAfterEnzyme` - print the module after Enzyme differentiated everything
+`PrintModAfter` - print the module after Enzyme differentiated everything
 `LooseTypes` - Enzyme's loose type debug helper (can cause incorrect gradients)
 `Inline` - runs Enzyme specific Inlining
-`NoModOptAfter` - do not optimize the module after Enzyme is done
-`EnableFncOpt` - tell Enzyme to run LLVM Opts on each function it generated
-`NoVecUnroll` - do not unroll vectorized loops
 `RuntimeActivity` - allow specifying activity at runtime
diff --git a/src/doc/unstable-book/src/language-features/asm-experimental-arch.md b/src/doc/unstable-book/src/language-features/asm-experimental-arch.md
index c2f4170d7d2..d9566c9f55c 100644
--- a/src/doc/unstable-book/src/language-features/asm-experimental-arch.md
+++ b/src/doc/unstable-book/src/language-features/asm-experimental-arch.md
@@ -199,3 +199,5 @@ These flags registers must be restored upon exiting the asm block if the `preser
 - SPARC
   - Integer condition codes (`icc` and `xcc`)
   - Floating-point condition codes (`fcc[0-3]`)
+- CSKY
+  - Condition/carry bit (C) in `PSR`.
diff --git a/src/doc/unstable-book/src/language-features/extended-varargs-abi-support.md b/src/doc/unstable-book/src/language-features/extended-varargs-abi-support.md
new file mode 100644
index 00000000000..b20c30ec8f1
--- /dev/null
+++ b/src/doc/unstable-book/src/language-features/extended-varargs-abi-support.md
@@ -0,0 +1,10 @@
+# `extended_varargs_abi_support`
+
+The tracking issue for this feature is: [#100189]
+
+[#100189]: https://github.com/rust-lang/rust/issues/100189
+
+------------------------
+
+This feature adds the possibility of using `sysv64`, `win64` or `efiapi` calling
+conventions on functions with varargs.
diff --git a/src/doc/unstable-book/src/language-features/intrinsics.md b/src/doc/unstable-book/src/language-features/intrinsics.md
index 13a6814d31b..975b400447e 100644
--- a/src/doc/unstable-book/src/language-features/intrinsics.md
+++ b/src/doc/unstable-book/src/language-features/intrinsics.md
@@ -62,13 +62,19 @@ These must be implemented by all backends.
 
 ### `#[rustc_intrinsic]` declarations
 
-These are written like intrinsics with fallback bodies, but the body is irrelevant.
-Use `loop {}` for the body or call the intrinsic recursively and add
-`#[rustc_intrinsic_must_be_overridden]` to the function to ensure that backends don't
-invoke the body.
+These are written without a body:
+```rust
+#![feature(intrinsics)]
+#![allow(internal_features)]
+
+#[rustc_intrinsic]
+pub fn abort() -> !;
+```
 
 ### Legacy extern ABI based intrinsics
 
+*This style is deprecated, always prefer the above form.*
+
 These are imported as if they were FFI functions, with the special
 `rust-intrinsic` ABI. For example, if one was in a freestanding
 context, but wished to be able to `transmute` between types, and
diff --git a/src/doc/unstable-book/src/language-features/ref-pat-eat-one-layer-2024-structural.md b/src/doc/unstable-book/src/language-features/ref-pat-eat-one-layer-2024-structural.md
index bc587686111..bfdb579cd35 100644
--- a/src/doc/unstable-book/src/language-features/ref-pat-eat-one-layer-2024-structural.md
+++ b/src/doc/unstable-book/src/language-features/ref-pat-eat-one-layer-2024-structural.md
@@ -10,10 +10,11 @@ This feature is incomplete and not yet intended for general use.
 
 This implements experimental, Edition-dependent match ergonomics under consideration for inclusion
 in Rust.
-For more information, see the corresponding typing rules for [Editions 2024 and later].
-On earlier Editions, the current behavior is unspecified.
+For more information, see the corresponding typing rules for [Editions 2021 and earlier] and for
+[Editions 2024 and later].
 
 For alternative experimental match ergonomics, see the feature
 [`ref_pat_eat_one_layer_2024`](./ref-pat-eat-one-layer-2024.md).
 
+[Editions 2021 and earlier]: https://nadrieril.github.io/typing-rust-patterns/?compare=false&opts1=AQEBAQIBAQEBAAAAAAAAAAAAAAAAAAA%3D&mode=rules&do_cmp=false
 [Editions 2024 and later]: https://nadrieril.github.io/typing-rust-patterns/?compare=false&opts1=AQEBAgEBAQEBAgIAAAAAAAAAAAAAAAA%3D&mode=rules&do_cmp=false
diff --git a/src/doc/unstable-book/src/language-features/ref-pat-eat-one-layer-2024.md b/src/doc/unstable-book/src/language-features/ref-pat-eat-one-layer-2024.md
index 43de1849a5e..0c90cec0dbd 100644
--- a/src/doc/unstable-book/src/language-features/ref-pat-eat-one-layer-2024.md
+++ b/src/doc/unstable-book/src/language-features/ref-pat-eat-one-layer-2024.md
@@ -10,10 +10,11 @@ This feature is incomplete and not yet intended for general use.
 
 This implements experimental, Edition-dependent match ergonomics under consideration for inclusion
 in Rust.
-For more information, see the corresponding typing rules for [Editions 2024 and later].
-On earlier Editions, the current behavior is unspecified.
+For more information, see the corresponding typing rules for [Editions 2021 and earlier] and for
+[Editions 2024 and later].
 
 For alternative experimental match ergonomics, see the feature
 [`ref_pat_eat_one_layer_2024_structural`](./ref-pat-eat-one-layer-2024-structural.md).
 
+[Editions 2021 and earlier]: https://nadrieril.github.io/typing-rust-patterns/?compare=false&opts1=AQEBAQIBAQABAAAAAQEBAAEBAAABAAA%3D&mode=rules&do_cmp=false
 [Editions 2024 and later]: https://nadrieril.github.io/typing-rust-patterns/?compare=false&opts1=AQEBAAABAQABAgIAAQEBAAEBAAABAAA%3D&mode=rules&do_cmp=false
diff --git a/src/etc/lldb_commands b/src/etc/lldb_commands
index ef0c3740f03..508296c3a5a 100644
--- a/src/etc/lldb_commands
+++ b/src/etc/lldb_commands
@@ -1,43 +1,80 @@
-type synthetic add -l lldb_lookup.synthetic_lookup -x "^(alloc::([a-z_]+::)+)String$" --category Rust
+# Forces test-compliant formatting to all other types
+type synthetic add -l lldb_lookup.synthetic_lookup -x ".*" --category Rust
+type summary add -F _ -e -x -h "^.*$" --category Rust
+# Std String
+type synthetic add -l lldb_lookup.StdStringSyntheticProvider -x "^(alloc::([a-z_]+::)+)String$" --category Rust
+type summary add -F lldb_lookup.StdStringSummaryProvider  -e -x -h "^(alloc::([a-z_]+::)+)String$" --category Rust
+# Std str
 type synthetic add -l lldb_lookup.synthetic_lookup -x "^&(mut )?str$" --category Rust
+type summary add -F lldb_lookup.summary_lookup  -e -x -h "^&(mut )?str$" --category Rust
+## MSVC
+type synthetic add -l lldb_lookup.MSVCStrSyntheticProvider -x "^ref(_mut)?\$<str\$>$" --category Rust
+type summary add -F lldb_lookup.StdStrSummaryProvider -e -h -x "^ref(_mut)?\$<str\$>$" --category Rust
+# Array
 type synthetic add -l lldb_lookup.synthetic_lookup -x "^&(mut )?\\[.+\\]$" --category Rust
+type summary add -F lldb_lookup.summary_lookup  -e -x -h "^&(mut )?\\[.+\\]$" --category Rust
+# Slice
+## MSVC
+type synthetic add -l lldb_lookup.MSVCStdSliceSyntheticProvider -x "^ref(_mut)?\$<slice2\$<.+> >" --category Rust
+type summary add -F lldb_lookup.StdSliceSummaryProvider -e -x -h "^ref(_mut)?\$<slice2\$<.+> >" --category Rust
+# OsString
 type synthetic add -l lldb_lookup.synthetic_lookup -x "^(std::ffi::([a-z_]+::)+)OsString$" --category Rust
+type summary add -F lldb_lookup.summary_lookup  -e -x -h "^(std::ffi::([a-z_]+::)+)OsString$" --category Rust
+# Vec
 type synthetic add -l lldb_lookup.synthetic_lookup -x "^(alloc::([a-z_]+::)+)Vec<.+>$" --category Rust
+type summary add -F lldb_lookup.summary_lookup  -e -x -h "^(alloc::([a-z_]+::)+)Vec<.+>$" --category Rust
+# VecDeque
 type synthetic add -l lldb_lookup.synthetic_lookup -x "^(alloc::([a-z_]+::)+)VecDeque<.+>$" --category Rust
+type summary add -F lldb_lookup.summary_lookup  -e -x -h "^(alloc::([a-z_]+::)+)VecDeque<.+>$" --category Rust
+# BTreeSet
 type synthetic add -l lldb_lookup.synthetic_lookup -x "^(alloc::([a-z_]+::)+)BTreeSet<.+>$" --category Rust
+type summary add -F lldb_lookup.summary_lookup  -e -x -h "^(alloc::([a-z_]+::)+)BTreeSet<.+>$" --category Rust
+# BTreeMap
 type synthetic add -l lldb_lookup.synthetic_lookup -x "^(alloc::([a-z_]+::)+)BTreeMap<.+>$" --category Rust
+type summary add -F lldb_lookup.summary_lookup  -e -x -h "^(alloc::([a-z_]+::)+)BTreeMap<.+>$" --category Rust
+# HashMap
 type synthetic add -l lldb_lookup.synthetic_lookup -x "^(std::collections::([a-z_]+::)+)HashMap<.+>$" --category Rust
+type summary add -F lldb_lookup.summary_lookup  -e -x -h "^(std::collections::([a-z_]+::)+)HashMap<.+>$" --category Rust
+# HashSet
 type synthetic add -l lldb_lookup.synthetic_lookup -x "^(std::collections::([a-z_]+::)+)HashSet<.+>$" --category Rust
+type summary add -F lldb_lookup.summary_lookup  -e -x -h "^(std::collections::([a-z_]+::)+)HashSet<.+>$" --category Rust
+# Rc
 type synthetic add -l lldb_lookup.synthetic_lookup -x "^(alloc::([a-z_]+::)+)Rc<.+>$" --category Rust
+type summary add -F lldb_lookup.summary_lookup  -e -x -h "^(alloc::([a-z_]+::)+)Rc<.+>$" --category Rust
+# Arc
 type synthetic add -l lldb_lookup.synthetic_lookup -x "^(alloc::([a-z_]+::)+)Arc<.+>$" --category Rust
+type summary add -F lldb_lookup.summary_lookup  -e -x -h "^(alloc::([a-z_]+::)+)Arc<.+>$" --category Rust
+# Cell
 type synthetic add -l lldb_lookup.synthetic_lookup -x "^(core::([a-z_]+::)+)Cell<.+>$" --category Rust
+type summary add -F lldb_lookup.summary_lookup  -e -x -h "^(core::([a-z_]+::)+)Cell<.+>$" --category Rust
+# RefCell
 type synthetic add -l lldb_lookup.synthetic_lookup -x "^(core::([a-z_]+::)+)Ref<.+>$" --category Rust
 type synthetic add -l lldb_lookup.synthetic_lookup -x "^(core::([a-z_]+::)+)RefMut<.+>$" --category Rust
 type synthetic add -l lldb_lookup.synthetic_lookup -x "^(core::([a-z_]+::)+)RefCell<.+>$" --category Rust
-type synthetic add -l lldb_lookup.synthetic_lookup -x "^(core::([a-z_]+::)+)NonZero<.+>$" --category Rust
-type synthetic add -l lldb_lookup.synthetic_lookup -x "^core::num::([a-z_]+::)*NonZero.+$" --category Rust
-type synthetic add -l lldb_lookup.synthetic_lookup -x "^(std::([a-z_]+::)+)PathBuf$" --category Rust
-type synthetic add -l lldb_lookup.synthetic_lookup -x "^&(mut )?(std::([a-z_]+::)+)Path$" --category Rust
-type synthetic add -l lldb_lookup.synthetic_lookup -x "^(.*)$" --category Rust
-type summary add -F _ -e -x -h "^.*$" --category Rust
-type summary add -F lldb_lookup.summary_lookup  -e -x -h "^(alloc::([a-z_]+::)+)String$" --category Rust
-type summary add -F lldb_lookup.summary_lookup  -e -x -h "^&(mut )?str$" --category Rust
-type summary add -F lldb_lookup.summary_lookup  -e -x -h "^&(mut )?\\[.+\\]$" --category Rust
-type summary add -F lldb_lookup.summary_lookup  -e -x -h "^(std::ffi::([a-z_]+::)+)OsString$" --category Rust
-type summary add -F lldb_lookup.summary_lookup  -e -x -h "^(alloc::([a-z_]+::)+)Vec<.+>$" --category Rust
-type summary add -F lldb_lookup.summary_lookup  -e -x -h "^(alloc::([a-z_]+::)+)VecDeque<.+>$" --category Rust
-type summary add -F lldb_lookup.summary_lookup  -e -x -h "^(alloc::([a-z_]+::)+)BTreeSet<.+>$" --category Rust
-type summary add -F lldb_lookup.summary_lookup  -e -x -h "^(alloc::([a-z_]+::)+)BTreeMap<.+>$" --category Rust
-type summary add -F lldb_lookup.summary_lookup  -e -x -h "^(std::collections::([a-z_]+::)+)HashMap<.+>$" --category Rust
-type summary add -F lldb_lookup.summary_lookup  -e -x -h "^(std::collections::([a-z_]+::)+)HashSet<.+>$" --category Rust
-type summary add -F lldb_lookup.summary_lookup  -e -x -h "^(alloc::([a-z_]+::)+)Rc<.+>$" --category Rust
-type summary add -F lldb_lookup.summary_lookup  -e -x -h "^(alloc::([a-z_]+::)+)Arc<.+>$" --category Rust
-type summary add -F lldb_lookup.summary_lookup  -e -x -h "^(core::([a-z_]+::)+)Cell<.+>$" --category Rust
 type summary add -F lldb_lookup.summary_lookup  -e -x -h "^(core::([a-z_]+::)+)Ref<.+>$" --category Rust
 type summary add -F lldb_lookup.summary_lookup  -e -x -h "^(core::([a-z_]+::)+)RefMut<.+>$" --category Rust
 type summary add -F lldb_lookup.summary_lookup  -e -x -h "^(core::([a-z_]+::)+)RefCell<.+>$" --category Rust
+# NonZero
+type synthetic add -l lldb_lookup.synthetic_lookup -x "^(core::([a-z_]+::)+)NonZero<.+>$" --category Rust
+type synthetic add -l lldb_lookup.synthetic_lookup -x "^core::num::([a-z_]+::)*NonZero.+$" --category Rust
 type summary add -F lldb_lookup.summary_lookup  -e -x -h "^(core::([a-z_]+::)+)NonZero<.+>$" --category Rust
 type summary add -F lldb_lookup.summary_lookup  -e -x -h "^core::num::([a-z_]+::)*NonZero.+$" --category Rust
+# PathBuf
+type synthetic add -l lldb_lookup.synthetic_lookup -x "^(std::([a-z_]+::)+)PathBuf$" --category Rust
 type summary add -F lldb_lookup.summary_lookup  -e -x -h "^(std::([a-z_]+::)+)PathBuf$" --category Rust
+# Path
+type synthetic add -l lldb_lookup.synthetic_lookup -x "^&(mut )?(std::([a-z_]+::)+)Path$" --category Rust
 type summary add -F lldb_lookup.summary_lookup  -e -x -h "^&(mut )?(std::([a-z_]+::)+)Path$" --category Rust
+# Enum
+## MSVC
+type synthetic add -l lldb_lookup.MSVCEnumSyntheticProvider -x "^enum2\$<.+>$" --category Rust
+type summary add -F lldb_lookup.MSVCEnumSummaryProvider -e -x -h "^enum2\$<.+>$" --category Rust
+## MSVC Variants
+type synthetic add -l lldb_lookup.synthetic_lookup -x "^enum2\$<.+>::.*$" --category Rust
+type summary add -F lldb_lookup.summary_lookup  -e -x -h "^enum2\$<.+>::.*$" --category Rust
+# Tuple
+type synthetic add -l lldb_lookup.synthetic_lookup -x "^\(.*\)$" --category Rust
+## MSVC
+type synthetic add -l lldb_lookup.MSVCTupleSyntheticProvider -x "^tuple\$<.+>$" --category Rust
+type summary add -F lldb_lookup.TupleSummaryProvider -e -x -h "^tuple\$<.+>$" --category Rust
 type category enable Rust
diff --git a/src/etc/lldb_providers.py b/src/etc/lldb_providers.py
index 2f32ed833af..98426e42423 100644
--- a/src/etc/lldb_providers.py
+++ b/src/etc/lldb_providers.py
@@ -1,14 +1,19 @@
+from __future__ import annotations
 import sys
+from typing import List, TYPE_CHECKING
 
 from lldb import (
     SBData,
     SBError,
-    SBValue,
     eBasicTypeLong,
     eBasicTypeUnsignedLong,
     eBasicTypeUnsignedChar,
+    eFormatChar,
 )
 
+if TYPE_CHECKING:
+    from lldb import SBValue, SBType, SBTypeStaticField
+
 # from lldb.formatters import Logger
 
 ####################################################################################################
@@ -127,6 +132,36 @@ class EmptySyntheticProvider:
         return False
 
 
+def get_template_args(type_name: str) -> list[str]:
+    """
+    Takes a type name `T<A, tuple$<B, C>, D>` and returns a list of its generic args
+    `["A", "tuple$<B, C>", "D"]`.
+
+    String-based replacement for LLDB's `SBType.template_args`, as LLDB is currently unable to
+    populate this field for targets with PDB debug info. Also useful for manually altering the type
+    name of generics (e.g. `Vec<ref$<str$>` -> `Vec<&str>`).
+
+    Each element of the returned list can be looked up for its `SBType` value via
+    `SBTarget.FindFirstType()`
+    """
+    params = []
+    level = 0
+    start = 0
+    for i, c in enumerate(type_name):
+        if c == "<":
+            level += 1
+            if level == 1:
+                start = i + 1
+        elif c == ">":
+            level -= 1
+            if level == 0:
+                params.append(type_name[start:i].strip())
+        elif c == "," and level == 1:
+            params.append(type_name[start:i].strip())
+            start = i + 1
+    return params
+
+
 def SizeSummaryProvider(valobj: SBValue, _dict: LLDBOpaque) -> str:
     return "size=" + str(valobj.GetNumChildren())
 
@@ -141,11 +176,32 @@ def vec_to_string(vec: SBValue) -> str:
     )
 
 
-def StdStringSummaryProvider(valobj: SBValue, _dict: LLDBOpaque) -> str:
-    # logger = Logger.Logger()
-    # logger >> "[StdStringSummaryProvider] for " + str(valobj.GetName())
-    vec = valobj.GetChildAtIndex(0)
-    return '"%s"' % vec_to_string(vec)
+def StdStringSummaryProvider(valobj, dict):
+    inner_vec = (
+        valobj.GetNonSyntheticValue()
+        .GetChildMemberWithName("vec")
+        .GetNonSyntheticValue()
+    )
+
+    pointer = (
+        inner_vec.GetChildMemberWithName("buf")
+        .GetChildMemberWithName("inner")
+        .GetChildMemberWithName("ptr")
+        .GetChildMemberWithName("pointer")
+        .GetChildMemberWithName("pointer")
+    )
+
+    length = inner_vec.GetChildMemberWithName("len").GetValueAsUnsigned()
+
+    if length <= 0:
+        return '""'
+    error = SBError()
+    process = pointer.GetProcess()
+    data = process.ReadMemory(pointer.GetValueAsUnsigned(), length, error)
+    if error.Success():
+        return '"' + data.decode("utf8", "replace") + '"'
+    else:
+        raise Exception("ReadMemory error: %s", error.GetCString())
 
 
 def StdOsStringSummaryProvider(valobj: SBValue, _dict: LLDBOpaque) -> str:
@@ -205,6 +261,31 @@ def StdPathSummaryProvider(valobj: SBValue, _dict: LLDBOpaque) -> str:
     return '"%s"' % data
 
 
+def sequence_formatter(output: str, valobj: SBValue, _dict: LLDBOpaque):
+    length: int = valobj.GetNumChildren()
+
+    long: bool = False
+    for i in range(0, length):
+        if len(output) > 32:
+            long = True
+            break
+
+        child: SBValue = valobj.GetChildAtIndex(i)
+
+        summary = child.summary
+        if summary is None:
+            summary = child.value
+            if summary is None:
+                summary = "{...}"
+        output += f"{summary}, "
+    if long:
+        output = f"(len: {length}) " + output + "..."
+    else:
+        output = output[:-2]
+
+    return output
+
+
 class StructSyntheticProvider:
     """Pretty-printer for structs and struct enum variants"""
 
@@ -246,6 +327,89 @@ class StructSyntheticProvider:
         return True
 
 
+class StdStringSyntheticProvider:
+    def __init__(self, valobj: SBValue, _dict: LLDBOpaque):
+        self.valobj = valobj
+        self.update()
+
+    def update(self):
+        inner_vec = self.valobj.GetChildMemberWithName("vec").GetNonSyntheticValue()
+        self.data_ptr = (
+            inner_vec.GetChildMemberWithName("buf")
+            .GetChildMemberWithName("inner")
+            .GetChildMemberWithName("ptr")
+            .GetChildMemberWithName("pointer")
+            .GetChildMemberWithName("pointer")
+        )
+        self.length = inner_vec.GetChildMemberWithName("len").GetValueAsUnsigned()
+        self.element_type = self.data_ptr.GetType().GetPointeeType()
+
+    def has_children(self) -> bool:
+        return True
+
+    def num_children(self) -> int:
+        return self.length
+
+    def get_child_index(self, name: str) -> int:
+        index = name.lstrip("[").rstrip("]")
+        if index.isdigit():
+            return int(index)
+
+        return -1
+
+    def get_child_at_index(self, index: int) -> SBValue:
+        if not 0 <= index < self.length:
+            return None
+        start = self.data_ptr.GetValueAsUnsigned()
+        address = start + index
+        element = self.data_ptr.CreateValueFromAddress(
+            f"[{index}]", address, self.element_type
+        )
+        element.SetFormat(eFormatChar)
+        return element
+
+
+class MSVCStrSyntheticProvider:
+    __slots__ = ["valobj", "data_ptr", "length"]
+
+    def __init__(self, valobj: SBValue, _dict: LLDBOpaque):
+        self.valobj = valobj
+        self.update()
+
+    def update(self):
+        self.data_ptr = self.valobj.GetChildMemberWithName("data_ptr")
+        self.length = self.valobj.GetChildMemberWithName("length").GetValueAsUnsigned()
+
+    def has_children(self) -> bool:
+        return True
+
+    def num_children(self) -> int:
+        return self.length
+
+    def get_child_index(self, name: str) -> int:
+        index = name.lstrip("[").rstrip("]")
+        if index.isdigit():
+            return int(index)
+
+        return -1
+
+    def get_child_at_index(self, index: int) -> SBValue:
+        if not 0 <= index < self.length:
+            return None
+        start = self.data_ptr.GetValueAsUnsigned()
+        address = start + index
+        element = self.data_ptr.CreateValueFromAddress(
+            f"[{index}]", address, self.data_ptr.GetType().GetPointeeType()
+        )
+        return element
+
+    def get_type_name(self):
+        if self.valobj.GetTypeName().startswith("ref_mut"):
+            return "&mut str"
+        else:
+            return "&str"
+
+
 class ClangEncodedEnumProvider:
     """Pretty-printer for 'clang-encoded' enums support implemented in LLDB"""
 
@@ -308,6 +472,242 @@ class ClangEncodedEnumProvider:
         return default_index
 
 
+class MSVCEnumSyntheticProvider:
+    """
+    Synthetic provider for sum-type enums on MSVC. For a detailed explanation of the internals,
+    see:
+
+    https://github.com/rust-lang/rust/blob/master/compiler/rustc_codegen_llvm/src/debuginfo/metadata/enums/cpp_like.rs
+    """
+
+    __slots__ = ["valobj", "variant", "value"]
+
+    def __init__(self, valobj: SBValue, _dict: LLDBOpaque):
+        self.valobj = valobj
+        self.variant: SBValue
+        self.value: SBValue
+        self.update()
+
+    def update(self):
+        tag: SBValue = self.valobj.GetChildMemberWithName("tag")
+
+        if tag.IsValid():
+            tag: int = tag.GetValueAsUnsigned()
+            for child in self.valobj.GetNonSyntheticValue().children:
+                if not child.name.startswith("variant"):
+                    continue
+
+                variant_type: SBType = child.GetType()
+                try:
+                    exact: SBTypeStaticField = variant_type.GetStaticFieldWithName(
+                        "DISCR_EXACT"
+                    )
+                except AttributeError:
+                    # LLDB versions prior to 19.0.0 do not have the `SBTypeGetStaticField` API.
+                    # With current DI generation there's not a great way to provide a "best effort"
+                    # evaluation either, so we just return the object itself with no further
+                    # attempts to inspect the type information
+                    self.variant = self.valobj
+                    self.value = self.valobj
+                    return
+
+                if exact.IsValid():
+                    discr: int = exact.GetConstantValue(
+                        self.valobj.target
+                    ).GetValueAsUnsigned()
+                    if tag == discr:
+                        self.variant = child
+                        self.value = child.GetChildMemberWithName(
+                            "value"
+                        ).GetSyntheticValue()
+                        return
+                else:  # if invalid, DISCR must be a range
+                    begin: int = (
+                        variant_type.GetStaticFieldWithName("DISCR_BEGIN")
+                        .GetConstantValue(self.valobj.target)
+                        .GetValueAsUnsigned()
+                    )
+                    end: int = (
+                        variant_type.GetStaticFieldWithName("DISCR_END")
+                        .GetConstantValue(self.valobj.target)
+                        .GetValueAsUnsigned()
+                    )
+
+                    # begin isn't necessarily smaller than end, so we must test for both cases
+                    if begin < end:
+                        if begin <= tag <= end:
+                            self.variant = child
+                            self.value = child.GetChildMemberWithName(
+                                "value"
+                            ).GetSyntheticValue()
+                            return
+                    else:
+                        if tag >= begin or tag <= end:
+                            self.variant = child
+                            self.value = child.GetChildMemberWithName(
+                                "value"
+                            ).GetSyntheticValue()
+                            return
+        else:  # if invalid, tag is a 128 bit value
+            tag_lo: int = self.valobj.GetChildMemberWithName(
+                "tag128_lo"
+            ).GetValueAsUnsigned()
+            tag_hi: int = self.valobj.GetChildMemberWithName(
+                "tag128_hi"
+            ).GetValueAsUnsigned()
+
+            tag: int = (tag_hi << 64) | tag_lo
+
+            for child in self.valobj.GetNonSyntheticValue().children:
+                if not child.name.startswith("variant"):
+                    continue
+
+                variant_type: SBType = child.GetType()
+                exact_lo: SBTypeStaticField = variant_type.GetStaticFieldWithName(
+                    "DISCR128_EXACT_LO"
+                )
+
+                if exact_lo.IsValid():
+                    exact_lo: int = exact_lo.GetConstantValue(
+                        self.valobj.target
+                    ).GetValueAsUnsigned()
+                    exact_hi: int = (
+                        variant_type.GetStaticFieldWithName("DISCR128_EXACT_HI")
+                        .GetConstantValue(self.valobj.target)
+                        .GetValueAsUnsigned()
+                    )
+
+                    discr: int = (exact_hi << 64) | exact_lo
+                    if tag == discr:
+                        self.variant = child
+                        self.value = child.GetChildMemberWithName(
+                            "value"
+                        ).GetSyntheticValue()
+                        return
+                else:  # if invalid, DISCR must be a range
+                    begin_lo: int = (
+                        variant_type.GetStaticFieldWithName("DISCR128_BEGIN_LO")
+                        .GetConstantValue(self.valobj.target)
+                        .GetValueAsUnsigned()
+                    )
+                    begin_hi: int = (
+                        variant_type.GetStaticFieldWithName("DISCR128_BEGIN_HI")
+                        .GetConstantValue(self.valobj.target)
+                        .GetValueAsUnsigned()
+                    )
+
+                    end_lo: int = (
+                        variant_type.GetStaticFieldWithName("DISCR128_END_LO")
+                        .GetConstantValue(self.valobj.target)
+                        .GetValueAsUnsigned()
+                    )
+                    end_hi: int = (
+                        variant_type.GetStaticFieldWithName("DISCR128_END_HI")
+                        .GetConstantValue(self.valobj.target)
+                        .GetValueAsUnsigned()
+                    )
+
+                    begin = (begin_hi << 64) | begin_lo
+                    end = (end_hi << 64) | end_lo
+
+                    # begin isn't necessarily smaller than end, so we must test for both cases
+                    if begin < end:
+                        if begin <= tag <= end:
+                            self.variant = child
+                            self.value = child.GetChildMemberWithName(
+                                "value"
+                            ).GetSyntheticValue()
+                            return
+                    else:
+                        if tag >= begin or tag <= end:
+                            self.variant = child
+                            self.value = child.GetChildMemberWithName(
+                                "value"
+                            ).GetSyntheticValue()
+                            return
+
+    def num_children(self) -> int:
+        return self.value.GetNumChildren()
+
+    def get_child_index(self, name: str) -> int:
+        return self.value.GetIndexOfChildWithName(name)
+
+    def get_child_at_index(self, index: int) -> SBValue:
+        return self.value.GetChildAtIndex(index)
+
+    def has_children(self) -> bool:
+        return self.value.MightHaveChildren()
+
+    def get_type_name(self) -> str:
+        name = self.valobj.GetTypeName()
+        # remove "enum2$<", str.removeprefix() is python 3.9+
+        name = name[7:]
+
+        # MSVC misinterprets ">>" as a shift operator, so spaces are inserted by rust to
+        # avoid that
+        if name.endswith(" >"):
+            name = name[:-2]
+        elif name.endswith(">"):
+            name = name[:-1]
+
+        return name
+
+
+def MSVCEnumSummaryProvider(valobj: SBValue, _dict: LLDBOpaque) -> str:
+    enum_synth = MSVCEnumSyntheticProvider(valobj.GetNonSyntheticValue(), _dict)
+    variant_names: SBType = valobj.target.FindFirstType(
+        f"{enum_synth.valobj.GetTypeName()}::VariantNames"
+    )
+    try:
+        name_idx = (
+            enum_synth.variant.GetType()
+            .GetStaticFieldWithName("NAME")
+            .GetConstantValue(valobj.target)
+            .GetValueAsUnsigned()
+        )
+    except AttributeError:
+        # LLDB versions prior to 19 do not have the `SBTypeGetStaticField` API, and have no way
+        # to determine the value based on the tag field.
+        tag: SBValue = valobj.GetChildMemberWithName("tag")
+
+        if tag.IsValid():
+            discr: int = tag.GetValueAsUnsigned()
+            return "".join(["{tag = ", str(tag.unsigned), "}"])
+        else:
+            tag_lo: int = valobj.GetChildMemberWithName(
+                "tag128_lo"
+            ).GetValueAsUnsigned()
+            tag_hi: int = valobj.GetChildMemberWithName(
+                "tag128_hi"
+            ).GetValueAsUnsigned()
+
+            discr: int = (tag_hi << 64) | tag_lo
+
+        return "".join(["{tag = ", str(discr), "}"])
+
+    name: str = variant_names.enum_members[name_idx].name
+
+    if enum_synth.num_children() == 0:
+        return name
+
+    child_name: str = enum_synth.value.GetChildAtIndex(0).name
+    if child_name == "0" or child_name == "__0":
+        # enum variant is a tuple struct
+        return name + TupleSummaryProvider(enum_synth.value, _dict)
+    else:
+        # enum variant is a regular struct
+        var_list = (
+            str(enum_synth.value.GetNonSyntheticValue()).split("= ", 1)[1].splitlines()
+        )
+        vars = [x.strip() for x in var_list if x not in ("{", "}")]
+        if vars[0][0] == "(":
+            vars[0] = vars[0][1:]
+        if vars[-1][-1] == ")":
+            vars[-1] = vars[-1][:-1]
+
+        return f"{name}{{{', '.join(vars)}}}"
+
+
 class TupleSyntheticProvider:
     """Pretty-printer for tuples and tuple enum variants"""
 
@@ -348,6 +748,50 @@ class TupleSyntheticProvider:
         return True
 
 
+class MSVCTupleSyntheticProvider:
+    __slots__ = ["valobj"]
+
+    def __init__(self, valobj: SBValue, _dict: LLDBOpaque):
+        self.valobj = valobj
+
+    def num_children(self) -> int:
+        return self.valobj.GetNumChildren()
+
+    def get_child_index(self, name: str) -> int:
+        return self.valobj.GetIndexOfChildWithName(name)
+
+    def get_child_at_index(self, index: int) -> SBValue:
+        child: SBValue = self.valobj.GetChildAtIndex(index)
+        return child.CreateChildAtOffset(str(index), 0, child.GetType())
+
+    def update(self):
+        pass
+
+    def has_children(self) -> bool:
+        return self.valobj.MightHaveChildren()
+
+    def get_type_name(self) -> str:
+        name = self.valobj.GetTypeName()
+        # remove "tuple$<" and ">", str.removeprefix and str.removesuffix require python 3.9+
+        name = name[7:-1]
+        return "(" + name + ")"
+
+
+def TupleSummaryProvider(valobj: SBValue, _dict: LLDBOpaque):
+    output: List[str] = []
+
+    for i in range(0, valobj.GetNumChildren()):
+        child: SBValue = valobj.GetChildAtIndex(i)
+        summary = child.summary
+        if summary is None:
+            summary = child.value
+            if summary is None:
+                summary = "{...}"
+        output.append(summary)
+
+    return "(" + ", ".join(output) + ")"
+
+
 class StdVecSyntheticProvider:
     """Pretty-printer for alloc::vec::Vec<T>
 
@@ -396,6 +840,11 @@ class StdVecSyntheticProvider:
         )
 
         self.element_type = self.valobj.GetType().GetTemplateArgumentType(0)
+
+        if not self.element_type.IsValid():
+            element_name = get_template_args(self.valobj.GetTypeName())[0]
+            self.element_type = self.valobj.target.FindFirstType(element_name)
+
         self.element_type_size = self.element_type.GetByteSize()
 
     def has_children(self) -> bool:
@@ -403,6 +852,8 @@ class StdVecSyntheticProvider:
 
 
 class StdSliceSyntheticProvider:
+    __slots__ = ["valobj", "length", "data_ptr", "element_type", "element_size"]
+
     def __init__(self, valobj: SBValue, _dict: LLDBOpaque):
         self.valobj = valobj
         self.update()
@@ -419,7 +870,7 @@ class StdSliceSyntheticProvider:
 
     def get_child_at_index(self, index: int) -> SBValue:
         start = self.data_ptr.GetValueAsUnsigned()
-        address = start + index * self.element_type_size
+        address = start + index * self.element_size
         element = self.data_ptr.CreateValueFromAddress(
             "[%s]" % index, address, self.element_type
         )
@@ -430,12 +881,34 @@ class StdSliceSyntheticProvider:
         self.data_ptr = self.valobj.GetChildMemberWithName("data_ptr")
 
         self.element_type = self.data_ptr.GetType().GetPointeeType()
-        self.element_type_size = self.element_type.GetByteSize()
+        self.element_size = self.element_type.GetByteSize()
 
     def has_children(self) -> bool:
         return True
 
 
+class MSVCStdSliceSyntheticProvider(StdSliceSyntheticProvider):
+    def get_type_name(self) -> str:
+        name = self.valobj.GetTypeName()
+
+        if name.startswith("ref_mut"):
+            # remove "ref_mut$<slice2$<" and trailing "> >"
+            name = name[17:-3]
+            ref = "&mut "
+        else:
+            # remove "ref$<slice2$<" and trailing "> >"
+            name = name[13:-3]
+            ref = "&"
+
+        return "".join([ref, "[", name, "]"])
+
+
+def StdSliceSummaryProvider(valobj, dict):
+    output = sequence_formatter("[", valobj, dict)
+    output += "]"
+    return output
+
+
 class StdVecDequeSyntheticProvider:
     """Pretty-printer for alloc::collections::vec_deque::VecDeque<T>
 
@@ -627,7 +1100,16 @@ class StdHashMapSyntheticProvider:
         ctrl = inner_table.GetChildMemberWithName("ctrl").GetChildAtIndex(0)
 
         self.size = inner_table.GetChildMemberWithName("items").GetValueAsUnsigned()
-        self.pair_type = table.type.template_args[0]
+
+        template_args = table.type.template_args
+
+        if template_args is None:
+            type_name = table.GetTypeName()
+            args = get_template_args(type_name)
+            self.pair_type = self.valobj.target.FindFirstType(args[0])
+        else:
+            self.pair_type = template_args[0]
+
         if self.pair_type.IsTypedefType():
             self.pair_type = self.pair_type.GetTypedefedType()
         self.pair_type_size = self.pair_type.GetByteSize()
diff --git a/src/etc/rust_analyzer_zed.json b/src/etc/rust_analyzer_zed.json
new file mode 100644
index 00000000000..469ea050621
--- /dev/null
+++ b/src/etc/rust_analyzer_zed.json
@@ -0,0 +1,52 @@
+{
+  "lsp": {
+    "rust-analyzer": {
+      "initialization_options": {
+        "cargo": {
+          "buildScripts": {
+            "enable": true,
+            "invocationLocation": "root",
+            "invocationStrategy": "once",
+            "overrideCommand": ["python3", "x.py", "check", "--json-output"]
+          },
+          "extraEnv": {
+            "RUSTC_BOOTSTRAP": "1"
+          },
+          "sysrootSrc": "./library"
+        },
+        "check": {
+          "invocationLocation": "root",
+          "invocationStrategy": "once",
+          "overrideCommand": ["python3", "x.py", "check", "--json-output"]
+        },
+        "linkedProjects": [
+          "Cargo.toml",
+          "library/Cargo.toml",
+          "src/tools/x/Cargo.toml",
+          "src/bootstrap/Cargo.toml",
+          "src/tools/rust-analyzer/Cargo.toml",
+          "compiler/rustc_codegen_cranelift/Cargo.toml",
+          "compiler/rustc_codegen_gcc/Cargo.toml"
+        ],
+        "procMacro": {
+            "enable": true,
+            "server": "${workspaceFolder}/build/host/stage0/libexec/rust-analyzer-proc-macro-srv"
+        },
+        "rustc": {
+          "source": "./Cargo.toml"
+        },
+        "rustfmt": {
+          "overrideCommand": [
+            "${workspaceFolder}/build/host/rustfmt/bin/rustfmt",
+            "--edition=2021"
+          ]
+        },
+        "server": {
+          "extraEnv": {
+            "RUSTUP_TOOLCHAIN": "nightly"
+          }
+        }
+      }
+    }
+  }
+}
diff --git a/src/librustdoc/Cargo.toml b/src/librustdoc/Cargo.toml
index d9bd11267da..91cc4088788 100644
--- a/src/librustdoc/Cargo.toml
+++ b/src/librustdoc/Cargo.toml
@@ -13,8 +13,9 @@ rinja = { version = "0.3", default-features = false, features = ["config"] }
 base64 = "0.21.7"
 itertools = "0.12"
 indexmap = "2"
-minifier = { version = "0.3.2", default-features = false }
+minifier = { version = "0.3.5", default-features = false }
 pulldown-cmark-old = { version = "0.9.6", package = "pulldown-cmark", default-features = false }
+pulldown-cmark-escape = { version = "0.11.0", features = ["simd"] }
 regex = "1"
 rustdoc-json-types = { path = "../rustdoc-json-types" }
 serde_json = "1.0"
diff --git a/src/librustdoc/clean/cfg.rs b/src/librustdoc/clean/cfg.rs
index b576f28176e..bec7fbe8f52 100644
--- a/src/librustdoc/clean/cfg.rs
+++ b/src/librustdoc/clean/cfg.rs
@@ -13,8 +13,8 @@ use rustc_session::parse::ParseSess;
 use rustc_span::Span;
 use rustc_span::symbol::{Symbol, sym};
 
+use crate::display::Joined as _;
 use crate::html::escape::Escape;
-use crate::joined::Joined as _;
 
 #[cfg(test)]
 mod tests;
diff --git a/src/librustdoc/clean/inline.rs b/src/librustdoc/clean/inline.rs
index 3d51ab1967d..e10a74221ae 100644
--- a/src/librustdoc/clean/inline.rs
+++ b/src/librustdoc/clean/inline.rs
@@ -488,7 +488,7 @@ pub(crate) fn build_impl(
             impl_
                 .items
                 .iter()
-                .map(|item| tcx.hir().impl_item(item.id))
+                .map(|item| tcx.hir_impl_item(item.id))
                 .filter(|item| {
                     // Filter out impl items whose corresponding trait item has `doc(hidden)`
                     // not to document such impl items.
@@ -703,7 +703,7 @@ fn build_module_items(
 pub(crate) fn print_inlined_const(tcx: TyCtxt<'_>, did: DefId) -> String {
     if let Some(did) = did.as_local() {
         let hir_id = tcx.local_def_id_to_hir_id(did);
-        rustc_hir_pretty::id_to_string(&tcx.hir(), hir_id)
+        rustc_hir_pretty::id_to_string(&tcx, hir_id)
     } else {
         tcx.rendered_const(did).clone()
     }
diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs
index 1d62a93e723..ceffe5e5ce0 100644
--- a/src/librustdoc/clean/mod.rs
+++ b/src/librustdoc/clean/mod.rs
@@ -1145,14 +1145,14 @@ fn clean_args_from_types_and_body_id<'tcx>(
     types: &[hir::Ty<'tcx>],
     body_id: hir::BodyId,
 ) -> Arguments {
-    let body = cx.tcx.hir().body(body_id);
+    let body = cx.tcx.hir_body(body_id);
 
     Arguments {
         values: types
             .iter()
-            .enumerate()
-            .map(|(i, ty)| Argument {
-                name: name_from_pat(body.params[i].pat),
+            .zip(body.params)
+            .map(|(ty, param)| Argument {
+                name: name_from_pat(param.pat),
                 type_: clean_ty(ty, cx),
                 is_const: false,
             })
@@ -2737,13 +2737,13 @@ fn add_without_unwanted_attributes<'hir>(
     import_parent: Option<DefId>,
 ) {
     for attr in new_attrs {
-        if matches!(attr.kind, hir::AttrKind::DocComment(..)) {
+        if attr.is_doc_comment() {
             attrs.push((Cow::Borrowed(attr), import_parent));
             continue;
         }
         let mut attr = attr.clone();
-        match attr.kind {
-            hir::AttrKind::Normal(ref mut normal) => {
+        match attr {
+            hir::Attribute::Unparsed(ref mut normal) => {
                 if let [ident] = &*normal.path.segments {
                     let ident = ident.name;
                     if ident == sym::doc {
@@ -2755,7 +2755,11 @@ fn add_without_unwanted_attributes<'hir>(
                     }
                 }
             }
-            _ => unreachable!(),
+            hir::Attribute::Parsed(..) => {
+                if is_inline {
+                    attrs.push((Cow::Owned(attr), import_parent));
+                }
+            }
         }
     }
 }
@@ -2845,7 +2849,7 @@ fn clean_maybe_renamed_item<'tcx>(
             ItemKind::Trait(_, _, generics, bounds, item_ids) => {
                 let items = item_ids
                     .iter()
-                    .map(|ti| clean_trait_item(cx.tcx.hir().trait_item(ti.id), cx))
+                    .map(|ti| clean_trait_item(cx.tcx.hir_trait_item(ti.id), cx))
                     .collect();
 
                 TraitItem(Box::new(Trait {
@@ -2891,7 +2895,7 @@ fn clean_impl<'tcx>(
     let items = impl_
         .items
         .iter()
-        .map(|ii| clean_impl_item(tcx.hir().impl_item(ii.id), cx))
+        .map(|ii| clean_impl_item(tcx.hir_impl_item(ii.id), cx))
         .collect::<Vec<_>>();
 
     // If this impl block is an implementation of the Deref trait, then we
diff --git a/src/librustdoc/clean/types.rs b/src/librustdoc/clean/types.rs
index 5f7c30a33ab..178b6a60b41 100644
--- a/src/librustdoc/clean/types.rs
+++ b/src/librustdoc/clean/types.rs
@@ -220,12 +220,11 @@ impl ExternalCrate {
             None
         };
         if root.is_local() {
-            tcx.hir()
-                .root_module()
+            tcx.hir_root_module()
                 .item_ids
                 .iter()
                 .filter_map(|&id| {
-                    let item = tcx.hir().item(id);
+                    let item = tcx.hir_item(id);
                     match item.kind {
                         hir::ItemKind::Mod(_) => {
                             as_keyword(Res::Def(DefKind::Mod, id.owner_id.to_def_id()))
@@ -266,7 +265,7 @@ impl ExternalCrate {
                     let attr_value = attr.value_str().expect("syntax should already be validated");
                     let Some(prim) = PrimitiveType::from_symbol(attr_value) else {
                         span_bug!(
-                            attr.span,
+                            attr.span(),
                             "primitive `{attr_value}` is not a member of `PrimitiveType`"
                         );
                     };
@@ -277,12 +276,11 @@ impl ExternalCrate {
         };
 
         if root.is_local() {
-            tcx.hir()
-                .root_module()
+            tcx.hir_root_module()
                 .item_ids
                 .iter()
                 .filter_map(|&id| {
-                    let item = tcx.hir().item(id);
+                    let item = tcx.hir_item(id);
                     match item.kind {
                         hir::ItemKind::Mod(_) => {
                             as_primitive(Res::Def(DefKind::Mod, id.owner_id.to_def_id()))
@@ -2122,9 +2120,8 @@ impl Discriminant {
     /// Will be `None` in the case of cross-crate reexports, and may be
     /// simplified
     pub(crate) fn expr(&self, tcx: TyCtxt<'_>) -> Option<String> {
-        self.expr.map(|body| {
-            rendered_const(tcx, tcx.hir().body(body), tcx.hir().body_owner_def_id(body))
-        })
+        self.expr
+            .map(|body| rendered_const(tcx, tcx.hir_body(body), tcx.hir_body_owner_def_id(body)))
     }
     pub(crate) fn value(&self, tcx: TyCtxt<'_>, with_underscores: bool) -> String {
         print_evaluated_const(tcx, self.value, with_underscores, false).unwrap()
@@ -2420,7 +2417,7 @@ impl ConstantKind {
             ConstantKind::Path { ref path } => path.to_string(),
             ConstantKind::Extern { def_id } => print_inlined_const(tcx, def_id),
             ConstantKind::Local { body, .. } | ConstantKind::Anonymous { body } => {
-                rendered_const(tcx, tcx.hir().body(body), tcx.hir().body_owner_def_id(body))
+                rendered_const(tcx, tcx.hir_body(body), tcx.hir_body_owner_def_id(body))
             }
             ConstantKind::Infer { .. } => "_".to_string(),
         }
diff --git a/src/librustdoc/clean/utils.rs b/src/librustdoc/clean/utils.rs
index 8a7d140bb1a..34656b26ce2 100644
--- a/src/librustdoc/clean/utils.rs
+++ b/src/librustdoc/clean/utils.rs
@@ -1,5 +1,5 @@
 use std::assert_matches::debug_assert_matches;
-use std::fmt::Write as _;
+use std::fmt::{self, Display, Write as _};
 use std::mem;
 use std::sync::LazyLock as Lazy;
 
@@ -24,6 +24,7 @@ use crate::clean::{
     clean_middle_ty, inline,
 };
 use crate::core::DocContext;
+use crate::display::Joined as _;
 
 #[cfg(test)]
 mod tests;
@@ -250,16 +251,20 @@ pub(crate) fn qpath_to_string(p: &hir::QPath<'_>) -> String {
         hir::QPath::LangItem(lang_item, ..) => return lang_item.name().to_string(),
     };
 
-    let mut s = String::new();
-    for (i, seg) in segments.iter().enumerate() {
-        if i > 0 {
-            s.push_str("::");
-        }
-        if seg.ident.name != kw::PathRoot {
-            s.push_str(seg.ident.as_str());
-        }
-    }
-    s
+    fmt::from_fn(|f| {
+        segments
+            .iter()
+            .map(|seg| {
+                fmt::from_fn(|f| {
+                    if seg.ident.name != kw::PathRoot {
+                        write!(f, "{}", seg.ident)?;
+                    }
+                    Ok(())
+                })
+            })
+            .joined("::", f)
+    })
+    .to_string()
 }
 
 pub(crate) fn build_deref_target_impls(
@@ -299,35 +304,49 @@ pub(crate) fn name_from_pat(p: &hir::Pat<'_>) -> Symbol {
 
     Symbol::intern(&match p.kind {
         // FIXME(never_patterns): does this make sense?
-        PatKind::Wild | PatKind::Err(_) | PatKind::Never | PatKind::Struct(..) => {
+        PatKind::Wild
+        | PatKind::Err(_)
+        | PatKind::Never
+        | PatKind::Struct(..)
+        | PatKind::Range(..) => {
             return kw::Underscore;
         }
         PatKind::Binding(_, _, ident, _) => return ident.name,
+        PatKind::Box(p) | PatKind::Ref(p, _) | PatKind::Guard(p, _) => return name_from_pat(p),
         PatKind::TupleStruct(ref p, ..)
         | PatKind::Expr(PatExpr { kind: PatExprKind::Path(ref p), .. }) => qpath_to_string(p),
         PatKind::Or(pats) => {
-            pats.iter().map(|p| name_from_pat(p).to_string()).collect::<Vec<String>>().join(" | ")
+            fmt::from_fn(|f| pats.iter().map(|p| name_from_pat(p)).joined(" | ", f)).to_string()
+        }
+        PatKind::Tuple(elts, _) => {
+            format!("({})", fmt::from_fn(|f| elts.iter().map(|p| name_from_pat(p)).joined(", ", f)))
         }
-        PatKind::Tuple(elts, _) => format!(
-            "({})",
-            elts.iter().map(|p| name_from_pat(p).to_string()).collect::<Vec<String>>().join(", ")
-        ),
-        PatKind::Box(p) => return name_from_pat(p),
         PatKind::Deref(p) => format!("deref!({})", name_from_pat(p)),
-        PatKind::Ref(p, _) => return name_from_pat(p),
         PatKind::Expr(..) => {
             warn!(
                 "tried to get argument name from PatKind::Expr, which is silly in function arguments"
             );
             return Symbol::intern("()");
         }
-        PatKind::Guard(p, _) => return name_from_pat(p),
-        PatKind::Range(..) => return kw::Underscore,
-        PatKind::Slice(begin, ref mid, end) => {
-            let begin = begin.iter().map(|p| name_from_pat(p).to_string());
-            let mid = mid.as_ref().map(|p| format!("..{}", name_from_pat(p))).into_iter();
-            let end = end.iter().map(|p| name_from_pat(p).to_string());
-            format!("[{}]", begin.chain(mid).chain(end).collect::<Vec<_>>().join(", "))
+        PatKind::Slice(begin, mid, end) => {
+            fn print_pat<'a>(pat: &'a Pat<'a>, wild: bool) -> impl Display + 'a {
+                fmt::from_fn(move |f| {
+                    if wild {
+                        f.write_str("..")?;
+                    }
+                    name_from_pat(pat).fmt(f)
+                })
+            }
+
+            format!(
+                "[{}]",
+                fmt::from_fn(|f| {
+                    let begin = begin.iter().map(|p| print_pat(p, false));
+                    let mid = mid.map(|p| print_pat(p, true));
+                    let end = end.iter().map(|p| print_pat(p, false));
+                    begin.chain(mid).chain(end).joined(", ", f)
+                })
+            )
         }
     })
 }
@@ -336,7 +355,7 @@ pub(crate) fn print_const(cx: &DocContext<'_>, n: ty::Const<'_>) -> String {
     match n.kind() {
         ty::ConstKind::Unevaluated(ty::UnevaluatedConst { def, args: _ }) => {
             let s = if let Some(def) = def.as_local() {
-                rendered_const(cx.tcx, cx.tcx.hir().body_owned_by(def), def)
+                rendered_const(cx.tcx, cx.tcx.hir_body_owned_by(def), def)
             } else {
                 inline::print_inlined_const(cx.tcx, def)
             };
diff --git a/src/librustdoc/core.rs b/src/librustdoc/core.rs
index 5c146da03ac..757a2a6e0dd 100644
--- a/src/librustdoc/core.rs
+++ b/src/librustdoc/core.rs
@@ -304,8 +304,7 @@ pub(crate) fn create_config(
                     return tcx.typeck(typeck_root_def_id);
                 }
 
-                let hir = tcx.hir();
-                let body = hir.body_owned_by(def_id);
+                let body = tcx.hir_body_owned_by(def_id);
                 debug!("visiting body for {def_id:?}");
                 EmitIgnoredResolutionErrors::new(tcx).visit_body(body);
                 (rustc_interface::DEFAULT_QUERY_PROVIDERS.typeck)(tcx, def_id)
@@ -335,14 +334,14 @@ pub(crate) fn run_global_ctxt(
 
     // NOTE: These are copy/pasted from typeck/lib.rs and should be kept in sync with those changes.
     let _ = tcx.sess.time("wf_checking", || {
-        tcx.hir().try_par_for_each_module(|module| tcx.ensure_ok().check_mod_type_wf(module))
+        tcx.try_par_hir_for_each_module(|module| tcx.ensure_ok().check_mod_type_wf(module))
     });
 
     tcx.dcx().abort_if_errors();
 
     tcx.sess.time("missing_docs", || rustc_lint::check_crate(tcx));
     tcx.sess.time("check_mod_attrs", || {
-        tcx.hir().for_each_module(|module| tcx.ensure_ok().check_mod_attrs(module))
+        tcx.hir_for_each_module(|module| tcx.ensure_ok().check_mod_attrs(module))
     });
     rustc_passes::stability::check_unused_or_stable_features(tcx);
 
@@ -378,7 +377,7 @@ pub(crate) fn run_global_ctxt(
         ctxt.external_traits.insert(sized_trait_did, sized_trait);
     }
 
-    debug!("crate: {:?}", tcx.hir().krate());
+    debug!("crate: {:?}", tcx.hir_crate(()));
 
     let mut krate = tcx.sess.time("clean_crate", || clean::krate(&mut ctxt));
 
@@ -464,10 +463,10 @@ impl<'tcx> EmitIgnoredResolutionErrors<'tcx> {
 impl<'tcx> Visitor<'tcx> for EmitIgnoredResolutionErrors<'tcx> {
     type NestedFilter = nested_filter::OnlyBodies;
 
-    fn nested_visit_map(&mut self) -> Self::Map {
+    fn maybe_tcx(&mut self) -> Self::MaybeTyCtxt {
         // We need to recurse into nested closures,
         // since those will fallback to the parent for type checking.
-        self.tcx.hir()
+        self.tcx
     }
 
     fn visit_path(&mut self, path: &Path<'tcx>, _id: HirId) {
diff --git a/src/librustdoc/joined.rs b/src/librustdoc/display.rs
index f369c6cf237..ee8dde013ee 100644
--- a/src/librustdoc/joined.rs
+++ b/src/librustdoc/display.rs
@@ -1,3 +1,5 @@
+//! Various utilities for working with [`fmt::Display`] implementations.
+
 use std::fmt::{self, Display, Formatter};
 
 pub(crate) trait Joined: IntoIterator {
@@ -27,3 +29,19 @@ where
         Ok(())
     }
 }
+
+pub(crate) trait MaybeDisplay {
+    /// For a given `Option<T: Display>`, returns a `Display` implementation that will display `t` if `Some(t)`, or nothing if `None`.
+    fn maybe_display(self) -> impl Display;
+}
+
+impl<T: Display> MaybeDisplay for Option<T> {
+    fn maybe_display(self) -> impl Display {
+        fmt::from_fn(move |f| {
+            if let Some(t) = self.as_ref() {
+                t.fmt(f)?;
+            }
+            Ok(())
+        })
+    }
+}
diff --git a/src/librustdoc/doctest.rs b/src/librustdoc/doctest.rs
index 8b522e614b8..4a379b4235f 100644
--- a/src/librustdoc/doctest.rs
+++ b/src/librustdoc/doctest.rs
@@ -786,6 +786,7 @@ impl IndividualTestOptions {
 /// [`clean`]: crate::clean
 /// [`run_merged_tests`]: crate::doctest::runner::DocTestRunner::run_merged_tests
 /// [`generate_unique_doctest`]: crate::doctest::make::DocTestBuilder::generate_unique_doctest
+#[derive(Debug)]
 pub(crate) struct ScrapedDocTest {
     filename: FileName,
     line: usize,
diff --git a/src/librustdoc/doctest/make.rs b/src/librustdoc/doctest/make.rs
index d89caabefe3..4792bc525a5 100644
--- a/src/librustdoc/doctest/make.rs
+++ b/src/librustdoc/doctest/make.rs
@@ -1,6 +1,7 @@
 //! Logic for transforming the raw code given by the user into something actually
 //! runnable, e.g. by adding a `main` function if it doesn't already exist.
 
+use std::fmt::{self, Write as _};
 use std::io;
 use std::sync::Arc;
 
@@ -17,6 +18,7 @@ use rustc_span::symbol::sym;
 use tracing::debug;
 
 use super::GlobalTestOptions;
+use crate::display::Joined as _;
 use crate::html::markdown::LangString;
 
 /// This struct contains information about the doctest itself which is then used to generate
@@ -232,13 +234,15 @@ impl DocTestBuilder {
 
             // add extra 4 spaces for each line to offset the code block
             if opts.insert_indent_space {
-                prog.push_str(
-                    &everything_else
+                write!(
+                    prog,
+                    "{}",
+                    fmt::from_fn(|f| everything_else
                         .lines()
-                        .map(|line| format!("    {}", line))
-                        .collect::<Vec<String>>()
-                        .join("\n"),
-                );
+                        .map(|line| fmt::from_fn(move |f| write!(f, "    {line}")))
+                        .joined("\n", f))
+                )
+                .unwrap();
             } else {
                 prog.push_str(everything_else);
             };
diff --git a/src/librustdoc/doctest/rust.rs b/src/librustdoc/doctest/rust.rs
index c4956bfd2b4..3ac7abd0aa5 100644
--- a/src/librustdoc/doctest/rust.rs
+++ b/src/librustdoc/doctest/rust.rs
@@ -81,7 +81,7 @@ impl<'tcx> HirCollector<'tcx> {
     pub fn collect_crate(mut self) -> Vec<ScrapedDocTest> {
         let tcx = self.tcx;
         self.visit_testable("".to_string(), CRATE_DEF_ID, tcx.hir().span(CRATE_HIR_ID), |this| {
-            tcx.hir().walk_toplevel_module(this)
+            tcx.hir_walk_toplevel_module(this)
         });
         self.collector.tests
     }
@@ -123,7 +123,7 @@ impl HirCollector<'_> {
                     .iter()
                     .find(|attr| attr.doc_str().is_some())
                     .map(|attr| {
-                        attr.span.ctxt().outer_expn().expansion_cause().unwrap_or(attr.span)
+                        attr.span().ctxt().outer_expn().expansion_cause().unwrap_or(attr.span())
                     })
                     .unwrap_or(DUMMY_SP)
             };
@@ -147,14 +147,14 @@ impl HirCollector<'_> {
 impl<'tcx> intravisit::Visitor<'tcx> for HirCollector<'tcx> {
     type NestedFilter = nested_filter::All;
 
-    fn nested_visit_map(&mut self) -> Self::Map {
-        self.tcx.hir()
+    fn maybe_tcx(&mut self) -> Self::MaybeTyCtxt {
+        self.tcx
     }
 
     fn visit_item(&mut self, item: &'tcx hir::Item<'_>) {
         let name = match &item.kind {
             hir::ItemKind::Impl(impl_) => {
-                rustc_hir_pretty::id_to_string(&self.tcx.hir(), impl_.self_ty.hir_id)
+                rustc_hir_pretty::id_to_string(&self.tcx, impl_.self_ty.hir_id)
             }
             _ => item.ident.to_string(),
         };
diff --git a/src/librustdoc/html/escape.rs b/src/librustdoc/html/escape.rs
index 88654ed32da..ac9e2f42cc6 100644
--- a/src/librustdoc/html/escape.rs
+++ b/src/librustdoc/html/escape.rs
@@ -5,6 +5,7 @@
 
 use std::fmt;
 
+use pulldown_cmark_escape::FmtWriter;
 use unicode_segmentation::UnicodeSegmentation;
 
 /// Wrapper struct which will emit the HTML-escaped version of the contained
@@ -13,31 +14,7 @@ pub(crate) struct Escape<'a>(pub &'a str);
 
 impl fmt::Display for Escape<'_> {
     fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
-        // Because the internet is always right, turns out there's not that many
-        // characters to escape: http://stackoverflow.com/questions/7381974
-        let Escape(s) = *self;
-        let pile_o_bits = s;
-        let mut last = 0;
-        for (i, ch) in s.char_indices() {
-            let s = match ch {
-                '>' => "&gt;",
-                '<' => "&lt;",
-                '&' => "&amp;",
-                '\'' => "&#39;",
-                '"' => "&quot;",
-                _ => continue,
-            };
-            fmt.write_str(&pile_o_bits[last..i])?;
-            fmt.write_str(s)?;
-            // NOTE: we only expect single byte characters here - which is fine as long as we
-            // only match single byte characters
-            last = i + 1;
-        }
-
-        if last < s.len() {
-            fmt.write_str(&pile_o_bits[last..])?;
-        }
-        Ok(())
+        pulldown_cmark_escape::escape_html(FmtWriter(fmt), self.0)
     }
 }
 
@@ -51,29 +28,7 @@ pub(crate) struct EscapeBodyText<'a>(pub &'a str);
 
 impl fmt::Display for EscapeBodyText<'_> {
     fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
-        // Because the internet is always right, turns out there's not that many
-        // characters to escape: http://stackoverflow.com/questions/7381974
-        let EscapeBodyText(s) = *self;
-        let pile_o_bits = s;
-        let mut last = 0;
-        for (i, ch) in s.char_indices() {
-            let s = match ch {
-                '>' => "&gt;",
-                '<' => "&lt;",
-                '&' => "&amp;",
-                _ => continue,
-            };
-            fmt.write_str(&pile_o_bits[last..i])?;
-            fmt.write_str(s)?;
-            // NOTE: we only expect single byte characters here - which is fine as long as we
-            // only match single byte characters
-            last = i + 1;
-        }
-
-        if last < s.len() {
-            fmt.write_str(&pile_o_bits[last..])?;
-        }
-        Ok(())
+        pulldown_cmark_escape::escape_html_body_text(FmtWriter(fmt), self.0)
     }
 }
 
diff --git a/src/librustdoc/html/format.rs b/src/librustdoc/html/format.rs
index b6a73602a32..91b4b3ba1eb 100644
--- a/src/librustdoc/html/format.rs
+++ b/src/librustdoc/html/format.rs
@@ -30,120 +30,15 @@ use super::url_parts_builder::{UrlPartsBuilder, estimate_item_path_byte_length};
 use crate::clean::types::ExternalLocation;
 use crate::clean::utils::find_nearest_parent_module;
 use crate::clean::{self, ExternalCrate, PrimitiveType};
+use crate::display::Joined as _;
 use crate::formats::cache::Cache;
 use crate::formats::item_type::ItemType;
 use crate::html::escape::{Escape, EscapeBodyText};
 use crate::html::render::Context;
-use crate::joined::Joined as _;
 use crate::passes::collect_intra_doc_links::UrlFragment;
 
-pub(crate) trait Print {
-    fn print(self, buffer: &mut Buffer);
-}
-
-impl<F> Print for F
-where
-    F: FnOnce(&mut Buffer),
-{
-    fn print(self, buffer: &mut Buffer) {
-        (self)(buffer)
-    }
-}
-
-impl Print for String {
-    fn print(self, buffer: &mut Buffer) {
-        buffer.write_str(&self);
-    }
-}
-
-impl Print for &'_ str {
-    fn print(self, buffer: &mut Buffer) {
-        buffer.write_str(self);
-    }
-}
-
-#[derive(Debug, Clone)]
-pub(crate) struct Buffer {
-    for_html: bool,
-    buffer: String,
-}
-
-impl core::fmt::Write for Buffer {
-    #[inline]
-    fn write_str(&mut self, s: &str) -> fmt::Result {
-        self.buffer.write_str(s)
-    }
-
-    #[inline]
-    fn write_char(&mut self, c: char) -> fmt::Result {
-        self.buffer.write_char(c)
-    }
-
-    #[inline]
-    fn write_fmt(&mut self, args: fmt::Arguments<'_>) -> fmt::Result {
-        self.buffer.write_fmt(args)
-    }
-}
-
-impl Buffer {
-    pub(crate) fn empty_from(v: &Buffer) -> Buffer {
-        Buffer { for_html: v.for_html, buffer: String::new() }
-    }
-
-    pub(crate) fn html() -> Buffer {
-        Buffer { for_html: true, buffer: String::new() }
-    }
-
-    pub(crate) fn new() -> Buffer {
-        Buffer { for_html: false, buffer: String::new() }
-    }
-
-    pub(crate) fn is_empty(&self) -> bool {
-        self.buffer.is_empty()
-    }
-
-    pub(crate) fn into_inner(self) -> String {
-        self.buffer
-    }
-
-    pub(crate) fn push(&mut self, c: char) {
-        self.buffer.push(c);
-    }
-
-    pub(crate) fn push_str(&mut self, s: &str) {
-        self.buffer.push_str(s);
-    }
-
-    pub(crate) fn push_buffer(&mut self, other: Buffer) {
-        self.buffer.push_str(&other.buffer);
-    }
-
-    // Intended for consumption by write! and writeln! (std::fmt) but without
-    // the fmt::Result return type imposed by fmt::Write (and avoiding the trait
-    // import).
-    pub(crate) fn write_str(&mut self, s: &str) {
-        self.buffer.push_str(s);
-    }
-
-    // Intended for consumption by write! and writeln! (std::fmt) but without
-    // the fmt::Result return type imposed by fmt::Write (and avoiding the trait
-    // import).
-    pub(crate) fn write_fmt(&mut self, v: fmt::Arguments<'_>) {
-        self.buffer.write_fmt(v).unwrap();
-    }
-
-    pub(crate) fn to_display<T: Print>(mut self, t: T) -> String {
-        t.print(&mut self);
-        self.into_inner()
-    }
-
-    pub(crate) fn reserve(&mut self, additional: usize) {
-        self.buffer.reserve(additional)
-    }
-
-    pub(crate) fn len(&self) -> usize {
-        self.buffer.len()
-    }
+pub(crate) fn write_str(s: &mut String, f: fmt::Arguments<'_>) {
+    s.write_fmt(f).unwrap();
 }
 
 pub(crate) fn print_generic_bounds<'a, 'tcx: 'a>(
@@ -772,7 +667,7 @@ pub(crate) fn link_tooltip(did: DefId, fragment: &Option<UrlFragment>, cx: &Cont
     else {
         return String::new();
     };
-    let mut buf = Buffer::new();
+    let mut buf = String::new();
     let fqp = if *shortty == ItemType::Primitive {
         // primitives are documented in a crate, but not actually part of it
         &fqp[fqp.len() - 1..]
@@ -780,19 +675,19 @@ pub(crate) fn link_tooltip(did: DefId, fragment: &Option<UrlFragment>, cx: &Cont
         fqp
     };
     if let &Some(UrlFragment::Item(id)) = fragment {
-        write!(buf, "{} ", cx.tcx().def_descr(id));
+        write_str(&mut buf, format_args!("{} ", cx.tcx().def_descr(id)));
         for component in fqp {
-            write!(buf, "{component}::");
+            write_str(&mut buf, format_args!("{component}::"));
         }
-        write!(buf, "{}", cx.tcx().item_name(id));
+        write_str(&mut buf, format_args!("{}", cx.tcx().item_name(id)));
     } else if !fqp.is_empty() {
         let mut fqp_it = fqp.iter();
-        write!(buf, "{shortty} {}", fqp_it.next().unwrap());
+        write_str(&mut buf, format_args!("{shortty} {}", fqp_it.next().unwrap()));
         for component in fqp_it {
-            write!(buf, "::{component}");
+            write_str(&mut buf, format_args!("::{component}"));
         }
     }
-    buf.into_inner()
+    buf
 }
 
 /// Used to render a [`clean::Path`].
@@ -814,19 +709,22 @@ fn resolved_path(
     if w.alternate() {
         write!(w, "{}{:#}", last.name, last.args.print(cx))?;
     } else {
-        let path = if use_absolute {
-            if let Ok((_, _, fqp)) = href(did, cx) {
-                format!(
-                    "{path}::{anchor}",
-                    path = join_with_double_colon(&fqp[..fqp.len() - 1]),
-                    anchor = anchor(did, *fqp.last().unwrap(), cx)
-                )
+        let path = fmt::from_fn(|f| {
+            if use_absolute {
+                if let Ok((_, _, fqp)) = href(did, cx) {
+                    write!(
+                        f,
+                        "{path}::{anchor}",
+                        path = join_with_double_colon(&fqp[..fqp.len() - 1]),
+                        anchor = anchor(did, *fqp.last().unwrap(), cx)
+                    )
+                } else {
+                    write!(f, "{}", last.name)
+                }
             } else {
-                last.name.to_string()
+                write!(f, "{}", anchor(did, last.name, cx))
             }
-        } else {
-            anchor(did, last.name, cx).to_string()
-        };
+        });
         write!(w, "{path}{args}", args = last.args.print(cx))?;
     }
     Ok(())
@@ -854,16 +752,20 @@ fn primitive_link_fragment(
         match m.primitive_locations.get(&prim) {
             Some(&def_id) if def_id.is_local() => {
                 let len = cx.current.len();
-                let path = if len == 0 {
-                    let cname_sym = ExternalCrate { crate_num: def_id.krate }.name(cx.tcx());
-                    format!("{cname_sym}/")
-                } else {
-                    "../".repeat(len - 1)
-                };
+                let path = fmt::from_fn(|f| {
+                    if len == 0 {
+                        let cname_sym = ExternalCrate { crate_num: def_id.krate }.name(cx.tcx());
+                        write!(f, "{cname_sym}/")?;
+                    } else {
+                        for _ in 0..(len - 1) {
+                            f.write_str("../")?;
+                        }
+                    }
+                    Ok(())
+                });
                 write!(
                     f,
-                    "<a class=\"primitive\" href=\"{}primitive.{}.html{fragment}\">",
-                    path,
+                    "<a class=\"primitive\" href=\"{path}primitive.{}.html{fragment}\">",
                     prim.as_sym()
                 )?;
                 needs_termination = true;
diff --git a/src/librustdoc/html/highlight.rs b/src/librustdoc/html/highlight.rs
index 8c91cae4931..ed4b97d3625 100644
--- a/src/librustdoc/html/highlight.rs
+++ b/src/librustdoc/html/highlight.rs
@@ -14,7 +14,7 @@ use rustc_span::edition::Edition;
 use rustc_span::symbol::Symbol;
 use rustc_span::{BytePos, DUMMY_SP, Span};
 
-use super::format::{self, Buffer};
+use super::format::{self, write_str};
 use crate::clean::PrimitiveType;
 use crate::html::escape::EscapeBodyText;
 use crate::html::render::{Context, LinkFromSrc};
@@ -48,72 +48,80 @@ pub(crate) enum Tooltip {
 /// Highlights `src` as an inline example, returning the HTML output.
 pub(crate) fn render_example_with_highlighting(
     src: &str,
-    out: &mut Buffer,
+    out: &mut String,
     tooltip: Tooltip,
     playground_button: Option<&str>,
     extra_classes: &[String],
 ) {
     write_header(out, "rust-example-rendered", None, tooltip, extra_classes);
-    write_code(out, src, None, None);
+    write_code(out, src, None, None, None);
     write_footer(out, playground_button);
 }
 
 fn write_header(
-    out: &mut Buffer,
+    out: &mut String,
     class: &str,
-    extra_content: Option<Buffer>,
+    extra_content: Option<&str>,
     tooltip: Tooltip,
     extra_classes: &[String],
 ) {
-    write!(
+    write_str(
         out,
-        "<div class=\"example-wrap{}\">",
-        match tooltip {
-            Tooltip::Ignore => " ignore",
-            Tooltip::CompileFail => " compile_fail",
-            Tooltip::ShouldPanic => " should_panic",
-            Tooltip::Edition(_) => " edition",
-            Tooltip::None => "",
-        },
+        format_args!(
+            "<div class=\"example-wrap{}\">",
+            match tooltip {
+                Tooltip::Ignore => " ignore",
+                Tooltip::CompileFail => " compile_fail",
+                Tooltip::ShouldPanic => " should_panic",
+                Tooltip::Edition(_) => " edition",
+                Tooltip::None => "",
+            }
+        ),
     );
 
     if tooltip != Tooltip::None {
         let edition_code;
-        write!(
+        write_str(
             out,
-            "<a href=\"#\" class=\"tooltip\" title=\"{}\">ⓘ</a>",
-            match tooltip {
-                Tooltip::Ignore => "This example is not tested",
-                Tooltip::CompileFail => "This example deliberately fails to compile",
-                Tooltip::ShouldPanic => "This example panics",
-                Tooltip::Edition(edition) => {
-                    edition_code = format!("This example runs with edition {edition}");
-                    &edition_code
+            format_args!(
+                "<a href=\"#\" class=\"tooltip\" title=\"{}\">ⓘ</a>",
+                match tooltip {
+                    Tooltip::Ignore => "This example is not tested",
+                    Tooltip::CompileFail => "This example deliberately fails to compile",
+                    Tooltip::ShouldPanic => "This example panics",
+                    Tooltip::Edition(edition) => {
+                        edition_code = format!("This example runs with edition {edition}");
+                        &edition_code
+                    }
+                    Tooltip::None => unreachable!(),
                 }
-                Tooltip::None => unreachable!(),
-            },
+            ),
         );
     }
 
     if let Some(extra) = extra_content {
-        out.push_buffer(extra);
+        out.push_str(&extra);
     }
     if class.is_empty() {
-        write!(
+        write_str(
             out,
-            "<pre class=\"rust{}{}\">",
-            if extra_classes.is_empty() { "" } else { " " },
-            extra_classes.join(" "),
+            format_args!(
+                "<pre class=\"rust{}{}\">",
+                if extra_classes.is_empty() { "" } else { " " },
+                extra_classes.join(" ")
+            ),
         );
     } else {
-        write!(
+        write_str(
             out,
-            "<pre class=\"rust {class}{}{}\">",
-            if extra_classes.is_empty() { "" } else { " " },
-            extra_classes.join(" "),
+            format_args!(
+                "<pre class=\"rust {class}{}{}\">",
+                if extra_classes.is_empty() { "" } else { " " },
+                extra_classes.join(" ")
+            ),
         );
     }
-    write!(out, "<code>");
+    write_str(out, format_args!("<code>"));
 }
 
 /// Check if two `Class` can be merged together. In the following rules, "unclassified" means `None`
@@ -150,6 +158,7 @@ struct TokenHandler<'a, 'tcx, F: Write> {
     /// used to generate links.
     pending_elems: Vec<(&'a str, Option<Class>)>,
     href_context: Option<HrefContext<'a, 'tcx>>,
+    write_line_number: fn(&mut F, u32, &'static str),
 }
 
 impl<F: Write> TokenHandler<'_, '_, F> {
@@ -182,7 +191,14 @@ impl<F: Write> TokenHandler<'_, '_, F> {
             && can_merge(current_class, Some(*parent_class), "")
         {
             for (text, class) in self.pending_elems.iter() {
-                string(self.out, EscapeBodyText(text), *class, &self.href_context, false);
+                string(
+                    self.out,
+                    EscapeBodyText(text),
+                    *class,
+                    &self.href_context,
+                    false,
+                    self.write_line_number,
+                );
             }
         } else {
             // We only want to "open" the tag ourselves if we have more than one pending and if the
@@ -204,6 +220,7 @@ impl<F: Write> TokenHandler<'_, '_, F> {
                     *class,
                     &self.href_context,
                     close_tag.is_none(),
+                    self.write_line_number,
                 );
             }
             if let Some(close_tag) = close_tag {
@@ -213,6 +230,11 @@ impl<F: Write> TokenHandler<'_, '_, F> {
         self.pending_elems.clear();
         true
     }
+
+    #[inline]
+    fn write_line_number(&mut self, line: u32, extra: &'static str) {
+        (self.write_line_number)(&mut self.out, line, extra);
+    }
 }
 
 impl<F: Write> Drop for TokenHandler<'_, '_, F> {
@@ -226,6 +248,43 @@ impl<F: Write> Drop for TokenHandler<'_, '_, F> {
     }
 }
 
+fn write_scraped_line_number(out: &mut impl Write, line: u32, extra: &'static str) {
+    // https://developers.google.com/search/docs/crawling-indexing/robots-meta-tag#data-nosnippet-attr
+    // Do not show "1 2 3 4 5 ..." in web search results.
+    write!(out, "{extra}<span data-nosnippet>{line}</span>",).unwrap();
+}
+
+fn write_line_number(out: &mut impl Write, line: u32, extra: &'static str) {
+    // https://developers.google.com/search/docs/crawling-indexing/robots-meta-tag#data-nosnippet-attr
+    // Do not show "1 2 3 4 5 ..." in web search results.
+    write!(out, "{extra}<a href=#{line} id={line} data-nosnippet>{line}</a>",).unwrap();
+}
+
+fn empty_line_number(out: &mut impl Write, _: u32, extra: &'static str) {
+    out.write_str(extra).unwrap();
+}
+
+#[derive(Clone, Copy)]
+pub(super) struct LineInfo {
+    pub(super) start_line: u32,
+    max_lines: u32,
+    pub(super) is_scraped_example: bool,
+}
+
+impl LineInfo {
+    pub(super) fn new(max_lines: u32) -> Self {
+        Self { start_line: 1, max_lines: max_lines + 1, is_scraped_example: false }
+    }
+
+    pub(super) fn new_scraped(max_lines: u32, start_line: u32) -> Self {
+        Self {
+            start_line: start_line + 1,
+            max_lines: max_lines + start_line + 1,
+            is_scraped_example: true,
+        }
+    }
+}
+
 /// Convert the given `src` source code into HTML by adding classes for highlighting.
 ///
 /// This code is used to render code blocks (in the documentation) as well as the source code pages.
@@ -242,6 +301,7 @@ pub(super) fn write_code(
     src: &str,
     href_context: Option<HrefContext<'_, '_>>,
     decoration_info: Option<&DecorationInfo>,
+    line_info: Option<LineInfo>,
 ) {
     // This replace allows to fix how the code source with DOS backline characters is displayed.
     let src = src.replace("\r\n", "\n");
@@ -252,6 +312,23 @@ pub(super) fn write_code(
         current_class: None,
         pending_elems: Vec::new(),
         href_context,
+        write_line_number: match line_info {
+            Some(line_info) => {
+                if line_info.is_scraped_example {
+                    write_scraped_line_number
+                } else {
+                    write_line_number
+                }
+            }
+            None => empty_line_number,
+        },
+    };
+
+    let (mut line, max_lines) = if let Some(line_info) = line_info {
+        token_handler.write_line_number(line_info.start_line, "");
+        (line_info.start_line, line_info.max_lines)
+    } else {
+        (0, u32::MAX)
     };
 
     Classifier::new(
@@ -282,7 +359,14 @@ pub(super) fn write_code(
                 if need_current_class_update {
                     token_handler.current_class = class.map(Class::dummy);
                 }
-                token_handler.pending_elems.push((text, class));
+                if text == "\n" {
+                    line += 1;
+                    if line < max_lines {
+                        token_handler.pending_elems.push((text, Some(Class::Backline(line))));
+                    }
+                } else {
+                    token_handler.pending_elems.push((text, class));
+                }
             }
             Highlight::EnterSpan { class } => {
                 let mut should_add = true;
@@ -322,8 +406,8 @@ pub(super) fn write_code(
     });
 }
 
-fn write_footer(out: &mut Buffer, playground_button: Option<&str>) {
-    writeln!(out, "</code></pre>{}</div>", playground_button.unwrap_or_default());
+fn write_footer(out: &mut String, playground_button: Option<&str>) {
+    write_str(out, format_args_nl!("</code></pre>{}</div>", playground_button.unwrap_or_default()));
 }
 
 /// How a span of text is classified. Mostly corresponds to token kinds.
@@ -348,6 +432,7 @@ enum Class {
     PreludeVal(Span),
     QuestionMark,
     Decoration(&'static str),
+    Backline(u32),
 }
 
 impl Class {
@@ -396,6 +481,7 @@ impl Class {
             Class::PreludeVal(_) => "prelude-val",
             Class::QuestionMark => "question-mark",
             Class::Decoration(kind) => kind,
+            Class::Backline(_) => "",
         }
     }
 
@@ -419,7 +505,8 @@ impl Class {
             | Self::Bool
             | Self::Lifetime
             | Self::QuestionMark
-            | Self::Decoration(_) => None,
+            | Self::Decoration(_)
+            | Self::Backline(_) => None,
         }
     }
 }
@@ -694,8 +781,13 @@ impl<'src> Classifier<'src> {
     ) {
         let lookahead = self.peek();
         let no_highlight = |sink: &mut dyn FnMut(_)| sink(Highlight::Token { text, class: None });
+        let whitespace = |sink: &mut dyn FnMut(_)| {
+            for part in text.split('\n').intersperse("\n").filter(|s| !s.is_empty()) {
+                sink(Highlight::Token { text: part, class: None });
+            }
+        };
         let class = match token {
-            TokenKind::Whitespace => return no_highlight(sink),
+            TokenKind::Whitespace => return whitespace(sink),
             TokenKind::LineComment { doc_style } | TokenKind::BlockComment { doc_style, .. } => {
                 if doc_style.is_some() {
                     Class::DocComment
@@ -716,7 +808,7 @@ impl<'src> Classifier<'src> {
             // or a reference or pointer type. Unless, of course, it looks like
             // a logical and or a multiplication operator: `&&` or `* `.
             TokenKind::Star => match self.tokens.peek() {
-                Some((TokenKind::Whitespace, _)) => return no_highlight(sink),
+                Some((TokenKind::Whitespace, _)) => return whitespace(sink),
                 Some((TokenKind::Ident, "mut")) => {
                     self.next();
                     sink(Highlight::Token { text: "*mut", class: Some(Class::RefKeyWord) });
@@ -740,7 +832,7 @@ impl<'src> Classifier<'src> {
                     sink(Highlight::Token { text: "&=", class: None });
                     return;
                 }
-                Some((TokenKind::Whitespace, _)) => return no_highlight(sink),
+                Some((TokenKind::Whitespace, _)) => return whitespace(sink),
                 Some((TokenKind::Ident, "mut")) => {
                     self.next();
                     sink(Highlight::Token { text: "&mut", class: Some(Class::RefKeyWord) });
@@ -887,7 +979,9 @@ impl<'src> Classifier<'src> {
         };
         // Anything that didn't return above is the simple case where we the
         // class just spans a single token, so we can use the `string` method.
-        sink(Highlight::Token { text, class: Some(class) });
+        for part in text.split('\n').intersperse("\n").filter(|s| !s.is_empty()) {
+            sink(Highlight::Token { text: part, class: Some(class) });
+        }
     }
 
     fn peek(&mut self) -> Option<TokenKind> {
@@ -939,14 +1033,18 @@ fn exit_span(out: &mut impl Write, closing_tag: &str) {
 /// Note that if `context` is not `None` and that the given `klass` contains a `Span`, the function
 /// will then try to find this `span` in the `span_correspondence_map`. If found, it'll then
 /// generate a link for this element (which corresponds to where its definition is located).
-fn string<T: Display>(
-    out: &mut impl Write,
+fn string<T: Display, W: Write>(
+    out: &mut W,
     text: T,
     klass: Option<Class>,
     href_context: &Option<HrefContext<'_, '_>>,
     open_tag: bool,
+    write_line_number_callback: fn(&mut W, u32, &'static str),
 ) {
-    if let Some(closing_tag) = string_without_closing_tag(out, text, klass, href_context, open_tag)
+    if let Some(Class::Backline(line)) = klass {
+        write_line_number_callback(out, line, "\n");
+    } else if let Some(closing_tag) =
+        string_without_closing_tag(out, text, klass, href_context, open_tag)
     {
         out.write_str(closing_tag).unwrap();
     }
diff --git a/src/librustdoc/html/highlight/tests.rs b/src/librustdoc/html/highlight/tests.rs
index fccbb98f80f..2603e887bea 100644
--- a/src/librustdoc/html/highlight/tests.rs
+++ b/src/librustdoc/html/highlight/tests.rs
@@ -3,7 +3,6 @@ use rustc_data_structures::fx::FxIndexMap;
 use rustc_span::create_default_session_globals_then;
 
 use super::{DecorationInfo, write_code};
-use crate::html::format::Buffer;
 
 const STYLE: &str = r#"
 <style>
@@ -22,9 +21,9 @@ fn test_html_highlighting() {
     create_default_session_globals_then(|| {
         let src = include_str!("fixtures/sample.rs");
         let html = {
-            let mut out = Buffer::new();
-            write_code(&mut out, src, None, None);
-            format!("{STYLE}<pre><code>{}</code></pre>\n", out.into_inner())
+            let mut out = String::new();
+            write_code(&mut out, src, None, None, None);
+            format!("{STYLE}<pre><code>{out}</code></pre>\n")
         };
         expect_file!["fixtures/sample.html"].assert_eq(&html);
     });
@@ -36,9 +35,9 @@ fn test_dos_backline() {
         let src = "pub fn foo() {\r\n\
     println!(\"foo\");\r\n\
 }\r\n";
-        let mut html = Buffer::new();
-        write_code(&mut html, src, None, None);
-        expect_file!["fixtures/dos_line.html"].assert_eq(&html.into_inner());
+        let mut html = String::new();
+        write_code(&mut html, src, None, None, None);
+        expect_file!["fixtures/dos_line.html"].assert_eq(&html);
     });
 }
 
@@ -50,9 +49,9 @@ use self::whatever;
 let x = super::b::foo;
 let y = Self::whatever;";
 
-        let mut html = Buffer::new();
-        write_code(&mut html, src, None, None);
-        expect_file!["fixtures/highlight.html"].assert_eq(&html.into_inner());
+        let mut html = String::new();
+        write_code(&mut html, src, None, None, None);
+        expect_file!["fixtures/highlight.html"].assert_eq(&html);
     });
 }
 
@@ -60,9 +59,9 @@ let y = Self::whatever;";
 fn test_union_highlighting() {
     create_default_session_globals_then(|| {
         let src = include_str!("fixtures/union.rs");
-        let mut html = Buffer::new();
-        write_code(&mut html, src, None, None);
-        expect_file!["fixtures/union.html"].assert_eq(&html.into_inner());
+        let mut html = String::new();
+        write_code(&mut html, src, None, None, None);
+        expect_file!["fixtures/union.html"].assert_eq(&html);
     });
 }
 
@@ -77,8 +76,8 @@ let a = 4;";
         decorations.insert("example", vec![(0, 10), (11, 21)]);
         decorations.insert("example2", vec![(22, 32)]);
 
-        let mut html = Buffer::new();
-        write_code(&mut html, src, None, Some(&DecorationInfo(decorations)));
-        expect_file!["fixtures/decorations.html"].assert_eq(&html.into_inner());
+        let mut html = String::new();
+        write_code(&mut html, src, None, Some(&DecorationInfo(decorations)), None);
+        expect_file!["fixtures/decorations.html"].assert_eq(&html);
     });
 }
diff --git a/src/librustdoc/html/layout.rs b/src/librustdoc/html/layout.rs
index d957cf1b569..df70df062fe 100644
--- a/src/librustdoc/html/layout.rs
+++ b/src/librustdoc/html/layout.rs
@@ -1,3 +1,4 @@
+use std::fmt::{self, Display};
 use std::path::PathBuf;
 
 use rinja::Template;
@@ -5,7 +6,6 @@ use rustc_data_structures::fx::FxIndexMap;
 
 use super::static_files::{STATIC_FILES, StaticFiles};
 use crate::externalfiles::ExternalHtml;
-use crate::html::format::{Buffer, Print};
 use crate::html::render::{StylePath, ensure_trailing_slash};
 
 #[derive(Clone)]
@@ -71,7 +71,24 @@ struct PageLayout<'a> {
 
 pub(crate) use crate::html::render::sidebar::filters;
 
-pub(crate) fn render<T: Print, S: Print>(
+/// Implements [`Display`] for a function that accepts a mutable reference to a [`String`], and (optionally) writes to it.
+///
+/// The wrapped function will receive an empty string, and can modify it,
+/// and the `Display` implementation will write the contents of the string after the function has finished.
+pub(crate) struct BufDisplay<F>(pub F);
+
+impl<F> Display for BufDisplay<F>
+where
+    F: Fn(&mut String),
+{
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        let mut buf = String::new();
+        self.0(&mut buf);
+        f.write_str(&buf)
+    }
+}
+
+pub(crate) fn render<T: Display, S: Display>(
     layout: &Layout,
     page: &Page<'_>,
     sidebar: S,
@@ -98,8 +115,8 @@ pub(crate) fn render<T: Print, S: Print>(
     let mut themes: Vec<String> = style_files.iter().map(|s| s.basename().unwrap()).collect();
     themes.sort();
 
-    let content = Buffer::html().to_display(t); // Note: This must happen before making the sidebar.
-    let sidebar = Buffer::html().to_display(sidebar);
+    let content = t.to_string(); // Note: This must happen before making the sidebar.
+    let sidebar = sidebar.to_string();
     PageLayout {
         static_root_path,
         page,
diff --git a/src/librustdoc/html/markdown.rs b/src/librustdoc/html/markdown.rs
index 7e835585b73..d9e49577d39 100644
--- a/src/librustdoc/html/markdown.rs
+++ b/src/librustdoc/html/markdown.rs
@@ -38,7 +38,7 @@ use std::sync::{Arc, Weak};
 use pulldown_cmark::{
     BrokenLink, CodeBlockKind, CowStr, Event, LinkType, Options, Parser, Tag, TagEnd, html,
 };
-use rustc_data_structures::fx::FxHashMap;
+use rustc_data_structures::fx::{FxHashMap, FxIndexMap};
 use rustc_errors::{Diag, DiagMessage};
 use rustc_hir::def_id::LocalDefId;
 use rustc_middle::ty::TyCtxt;
@@ -52,7 +52,6 @@ use crate::clean::RenderedLink;
 use crate::doctest;
 use crate::doctest::GlobalTestOptions;
 use crate::html::escape::{Escape, EscapeBodyText};
-use crate::html::format::Buffer;
 use crate::html::highlight;
 use crate::html::length_limit::HtmlWithLimit;
 use crate::html::render::small_url_encode;
@@ -140,7 +139,7 @@ impl ErrorCodes {
 /// Controls whether a line will be hidden or shown in HTML output.
 ///
 /// All lines are used in documentation tests.
-enum Line<'a> {
+pub(crate) enum Line<'a> {
     Hidden(&'a str),
     Shown(Cow<'a, str>),
 }
@@ -153,7 +152,7 @@ impl<'a> Line<'a> {
         }
     }
 
-    fn for_code(self) -> Cow<'a, str> {
+    pub(crate) fn for_code(self) -> Cow<'a, str> {
         match self {
             Line::Shown(l) => l,
             Line::Hidden(l) => Cow::Borrowed(l),
@@ -161,12 +160,14 @@ impl<'a> Line<'a> {
     }
 }
 
+/// This function is used to handle the "hidden lines" (ie starting with `#`) in
+/// doctests. It also transforms `##` back into `#`.
 // FIXME: There is a minor inconsistency here. For lines that start with ##, we
 // have no easy way of removing a potential single space after the hashes, which
 // is done in the single # case. This inconsistency seems okay, if non-ideal. In
 // order to fix it we'd have to iterate to find the first non-# character, and
 // then reallocate to remove it; which would make us return a String.
-fn map_line(s: &str) -> Line<'_> {
+pub(crate) fn map_line(s: &str) -> Line<'_> {
     let trimmed = s.trim();
     if trimmed.starts_with("##") {
         Line::Shown(Cow::Owned(s.replacen("##", "#", 1)))
@@ -329,7 +330,7 @@ impl<'a, I: Iterator<Item = Event<'a>>> Iterator for CodeBlocks<'_, 'a, I> {
 
         // insert newline to clearly separate it from the
         // previous block so we can shorten the html output
-        let mut s = Buffer::new();
+        let mut s = String::new();
         s.push('\n');
 
         highlight::render_example_with_highlighting(
@@ -339,7 +340,7 @@ impl<'a, I: Iterator<Item = Event<'a>>> Iterator for CodeBlocks<'_, 'a, I> {
             playground_button.as_deref(),
             &added_classes,
         );
-        Some(Event::Html(s.into_inner().into()))
+        Some(Event::Html(s.into()))
     }
 }
 
@@ -1762,6 +1763,46 @@ pub(crate) fn markdown_links<'md, R>(
         }
     };
 
+    let span_for_refdef = |link: &CowStr<'_>, span: Range<usize>| {
+        // We want to underline the link's definition, but `span` will point at the entire refdef.
+        // Skip the label, then try to find the entire URL.
+        let mut square_brace_count = 0;
+        let mut iter = md.as_bytes()[span.start..span.end].iter().copied().enumerate();
+        for (_i, c) in &mut iter {
+            match c {
+                b':' if square_brace_count == 0 => break,
+                b'[' => square_brace_count += 1,
+                b']' => square_brace_count -= 1,
+                _ => {}
+            }
+        }
+        while let Some((i, c)) = iter.next() {
+            if c == b'<' {
+                while let Some((j, c)) = iter.next() {
+                    match c {
+                        b'\\' => {
+                            let _ = iter.next();
+                        }
+                        b'>' => {
+                            return MarkdownLinkRange::Destination(
+                                i + 1 + span.start..j + span.start,
+                            );
+                        }
+                        _ => {}
+                    }
+                }
+            } else if !c.is_ascii_whitespace() {
+                while let Some((j, c)) = iter.next() {
+                    if c.is_ascii_whitespace() {
+                        return MarkdownLinkRange::Destination(i + span.start..j + span.start);
+                    }
+                }
+                return MarkdownLinkRange::Destination(i + span.start..span.end);
+            }
+        }
+        span_for_link(link, span)
+    };
+
     let span_for_offset_backward = |span: Range<usize>, open: u8, close: u8| {
         let mut open_brace = !0;
         let mut close_brace = !0;
@@ -1843,9 +1884,16 @@ pub(crate) fn markdown_links<'md, R>(
     .into_offset_iter();
     let mut links = Vec::new();
 
+    let mut refdefs = FxIndexMap::default();
+    for (label, refdef) in event_iter.reference_definitions().iter() {
+        refdefs.insert(label.to_string(), (false, refdef.dest.to_string(), refdef.span.clone()));
+    }
+
     for (event, span) in event_iter {
         match event {
-            Event::Start(Tag::Link { link_type, dest_url, .. }) if may_be_doc_link(link_type) => {
+            Event::Start(Tag::Link { link_type, dest_url, id, .. })
+                if may_be_doc_link(link_type) =>
+            {
                 let range = match link_type {
                     // Link is pulled from the link itself.
                     LinkType::ReferenceUnknown | LinkType::ShortcutUnknown => {
@@ -1855,7 +1903,12 @@ pub(crate) fn markdown_links<'md, R>(
                     LinkType::Inline => span_for_offset_backward(span, b'(', b')'),
                     // Link is pulled from elsewhere in the document.
                     LinkType::Reference | LinkType::Collapsed | LinkType::Shortcut => {
-                        span_for_link(&dest_url, span)
+                        if let Some((is_used, dest_url, span)) = refdefs.get_mut(&id[..]) {
+                            *is_used = true;
+                            span_for_refdef(&CowStr::from(&dest_url[..]), span.clone())
+                        } else {
+                            span_for_link(&dest_url, span)
+                        }
                     }
                     LinkType::Autolink | LinkType::Email => unreachable!(),
                 };
@@ -1872,6 +1925,18 @@ pub(crate) fn markdown_links<'md, R>(
         }
     }
 
+    for (_label, (is_used, dest_url, span)) in refdefs.into_iter() {
+        if !is_used
+            && let Some(link) = preprocess_link(MarkdownLink {
+                kind: LinkType::Reference,
+                range: span_for_refdef(&CowStr::from(&dest_url[..]), span),
+                link: dest_url,
+            })
+        {
+            links.push(link);
+        }
+    }
+
     links
 }
 
diff --git a/src/librustdoc/html/render/context.rs b/src/librustdoc/html/render/context.rs
index 1cefdf96bbc..146bdd34069 100644
--- a/src/librustdoc/html/render/context.rs
+++ b/src/librustdoc/html/render/context.rs
@@ -1,5 +1,6 @@
 use std::cell::RefCell;
 use std::collections::BTreeMap;
+use std::fmt::{self, Write as _};
 use std::io;
 use std::path::{Path, PathBuf};
 use std::sync::mpsc::{Receiver, channel};
@@ -26,11 +27,12 @@ use crate::formats::FormatRenderer;
 use crate::formats::cache::Cache;
 use crate::formats::item_type::ItemType;
 use crate::html::escape::Escape;
-use crate::html::format::{Buffer, join_with_double_colon};
+use crate::html::format::join_with_double_colon;
+use crate::html::layout::{self, BufDisplay};
 use crate::html::markdown::{self, ErrorCodes, IdMap, plain_text_summary};
 use crate::html::render::write_shared::write_shared;
 use crate::html::url_parts_builder::UrlPartsBuilder;
-use crate::html::{layout, sources, static_files};
+use crate::html::{sources, static_files};
 use crate::scrape_examples::AllCallLocations;
 use crate::{DOC_RUST_LANG_ORG_VERSION, try_err};
 
@@ -235,7 +237,7 @@ impl<'tcx> Context<'tcx> {
         };
 
         if !render_redirect_pages {
-            let mut page_buffer = Buffer::html();
+            let mut page_buffer = String::new();
             print_item(self, it, &mut page_buffer);
             let page = layout::Page {
                 css_class: tyname_s,
@@ -249,8 +251,10 @@ impl<'tcx> Context<'tcx> {
             layout::render(
                 &self.shared.layout,
                 &page,
-                |buf: &mut _| print_sidebar(self, it, buf),
-                move |buf: &mut Buffer| buf.push_buffer(page_buffer),
+                BufDisplay(|buf: &mut String| {
+                    print_sidebar(self, it, buf);
+                }),
+                page_buffer,
                 &self.shared.style_files,
             )
         } else {
@@ -262,12 +266,12 @@ impl<'tcx> Context<'tcx> {
                     // preventing an infinite redirection loop in the generated
                     // documentation.
 
-                    let mut path = String::new();
-                    for name in &names[..names.len() - 1] {
-                        path.push_str(name.as_str());
-                        path.push('/');
-                    }
-                    path.push_str(&item_path(ty, names.last().unwrap().as_str()));
+                    let path = fmt::from_fn(|f| {
+                        for name in &names[..names.len() - 1] {
+                            write!(f, "{name}/")?;
+                        }
+                        write!(f, "{}", item_path(ty, names.last().unwrap().as_str()))
+                    });
                     match self.shared.redirections {
                         Some(ref redirections) => {
                             let mut current_path = String::new();
@@ -275,8 +279,12 @@ impl<'tcx> Context<'tcx> {
                                 current_path.push_str(name.as_str());
                                 current_path.push('/');
                             }
-                            current_path.push_str(&item_path(ty, names.last().unwrap().as_str()));
-                            redirections.borrow_mut().insert(current_path, path);
+                            let _ = write!(
+                                current_path,
+                                "{}",
+                                item_path(ty, names.last().unwrap().as_str())
+                            );
+                            redirections.borrow_mut().insert(current_path, path.to_string());
                         }
                         None => {
                             return layout::redirect(&format!(
@@ -627,7 +635,7 @@ impl<'tcx> FormatRenderer<'tcx> for Context<'tcx> {
             rust_logo: has_doc_flag(self.tcx(), LOCAL_CRATE.as_def_id(), sym::rust_logo),
         };
         let all = shared.all.replace(AllTypes::new());
-        let mut sidebar = Buffer::html();
+        let mut sidebar = String::new();
 
         // all.html is not customizable, so a blank id map is fine
         let blocks = sidebar_module_like(all.item_sections(), &mut IdMap::new(), ModuleLike::Crate);
@@ -646,8 +654,10 @@ impl<'tcx> FormatRenderer<'tcx> for Context<'tcx> {
         let v = layout::render(
             &shared.layout,
             &page,
-            sidebar.into_inner(),
-            |buf: &mut Buffer| all.print(buf),
+            sidebar,
+            BufDisplay(|buf: &mut String| {
+                all.print(buf);
+            }),
             &shared.style_files,
         );
         shared.fs.write(final_file, v)?;
@@ -665,7 +675,7 @@ impl<'tcx> FormatRenderer<'tcx> for Context<'tcx> {
                 &shared.layout,
                 &page,
                 sidebar,
-                |buf: &mut Buffer| {
+                fmt::from_fn(|buf| {
                     write!(
                         buf,
                         "<div class=\"main-heading\">\
@@ -684,7 +694,7 @@ impl<'tcx> FormatRenderer<'tcx> for Context<'tcx> {
                          <script defer src=\"{static_root_path}{settings_js}\"></script>",
                         static_root_path = page.get_static_root_path(),
                         settings_js = static_files::STATIC_FILES.settings_js,
-                    );
+                    )?;
                     // Pre-load all theme CSS files, so that switching feels seamless.
                     //
                     // When loading settings.html as a popover, the equivalent HTML is
@@ -697,10 +707,11 @@ impl<'tcx> FormatRenderer<'tcx> for Context<'tcx> {
                                     as=\"style\">",
                                 root_path = page.static_root_path.unwrap_or(""),
                                 suffix = page.resource_suffix,
-                            );
+                            )?;
                         }
                     }
-                },
+                    Ok(())
+                }),
                 &shared.style_files,
             );
             shared.fs.write(settings_file, v)?;
@@ -716,25 +727,22 @@ impl<'tcx> FormatRenderer<'tcx> for Context<'tcx> {
                 &shared.layout,
                 &page,
                 sidebar,
-                |buf: &mut Buffer| {
-                    write!(
-                        buf,
-                        "<div class=\"main-heading\">\
-                         <h1>Rustdoc help</h1>\
-                         <span class=\"out-of-band\">\
-                             <a id=\"back\" href=\"javascript:void(0)\" onclick=\"history.back();\">\
-                                Back\
-                            </a>\
-                         </span>\
-                         </div>\
-                         <noscript>\
-                            <section>\
-                                <p>You need to enable JavaScript to use keyboard commands or search.</p>\
-                                <p>For more information, browse the <a href=\"{DOC_RUST_LANG_ORG_VERSION}/rustdoc/\">rustdoc handbook</a>.</p>\
-                            </section>\
-                         </noscript>",
-                    )
-                },
+                format_args!(
+                    "<div class=\"main-heading\">\
+                        <h1>Rustdoc help</h1>\
+                        <span class=\"out-of-band\">\
+                            <a id=\"back\" href=\"javascript:void(0)\" onclick=\"history.back();\">\
+                            Back\
+                        </a>\
+                        </span>\
+                        </div>\
+                        <noscript>\
+                        <section>\
+                            <p>You need to enable JavaScript to use keyboard commands or search.</p>\
+                            <p>For more information, browse the <a href=\"{DOC_RUST_LANG_ORG_VERSION}/rustdoc/\">rustdoc handbook</a>.</p>\
+                        </section>\
+                        </noscript>",
+                ),
                 &shared.style_files,
             );
             shared.fs.write(help_file, v)?;
@@ -851,9 +859,9 @@ impl<'tcx> FormatRenderer<'tcx> for Context<'tcx> {
         if !buf.is_empty() {
             let name = item.name.as_ref().unwrap();
             let item_type = item.type_();
-            let file_name = &item_path(item_type, name.as_str());
+            let file_name = item_path(item_type, name.as_str()).to_string();
             self.shared.ensure_dir(&self.dst)?;
-            let joint_dst = self.dst.join(file_name);
+            let joint_dst = self.dst.join(&file_name);
             self.shared.fs.write(joint_dst, buf)?;
 
             if !self.info.render_redirect_pages {
@@ -870,7 +878,7 @@ impl<'tcx> FormatRenderer<'tcx> for Context<'tcx> {
                         format!("{crate_name}/{file_name}"),
                     );
                 } else {
-                    let v = layout::redirect(file_name);
+                    let v = layout::redirect(&file_name);
                     let redir_dst = self.dst.join(redir_name);
                     self.shared.fs.write(redir_dst, v)?;
                 }
diff --git a/src/librustdoc/html/render/mod.rs b/src/librustdoc/html/render/mod.rs
index f7dcb87e4f3..204631063a2 100644
--- a/src/librustdoc/html/render/mod.rs
+++ b/src/librustdoc/html/render/mod.rs
@@ -63,15 +63,16 @@ pub(crate) use self::context::*;
 pub(crate) use self::span_map::{LinkFromSrc, collect_spans_and_sources};
 pub(crate) use self::write_shared::*;
 use crate::clean::{self, ItemId, RenderedLink};
+use crate::display::{Joined as _, MaybeDisplay as _};
 use crate::error::Error;
 use crate::formats::Impl;
 use crate::formats::cache::Cache;
 use crate::formats::item_type::ItemType;
 use crate::html::escape::Escape;
 use crate::html::format::{
-    Buffer, Ending, HrefError, PrintWithSpace, href, join_with_double_colon, print_abi_with_space,
+    Ending, HrefError, PrintWithSpace, href, join_with_double_colon, print_abi_with_space,
     print_constness_with_space, print_default_space, print_generic_bounds, print_where_clause,
-    visibility_print_with_space,
+    visibility_print_with_space, write_str,
 };
 use crate::html::markdown::{
     HeadingOffset, IdMap, Markdown, MarkdownItemInfo, MarkdownSummaryLine,
@@ -436,27 +437,29 @@ impl AllTypes {
         sections
     }
 
-    fn print(self, f: &mut Buffer) {
-        fn print_entries(f: &mut Buffer, e: &FxIndexSet<ItemEntry>, kind: ItemSection) {
+    fn print(&self, f: &mut String) {
+        fn print_entries(f: &mut String, e: &FxIndexSet<ItemEntry>, kind: ItemSection) {
             if !e.is_empty() {
                 let mut e: Vec<&ItemEntry> = e.iter().collect();
                 e.sort();
-                write!(
+                write_str(
                     f,
-                    "<h3 id=\"{id}\">{title}</h3><ul class=\"all-items\">",
-                    id = kind.id(),
-                    title = kind.name(),
+                    format_args!(
+                        "<h3 id=\"{id}\">{title}</h3><ul class=\"all-items\">",
+                        id = kind.id(),
+                        title = kind.name(),
+                    ),
                 );
 
                 for s in e.iter() {
-                    write!(f, "<li>{}</li>", s.print());
+                    write_str(f, format_args!("<li>{}</li>", s.print()));
                 }
 
-                f.write_str("</ul>");
+                f.push_str("</ul>");
             }
         }
 
-        f.write_str("<h1>List of all items</h1>");
+        f.push_str("<h1>List of all items</h1>");
         // Note: print_entries does not escape the title, because we know the current set of titles
         // doesn't require escaping.
         print_entries(f, &self.structs, ItemSection::Structs);
@@ -566,17 +569,27 @@ fn document_short<'a, 'cx: 'a>(
             let (mut summary_html, has_more_content) =
                 MarkdownSummaryLine(&s, &item.links(cx)).into_string_with_has_more_content();
 
-            if has_more_content {
-                let link = format!(" <a{}>Read more</a>", assoc_href_attr(item, link, cx));
+            let link = if has_more_content {
+                let link = fmt::from_fn(|f| {
+                    write!(
+                        f,
+                        " <a{}>Read more</a>",
+                        assoc_href_attr(item, link, cx).maybe_display()
+                    )
+                });
 
                 if let Some(idx) = summary_html.rfind("</p>") {
-                    summary_html.insert_str(idx, &link);
+                    summary_html.insert_str(idx, &link.to_string());
+                    None
                 } else {
-                    summary_html.push_str(&link);
+                    Some(link)
                 }
+            } else {
+                None
             }
+            .maybe_display();
 
-            write!(f, "<div class='docblock'>{summary_html}</div>")?;
+            write!(f, "<div class='docblock'>{summary_html}{link}</div>")?;
         }
         Ok(())
     })
@@ -761,7 +774,7 @@ pub(crate) fn render_impls(
             let did = i.trait_did().unwrap();
             let provided_trait_methods = i.inner_impl().provided_trait_methods(cx.tcx());
             let assoc_link = AssocItemLink::GotoSource(did.into(), &provided_trait_methods);
-            let mut buffer = Buffer::new();
+            let mut buffer = String::new();
             render_impl(
                 &mut buffer,
                 cx,
@@ -778,7 +791,7 @@ pub(crate) fn render_impls(
                     toggle_open_by_default,
                 },
             );
-            buffer.into_inner()
+            buffer
         })
         .collect::<Vec<_>>();
     rendered_impls.sort();
@@ -786,13 +799,23 @@ pub(crate) fn render_impls(
 }
 
 /// Build a (possibly empty) `href` attribute (a key-value pair) for the given associated item.
-fn assoc_href_attr(it: &clean::Item, link: AssocItemLink<'_>, cx: &Context<'_>) -> String {
+fn assoc_href_attr<'a, 'tcx>(
+    it: &clean::Item,
+    link: AssocItemLink<'a>,
+    cx: &Context<'tcx>,
+) -> Option<impl fmt::Display + 'a + Captures<'tcx>> {
     let name = it.name.unwrap();
     let item_type = it.type_();
 
+    enum Href<'a> {
+        AnchorId(&'a str),
+        Anchor(ItemType),
+        Url(String, ItemType),
+    }
+
     let href = match link {
-        AssocItemLink::Anchor(Some(ref id)) => Some(format!("#{id}")),
-        AssocItemLink::Anchor(None) => Some(format!("#{item_type}.{name}")),
+        AssocItemLink::Anchor(Some(ref id)) => Href::AnchorId(id),
+        AssocItemLink::Anchor(None) => Href::Anchor(item_type),
         AssocItemLink::GotoSource(did, provided_methods) => {
             // We're creating a link from the implementation of an associated item to its
             // declaration in the trait declaration.
@@ -812,7 +835,7 @@ fn assoc_href_attr(it: &clean::Item, link: AssocItemLink<'_>, cx: &Context<'_>)
             };
 
             match href(did.expect_def_id(), cx) {
-                Ok((url, ..)) => Some(format!("{url}#{item_type}.{name}")),
+                Ok((url, ..)) => Href::Url(url, item_type),
                 // The link is broken since it points to an external crate that wasn't documented.
                 // Do not create any link in such case. This is better than falling back to a
                 // dummy anchor like `#{item_type}.{name}` representing the `id` of *this* impl item
@@ -824,15 +847,25 @@ fn assoc_href_attr(it: &clean::Item, link: AssocItemLink<'_>, cx: &Context<'_>)
                 // those two items are distinct!
                 // In this scenario, the actual `id` of this impl item would be
                 // `#{item_type}.{name}-{n}` for some number `n` (a disambiguator).
-                Err(HrefError::DocumentationNotBuilt) => None,
-                Err(_) => Some(format!("#{item_type}.{name}")),
+                Err(HrefError::DocumentationNotBuilt) => return None,
+                Err(_) => Href::Anchor(item_type),
             }
         }
     };
 
+    let href = fmt::from_fn(move |f| match &href {
+        Href::AnchorId(id) => write!(f, "#{id}"),
+        Href::Url(url, item_type) => {
+            write!(f, "{url}#{item_type}.{name}")
+        }
+        Href::Anchor(item_type) => {
+            write!(f, "#{item_type}.{name}")
+        }
+    });
+
     // If there is no `href` for the reason explained above, simply do not render it which is valid:
     // https://html.spec.whatwg.org/multipage/links.html#links-created-by-a-and-area-elements
-    href.map(|href| format!(" href=\"{href}\"")).unwrap_or_default()
+    Some(fmt::from_fn(move |f| write!(f, " href=\"{href}\"")))
 }
 
 #[derive(Debug)]
@@ -847,7 +880,7 @@ enum AssocConstValue<'a> {
 }
 
 fn assoc_const(
-    w: &mut Buffer,
+    w: &mut String,
     it: &clean::Item,
     generics: &clean::Generics,
     ty: &clean::Type,
@@ -857,15 +890,17 @@ fn assoc_const(
     cx: &Context<'_>,
 ) {
     let tcx = cx.tcx();
-    write!(
+    write_str(
         w,
-        "{indent}{vis}const <a{href} class=\"constant\">{name}</a>{generics}: {ty}",
-        indent = " ".repeat(indent),
-        vis = visibility_print_with_space(it, cx),
-        href = assoc_href_attr(it, link, cx),
-        name = it.name.as_ref().unwrap(),
-        generics = generics.print(cx),
-        ty = ty.print(cx),
+        format_args!(
+            "{indent}{vis}const <a{href} class=\"constant\">{name}</a>{generics}: {ty}",
+            indent = " ".repeat(indent),
+            vis = visibility_print_with_space(it, cx),
+            href = assoc_href_attr(it, link, cx).maybe_display(),
+            name = it.name.as_ref().unwrap(),
+            generics = generics.print(cx),
+            ty = ty.print(cx),
+        ),
     );
     if let AssocConstValue::TraitDefault(konst) | AssocConstValue::Impl(konst) = value {
         // FIXME: `.value()` uses `clean::utils::format_integer_with_underscore_sep` under the
@@ -879,14 +914,14 @@ fn assoc_const(
             AssocConstValue::Impl(_) => repr != "_",  // show if there is a meaningful value to show
             AssocConstValue::None => unreachable!(),
         } {
-            write!(w, " = {}", Escape(&repr));
+            write_str(w, format_args!(" = {}", Escape(&repr)));
         }
     }
-    write!(w, "{}", print_where_clause(generics, cx, indent, Ending::NoNewline));
+    write_str(w, format_args!("{}", print_where_clause(generics, cx, indent, Ending::NoNewline)));
 }
 
 fn assoc_type(
-    w: &mut Buffer,
+    w: &mut String,
     it: &clean::Item,
     generics: &clean::Generics,
     bounds: &[clean::GenericBound],
@@ -895,27 +930,29 @@ fn assoc_type(
     indent: usize,
     cx: &Context<'_>,
 ) {
-    write!(
+    write_str(
         w,
-        "{indent}{vis}type <a{href} class=\"associatedtype\">{name}</a>{generics}",
-        indent = " ".repeat(indent),
-        vis = visibility_print_with_space(it, cx),
-        href = assoc_href_attr(it, link, cx),
-        name = it.name.as_ref().unwrap(),
-        generics = generics.print(cx),
+        format_args!(
+            "{indent}{vis}type <a{href} class=\"associatedtype\">{name}</a>{generics}",
+            indent = " ".repeat(indent),
+            vis = visibility_print_with_space(it, cx),
+            href = assoc_href_attr(it, link, cx).maybe_display(),
+            name = it.name.as_ref().unwrap(),
+            generics = generics.print(cx),
+        ),
     );
     if !bounds.is_empty() {
-        write!(w, ": {}", print_generic_bounds(bounds, cx))
+        write_str(w, format_args!(": {}", print_generic_bounds(bounds, cx)));
     }
     // Render the default before the where-clause which aligns with the new recommended style. See #89122.
     if let Some(default) = default {
-        write!(w, " = {}", default.print(cx))
+        write_str(w, format_args!(" = {}", default.print(cx)));
     }
-    write!(w, "{}", print_where_clause(generics, cx, indent, Ending::NoNewline));
+    write_str(w, format_args!("{}", print_where_clause(generics, cx, indent, Ending::NoNewline)));
 }
 
 fn assoc_method(
-    w: &mut Buffer,
+    w: &mut String,
     meth: &clean::Item,
     g: &clean::Generics,
     d: &clean::FnDecl,
@@ -942,7 +979,7 @@ fn assoc_method(
     let asyncness = header.asyncness.print_with_space();
     let safety = header.safety.print_with_space();
     let abi = print_abi_with_space(header.abi).to_string();
-    let href = assoc_href_attr(meth, link, cx);
+    let href = assoc_href_attr(meth, link, cx).maybe_display();
 
     // NOTE: `{:#}` does not print HTML formatting, `{}` does. So `g.print` can't be reused between the length calculation and `write!`.
     let generics_len = format!("{:#}", g.print(cx)).len();
@@ -956,35 +993,36 @@ fn assoc_method(
         + name.as_str().len()
         + generics_len;
 
-    let notable_traits = notable_traits_button(&d.output, cx);
+    let notable_traits = notable_traits_button(&d.output, cx).maybe_display();
 
     let (indent, indent_str, end_newline) = if parent == ItemType::Trait {
         header_len += 4;
         let indent_str = "    ";
-        write!(w, "{}", render_attributes_in_pre(meth, indent_str, cx));
+        write_str(w, format_args!("{}", render_attributes_in_pre(meth, indent_str, cx)));
         (4, indent_str, Ending::NoNewline)
     } else {
         render_attributes_in_code(w, meth, cx);
         (0, "", Ending::Newline)
     };
     w.reserve(header_len + "<a href=\"\" class=\"fn\">{".len() + "</a>".len());
-    write!(
+    write_str(
         w,
-        "{indent}{vis}{defaultness}{constness}{asyncness}{safety}{abi}fn \
+        format_args!(
+            "{indent}{vis}{defaultness}{constness}{asyncness}{safety}{abi}fn \
          <a{href} class=\"fn\">{name}</a>{generics}{decl}{notable_traits}{where_clause}",
-        indent = indent_str,
-        vis = vis,
-        defaultness = defaultness,
-        constness = constness,
-        asyncness = asyncness,
-        safety = safety,
-        abi = abi,
-        href = href,
-        name = name,
-        generics = g.print(cx),
-        decl = d.full_print(header_len, indent, cx),
-        notable_traits = notable_traits.unwrap_or_default(),
-        where_clause = print_where_clause(g, cx, indent, end_newline),
+            indent = indent_str,
+            vis = vis,
+            defaultness = defaultness,
+            constness = constness,
+            asyncness = asyncness,
+            safety = safety,
+            abi = abi,
+            href = href,
+            name = name,
+            generics = g.print(cx),
+            decl = d.full_print(header_len, indent, cx),
+            where_clause = print_where_clause(g, cx, indent, end_newline),
+        ),
     );
 }
 
@@ -1003,7 +1041,7 @@ fn assoc_method(
 /// will include the const-stable version, but no stable version will be emitted, as a natural
 /// consequence of the above rules.
 fn render_stability_since_raw_with_extra(
-    w: &mut Buffer,
+    w: &mut String,
     stable_version: Option<StableSince>,
     const_stability: Option<ConstStability>,
     extra_class: &str,
@@ -1058,7 +1096,10 @@ fn render_stability_since_raw_with_extra(
     }
 
     if !stability.is_empty() {
-        write!(w, r#"<span class="since{extra_class}" title="{title}">{stability}</span>"#);
+        write_str(
+            w,
+            format_args!(r#"<span class="since{extra_class}" title="{title}">{stability}</span>"#),
+        );
     }
 
     !stability.is_empty()
@@ -1074,7 +1115,7 @@ fn since_to_string(since: &StableSince) -> Option<String> {
 
 #[inline]
 fn render_stability_since_raw(
-    w: &mut Buffer,
+    w: &mut String,
     ver: Option<StableSince>,
     const_stability: Option<ConstStability>,
 ) -> bool {
@@ -1082,7 +1123,7 @@ fn render_stability_since_raw(
 }
 
 fn render_assoc_item(
-    w: &mut Buffer,
+    w: &mut String,
     item: &clean::Item,
     link: AssocItemLink<'_>,
     parent: ItemType,
@@ -1222,9 +1263,11 @@ pub(crate) fn render_all_impls(
     synthetic: &[&Impl],
     blanket_impl: &[&Impl],
 ) {
-    let mut impls = Buffer::html();
-    render_impls(cx, &mut impls, concrete, containing_item, true);
-    let impls = impls.into_inner();
+    let impls = {
+        let mut buf = String::new();
+        render_impls(cx, &mut buf, concrete, containing_item, true);
+        buf
+    };
     if !impls.is_empty() {
         write_impl_section_heading(&mut w, "Trait Implementations", "trait-implementations");
         write!(w, "<div id=\"trait-implementations-list\">{impls}</div>").unwrap();
@@ -1277,7 +1320,7 @@ fn render_assoc_items_inner(
     let (non_trait, traits): (Vec<_>, _) = v.iter().partition(|i| i.inner_impl().trait_.is_none());
     if !non_trait.is_empty() {
         let mut close_tags = <Vec<&str>>::with_capacity(1);
-        let mut tmp_buf = Buffer::html();
+        let mut tmp_buf = String::new();
         let (render_mode, id, class_html) = match what {
             AssocItemRender::All => {
                 write_impl_section_heading(&mut tmp_buf, "Implementations", "implementations");
@@ -1287,7 +1330,7 @@ fn render_assoc_items_inner(
                 let id =
                     cx.derive_id(small_url_encode(format!("deref-methods-{:#}", type_.print(cx))));
                 let derived_id = cx.derive_id(&id);
-                tmp_buf.write_str("<details class=\"toggle big-toggle\" open><summary>");
+                tmp_buf.push_str("<details class=\"toggle big-toggle\" open><summary>");
                 close_tags.push("</details>");
                 write_impl_section_heading(
                     &mut tmp_buf,
@@ -1298,14 +1341,14 @@ fn render_assoc_items_inner(
                     ),
                     &id,
                 );
-                tmp_buf.write_str("</summary>");
+                tmp_buf.push_str("</summary>");
                 if let Some(def_id) = type_.def_id(cx.cache()) {
                     cx.deref_id_map.borrow_mut().insert(def_id, id);
                 }
                 (RenderMode::ForDeref { mut_: deref_mut_ }, derived_id, r#" class="impl-items""#)
             }
         };
-        let mut impls_buf = Buffer::html();
+        let mut impls_buf = String::new();
         for i in &non_trait {
             render_impl(
                 &mut impls_buf,
@@ -1325,13 +1368,7 @@ fn render_assoc_items_inner(
             );
         }
         if !impls_buf.is_empty() {
-            write!(
-                w,
-                "{}<div id=\"{id}\"{class_html}>{}</div>",
-                tmp_buf.into_inner(),
-                impls_buf.into_inner()
-            )
-            .unwrap();
+            write!(w, "{tmp_buf}<div id=\"{id}\"{class_html}>{impls_buf}</div>").unwrap();
             for tag in close_tags.into_iter().rev() {
                 w.write_str(tag).unwrap();
             }
@@ -1431,7 +1468,10 @@ fn should_render_item(item: &clean::Item, deref_mut_: bool, tcx: TyCtxt<'_>) ->
     }
 }
 
-pub(crate) fn notable_traits_button(ty: &clean::Type, cx: &Context<'_>) -> Option<String> {
+pub(crate) fn notable_traits_button<'a, 'tcx>(
+    ty: &'a clean::Type,
+    cx: &'a Context<'tcx>,
+) -> Option<impl fmt::Display + 'a + Captures<'tcx>> {
     let mut has_notable_trait = false;
 
     if ty.is_unit() {
@@ -1473,19 +1513,20 @@ pub(crate) fn notable_traits_button(ty: &clean::Type, cx: &Context<'_>) -> Optio
         }
     }
 
-    if has_notable_trait {
+    has_notable_trait.then(|| {
         cx.types_with_notable_traits.borrow_mut().insert(ty.clone());
-        Some(format!(
-            " <a href=\"#\" class=\"tooltip\" data-notable-ty=\"{ty}\">ⓘ</a>",
-            ty = Escape(&format!("{:#}", ty.print(cx))),
-        ))
-    } else {
-        None
-    }
+        fmt::from_fn(|f| {
+            write!(
+                f,
+                " <a href=\"#\" class=\"tooltip\" data-notable-ty=\"{ty}\">ⓘ</a>",
+                ty = Escape(&format!("{:#}", ty.print(cx))),
+            )
+        })
+    })
 }
 
 fn notable_traits_decl(ty: &clean::Type, cx: &Context<'_>) -> (String, String) {
-    let mut out = Buffer::html();
+    let mut out = String::new();
 
     let did = ty.def_id(cx.cache()).expect("notable_traits_button already checked this");
 
@@ -1507,15 +1548,20 @@ fn notable_traits_decl(ty: &clean::Type, cx: &Context<'_>) -> (String, String) {
 
             if cx.cache().traits.get(&trait_did).is_some_and(|t| t.is_notable_trait(cx.tcx())) {
                 if out.is_empty() {
-                    write!(
+                    write_str(
                         &mut out,
-                        "<h3>Notable traits for <code>{}</code></h3>\
-                     <pre><code>",
-                        impl_.for_.print(cx)
+                        format_args!(
+                            "<h3>Notable traits for <code>{}</code></h3>\
+                            <pre><code>",
+                            impl_.for_.print(cx)
+                        ),
                     );
                 }
 
-                write!(&mut out, "<div class=\"where\">{}</div>", impl_.print(false, cx));
+                write_str(
+                    &mut out,
+                    format_args!("<div class=\"where\">{}</div>", impl_.print(false, cx)),
+                );
                 for it in &impl_.items {
                     if let clean::AssocTypeItem(ref tydef, ref _bounds) = it.kind {
                         out.push_str("<div class=\"where\">    ");
@@ -1538,10 +1584,10 @@ fn notable_traits_decl(ty: &clean::Type, cx: &Context<'_>) -> (String, String) {
         }
     }
     if out.is_empty() {
-        out.write_str("</code></pre>");
+        out.push_str("</code></pre>");
     }
 
-    (format!("{:#}", ty.print(cx)), out.into_inner())
+    (format!("{:#}", ty.print(cx)), out)
 }
 
 pub(crate) fn notable_traits_json<'a>(
@@ -1577,7 +1623,7 @@ struct ImplRenderingParameters {
 }
 
 fn render_impl(
-    w: &mut Buffer,
+    w: &mut String,
     cx: &Context<'_>,
     i: &Impl,
     parent: &clean::Item,
@@ -1598,8 +1644,8 @@ fn render_impl(
     // `containing_item` is used for rendering stability info. If the parent is a trait impl,
     // `containing_item` will the grandparent, since trait impls can't have stability attached.
     fn doc_impl_item(
-        boring: &mut Buffer,
-        interesting: &mut Buffer,
+        boring: &mut String,
+        interesting: &mut String,
         cx: &Context<'_>,
         item: &clean::Item,
         parent: &clean::Item,
@@ -1622,8 +1668,8 @@ fn render_impl(
 
         let in_trait_class = if trait_.is_some() { " trait-impl" } else { "" };
 
-        let mut doc_buffer = Buffer::empty_from(boring);
-        let mut info_buffer = Buffer::empty_from(boring);
+        let mut doc_buffer = String::new();
+        let mut info_buffer = String::new();
         let mut short_documented = true;
 
         if render_method_item {
@@ -1638,25 +1684,26 @@ fn render_impl(
                             document_item_info(cx, it, Some(parent))
                                 .render_into(&mut info_buffer)
                                 .unwrap();
-                            write!(
+                            write_str(
                                 &mut doc_buffer,
-                                "{}",
-                                document_full(item, cx, HeadingOffset::H5)
+                                format_args!("{}", document_full(item, cx, HeadingOffset::H5)),
                             );
                             short_documented = false;
                         } else {
                             // In case the item isn't documented,
                             // provide short documentation from the trait.
-                            write!(
+                            write_str(
                                 &mut doc_buffer,
-                                "{}",
-                                document_short(
-                                    it,
-                                    cx,
-                                    link,
-                                    parent,
-                                    rendering_params.show_def_docs,
-                                )
+                                format_args!(
+                                    "{}",
+                                    document_short(
+                                        it,
+                                        cx,
+                                        link,
+                                        parent,
+                                        rendering_params.show_def_docs,
+                                    )
+                                ),
                             );
                         }
                     }
@@ -1665,15 +1712,20 @@ fn render_impl(
                         .render_into(&mut info_buffer)
                         .unwrap();
                     if rendering_params.show_def_docs {
-                        write!(&mut doc_buffer, "{}", document_full(item, cx, HeadingOffset::H5));
+                        write_str(
+                            &mut doc_buffer,
+                            format_args!("{}", document_full(item, cx, HeadingOffset::H5)),
+                        );
                         short_documented = false;
                     }
                 }
             } else {
-                write!(
+                write_str(
                     &mut doc_buffer,
-                    "{}",
-                    document_short(item, cx, link, parent, rendering_params.show_def_docs)
+                    format_args!(
+                        "{}",
+                        document_short(item, cx, link, parent, rendering_params.show_def_docs)
+                    ),
                 );
             }
         }
@@ -1682,7 +1734,10 @@ fn render_impl(
         let toggled = !doc_buffer.is_empty();
         if toggled {
             let method_toggle_class = if item_type.is_method() { " method-toggle" } else { "" };
-            write!(w, "<details class=\"toggle{method_toggle_class}\" open><summary>");
+            write_str(
+                w,
+                format_args!("<details class=\"toggle{method_toggle_class}\" open><summary>"),
+            );
         }
         match &item.kind {
             clean::MethodItem(..) | clean::RequiredMethodItem(_) => {
@@ -1697,13 +1752,16 @@ fn render_impl(
                                 .find(|item| item.name.map(|n| n == *name).unwrap_or(false))
                         })
                         .map(|item| format!("{}.{name}", item.type_()));
-                    write!(w, "<section id=\"{id}\" class=\"{item_type}{in_trait_class}\">");
+                    write_str(
+                        w,
+                        format_args!("<section id=\"{id}\" class=\"{item_type}{in_trait_class}\">"),
+                    );
                     render_rightside(w, cx, item, render_mode);
                     if trait_.is_some() {
                         // Anchors are only used on trait impls.
-                        write!(w, "<a href=\"#{id}\" class=\"anchor\">§</a>");
+                        write_str(w, format_args!("<a href=\"#{id}\" class=\"anchor\">§</a>"));
                     }
-                    w.write_str("<h4 class=\"code-header\">");
+                    w.push_str("<h4 class=\"code-header\">");
                     render_assoc_item(
                         w,
                         item,
@@ -1712,19 +1770,22 @@ fn render_impl(
                         cx,
                         render_mode,
                     );
-                    w.write_str("</h4></section>");
+                    w.push_str("</h4></section>");
                 }
             }
             clean::RequiredAssocConstItem(ref generics, ref ty) => {
                 let source_id = format!("{item_type}.{name}");
                 let id = cx.derive_id(&source_id);
-                write!(w, "<section id=\"{id}\" class=\"{item_type}{in_trait_class}\">");
+                write_str(
+                    w,
+                    format_args!("<section id=\"{id}\" class=\"{item_type}{in_trait_class}\">"),
+                );
                 render_rightside(w, cx, item, render_mode);
                 if trait_.is_some() {
                     // Anchors are only used on trait impls.
-                    write!(w, "<a href=\"#{id}\" class=\"anchor\">§</a>");
+                    write_str(w, format_args!("<a href=\"#{id}\" class=\"anchor\">§</a>"));
                 }
-                w.write_str("<h4 class=\"code-header\">");
+                w.push_str("<h4 class=\"code-header\">");
                 assoc_const(
                     w,
                     item,
@@ -1735,18 +1796,21 @@ fn render_impl(
                     0,
                     cx,
                 );
-                w.write_str("</h4></section>");
+                w.push_str("</h4></section>");
             }
             clean::ProvidedAssocConstItem(ci) | clean::ImplAssocConstItem(ci) => {
                 let source_id = format!("{item_type}.{name}");
                 let id = cx.derive_id(&source_id);
-                write!(w, "<section id=\"{id}\" class=\"{item_type}{in_trait_class}\">");
+                write_str(
+                    w,
+                    format_args!("<section id=\"{id}\" class=\"{item_type}{in_trait_class}\">"),
+                );
                 render_rightside(w, cx, item, render_mode);
                 if trait_.is_some() {
                     // Anchors are only used on trait impls.
-                    write!(w, "<a href=\"#{id}\" class=\"anchor\">§</a>");
+                    write_str(w, format_args!("<a href=\"#{id}\" class=\"anchor\">§</a>"));
                 }
-                w.write_str("<h4 class=\"code-header\">");
+                w.push_str("<h4 class=\"code-header\">");
                 assoc_const(
                     w,
                     item,
@@ -1761,18 +1825,21 @@ fn render_impl(
                     0,
                     cx,
                 );
-                w.write_str("</h4></section>");
+                w.push_str("</h4></section>");
             }
             clean::RequiredAssocTypeItem(ref generics, ref bounds) => {
                 let source_id = format!("{item_type}.{name}");
                 let id = cx.derive_id(&source_id);
-                write!(w, "<section id=\"{id}\" class=\"{item_type}{in_trait_class}\">");
+                write_str(
+                    w,
+                    format_args!("<section id=\"{id}\" class=\"{item_type}{in_trait_class}\">"),
+                );
                 render_rightside(w, cx, item, render_mode);
                 if trait_.is_some() {
                     // Anchors are only used on trait impls.
-                    write!(w, "<a href=\"#{id}\" class=\"anchor\">§</a>");
+                    write_str(w, format_args!("<a href=\"#{id}\" class=\"anchor\">§</a>"));
                 }
-                w.write_str("<h4 class=\"code-header\">");
+                w.push_str("<h4 class=\"code-header\">");
                 assoc_type(
                     w,
                     item,
@@ -1783,18 +1850,21 @@ fn render_impl(
                     0,
                     cx,
                 );
-                w.write_str("</h4></section>");
+                w.push_str("</h4></section>");
             }
             clean::AssocTypeItem(tydef, _bounds) => {
                 let source_id = format!("{item_type}.{name}");
                 let id = cx.derive_id(&source_id);
-                write!(w, "<section id=\"{id}\" class=\"{item_type}{in_trait_class}\">");
+                write_str(
+                    w,
+                    format_args!("<section id=\"{id}\" class=\"{item_type}{in_trait_class}\">"),
+                );
                 render_rightside(w, cx, item, render_mode);
                 if trait_.is_some() {
                     // Anchors are only used on trait impls.
-                    write!(w, "<a href=\"#{id}\" class=\"anchor\">§</a>");
+                    write_str(w, format_args!("<a href=\"#{id}\" class=\"anchor\">§</a>"));
                 }
-                w.write_str("<h4 class=\"code-header\">");
+                w.push_str("<h4 class=\"code-header\">");
                 assoc_type(
                     w,
                     item,
@@ -1805,22 +1875,22 @@ fn render_impl(
                     0,
                     cx,
                 );
-                w.write_str("</h4></section>");
+                w.push_str("</h4></section>");
             }
             clean::StrippedItem(..) => return,
             _ => panic!("can't make docs for trait item with name {:?}", item.name),
         }
 
-        w.push_buffer(info_buffer);
+        w.push_str(&info_buffer);
         if toggled {
-            w.write_str("</summary>");
-            w.push_buffer(doc_buffer);
+            w.push_str("</summary>");
+            w.push_str(&doc_buffer);
             w.push_str("</details>");
         }
     }
 
-    let mut impl_items = Buffer::empty_from(w);
-    let mut default_impl_items = Buffer::empty_from(w);
+    let mut impl_items = String::new();
+    let mut default_impl_items = String::new();
     let impl_ = i.inner_impl();
 
     // Impl items are grouped by kinds:
@@ -1894,8 +1964,8 @@ fn render_impl(
     }
 
     fn render_default_items(
-        boring: &mut Buffer,
-        interesting: &mut Buffer,
+        boring: &mut String,
+        interesting: &mut String,
         cx: &Context<'_>,
         t: &clean::Trait,
         i: &clean::Impl,
@@ -1960,11 +2030,13 @@ fn render_impl(
         let toggled = !(impl_items.is_empty() && default_impl_items.is_empty());
         if toggled {
             close_tags.push("</details>");
-            write!(
+            write_str(
                 w,
-                "<details class=\"toggle implementors-toggle\"{}>\
-                     <summary>",
-                if rendering_params.toggle_open_by_default { " open" } else { "" }
+                format_args!(
+                    "<details class=\"toggle implementors-toggle\"{}>\
+                        <summary>",
+                    if rendering_params.toggle_open_by_default { " open" } else { "" }
+                ),
             );
         }
 
@@ -1995,38 +2067,38 @@ fn render_impl(
             &before_dox,
         );
         if toggled {
-            w.write_str("</summary>");
+            w.push_str("</summary>");
         }
 
         if before_dox.is_some() {
             if trait_.is_none() && impl_.items.is_empty() {
-                w.write_str(
+                w.push_str(
                     "<div class=\"item-info\">\
                          <div class=\"stab empty-impl\">This impl block contains no items.</div>\
                      </div>",
                 );
             }
             if let Some(after_dox) = after_dox {
-                write!(w, "<div class=\"docblock\">{after_dox}</div>");
+                write_str(w, format_args!("<div class=\"docblock\">{after_dox}</div>"));
             }
         }
         if !default_impl_items.is_empty() || !impl_items.is_empty() {
-            w.write_str("<div class=\"impl-items\">");
+            w.push_str("<div class=\"impl-items\">");
             close_tags.push("</div>");
         }
     }
     if !default_impl_items.is_empty() || !impl_items.is_empty() {
-        w.push_buffer(default_impl_items);
-        w.push_buffer(impl_items);
+        w.push_str(&default_impl_items);
+        w.push_str(&impl_items);
     }
     for tag in close_tags.into_iter().rev() {
-        w.write_str(tag);
+        w.push_str(tag);
     }
 }
 
 // Render the items that appear on the right side of methods, impls, and
 // associated types. For example "1.0.0 (const: 1.39.0) · source".
-fn render_rightside(w: &mut Buffer, cx: &Context<'_>, item: &clean::Item, render_mode: RenderMode) {
+fn render_rightside(w: &mut String, cx: &Context<'_>, item: &clean::Item, render_mode: RenderMode) {
     let tcx = cx.tcx();
 
     // FIXME: Once https://github.com/rust-lang/rust/issues/67792 is implemented, we can remove
@@ -2038,7 +2110,7 @@ fn render_rightside(w: &mut Buffer, cx: &Context<'_>, item: &clean::Item, render
     let src_href = cx.src_href(item);
     let has_src_ref = src_href.is_some();
 
-    let mut rightside = Buffer::new();
+    let mut rightside = String::new();
     let has_stability = render_stability_since_raw_with_extra(
         &mut rightside,
         item.stable_since(tcx),
@@ -2047,20 +2119,26 @@ fn render_rightside(w: &mut Buffer, cx: &Context<'_>, item: &clean::Item, render
     );
     if let Some(link) = src_href {
         if has_stability {
-            write!(rightside, " · <a class=\"src\" href=\"{link}\">Source</a>")
+            write_str(
+                &mut rightside,
+                format_args!(" · <a class=\"src\" href=\"{link}\">Source</a>"),
+            );
         } else {
-            write!(rightside, "<a class=\"src rightside\" href=\"{link}\">Source</a>")
+            write_str(
+                &mut rightside,
+                format_args!("<a class=\"src rightside\" href=\"{link}\">Source</a>"),
+            );
         }
     }
     if has_stability && has_src_ref {
-        write!(w, "<span class=\"rightside\">{}</span>", rightside.into_inner());
+        write_str(w, format_args!("<span class=\"rightside\">{rightside}</span>"));
     } else {
-        w.push_buffer(rightside);
+        w.push_str(&rightside);
     }
 }
 
 pub(crate) fn render_impl_summary(
-    w: &mut Buffer,
+    w: &mut String,
     cx: &Context<'_>,
     i: &Impl,
     parent: &clean::Item,
@@ -2073,25 +2151,27 @@ pub(crate) fn render_impl_summary(
 ) {
     let inner_impl = i.inner_impl();
     let id = cx.derive_id(get_id_for_impl(cx.tcx(), i.impl_item.item_id));
-    let aliases = if aliases.is_empty() {
-        String::new()
-    } else {
-        format!(" data-aliases=\"{}\"", aliases.join(","))
-    };
-    write!(w, "<section id=\"{id}\" class=\"impl\"{aliases}>");
+    let aliases = (!aliases.is_empty())
+        .then_some(fmt::from_fn(|f| {
+            write!(f, " data-aliases=\"{}\"", fmt::from_fn(|f| aliases.iter().joined(",", f)))
+        }))
+        .maybe_display();
+    write_str(w, format_args!("<section id=\"{id}\" class=\"impl\"{aliases}>"));
     render_rightside(w, cx, &i.impl_item, RenderMode::Normal);
-    write!(
+    write_str(
         w,
-        "<a href=\"#{id}\" class=\"anchor\">§</a>\
-         <h3 class=\"code-header\">"
+        format_args!(
+            "<a href=\"#{id}\" class=\"anchor\">§</a>\
+            <h3 class=\"code-header\">"
+        ),
     );
 
     if let Some(use_absolute) = use_absolute {
-        write!(w, "{}", inner_impl.print(use_absolute, cx));
+        write_str(w, format_args!("{}", inner_impl.print(use_absolute, cx)));
         if show_def_docs {
             for it in &inner_impl.items {
                 if let clean::AssocTypeItem(ref tydef, ref _bounds) = it.kind {
-                    w.write_str("<div class=\"where\">  ");
+                    w.push_str("<div class=\"where\">  ");
                     assoc_type(
                         w,
                         it,
@@ -2102,30 +2182,32 @@ pub(crate) fn render_impl_summary(
                         0,
                         cx,
                     );
-                    w.write_str(";</div>");
+                    w.push_str(";</div>");
                 }
             }
         }
     } else {
-        write!(w, "{}", inner_impl.print(false, cx));
+        write_str(w, format_args!("{}", inner_impl.print(false, cx)));
     }
-    w.write_str("</h3>");
+    w.push_str("</h3>");
 
     let is_trait = inner_impl.trait_.is_some();
     if is_trait && let Some(portability) = portability(&i.impl_item, Some(parent)) {
-        write!(
+        write_str(
             w,
-            "<span class=\"item-info\">\
+            format_args!(
+                "<span class=\"item-info\">\
                  <div class=\"stab portability\">{portability}</div>\
              </span>",
+            ),
         );
     }
 
     if let Some(doc) = doc {
-        write!(w, "<div class=\"docblock\">{doc}</div>");
+        write_str(w, format_args!("<div class=\"docblock\">{doc}</div>"));
     }
 
-    w.write_str("</section>");
+    w.push_str("</section>");
 }
 
 pub(crate) fn small_url_encode(s: String) -> String {
@@ -2577,7 +2659,7 @@ fn render_call_locations<W: fmt::Write>(mut w: W, cx: &Context<'_>, item: &clean
             cx,
             &cx.root_path(),
             &highlight::DecorationInfo(decoration_info),
-            sources::SourceContext::Embedded(sources::ScrapedInfo {
+            &sources::SourceContext::Embedded(sources::ScrapedInfo {
                 needs_expansion,
                 offset: line_min,
                 name: &call_data.display_name,
diff --git a/src/librustdoc/html/render/print_item.rs b/src/librustdoc/html/render/print_item.rs
index 4f51b7a0108..f3201147039 100644
--- a/src/librustdoc/html/render/print_item.rs
+++ b/src/librustdoc/html/render/print_item.rs
@@ -1,8 +1,7 @@
 use std::cmp::Ordering;
 use std::fmt;
-use std::fmt::{Display, Write};
+use std::fmt::Display;
 
-use itertools::Itertools;
 use rinja::Template;
 use rustc_abi::VariantIdx;
 use rustc_data_structures::captures::Captures;
@@ -27,17 +26,17 @@ use super::{
 };
 use crate::clean;
 use crate::config::ModuleSorting;
+use crate::display::{Joined as _, MaybeDisplay as _};
 use crate::formats::Impl;
 use crate::formats::item_type::ItemType;
 use crate::html::escape::{Escape, EscapeBodyTextWithWbr};
 use crate::html::format::{
-    Buffer, Ending, PrintWithSpace, join_with_double_colon, print_abi_with_space,
-    print_constness_with_space, print_where_clause, visibility_print_with_space,
+    Ending, PrintWithSpace, join_with_double_colon, print_abi_with_space,
+    print_constness_with_space, print_where_clause, visibility_print_with_space, write_str,
 };
 use crate::html::markdown::{HeadingOffset, MarkdownSummaryLine};
 use crate::html::render::{document_full, document_item_info};
 use crate::html::url_parts_builder::UrlPartsBuilder;
-use crate::joined::Joined as _;
 
 /// Generates a Rinja template struct for rendering items with common methods.
 ///
@@ -165,16 +164,16 @@ struct ItemVars<'a> {
 
 /// Calls `print_where_clause` and returns `true` if a `where` clause was generated.
 fn print_where_clause_and_check<'a, 'tcx: 'a>(
-    buffer: &mut Buffer,
+    buffer: &mut String,
     gens: &'a clean::Generics,
     cx: &'a Context<'tcx>,
 ) -> bool {
     let len_before = buffer.len();
-    write!(buffer, "{}", print_where_clause(gens, cx, 0, Ending::Newline));
+    write_str(buffer, format_args!("{}", print_where_clause(gens, cx, 0, Ending::Newline)));
     len_before != buffer.len()
 }
 
-pub(super) fn print_item(cx: &Context<'_>, item: &clean::Item, buf: &mut Buffer) {
+pub(super) fn print_item(cx: &Context<'_>, item: &clean::Item, buf: &mut String) {
     debug_assert!(!item.is_stripped());
     let typ = match item.kind {
         clean::ModuleItem(_) => {
@@ -207,13 +206,15 @@ pub(super) fn print_item(cx: &Context<'_>, item: &clean::Item, buf: &mut Buffer)
             unreachable!();
         }
     };
-    let mut stability_since_raw = Buffer::new();
-    render_stability_since_raw(
-        &mut stability_since_raw,
-        item.stable_since(cx.tcx()),
-        item.const_stability(cx.tcx()),
-    );
-    let stability_since_raw: String = stability_since_raw.into_inner();
+    let stability_since_raw = {
+        let mut buf = String::new();
+        render_stability_since_raw(
+            &mut buf,
+            item.stable_since(cx.tcx()),
+            item.const_stability(cx.tcx()),
+        );
+        buf
+    };
 
     // Write source tag
     //
@@ -278,10 +279,12 @@ pub(super) fn print_item(cx: &Context<'_>, item: &clean::Item, buf: &mut Buffer)
     // Render notable-traits.js used for all methods in this module.
     let mut types_with_notable_traits = cx.types_with_notable_traits.borrow_mut();
     if !types_with_notable_traits.is_empty() {
-        write!(
+        write_str(
             buf,
-            r#"<script type="text/json" id="notable-traits-data">{}</script>"#,
-            notable_traits_json(types_with_notable_traits.iter(), cx)
+            format_args!(
+                r#"<script type="text/json" id="notable-traits-data">{}</script>"#,
+                notable_traits_json(types_with_notable_traits.iter(), cx)
+            ),
         );
         types_with_notable_traits.clear();
     }
@@ -311,8 +314,8 @@ trait ItemTemplate<'a, 'cx: 'a>: rinja::Template + Display {
     fn item_and_cx(&self) -> (&'a clean::Item, &'a Context<'cx>);
 }
 
-fn item_module(w: &mut Buffer, cx: &Context<'_>, item: &clean::Item, items: &[clean::Item]) {
-    write!(w, "{}", document(cx, item, None, HeadingOffset::H2));
+fn item_module(w: &mut String, cx: &Context<'_>, item: &clean::Item, items: &[clean::Item]) {
+    write_str(w, format_args!("{}", document(cx, item, None, HeadingOffset::H2)));
 
     let mut not_stripped_items =
         items.iter().filter(|i| !i.is_stripped()).enumerate().collect::<Vec<_>>();
@@ -398,7 +401,7 @@ fn item_module(w: &mut Buffer, cx: &Context<'_>, item: &clean::Item, items: &[cl
         let my_section = item_ty_to_section(myitem.type_());
         if Some(my_section) != last_section {
             if last_section.is_some() {
-                w.write_str(ITEM_TABLE_CLOSE);
+                w.push_str(ITEM_TABLE_CLOSE);
             }
             last_section = Some(my_section);
             let section_id = my_section.id();
@@ -412,21 +415,29 @@ fn item_module(w: &mut Buffer, cx: &Context<'_>, item: &clean::Item, items: &[cl
                 use crate::html::format::anchor;
 
                 match *src {
-                    Some(src) => write!(
-                        w,
-                        "<dt><code>{}extern crate {} as {};",
-                        visibility_print_with_space(myitem, cx),
-                        anchor(myitem.item_id.expect_def_id(), src, cx),
-                        EscapeBodyTextWithWbr(myitem.name.unwrap().as_str()),
-                    ),
-                    None => write!(
-                        w,
-                        "<dt><code>{}extern crate {};",
-                        visibility_print_with_space(myitem, cx),
-                        anchor(myitem.item_id.expect_def_id(), myitem.name.unwrap(), cx),
-                    ),
+                    Some(src) => {
+                        write_str(
+                            w,
+                            format_args!(
+                                "<dt><code>{}extern crate {} as {};",
+                                visibility_print_with_space(myitem, cx),
+                                anchor(myitem.item_id.expect_def_id(), src, cx),
+                                EscapeBodyTextWithWbr(myitem.name.unwrap().as_str())
+                            ),
+                        );
+                    }
+                    None => {
+                        write_str(
+                            w,
+                            format_args!(
+                                "<dt><code>{}extern crate {};",
+                                visibility_print_with_space(myitem, cx),
+                                anchor(myitem.item_id.expect_def_id(), myitem.name.unwrap(), cx)
+                            ),
+                        );
+                    }
                 }
-                w.write_str("</code></dt>");
+                w.push_str("</code></dt>");
             }
 
             clean::ImportItem(ref import) => {
@@ -440,13 +451,15 @@ fn item_module(w: &mut Buffer, cx: &Context<'_>, item: &clean::Item, items: &[cl
                     }
                     clean::ImportKind::Glob => String::new(),
                 };
-                write!(
+                write_str(
                     w,
-                    "<dt{id}>\
-                         <code>{vis}{imp}</code>{stab_tags}\
-                     </dt>",
-                    vis = visibility_print_with_space(myitem, cx),
-                    imp = import.print(cx),
+                    format_args!(
+                        "<dt{id}>\
+                            <code>{vis}{imp}</code>{stab_tags}\
+                        </dt>",
+                        vis = visibility_print_with_space(myitem, cx),
+                        imp = import.print(cx)
+                    ),
                 );
             }
 
@@ -484,33 +497,31 @@ fn item_module(w: &mut Buffer, cx: &Context<'_>, item: &clean::Item, items: &[cl
                     MarkdownSummaryLine(&myitem.doc_value(), &myitem.links(cx)).into_string();
                 let (docs_before, docs_after) =
                     if docs.is_empty() { ("", "") } else { ("<dd>", "</dd>") };
-                write!(
+                write_str(
                     w,
-                    "<dt>\
-                        <a class=\"{class}\" href=\"{href}\" title=\"{title}\">{name}</a>\
-                        {visibility_and_hidden}\
-                        {unsafety_flag}\
-                        {stab_tags}\
-                     </dt>\
-                     {docs_before}{docs}{docs_after}",
-                    name = EscapeBodyTextWithWbr(myitem.name.unwrap().as_str()),
-                    visibility_and_hidden = visibility_and_hidden,
-                    stab_tags = extra_info_tags(tcx, myitem, item, None),
-                    class = myitem.type_(),
-                    unsafety_flag = unsafety_flag,
-                    href = item_path(myitem.type_(), myitem.name.unwrap().as_str()),
-                    title = [myitem.type_().to_string(), full_path(cx, myitem)]
-                        .iter()
-                        .filter_map(|s| if !s.is_empty() { Some(s.as_str()) } else { None })
-                        .collect::<Vec<_>>()
-                        .join(" "),
+                    format_args!(
+                        "<dt>\
+                            <a class=\"{class}\" href=\"{href}\" title=\"{title}\">{name}</a>\
+                            {visibility_and_hidden}\
+                            {unsafety_flag}\
+                            {stab_tags}\
+                        </dt>\
+                        {docs_before}{docs}{docs_after}",
+                        name = EscapeBodyTextWithWbr(myitem.name.unwrap().as_str()),
+                        visibility_and_hidden = visibility_and_hidden,
+                        stab_tags = extra_info_tags(tcx, myitem, item, None),
+                        class = myitem.type_(),
+                        unsafety_flag = unsafety_flag,
+                        href = item_path(myitem.type_(), myitem.name.unwrap().as_str()),
+                        title = format_args!("{} {}", myitem.type_(), full_path(cx, myitem)),
+                    ),
                 );
             }
         }
     }
 
     if last_section.is_some() {
-        w.write_str(ITEM_TABLE_CLOSE);
+        w.push_str(ITEM_TABLE_CLOSE);
     }
 }
 
@@ -572,7 +583,7 @@ fn extra_info_tags<'a, 'tcx: 'a>(
     })
 }
 
-fn item_function(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, f: &clean::Function) {
+fn item_function(w: &mut String, cx: &Context<'_>, it: &clean::Item, f: &clean::Function) {
     let tcx = cx.tcx();
     let header = it.fn_header(tcx).expect("printing a function which isn't a function");
     debug!(
@@ -603,31 +614,32 @@ fn item_function(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, f: &clean::
         + name.as_str().len()
         + generics_len;
 
-    let notable_traits = notable_traits_button(&f.decl.output, cx);
+    let notable_traits = notable_traits_button(&f.decl.output, cx).maybe_display();
 
     wrap_item(w, |w| {
         w.reserve(header_len);
-        write!(
+        write_str(
             w,
-            "{attrs}{vis}{constness}{asyncness}{safety}{abi}fn \
+            format_args!(
+                "{attrs}{vis}{constness}{asyncness}{safety}{abi}fn \
                 {name}{generics}{decl}{notable_traits}{where_clause}",
-            attrs = render_attributes_in_pre(it, "", cx),
-            vis = visibility,
-            constness = constness,
-            asyncness = asyncness,
-            safety = safety,
-            abi = abi,
-            name = name,
-            generics = f.generics.print(cx),
-            where_clause = print_where_clause(&f.generics, cx, 0, Ending::Newline),
-            decl = f.decl.full_print(header_len, 0, cx),
-            notable_traits = notable_traits.unwrap_or_default(),
+                attrs = render_attributes_in_pre(it, "", cx),
+                vis = visibility,
+                constness = constness,
+                asyncness = asyncness,
+                safety = safety,
+                abi = abi,
+                name = name,
+                generics = f.generics.print(cx),
+                where_clause = print_where_clause(&f.generics, cx, 0, Ending::Newline),
+                decl = f.decl.full_print(header_len, 0, cx),
+            ),
         );
     });
-    write!(w, "{}", document(cx, it, None, HeadingOffset::H2));
+    write_str(w, format_args!("{}", document(cx, it, None, HeadingOffset::H2)));
 }
 
-fn item_trait(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, t: &clean::Trait) {
+fn item_trait(w: &mut String, cx: &Context<'_>, it: &clean::Item, t: &clean::Trait) {
     let tcx = cx.tcx();
     let bounds = bounds(&t.bounds, false, cx);
     let required_types =
@@ -645,28 +657,33 @@ fn item_trait(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, t: &clean::Tra
 
     // Output the trait definition
     wrap_item(w, |mut w| {
-        write!(
+        write_str(
             w,
-            "{attrs}{vis}{safety}{is_auto}trait {name}{generics}{bounds}",
-            attrs = render_attributes_in_pre(it, "", cx),
-            vis = visibility_print_with_space(it, cx),
-            safety = t.safety(tcx).print_with_space(),
-            is_auto = if t.is_auto(tcx) { "auto " } else { "" },
-            name = it.name.unwrap(),
-            generics = t.generics.print(cx),
+            format_args!(
+                "{attrs}{vis}{safety}{is_auto}trait {name}{generics}{bounds}",
+                attrs = render_attributes_in_pre(it, "", cx),
+                vis = visibility_print_with_space(it, cx),
+                safety = t.safety(tcx).print_with_space(),
+                is_auto = if t.is_auto(tcx) { "auto " } else { "" },
+                name = it.name.unwrap(),
+                generics = t.generics.print(cx),
+            ),
         );
 
         if !t.generics.where_predicates.is_empty() {
-            write!(w, "{}", print_where_clause(&t.generics, cx, 0, Ending::Newline));
+            write_str(
+                w,
+                format_args!("{}", print_where_clause(&t.generics, cx, 0, Ending::Newline)),
+            );
         } else {
-            w.write_str(" ");
+            w.push_str(" ");
         }
 
         if t.items.is_empty() {
-            w.write_str("{ }");
+            w.push_str("{ }");
         } else {
             // FIXME: we should be using a derived_id for the Anchors here
-            w.write_str("{\n");
+            w.push_str("{\n");
             let mut toggle = false;
 
             // If there are too many associated types, hide _everything_
@@ -687,7 +704,7 @@ fn item_trait(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, t: &clean::Tra
                         cx,
                         RenderMode::Normal,
                     );
-                    w.write_str(";\n");
+                    w.push_str(";\n");
                 }
             }
             // If there are too many associated constants, hide everything after them
@@ -707,7 +724,7 @@ fn item_trait(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, t: &clean::Tra
                 );
             }
             if count_types != 0 && (count_consts != 0 || count_methods != 0) {
-                w.write_str("\n");
+                w.push_str("\n");
             }
             for consts in [&required_consts, &provided_consts] {
                 for c in consts {
@@ -719,7 +736,7 @@ fn item_trait(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, t: &clean::Tra
                         cx,
                         RenderMode::Normal,
                     );
-                    w.write_str(";\n");
+                    w.push_str(";\n");
                 }
             }
             if !toggle && should_hide_fields(count_methods) {
@@ -727,11 +744,14 @@ fn item_trait(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, t: &clean::Tra
                 toggle_open(&mut w, format_args!("{count_methods} methods"));
             }
             if count_consts != 0 && count_methods != 0 {
-                w.write_str("\n");
+                w.push_str("\n");
             }
 
             if !required_methods.is_empty() {
-                writeln!(w, "    // Required method{}", pluralize(required_methods.len()));
+                write_str(
+                    w,
+                    format_args_nl!("    // Required method{}", pluralize(required_methods.len())),
+                );
             }
             for (pos, m) in required_methods.iter().enumerate() {
                 render_assoc_item(
@@ -742,18 +762,21 @@ fn item_trait(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, t: &clean::Tra
                     cx,
                     RenderMode::Normal,
                 );
-                w.write_str(";\n");
+                w.push_str(";\n");
 
                 if pos < required_methods.len() - 1 {
-                    w.write_str("<span class=\"item-spacer\"></span>");
+                    w.push_str("<span class=\"item-spacer\"></span>");
                 }
             }
             if !required_methods.is_empty() && !provided_methods.is_empty() {
-                w.write_str("\n");
+                w.push_str("\n");
             }
 
             if !provided_methods.is_empty() {
-                writeln!(w, "    // Provided method{}", pluralize(provided_methods.len()));
+                write_str(
+                    w,
+                    format_args_nl!("    // Provided method{}", pluralize(provided_methods.len())),
+                );
             }
             for (pos, m) in provided_methods.iter().enumerate() {
                 render_assoc_item(
@@ -765,39 +788,42 @@ fn item_trait(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, t: &clean::Tra
                     RenderMode::Normal,
                 );
 
-                w.write_str(" { ... }\n");
+                w.push_str(" { ... }\n");
 
                 if pos < provided_methods.len() - 1 {
-                    w.write_str("<span class=\"item-spacer\"></span>");
+                    w.push_str("<span class=\"item-spacer\"></span>");
                 }
             }
             if toggle {
                 toggle_close(&mut w);
             }
-            w.write_str("}");
+            w.push_str("}");
         }
     });
 
     // Trait documentation
-    write!(w, "{}", document(cx, it, None, HeadingOffset::H2));
+    write_str(w, format_args!("{}", document(cx, it, None, HeadingOffset::H2)));
 
-    fn trait_item(w: &mut Buffer, cx: &Context<'_>, m: &clean::Item, t: &clean::Item) {
+    fn trait_item(w: &mut String, cx: &Context<'_>, m: &clean::Item, t: &clean::Item) {
         let name = m.name.unwrap();
         info!("Documenting {name} on {ty_name:?}", ty_name = t.name);
         let item_type = m.type_();
         let id = cx.derive_id(format!("{item_type}.{name}"));
 
-        let mut content = Buffer::empty_from(w);
-        write!(content, "{}", document_full(m, cx, HeadingOffset::H5));
+        let mut content = String::new();
+        write_str(&mut content, format_args!("{}", document_full(m, cx, HeadingOffset::H5)));
 
         let toggled = !content.is_empty();
         if toggled {
             let method_toggle_class = if item_type.is_method() { " method-toggle" } else { "" };
-            write!(w, "<details class=\"toggle{method_toggle_class}\" open><summary>");
+            write_str(
+                w,
+                format_args!("<details class=\"toggle{method_toggle_class}\" open><summary>"),
+            );
         }
-        write!(w, "<section id=\"{id}\" class=\"method\">");
+        write_str(w, format_args!("<section id=\"{id}\" class=\"method\">"));
         render_rightside(w, cx, m, RenderMode::Normal);
-        write!(w, "<h4 class=\"code-header\">");
+        write_str(w, format_args!("<h4 class=\"code-header\">"));
         render_assoc_item(
             w,
             m,
@@ -806,12 +832,12 @@ fn item_trait(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, t: &clean::Tra
             cx,
             RenderMode::Normal,
         );
-        w.write_str("</h4></section>");
+        w.push_str("</h4></section>");
         document_item_info(cx, m, Some(t)).render_into(w).unwrap();
         if toggled {
-            write!(w, "</summary>");
-            w.push_buffer(content);
-            write!(w, "</details>");
+            write_str(w, format_args!("</summary>"));
+            w.push_str(&content);
+            write_str(w, format_args!("</details>"));
         }
     }
 
@@ -826,7 +852,7 @@ fn item_trait(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, t: &clean::Tra
         for t in required_consts {
             trait_item(w, cx, t, it);
         }
-        w.write_str("</div>");
+        w.push_str("</div>");
     }
     if !provided_consts.is_empty() {
         write_section_heading(
@@ -839,7 +865,7 @@ fn item_trait(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, t: &clean::Tra
         for t in provided_consts {
             trait_item(w, cx, t, it);
         }
-        w.write_str("</div>");
+        w.push_str("</div>");
     }
 
     if !required_types.is_empty() {
@@ -853,7 +879,7 @@ fn item_trait(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, t: &clean::Tra
         for t in required_types {
             trait_item(w, cx, t, it);
         }
-        w.write_str("</div>");
+        w.push_str("</div>");
     }
     if !provided_types.is_empty() {
         write_section_heading(
@@ -866,7 +892,7 @@ fn item_trait(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, t: &clean::Tra
         for t in provided_types {
             trait_item(w, cx, t, it);
         }
-        w.write_str("</div>");
+        w.push_str("</div>");
     }
 
     // Output the documentation for each function individually
@@ -880,17 +906,19 @@ fn item_trait(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, t: &clean::Tra
         );
 
         if let Some(list) = must_implement_one_of_functions.as_deref() {
-            write!(
+            write_str(
                 w,
-                "<div class=\"stab must_implement\">At least one of the `{}` methods is required.</div>",
-                list.iter().join("`, `")
+                format_args!(
+                    "<div class=\"stab must_implement\">At least one of the `{}` methods is required.</div>",
+                    fmt::from_fn(|f| list.iter().joined("`, `", f))
+                ),
             );
         }
 
         for m in required_methods {
             trait_item(w, cx, m, it);
         }
-        w.write_str("</div>");
+        w.push_str("</div>");
     }
     if !provided_methods.is_empty() {
         write_section_heading(
@@ -903,11 +931,17 @@ fn item_trait(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, t: &clean::Tra
         for m in provided_methods {
             trait_item(w, cx, m, it);
         }
-        w.write_str("</div>");
+        w.push_str("</div>");
     }
 
     // If there are methods directly on this trait object, render them here.
-    write!(w, "{}", render_assoc_items(cx, it, it.item_id.expect_def_id(), AssocItemRender::All));
+    write_str(
+        w,
+        format_args!(
+            "{}",
+            render_assoc_items(cx, it, it.item_id.expect_def_id(), AssocItemRender::All)
+        ),
+    );
 
     let mut extern_crates = FxIndexSet::default();
 
@@ -997,7 +1031,7 @@ fn item_trait(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, t: &clean::Tra
         for implementor in concrete {
             render_implementor(cx, implementor, it, w, &implementor_dups, &[]);
         }
-        w.write_str("</div>");
+        w.push_str("</div>");
 
         if t.is_auto(tcx) {
             write_section_heading(
@@ -1020,7 +1054,7 @@ fn item_trait(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, t: &clean::Tra
                     ),
                 );
             }
-            w.write_str("</div>");
+            w.push_str("</div>");
         }
     } else {
         // even without any implementations to write in, we still want the heading and list, so the
@@ -1129,17 +1163,20 @@ fn item_trait(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, t: &clean::Tra
         js_src_path.extend(cx.current.iter().copied());
         js_src_path.push_fmt(format_args!("{}.{}.js", it.type_(), it.name.unwrap()));
     }
-    let extern_crates = extern_crates
-        .into_iter()
-        .map(|cnum| tcx.crate_name(cnum).to_string())
-        .collect::<Vec<_>>()
-        .join(",");
-    let (extern_before, extern_after) =
-        if extern_crates.is_empty() { ("", "") } else { (" data-ignore-extern-crates=\"", "\"") };
-    write!(
+    let extern_crates = fmt::from_fn(|f| {
+        if !extern_crates.is_empty() {
+            f.write_str(" data-ignore-extern-crates=\"")?;
+            extern_crates.iter().map(|&cnum| tcx.crate_name(cnum)).joined(",", f)?;
+            f.write_str("\"")?;
+        }
+        Ok(())
+    });
+    write_str(
         w,
-        "<script src=\"{src}\"{extern_before}{extern_crates}{extern_after} async></script>",
-        src = js_src_path.finish(),
+        format_args!(
+            "<script src=\"{src}\"{extern_crates} async></script>",
+            src = js_src_path.finish()
+        ),
     );
 }
 
@@ -1171,21 +1208,23 @@ fn item_trait_alias(
         .unwrap();
 }
 
-fn item_type_alias(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, t: &clean::TypeAlias) {
+fn item_type_alias(w: &mut String, cx: &Context<'_>, it: &clean::Item, t: &clean::TypeAlias) {
     wrap_item(w, |w| {
-        write!(
+        write_str(
             w,
-            "{attrs}{vis}type {name}{generics}{where_clause} = {type_};",
-            attrs = render_attributes_in_pre(it, "", cx),
-            vis = visibility_print_with_space(it, cx),
-            name = it.name.unwrap(),
-            generics = t.generics.print(cx),
-            where_clause = print_where_clause(&t.generics, cx, 0, Ending::Newline),
-            type_ = t.type_.print(cx),
+            format_args!(
+                "{attrs}{vis}type {name}{generics}{where_clause} = {type_};",
+                attrs = render_attributes_in_pre(it, "", cx),
+                vis = visibility_print_with_space(it, cx),
+                name = it.name.unwrap(),
+                generics = t.generics.print(cx),
+                where_clause = print_where_clause(&t.generics, cx, 0, Ending::Newline),
+                type_ = t.type_.print(cx),
+            ),
         );
     });
 
-    write!(w, "{}", document(cx, it, None, HeadingOffset::H2));
+    write_str(w, format_args!("{}", document(cx, it, None, HeadingOffset::H2)));
 
     if let Some(inner_type) = &t.inner_type {
         write_section_heading(w, "Aliased Type", "aliased-type", None, "");
@@ -1201,7 +1240,7 @@ fn item_type_alias(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, t: &clean
                     let variants_count = variants_iter().count();
                     let has_stripped_entries = variants_len != variants_count;
 
-                    write!(w, "enum {}{}", it.name.unwrap(), t.generics.print(cx));
+                    write_str(w, format_args!("enum {}{}", it.name.unwrap(), t.generics.print(cx)));
                     render_enum_fields(
                         w,
                         cx,
@@ -1220,7 +1259,10 @@ fn item_type_alias(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, t: &clean
                     let fields_count = fields.iter().filter(|i| !i.is_stripped()).count();
                     let has_stripped_fields = fields.len() != fields_count;
 
-                    write!(w, "union {}{}", it.name.unwrap(), t.generics.print(cx));
+                    write_str(
+                        w,
+                        format_args!("union {}{}", it.name.unwrap(), t.generics.print(cx)),
+                    );
                     render_struct_fields(
                         w,
                         Some(&t.generics),
@@ -1239,7 +1281,10 @@ fn item_type_alias(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, t: &clean
                     let fields_count = fields.iter().filter(|i| !i.is_stripped()).count();
                     let has_stripped_fields = fields.len() != fields_count;
 
-                    write!(w, "struct {}{}", it.name.unwrap(), t.generics.print(cx));
+                    write_str(
+                        w,
+                        format_args!("struct {}{}", it.name.unwrap(), t.generics.print(cx)),
+                    );
                     render_struct_fields(
                         w,
                         Some(&t.generics),
@@ -1261,8 +1306,8 @@ fn item_type_alias(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, t: &clean
     // won't be visible anywhere in the docs. It would be nice to also show
     // associated items from the aliased type (see discussion in #32077), but
     // we need #14072 to make sense of the generics.
-    write!(w, "{}", render_assoc_items(cx, it, def_id, AssocItemRender::All));
-    write!(w, "{}", document_type_layout(cx, def_id));
+    write_str(w, format_args!("{}", render_assoc_items(cx, it, def_id, AssocItemRender::All)));
+    write_str(w, format_args!("{}", document_type_layout(cx, def_id)));
 
     // [RUSTDOCIMPL] type.impl
     //
@@ -1351,16 +1396,18 @@ fn item_type_alias(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, t: &clean
             .collect();
         js_src_path.extend(target_fqp[..target_fqp.len() - 1].iter().copied());
         js_src_path.push_fmt(format_args!("{target_type}.{}.js", target_fqp.last().unwrap()));
-        let self_path = self_fqp.iter().map(Symbol::as_str).collect::<Vec<&str>>().join("::");
-        write!(
+        let self_path = fmt::from_fn(|f| self_fqp.iter().joined("::", f));
+        write_str(
             w,
-            "<script src=\"{src}\" data-self-path=\"{self_path}\" async></script>",
-            src = js_src_path.finish(),
+            format_args!(
+                "<script src=\"{src}\" data-self-path=\"{self_path}\" async></script>",
+                src = js_src_path.finish()
+            ),
         );
     }
 }
 
-fn item_union(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, s: &clean::Union) {
+fn item_union(w: &mut String, cx: &Context<'_>, it: &clean::Item, s: &clean::Union) {
     item_template!(
         #[template(path = "item_union.html")]
         struct ItemUnion<'a, 'cx> {
@@ -1445,16 +1492,18 @@ fn print_tuple_struct_fields<'a, 'cx: 'a>(
     })
 }
 
-fn item_enum(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, e: &clean::Enum) {
+fn item_enum(w: &mut String, cx: &Context<'_>, it: &clean::Item, e: &clean::Enum) {
     let count_variants = e.variants().count();
     wrap_item(w, |w| {
         render_attributes_in_code(w, it, cx);
-        write!(
+        write_str(
             w,
-            "{}enum {}{}",
-            visibility_print_with_space(it, cx),
-            it.name.unwrap(),
-            e.generics.print(cx),
+            format_args!(
+                "{}enum {}{}",
+                visibility_print_with_space(it, cx),
+                it.name.unwrap(),
+                e.generics.print(cx),
+            ),
         );
 
         render_enum_fields(
@@ -1469,14 +1518,14 @@ fn item_enum(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, e: &clean::Enum
         );
     });
 
-    write!(w, "{}", document(cx, it, None, HeadingOffset::H2));
+    write_str(w, format_args!("{}", document(cx, it, None, HeadingOffset::H2)));
 
     if count_variants != 0 {
         item_variants(w, cx, it, &e.variants, it.def_id().unwrap());
     }
     let def_id = it.item_id.expect_def_id();
-    write!(w, "{}", render_assoc_items(cx, it, def_id, AssocItemRender::All));
-    write!(w, "{}", document_type_layout(cx, def_id));
+    write_str(w, format_args!("{}", render_assoc_items(cx, it, def_id, AssocItemRender::All)));
+    write_str(w, format_args!("{}", document_type_layout(cx, def_id)));
 }
 
 /// It'll return false if any variant is not a C-like variant. Otherwise it'll return true if at
@@ -1505,7 +1554,7 @@ fn should_show_enum_discriminant(
 }
 
 fn display_c_like_variant(
-    w: &mut Buffer,
+    w: &mut String,
     cx: &Context<'_>,
     item: &clean::Item,
     variant: &clean::Variant,
@@ -1515,22 +1564,22 @@ fn display_c_like_variant(
 ) {
     let name = item.name.unwrap();
     if let Some(ref value) = variant.discriminant {
-        write!(w, "{} = {}", name.as_str(), value.value(cx.tcx(), true));
+        write_str(w, format_args!("{} = {}", name.as_str(), value.value(cx.tcx(), true)));
     } else if should_show_enum_discriminant {
         let adt_def = cx.tcx().adt_def(enum_def_id);
         let discr = adt_def.discriminant_for_variant(cx.tcx(), index);
         if discr.ty.is_signed() {
-            write!(w, "{} = {}", name.as_str(), discr.val as i128);
+            write_str(w, format_args!("{} = {}", name.as_str(), discr.val as i128));
         } else {
-            write!(w, "{} = {}", name.as_str(), discr.val);
+            write_str(w, format_args!("{} = {}", name.as_str(), discr.val));
         }
     } else {
-        w.write_str(name.as_str());
+        w.push_str(name.as_str());
     }
 }
 
 fn render_enum_fields(
-    mut w: &mut Buffer,
+    mut w: &mut String,
     cx: &Context<'_>,
     g: Option<&clean::Generics>,
     variants: &IndexVec<VariantIdx, clean::Item>,
@@ -1542,14 +1591,14 @@ fn render_enum_fields(
     let should_show_enum_discriminant = should_show_enum_discriminant(cx, enum_def_id, variants);
     if !g.is_some_and(|g| print_where_clause_and_check(w, g, cx)) {
         // If there wasn't a `where` clause, we add a whitespace.
-        w.write_str(" ");
+        w.push_str(" ");
     }
 
     let variants_stripped = has_stripped_entries;
     if count_variants == 0 && !variants_stripped {
-        w.write_str("{}");
+        w.push_str("{}");
     } else {
-        w.write_str("{\n");
+        w.push_str("{\n");
         let toggle = should_hide_fields(count_variants);
         if toggle {
             toggle_open(&mut w, format_args!("{count_variants} variants"));
@@ -1559,7 +1608,7 @@ fn render_enum_fields(
             if v.is_stripped() {
                 continue;
             }
-            w.write_str(TAB);
+            w.push_str(TAB);
             match v.kind {
                 clean::VariantItem(ref var) => match var.kind {
                     clean::VariantKind::CLike => display_c_like_variant(
@@ -1572,7 +1621,14 @@ fn render_enum_fields(
                         enum_def_id,
                     ),
                     clean::VariantKind::Tuple(ref s) => {
-                        write!(w, "{}({})", v.name.unwrap(), print_tuple_struct_fields(cx, s));
+                        write_str(
+                            w,
+                            format_args!(
+                                "{}({})",
+                                v.name.unwrap(),
+                                print_tuple_struct_fields(cx, s)
+                            ),
+                        );
                     }
                     clean::VariantKind::Struct(ref s) => {
                         render_struct(w, v, None, None, &s.fields, TAB, false, cx);
@@ -1580,21 +1636,21 @@ fn render_enum_fields(
                 },
                 _ => unreachable!(),
             }
-            w.write_str(",\n");
+            w.push_str(",\n");
         }
 
         if variants_stripped && !is_non_exhaustive {
-            w.write_str("    <span class=\"comment\">// some variants omitted</span>\n");
+            w.push_str("    <span class=\"comment\">// some variants omitted</span>\n");
         }
         if toggle {
             toggle_close(&mut w);
         }
-        w.write_str("}");
+        w.push_str("}");
     }
 }
 
 fn item_variants(
-    w: &mut Buffer,
+    w: &mut String,
     cx: &Context<'_>,
     it: &clean::Item,
     variants: &IndexVec<VariantIdx, clean::Item>,
@@ -1615,10 +1671,12 @@ fn item_variants(
             continue;
         }
         let id = cx.derive_id(format!("{}.{}", ItemType::Variant, variant.name.unwrap()));
-        write!(
+        write_str(
             w,
-            "<section id=\"{id}\" class=\"variant\">\
-                <a href=\"#{id}\" class=\"anchor\">§</a>",
+            format_args!(
+                "<section id=\"{id}\" class=\"variant\">\
+                <a href=\"#{id}\" class=\"anchor\">§</a>"
+            ),
         );
         render_stability_since_raw_with_extra(
             w,
@@ -1626,7 +1684,7 @@ fn item_variants(
             variant.const_stability(tcx),
             " rightside",
         );
-        w.write_str("<h3 class=\"code-header\">");
+        w.push_str("<h3 class=\"code-header\">");
         if let clean::VariantItem(ref var) = variant.kind
             && let clean::VariantKind::CLike = var.kind
         {
@@ -1640,17 +1698,17 @@ fn item_variants(
                 enum_def_id,
             );
         } else {
-            w.write_str(variant.name.unwrap().as_str());
+            w.push_str(variant.name.unwrap().as_str());
         }
 
         let clean::VariantItem(variant_data) = &variant.kind else { unreachable!() };
 
         if let clean::VariantKind::Tuple(ref s) = variant_data.kind {
-            write!(w, "({})", print_tuple_struct_fields(cx, s));
+            write_str(w, format_args!("({})", print_tuple_struct_fields(cx, s)));
         }
-        w.write_str("</h3></section>");
+        w.push_str("</h3></section>");
 
-        write!(w, "{}", document(cx, variant, Some(it), HeadingOffset::H4));
+        write_str(w, format_args!("{}", document(cx, variant, Some(it), HeadingOffset::H4)));
 
         let heading_and_fields = match &variant_data.kind {
             clean::VariantKind::Struct(s) => {
@@ -1676,12 +1734,14 @@ fn item_variants(
         if let Some((heading, fields)) = heading_and_fields {
             let variant_id =
                 cx.derive_id(format!("{}.{}.fields", ItemType::Variant, variant.name.unwrap()));
-            write!(
+            write_str(
                 w,
-                "<div class=\"sub-variant\" id=\"{variant_id}\">\
-                    <h4>{heading}</h4>\
-                    {}",
-                document_non_exhaustive(variant)
+                format_args!(
+                    "<div class=\"sub-variant\" id=\"{variant_id}\">\
+                        <h4>{heading}</h4>\
+                        {}",
+                    document_non_exhaustive(variant)
+                ),
             );
             for field in fields {
                 match field.kind {
@@ -1692,40 +1752,44 @@ fn item_variants(
                             variant.name.unwrap(),
                             field.name.unwrap()
                         ));
-                        write!(
+                        write_str(
                             w,
-                            "<div class=\"sub-variant-field\">\
-                                 <span id=\"{id}\" class=\"section-header\">\
-                                     <a href=\"#{id}\" class=\"anchor field\">§</a>\
-                                     <code>{f}: {t}</code>\
-                                 </span>",
-                            f = field.name.unwrap(),
-                            t = ty.print(cx),
+                            format_args!(
+                                "<div class=\"sub-variant-field\">\
+                                    <span id=\"{id}\" class=\"section-header\">\
+                                        <a href=\"#{id}\" class=\"anchor field\">§</a>\
+                                        <code>{f}: {t}</code>\
+                                    </span>",
+                                f = field.name.unwrap(),
+                                t = ty.print(cx),
+                            ),
                         );
-                        write!(
+                        write_str(
                             w,
-                            "{}</div>",
-                            document(cx, field, Some(variant), HeadingOffset::H5)
+                            format_args!(
+                                "{}</div>",
+                                document(cx, field, Some(variant), HeadingOffset::H5),
+                            ),
                         );
                     }
                     _ => unreachable!(),
                 }
             }
-            w.write_str("</div>");
+            w.push_str("</div>");
         }
     }
-    write!(w, "</div>");
+    write_str(w, format_args!("</div>"));
 }
 
-fn item_macro(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, t: &clean::Macro) {
+fn item_macro(w: &mut String, cx: &Context<'_>, it: &clean::Item, t: &clean::Macro) {
     wrap_item(w, |w| {
         // FIXME: Also print `#[doc(hidden)]` for `macro_rules!` if it `is_doc_hidden`.
         if !t.macro_rules {
-            write!(w, "{}", visibility_print_with_space(it, cx));
+            write_str(w, format_args!("{}", visibility_print_with_space(it, cx)));
         }
-        write!(w, "{}", Escape(&t.source));
+        write_str(w, format_args!("{}", Escape(&t.source)));
     });
-    write!(w, "{}", document(cx, it, None, HeadingOffset::H2))
+    write_str(w, format_args!("{}", document(cx, it, None, HeadingOffset::H2)));
 }
 
 fn item_proc_macro(
@@ -1779,7 +1843,7 @@ fn item_primitive(w: &mut impl fmt::Write, cx: &Context<'_>, it: &clean::Item) {
 }
 
 fn item_constant(
-    w: &mut Buffer,
+    w: &mut String,
     cx: &Context<'_>,
     it: &clean::Item,
     generics: &clean::Generics,
@@ -1790,14 +1854,16 @@ fn item_constant(
         let tcx = cx.tcx();
         render_attributes_in_code(w, it, cx);
 
-        write!(
+        write_str(
             w,
-            "{vis}const {name}{generics}: {typ}{where_clause}",
-            vis = visibility_print_with_space(it, cx),
-            name = it.name.unwrap(),
-            generics = generics.print(cx),
-            typ = ty.print(cx),
-            where_clause = print_where_clause(generics, cx, 0, Ending::NoNewline),
+            format_args!(
+                "{vis}const {name}{generics}: {typ}{where_clause}",
+                vis = visibility_print_with_space(it, cx),
+                name = it.name.unwrap(),
+                generics = generics.print(cx),
+                typ = ty.print(cx),
+                where_clause = print_where_clause(generics, cx, 0, Ending::NoNewline)
+            ),
         );
 
         // FIXME: The code below now prints
@@ -1813,9 +1879,9 @@ fn item_constant(
         let is_literal = c.is_literal(tcx);
         let expr = c.expr(tcx);
         if value.is_some() || is_literal {
-            write!(w, " = {expr};", expr = Escape(&expr));
+            write_str(w, format_args!(" = {expr};", expr = Escape(&expr)));
         } else {
-            w.write_str(";");
+            w.push_str(";");
         }
 
         if !is_literal {
@@ -1826,32 +1892,32 @@ fn item_constant(
                 if value_lowercase != expr_lowercase
                     && value_lowercase.trim_end_matches("i32") != expr_lowercase
                 {
-                    write!(w, " // {value}", value = Escape(value));
+                    write_str(w, format_args!(" // {value}", value = Escape(value)));
                 }
             }
         }
     });
 
-    write!(w, "{}", document(cx, it, None, HeadingOffset::H2))
+    write_str(w, format_args!("{}", document(cx, it, None, HeadingOffset::H2)));
 }
 
-fn item_struct(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, s: &clean::Struct) {
+fn item_struct(w: &mut String, cx: &Context<'_>, it: &clean::Item, s: &clean::Struct) {
     wrap_item(w, |w| {
         render_attributes_in_code(w, it, cx);
         render_struct(w, it, Some(&s.generics), s.ctor_kind, &s.fields, "", true, cx);
     });
 
-    write!(w, "{}", document(cx, it, None, HeadingOffset::H2));
+    write_str(w, format_args!("{}", document(cx, it, None, HeadingOffset::H2)));
 
     item_fields(w, cx, it, &s.fields, s.ctor_kind);
 
     let def_id = it.item_id.expect_def_id();
-    write!(w, "{}", render_assoc_items(cx, it, def_id, AssocItemRender::All));
-    write!(w, "{}", document_type_layout(cx, def_id));
+    write_str(w, format_args!("{}", render_assoc_items(cx, it, def_id, AssocItemRender::All)));
+    write_str(w, format_args!("{}", document_type_layout(cx, def_id)));
 }
 
 fn item_fields(
-    w: &mut Buffer,
+    w: &mut String,
     cx: &Context<'_>,
     it: &clean::Item,
     fields: &[clean::Item],
@@ -1876,16 +1942,18 @@ fn item_fields(
                 let field_name =
                     field.name.map_or_else(|| index.to_string(), |sym| sym.as_str().to_string());
                 let id = cx.derive_id(format!("{typ}.{field_name}", typ = ItemType::StructField));
-                write!(
+                write_str(
                     w,
-                    "<span id=\"{id}\" class=\"{item_type} section-header\">\
-                         <a href=\"#{id}\" class=\"anchor field\">§</a>\
-                         <code>{field_name}: {ty}</code>\
-                     </span>",
-                    item_type = ItemType::StructField,
-                    ty = ty.print(cx)
+                    format_args!(
+                        "<span id=\"{id}\" class=\"{item_type} section-header\">\
+                            <a href=\"#{id}\" class=\"anchor field\">§</a>\
+                            <code>{field_name}: {ty}</code>\
+                        </span>",
+                        item_type = ItemType::StructField,
+                        ty = ty.print(cx)
+                    ),
                 );
-                write!(w, "{}", document(cx, field, Some(it), HeadingOffset::H3));
+                write_str(w, format_args!("{}", document(cx, field, Some(it), HeadingOffset::H3)));
             }
         }
     }
@@ -1933,8 +2001,8 @@ fn item_foreign_type(w: &mut impl fmt::Write, cx: &Context<'_>, it: &clean::Item
         .unwrap();
 }
 
-fn item_keyword(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item) {
-    write!(w, "{}", document(cx, it, None, HeadingOffset::H2))
+fn item_keyword(w: &mut String, cx: &Context<'_>, it: &clean::Item) {
+    write_str(w, format_args!("{}", document(cx, it, None, HeadingOffset::H2)));
 }
 
 /// Compare two strings treating multi-digit numbers as single units (i.e. natural sort order).
@@ -2043,34 +2111,33 @@ pub(super) fn full_path(cx: &Context<'_>, item: &clean::Item) -> String {
     s
 }
 
-pub(super) fn item_path(ty: ItemType, name: &str) -> String {
-    match ty {
-        ItemType::Module => format!("{}index.html", ensure_trailing_slash(name)),
-        _ => format!("{ty}.{name}.html"),
-    }
+pub(super) fn item_path(ty: ItemType, name: &str) -> impl Display + '_ {
+    fmt::from_fn(move |f| match ty {
+        ItemType::Module => write!(f, "{}index.html", ensure_trailing_slash(name)),
+        _ => write!(f, "{ty}.{name}.html"),
+    })
 }
 
-fn bounds(t_bounds: &[clean::GenericBound], trait_alias: bool, cx: &Context<'_>) -> String {
-    let mut bounds = String::new();
-    if t_bounds.is_empty() {
-        return bounds;
-    }
-    let has_lots_of_bounds = t_bounds.len() > 2;
-    let inter_str = if has_lots_of_bounds { "\n    + " } else { " + " };
-    if !trait_alias {
-        if has_lots_of_bounds {
-            bounds.push_str(":\n    ");
-        } else {
-            bounds.push_str(": ");
-        }
-    }
-    write!(
-        bounds,
-        "{}",
-        fmt::from_fn(|f| t_bounds.iter().map(|p| p.print(cx)).joined(inter_str, f))
-    )
-    .unwrap();
-    bounds
+fn bounds<'a, 'tcx>(
+    bounds: &'a [clean::GenericBound],
+    trait_alias: bool,
+    cx: &'a Context<'tcx>,
+) -> impl Display + 'a + Captures<'tcx> {
+    (!bounds.is_empty())
+        .then_some(fmt::from_fn(move |f| {
+            let has_lots_of_bounds = bounds.len() > 2;
+            let inter_str = if has_lots_of_bounds { "\n    + " } else { " + " };
+            if !trait_alias {
+                if has_lots_of_bounds {
+                    f.write_str(":\n    ")?;
+                } else {
+                    f.write_str(": ")?;
+                }
+            }
+
+            bounds.iter().map(|p| p.print(cx)).joined(inter_str, f)
+        }))
+        .maybe_display()
 }
 
 fn wrap_item<W, F>(w: &mut W, f: F)
@@ -2108,7 +2175,7 @@ fn render_implementor(
     cx: &Context<'_>,
     implementor: &Impl,
     trait_: &clean::Item,
-    w: &mut Buffer,
+    w: &mut String,
     implementor_dups: &FxHashMap<Symbol, (DefId, bool)>,
     aliases: &[String],
 ) {
@@ -2152,10 +2219,9 @@ fn render_union<'a, 'cx: 'a>(
 
         let where_displayed = g
             .map(|g| {
-                let mut buf = Buffer::html();
-                write!(buf, "{}", g.print(cx));
+                let mut buf = g.print(cx).to_string();
                 let where_displayed = print_where_clause_and_check(&mut buf, g, cx);
-                write!(f, "{buf}", buf = buf.into_inner()).unwrap();
+                f.write_str(&buf).unwrap();
                 where_displayed
             })
             .unwrap_or(false);
@@ -2197,7 +2263,7 @@ fn render_union<'a, 'cx: 'a>(
 }
 
 fn render_struct(
-    w: &mut Buffer,
+    w: &mut String,
     it: &clean::Item,
     g: Option<&clean::Generics>,
     ty: Option<CtorKind>,
@@ -2206,15 +2272,17 @@ fn render_struct(
     structhead: bool,
     cx: &Context<'_>,
 ) {
-    write!(
+    write_str(
         w,
-        "{}{}{}",
-        visibility_print_with_space(it, cx),
-        if structhead { "struct " } else { "" },
-        it.name.unwrap()
+        format_args!(
+            "{}{}{}",
+            visibility_print_with_space(it, cx),
+            if structhead { "struct " } else { "" },
+            it.name.unwrap()
+        ),
     );
     if let Some(g) = g {
-        write!(w, "{}", g.print(cx))
+        write_str(w, format_args!("{}", g.print(cx)));
     }
     render_struct_fields(
         w,
@@ -2229,7 +2297,7 @@ fn render_struct(
 }
 
 fn render_struct_fields(
-    mut w: &mut Buffer,
+    mut w: &mut String,
     g: Option<&clean::Generics>,
     ty: Option<CtorKind>,
     fields: &[clean::Item],
@@ -2245,9 +2313,9 @@ fn render_struct_fields(
 
             // If there wasn't a `where` clause, we add a whitespace.
             if !where_displayed {
-                w.write_str(" {");
+                w.push_str(" {");
             } else {
-                w.write_str("{");
+                w.push_str("{");
             }
             let count_fields =
                 fields.iter().filter(|f| matches!(f.kind, clean::StructFieldItem(..))).count();
@@ -2258,66 +2326,82 @@ fn render_struct_fields(
             }
             for field in fields {
                 if let clean::StructFieldItem(ref ty) = field.kind {
-                    write!(
+                    write_str(
                         w,
-                        "\n{tab}    {vis}{name}: {ty},",
-                        vis = visibility_print_with_space(field, cx),
-                        name = field.name.unwrap(),
-                        ty = ty.print(cx),
+                        format_args!(
+                            "\n{tab}    {vis}{name}: {ty},",
+                            vis = visibility_print_with_space(field, cx),
+                            name = field.name.unwrap(),
+                            ty = ty.print(cx)
+                        ),
                     );
                 }
             }
 
             if has_visible_fields {
                 if has_stripped_entries {
-                    write!(w, "\n{tab}    <span class=\"comment\">/* private fields */</span>");
+                    write_str(
+                        w,
+                        format_args!(
+                            "\n{tab}    <span class=\"comment\">/* private fields */</span>"
+                        ),
+                    );
                 }
-                write!(w, "\n{tab}");
+                write_str(w, format_args!("\n{tab}"));
             } else if has_stripped_entries {
-                write!(w, " <span class=\"comment\">/* private fields */</span> ");
+                write_str(w, format_args!(" <span class=\"comment\">/* private fields */</span> "));
             }
             if toggle {
                 toggle_close(&mut w);
             }
-            w.write_str("}");
+            w.push_str("}");
         }
         Some(CtorKind::Fn) => {
-            w.write_str("(");
+            w.push_str("(");
             if !fields.is_empty()
                 && fields.iter().all(|field| {
                     matches!(field.kind, clean::StrippedItem(box clean::StructFieldItem(..)))
                 })
             {
-                write!(w, "<span class=\"comment\">/* private fields */</span>");
+                write_str(w, format_args!("<span class=\"comment\">/* private fields */</span>"));
             } else {
                 for (i, field) in fields.iter().enumerate() {
                     if i > 0 {
-                        w.write_str(", ");
+                        w.push_str(", ");
                     }
                     match field.kind {
-                        clean::StrippedItem(box clean::StructFieldItem(..)) => write!(w, "_"),
+                        clean::StrippedItem(box clean::StructFieldItem(..)) => {
+                            write_str(w, format_args!("_"));
+                        }
                         clean::StructFieldItem(ref ty) => {
-                            write!(w, "{}{}", visibility_print_with_space(field, cx), ty.print(cx),)
+                            write_str(
+                                w,
+                                format_args!(
+                                    "{}{}",
+                                    visibility_print_with_space(field, cx),
+                                    ty.print(cx)
+                                ),
+                            );
                         }
                         _ => unreachable!(),
                     }
                 }
             }
-            w.write_str(")");
+            w.push_str(")");
             if let Some(g) = g {
-                write!(w, "{}", print_where_clause(g, cx, 0, Ending::NoNewline));
+                write_str(w, format_args!("{}", print_where_clause(g, cx, 0, Ending::NoNewline)));
             }
             // We only want a ";" when we are displaying a tuple struct, not a variant tuple struct.
             if structhead {
-                w.write_str(";");
+                w.push_str(";");
             }
         }
         Some(CtorKind::Const) => {
             // Needed for PhantomData.
             if let Some(g) = g {
-                write!(w, "{}", print_where_clause(g, cx, 0, Ending::NoNewline));
+                write_str(w, format_args!("{}", print_where_clause(g, cx, 0, Ending::NoNewline)));
             }
-            w.write_str(";");
+            w.push_str(";");
         }
     }
 }
diff --git a/src/librustdoc/html/render/sidebar.rs b/src/librustdoc/html/render/sidebar.rs
index 23ac568fdf8..64dbaf9083e 100644
--- a/src/librustdoc/html/render/sidebar.rs
+++ b/src/librustdoc/html/render/sidebar.rs
@@ -1,4 +1,5 @@
 use std::borrow::Cow;
+use std::cmp::Ordering;
 
 use rinja::Template;
 use rustc_data_structures::fx::FxHashSet;
@@ -11,8 +12,8 @@ use super::{Context, ItemSection, item_ty_to_section};
 use crate::clean;
 use crate::formats::Impl;
 use crate::formats::item_type::ItemType;
-use crate::html::format::Buffer;
 use crate::html::markdown::{IdMap, MarkdownWithToc};
+use crate::html::render::print_item::compare_names;
 
 #[derive(Clone, Copy)]
 pub(crate) enum ModuleLike {
@@ -78,7 +79,7 @@ impl<'a> LinkBlock<'a> {
 }
 
 /// A link to an item. Content should not be escaped.
-#[derive(PartialOrd, Ord, PartialEq, Eq, Hash, Clone)]
+#[derive(Ord, PartialEq, Eq, Hash, Clone)]
 pub(crate) struct Link<'a> {
     /// The content for the anchor tag and title attr
     name: Cow<'a, str>,
@@ -90,6 +91,20 @@ pub(crate) struct Link<'a> {
     children: Vec<Link<'a>>,
 }
 
+impl PartialOrd for Link<'_> {
+    fn partial_cmp(&self, other: &Link<'_>) -> Option<Ordering> {
+        match compare_names(&self.name, &other.name) {
+            Ordering::Equal => (),
+            result => return Some(result),
+        }
+        (&self.name_html, &self.href, &self.children).partial_cmp(&(
+            &other.name_html,
+            &other.href,
+            &other.children,
+        ))
+    }
+}
+
 impl<'a> Link<'a> {
     pub fn new(href: impl Into<Cow<'a, str>>, name: impl Into<Cow<'a, str>>) -> Self {
         Self { href: href.into(), name: name.into(), children: vec![], name_html: None }
@@ -114,7 +129,7 @@ pub(crate) mod filters {
     }
 }
 
-pub(super) fn print_sidebar(cx: &Context<'_>, it: &clean::Item, buffer: &mut Buffer) {
+pub(super) fn print_sidebar(cx: &Context<'_>, it: &clean::Item, buffer: &mut String) {
     let mut ids = IdMap::new();
     let mut blocks: Vec<LinkBlock<'_>> = docblock_toc(cx, it, &mut ids).into_iter().collect();
     let deref_id_map = cx.deref_id_map.borrow();
diff --git a/src/librustdoc/html/render/span_map.rs b/src/librustdoc/html/render/span_map.rs
index a15ac155123..ce9c42c01cc 100644
--- a/src/librustdoc/html/render/span_map.rs
+++ b/src/librustdoc/html/render/span_map.rs
@@ -51,7 +51,7 @@ pub(crate) fn collect_spans_and_sources(
         let mut visitor = SpanMapVisitor { tcx, matches: FxHashMap::default() };
 
         if generate_link_to_definition {
-            tcx.hir().walk_toplevel_module(&mut visitor);
+            tcx.hir_walk_toplevel_module(&mut visitor);
         }
         let sources = sources::collect_local_sources(tcx, src_root, krate);
         (sources, visitor.matches)
@@ -173,18 +173,18 @@ impl SpanMapVisitor<'_> {
     }
 
     fn infer_id(&mut self, hir_id: HirId, expr_hir_id: Option<HirId>, span: Span) {
-        let hir = self.tcx.hir();
-        let body_id = hir.enclosing_body_owner(hir_id);
+        let tcx = self.tcx;
+        let body_id = tcx.hir_enclosing_body_owner(hir_id);
         // FIXME: this is showing error messages for parts of the code that are not
         // compiled (because of cfg)!
         //
         // See discussion in https://github.com/rust-lang/rust/issues/69426#issuecomment-1019412352
-        let typeck_results = self.tcx.typeck_body(hir.body_owned_by(body_id).id());
+        let typeck_results = tcx.typeck_body(tcx.hir_body_owned_by(body_id).id());
         // Interestingly enough, for method calls, we need the whole expression whereas for static
         // method/function calls, we need the call expression specifically.
         if let Some(def_id) = typeck_results.type_dependent_def_id(expr_hir_id.unwrap_or(hir_id)) {
             let link = if def_id.as_local().is_some() {
-                LinkFromSrc::Local(rustc_span(def_id, self.tcx))
+                LinkFromSrc::Local(rustc_span(def_id, tcx))
             } else {
                 LinkFromSrc::External(def_id)
             };
@@ -221,8 +221,8 @@ impl SpanMapVisitor<'_> {
 impl<'tcx> Visitor<'tcx> for SpanMapVisitor<'tcx> {
     type NestedFilter = nested_filter::All;
 
-    fn nested_visit_map(&mut self) -> Self::Map {
-        self.tcx.hir()
+    fn maybe_tcx(&mut self) -> Self::MaybeTyCtxt {
+        self.tcx
     }
 
     fn visit_path(&mut self, path: &rustc_hir::Path<'tcx>, _id: HirId) {
@@ -288,7 +288,7 @@ impl<'tcx> Visitor<'tcx> for SpanMapVisitor<'tcx> {
             | ItemKind::Use(_, _)
             | ItemKind::ExternCrate(_)
             | ItemKind::ForeignMod { .. }
-            | ItemKind::GlobalAsm(_)
+            | ItemKind::GlobalAsm { .. }
             // We already have "visit_mod" above so no need to check it here.
             | ItemKind::Mod(_) => {}
         }
diff --git a/src/librustdoc/html/render/tests.rs b/src/librustdoc/html/render/tests.rs
index 4a9724a6f84..657cd3c82aa 100644
--- a/src/librustdoc/html/render/tests.rs
+++ b/src/librustdoc/html/render/tests.rs
@@ -1,7 +1,7 @@
 use std::cmp::Ordering;
 
+use super::AllTypes;
 use super::print_item::compare_names;
-use super::{AllTypes, Buffer};
 
 #[test]
 fn test_compare_names() {
@@ -47,8 +47,8 @@ fn test_all_types_prints_header_once() {
     // Regression test for #82477
     let all_types = AllTypes::new();
 
-    let mut buffer = Buffer::new();
+    let mut buffer = String::new();
     all_types.print(&mut buffer);
 
-    assert_eq!(1, buffer.into_inner().matches("List of all items").count());
+    assert_eq!(1, buffer.matches("List of all items").count());
 }
diff --git a/src/librustdoc/html/render/write_shared.rs b/src/librustdoc/html/render/write_shared.rs
index 57d07c05c11..a4dec013fc0 100644
--- a/src/librustdoc/html/render/write_shared.rs
+++ b/src/librustdoc/html/render/write_shared.rs
@@ -44,7 +44,6 @@ use crate::docfs::PathError;
 use crate::error::Error;
 use crate::formats::Impl;
 use crate::formats::item_type::ItemType;
-use crate::html::format::Buffer;
 use crate::html::layout;
 use crate::html::render::ordered_json::{EscapedJson, OrderedJson};
 use crate::html::render::search_index::{SerializedSearchIndex, build_index};
@@ -622,7 +621,6 @@ impl TypeAliasPart {
                     // to make that functionality work here, it needs to be called with
                     // each type alias, and if it gives a different result, split the impl
                     for &(type_alias_fqp, type_alias_item) in type_aliases {
-                        let mut buf = Buffer::html();
                         cx.id_map.borrow_mut().clear();
                         cx.deref_id_map.borrow_mut().clear();
                         let target_did = impl_
@@ -638,23 +636,26 @@ impl TypeAliasPart {
                         } else {
                             AssocItemLink::Anchor(None)
                         };
-                        super::render_impl(
-                            &mut buf,
-                            cx,
-                            impl_,
-                            type_alias_item,
-                            assoc_link,
-                            RenderMode::Normal,
-                            None,
-                            &[],
-                            ImplRenderingParameters {
-                                show_def_docs: true,
-                                show_default_items: true,
-                                show_non_assoc_items: true,
-                                toggle_open_by_default: true,
-                            },
-                        );
-                        let text = buf.into_inner();
+                        let text = {
+                            let mut buf = String::new();
+                            super::render_impl(
+                                &mut buf,
+                                cx,
+                                impl_,
+                                type_alias_item,
+                                assoc_link,
+                                RenderMode::Normal,
+                                None,
+                                &[],
+                                ImplRenderingParameters {
+                                    show_def_docs: true,
+                                    show_default_items: true,
+                                    show_non_assoc_items: true,
+                                    toggle_open_by_default: true,
+                                },
+                            );
+                            buf
+                        };
                         let type_alias_fqp = (*type_alias_fqp).iter().join("::");
                         if Some(&text) == ret.last().map(|s: &AliasSerializableImpl| &s.text) {
                             ret.last_mut()
diff --git a/src/librustdoc/html/sources.rs b/src/librustdoc/html/sources.rs
index 1ac0c10c612..78c86a27632 100644
--- a/src/librustdoc/html/sources.rs
+++ b/src/librustdoc/html/sources.rs
@@ -1,6 +1,5 @@
 use std::cell::RefCell;
 use std::ffi::OsStr;
-use std::ops::RangeInclusive;
 use std::path::{Component, Path, PathBuf};
 use std::{fmt, fs};
 
@@ -12,12 +11,13 @@ use rustc_session::Session;
 use rustc_span::{FileName, FileNameDisplayPreference, RealFileName, sym};
 use tracing::info;
 
+use super::highlight;
+use super::layout::{self, BufDisplay};
+use super::render::Context;
 use crate::clean;
 use crate::clean::utils::has_doc_flag;
 use crate::docfs::PathError;
 use crate::error::Error;
-use crate::html::render::Context;
-use crate::html::{highlight, layout};
 use crate::visit::DocVisitor;
 
 pub(crate) fn render(cx: &mut Context<'_>, krate: &clean::Crate) -> Result<(), Error> {
@@ -238,11 +238,12 @@ impl SourceCollector<'_, '_> {
             resource_suffix: &shared.resource_suffix,
             rust_logo: has_doc_flag(self.cx.tcx(), LOCAL_CRATE.as_def_id(), sym::rust_logo),
         };
+        let source_context = SourceContext::Standalone { file_path };
         let v = layout::render(
             &shared.layout,
             &page,
             "",
-            |buf: &mut _| {
+            BufDisplay(|buf: &mut String| {
                 print_src(
                     buf,
                     contents,
@@ -250,9 +251,9 @@ impl SourceCollector<'_, '_> {
                     self.cx,
                     &root_path,
                     &highlight::DecorationInfo::default(),
-                    SourceContext::Standalone { file_path },
-                )
-            },
+                    &source_context,
+                );
+            }),
             &shared.style_files,
         );
         shared.fs.write(cur, v)?;
@@ -302,17 +303,17 @@ pub(crate) struct ScrapedInfo<'a> {
 #[derive(Template)]
 #[template(path = "scraped_source.html")]
 struct ScrapedSource<'a, Code: std::fmt::Display> {
-    info: ScrapedInfo<'a>,
-    lines: RangeInclusive<usize>,
+    info: &'a ScrapedInfo<'a>,
     code_html: Code,
+    max_nb_digits: u32,
 }
 
 #[derive(Template)]
 #[template(path = "source.html")]
 struct Source<Code: std::fmt::Display> {
-    lines: RangeInclusive<usize>,
     code_html: Code,
     file_path: Option<(String, String)>,
+    max_nb_digits: u32,
 }
 
 pub(crate) enum SourceContext<'a> {
@@ -329,8 +330,17 @@ pub(crate) fn print_src(
     context: &Context<'_>,
     root_path: &str,
     decoration_info: &highlight::DecorationInfo,
-    source_context: SourceContext<'_>,
+    source_context: &SourceContext<'_>,
 ) {
+    let mut lines = s.lines().count();
+    let line_info = if let SourceContext::Embedded(ref info) = source_context {
+        highlight::LineInfo::new_scraped(lines as u32, info.offset as u32)
+    } else {
+        highlight::LineInfo::new(lines as u32)
+    };
+    if line_info.is_scraped_example {
+        lines += line_info.start_line as usize;
+    }
     let code = fmt::from_fn(move |fmt| {
         let current_href = context
             .href_from_span(clean::Span::new(file_span), false)
@@ -340,13 +350,13 @@ pub(crate) fn print_src(
             s,
             Some(highlight::HrefContext { context, file_span, root_path, current_href }),
             Some(decoration_info),
+            Some(line_info),
         );
         Ok(())
     });
-    let lines = s.lines().count();
+    let max_nb_digits = if lines > 0 { lines.ilog(10) + 1 } else { 1 };
     match source_context {
         SourceContext::Standalone { file_path } => Source {
-            lines: (1..=lines),
             code_html: code,
             file_path: if let Some(file_name) = file_path.file_name()
                 && let Some(file_path) = file_path.parent()
@@ -355,12 +365,14 @@ pub(crate) fn print_src(
             } else {
                 None
             },
+            max_nb_digits,
         }
         .render_into(&mut writer)
         .unwrap(),
         SourceContext::Embedded(info) => {
-            let lines = (1 + info.offset)..=(lines + info.offset);
-            ScrapedSource { info, lines, code_html: code }.render_into(&mut writer).unwrap();
+            ScrapedSource { info, code_html: code, max_nb_digits }
+                .render_into(&mut writer)
+                .unwrap();
         }
     };
 }
diff --git a/src/librustdoc/html/static/css/rustdoc.css b/src/librustdoc/html/static/css/rustdoc.css
index d0612e997fd..0ea4d8f1e39 100644
--- a/src/librustdoc/html/static/css/rustdoc.css
+++ b/src/librustdoc/html/static/css/rustdoc.css
@@ -40,6 +40,21 @@ xmlns="http://www.w3.org/2000/svg" fill="black" height="18px">\
 	--docblock-indent: 24px;
 	--font-family: "Source Serif 4", NanumBarunGothic, serif;
 	--font-family-code: "Source Code Pro", monospace;
+	--line-number-padding: 4px;
+	--line-number-right-margin: 20px;
+	/* scraped examples icons (34x33px) */
+	--prev-arrow-image: url('data:image/svg+xml,<svg width="16" height="16" viewBox="0 0 16 16" \
+	enable-background="new 0 0 16 16" xmlns="http://www.w3.org/2000/svg"><path fill="none" \
+	d="M8,3l-4,5l4,5m-4,-5h10" stroke="black" stroke-width="2"/></svg>');
+	--next-arrow-image: url('data:image/svg+xml,<svg width="16" height="16" viewBox="0 0 16 16" \
+	enable-background="new 0 0 16 16" xmlns="http://www.w3.org/2000/svg"><path fill="none" \
+	d="M8,3l4,5l-4,5m4,-5h-10" stroke="black" stroke-width="2"/></svg>');
+	--expand-arrow-image: url('data:image/svg+xml,<svg width="16" height="16" viewBox="0 0 16 16" \
+	enable-background="new 0 0 16 16" xmlns="http://www.w3.org/2000/svg"><path fill="none" \
+	d="M3,10l4,4l4,-4m-4,4M3,7l4,-4l4,4" stroke="black" stroke-width="2"/></svg>');
+	--collapse-arrow-image: url('data:image/svg+xml,<svg width="16" height="16" viewBox="0 0 16 16" \
+	enable-background="new 0 0 16 16" xmlns="http://www.w3.org/2000/svg"><path fill="none" \
+	d="M3,8l4,4l4,-4m-4,4M3,4l4,4l4,-4" stroke="black" stroke-width="2"/></svg>');
 }
 
 :root.sans-serif {
@@ -450,9 +465,7 @@ pre.item-decl {
 
 .src .content pre {
 	padding: 20px;
-}
-.rustdoc.src .example-wrap .src-line-numbers  {
-	padding: 20px 0 20px 4px;
+	padding-left: 16px;
 }
 
 img {
@@ -836,22 +849,6 @@ ul.block, .block li, .block ul {
 	border-radius: 6px;
 }
 
-/*
-If the code example line numbers are displayed, there will be a weird radius in the middle from
-both the code example and the line numbers, so we need to remove the radius in this case.
-*/
-.rustdoc .example-wrap > .example-line-numbers,
-.rustdoc .scraped-example .src-line-numbers,
-.rustdoc .scraped-example .src-line-numbers > pre {
-	border-top-right-radius: 0;
-	border-bottom-right-radius: 0;
-}
-.rustdoc .example-wrap > .example-line-numbers + pre,
-.rustdoc .scraped-example .rust {
-	border-top-left-radius: 0;
-	border-bottom-left-radius: 0;
-}
-
 .rustdoc .scraped-example {
 	position: relative;
 }
@@ -896,36 +893,105 @@ both the code example and the line numbers, so we need to remove the radius in t
 	overflow: auto;
 }
 
-.rustdoc .example-wrap pre.example-line-numbers,
-.rustdoc .example-wrap .src-line-numbers {
-	min-width: fit-content; /* prevent collapsing into nothing in truncated scraped examples */
-	flex-grow: 0;
-	text-align: right;
-	-webkit-user-select: none;
-	user-select: none;
-	padding: 14px 8px;
-	padding-right: 2px;
-	color: var(--src-line-numbers-span-color);
+.example-wrap.digits-1:not(.hide-lines) [data-nosnippet] {
+	width: calc(1ch + var(--line-number-padding) * 2);
 }
-
-.rustdoc .scraped-example .example-wrap .src-line-numbers {
-	padding: 0;
+.example-wrap.digits-2:not(.hide-lines) [data-nosnippet] {
+	width: calc(2ch + var(--line-number-padding) * 2);
+}
+.example-wrap.digits-3:not(.hide-lines) [data-nosnippet] {
+	width: calc(3ch + var(--line-number-padding) * 2);
 }
-.rustdoc .src-line-numbers pre {
-	padding: 14px 0;
+.example-wrap.digits-4:not(.hide-lines) [data-nosnippet] {
+	width: calc(4ch + var(--line-number-padding) * 2);
 }
-.src-line-numbers a, .src-line-numbers span {
+.example-wrap.digits-5:not(.hide-lines) [data-nosnippet] {
+	width: calc(5ch + var(--line-number-padding) * 2);
+}
+.example-wrap.digits-6:not(.hide-lines) [data-nosnippet] {
+	width: calc(6ch + var(--line-number-padding) * 2);
+}
+.example-wrap.digits-7:not(.hide-lines) [data-nosnippet] {
+	width: calc(7ch + var(--line-number-padding) * 2);
+}
+.example-wrap.digits-8:not(.hide-lines) [data-nosnippet] {
+	width: calc(8ch + var(--line-number-padding) * 2);
+}
+.example-wrap.digits-9:not(.hide-lines) [data-nosnippet] {
+	width: calc(9ch + var(--line-number-padding) * 2);
+}
+
+.example-wrap [data-nosnippet] {
 	color: var(--src-line-numbers-span-color);
-	padding: 0 8px;
+	text-align: right;
+	display: inline-block;
+	margin-right: var(--line-number-right-margin);
+	-moz-user-select: none;
+	-webkit-user-select: none;
+	-ms-user-select: none;
+	user-select: none;
+	padding: 0 var(--line-number-padding);
 }
-.src-line-numbers :target {
-	background-color: transparent;
+.example-wrap [data-nosnippet]:target {
 	border-right: none;
-	padding: 0 8px;
 }
-.src-line-numbers .line-highlighted {
+.example-wrap .line-highlighted[data-nosnippet] {
 	background-color: var(--src-line-number-highlighted-background-color);
 }
+:root.word-wrap-source-code .example-wrap [data-nosnippet] {
+	position: absolute;
+	left: 0;
+}
+.word-wrap-source-code .example-wrap pre > code {
+	position: relative;
+	word-break: break-all;
+}
+:root.word-wrap-source-code .example-wrap pre > code {
+	display: block;
+	white-space: pre-wrap;
+}
+:root.word-wrap-source-code .example-wrap pre > code * {
+	word-break: break-all;
+}
+:root.word-wrap-source-code .example-wrap.digits-1 pre > code {
+	padding-left: calc(
+		1ch + var(--line-number-padding) * 2 + var(--line-number-right-margin));
+}
+:root.word-wrap-source-code .example-wrap.digits-2 pre > code {
+	padding-left: calc(
+		2ch + var(--line-number-padding) * 2 + var(--line-number-right-margin));
+}
+:root.word-wrap-source-code .example-wrap.digits-3 pre > code {
+	padding-left: calc(
+		3ch + var(--line-number-padding) * 2 + var(--line-number-right-margin));
+}
+:root.word-wrap-source-code .example-wrap.digits-4 pre > code {
+	padding-left: calc(
+		4ch + var(--line-number-padding) * 2 + var(--line-number-right-margin));
+}
+:root.word-wrap-source-code .example-wrap.digits-5 pre > code {
+	padding-left: calc(
+		5ch + var(--line-number-padding) * 2 + var(--line-number-right-margin));
+}
+:root.word-wrap-source-code .example-wrap.digits-6 pre > code {
+	padding-left: calc(
+		6ch + var(--line-number-padding) * 2 + var(--line-number-right-margin));
+}
+:root.word-wrap-source-code .example-wrap.digits-7 pre > code {
+	padding-left: calc(
+		7ch + var(--line-number-padding) * 2 + var(--line-number-right-margin));
+}
+:root.word-wrap-source-code .example-wrap.digits-8 pre > code {
+	padding-left: calc(
+		8ch + var(--line-number-padding) * 2 + var(--line-number-right-margin));
+}
+:root.word-wrap-source-code .example-wrap.digits-9 pre > code {
+	padding-left: calc(
+		9ch + var(--line-number-padding) * 2 + var(--line-number-right-margin));
+}
+.example-wrap.hide-lines [data-nosnippet] {
+	display: none;
+}
 
 .search-loading {
 	text-align: center;
@@ -1110,7 +1176,7 @@ because of the `[-]` element which would overlap with it. */
 }
 
 .main-heading a:hover,
-.example-wrap .rust a:hover,
+.example-wrap .rust a:hover:not([data-nosnippet]),
 .all-items a:hover,
 .docblock a:not(.scrape-help):not(.tooltip):hover:not(.doc-anchor),
 .item-table dd a:not(.scrape-help):not(.tooltip):hover,
@@ -1568,7 +1634,7 @@ pre.rust .doccomment {
 	color: var(--code-highlight-doc-comment-color);
 }
 
-.rustdoc.src .example-wrap pre.rust a {
+.rustdoc.src .example-wrap pre.rust a:not([data-nosnippet]) {
 	background: var(--codeblock-link-background);
 }
 
@@ -1701,7 +1767,10 @@ instead, we check that it's not a "finger" cursor.
 	padding: 2px 0 0 4px;
 }
 .example-wrap .button-holder .copy-button::before,
-.example-wrap .test-arrow::before {
+.example-wrap .test-arrow::before,
+.example-wrap .button-holder .prev::before,
+.example-wrap .button-holder .next::before,
+.example-wrap .button-holder .expand::before {
 	filter: var(--copy-path-img-filter);
 }
 .example-wrap .button-holder .copy-button::before {
@@ -1716,6 +1785,24 @@ instead, we check that it's not a "finger" cursor.
 	padding-right: 5px;
 }
 
+.example-wrap .button-holder .prev,
+.example-wrap .button-holder .next,
+.example-wrap .button-holder .expand {
+	line-height: 0px;
+}
+.example-wrap .button-holder .prev::before {
+	content: var(--prev-arrow-image);
+}
+.example-wrap .button-holder .next::before {
+	content: var(--next-arrow-image);
+}
+.example-wrap .button-holder .expand::before {
+	content: var(--expand-arrow-image);
+}
+.example-wrap .button-holder .expand.collapse::before {
+	content: var(--collapse-arrow-image);
+}
+
 .code-attribute {
 	font-weight: 300;
 	color: var(--code-attribute-color);
@@ -1759,8 +1846,7 @@ instead, we check that it's not a "finger" cursor.
 	}
 }
 
-:target {
-	padding-right: 3px;
+:target:not([data-nosnippet]) {
 	background-color: var(--target-background-color);
 	border-right: 3px solid var(--target-border-color);
 }
@@ -1985,6 +2071,13 @@ button#toggle-all-docs:before {
 	filter: var(--settings-menu-filter);
 }
 
+button#toggle-all-docs.will-expand:before {
+	/* Custom arrow icon */
+	content: url('data:image/svg+xml,<svg width="18" height="18" viewBox="0 0 12 12" \
+	enable-background="new 0 0 12 12" xmlns="http://www.w3.org/2000/svg">\
+	<path d="M2,5l4,-4l4,4M2,7l4,4l4,-4" stroke="black" fill="none" stroke-width="2px"/></svg>');
+}
+
 #help-button > a:before {
 	/* Question mark with circle */
 	content: url('data:image/svg+xml,<svg width="18" height="18" viewBox="0 0 12 12" \
@@ -3153,7 +3246,7 @@ Original by Dempfi (https://github.com/dempfi/ayu)
 	color: #ff7733;
 }
 
-:root[data-theme="ayu"] .src-line-numbers .line-highlighted {
+:root[data-theme="ayu"] a[data-nosnippet].line-highlighted {
 	color: #708090;
 	padding-right: 7px;
 	border-right: 1px solid #ffb44c;
diff --git a/src/librustdoc/html/static/js/main.js b/src/librustdoc/html/static/js/main.js
index a348c6c5678..edfcc1291b9 100644
--- a/src/librustdoc/html/static/js/main.js
+++ b/src/librustdoc/html/static/js/main.js
@@ -1112,35 +1112,32 @@ function preLoadCss(cssUrl) {
 
     // @ts-expect-error
     window.rustdoc_add_line_numbers_to_examples = () => {
-        if (document.querySelector(".rustdoc.src")) {
-            // We are in the source code page, nothing to be done here!
-            return;
+        // @ts-expect-error
+        function generateLine(nb) {
+            return `<span data-nosnippet>${nb}</span>`;
         }
+
         onEachLazy(document.querySelectorAll(
-            ":not(.scraped-example) > .example-wrap > pre:not(.example-line-numbers)",
-        ), x => {
-            const parent = x.parentNode;
-            const line_numbers = parent.querySelectorAll(".example-line-numbers");
-            if (line_numbers.length > 0) {
+            ".rustdoc:not(.src) :not(.scraped-example) > .example-wrap > pre > code",
+        ), code => {
+            if (hasClass(code.parentElement.parentElement, "hide-lines")) {
+                removeClass(code.parentElement.parentElement, "hide-lines");
                 return;
             }
-            const count = x.textContent.split("\n").length;
-            const elems = [];
-            for (let i = 0; i < count; ++i) {
-                elems.push(i + 1);
-            }
-            const node = document.createElement("pre");
-            addClass(node, "example-line-numbers");
-            node.innerHTML = elems.join("\n");
-            parent.insertBefore(node, x);
+            const lines = code.innerHTML.split("\n");
+            const digits = (lines.length + "").length;
+            // @ts-expect-error
+            code.innerHTML = lines.map((line, index) => generateLine(index + 1) + line).join("\n");
+            addClass(code.parentElement.parentElement, `digits-${digits}`);
         });
     };
 
     // @ts-expect-error
     window.rustdoc_remove_line_numbers_from_examples = () => {
-        onEachLazy(document.querySelectorAll(".example-wrap > .example-line-numbers"), x => {
-            x.parentNode.removeChild(x);
-        });
+        onEachLazy(
+            document.querySelectorAll(".rustdoc:not(.src) :not(.scraped-example) > .example-wrap"),
+            x => addClass(x, "hide-lines"),
+        );
     };
 
     if (getSettingValue("line-numbers") === "true") {
@@ -2039,7 +2036,10 @@ function preLoadCss(cssUrl) {
         // Most page titles are '<Item> in <path::to::module> - Rust', except
         // modules (which don't have the first part) and keywords/primitives
         // (which don't have a module path)
-        const [item, module] = document.title.split(" in ");
+        const titleElement = document.querySelector("title");
+        const title = titleElement && titleElement.textContent ?
+                      titleElement.textContent.replace(" - Rust", "") : "";
+        const [item, module] = title.split(" in ");
         const path = [item];
         if (module !== undefined) {
             path.unshift(module);
diff --git a/src/librustdoc/html/static/js/scrape-examples.js b/src/librustdoc/html/static/js/scrape-examples.js
index d08f15a5bfa..d641405c875 100644
--- a/src/librustdoc/html/static/js/scrape-examples.js
+++ b/src/librustdoc/html/static/js/scrape-examples.js
@@ -16,7 +16,7 @@
 
     // Scroll code block to the given code location
     function scrollToLoc(elt, loc, isHidden) {
-        const lines = elt.querySelector(".src-line-numbers > pre");
+        const lines = elt.querySelectorAll("[data-nosnippet]");
         let scrollOffset;
 
         // If the block is greater than the size of the viewer,
@@ -25,24 +25,24 @@
         const maxLines = isHidden ? HIDDEN_MAX_LINES : DEFAULT_MAX_LINES;
         if (loc[1] - loc[0] > maxLines) {
             const line = Math.max(0, loc[0] - 1);
-            scrollOffset = lines.children[line].offsetTop;
+            scrollOffset = lines[line].offsetTop;
         } else {
             const halfHeight = elt.offsetHeight / 2;
-            const offsetTop = lines.children[loc[0]].offsetTop;
-            const lastLine = lines.children[loc[1]];
+            const offsetTop = lines[loc[0]].offsetTop;
+            const lastLine = lines[loc[1]];
             const offsetBot = lastLine.offsetTop + lastLine.offsetHeight;
             const offsetMid = (offsetTop + offsetBot) / 2;
             scrollOffset = offsetMid - halfHeight;
         }
 
-        lines.parentElement.scrollTo(0, scrollOffset);
+        lines[0].parentElement.scrollTo(0, scrollOffset);
         elt.querySelector(".rust").scrollTo(0, scrollOffset);
     }
 
     function createScrapeButton(parent, className, content) {
         const button = document.createElement("button");
         button.className = className;
-        button.innerText = content;
+        button.title = content;
         parent.insertBefore(button, parent.firstChild);
         return button;
     }
@@ -54,14 +54,14 @@
         let expandButton = null;
 
         if (!example.classList.contains("expanded")) {
-            expandButton = createScrapeButton(buttonHolder, "expand", "↕");
+            expandButton = createScrapeButton(buttonHolder, "expand", "Show all");
         }
         const isHidden = example.parentElement.classList.contains("more-scraped-examples");
 
         const locs = example.locs;
         if (locs.length > 1) {
-            const next = createScrapeButton(buttonHolder, "next", "≻");
-            const prev = createScrapeButton(buttonHolder, "prev", "≺");
+            const next = createScrapeButton(buttonHolder, "next", "Next usage");
+            const prev = createScrapeButton(buttonHolder, "prev", "Previous usage");
 
             // Toggle through list of examples in a given file
             const onChangeLoc = changeIndex => {
@@ -94,9 +94,13 @@
             expandButton.addEventListener("click", () => {
                 if (hasClass(example, "expanded")) {
                     removeClass(example, "expanded");
+                    removeClass(expandButton, "collapse");
+                    expandButton.title = "Show all";
                     scrollToLoc(example, locs[0][0], isHidden);
                 } else {
                     addClass(example, "expanded");
+                    addClass(expandButton, "collapse");
+                    expandButton.title = "Show single example";
                 }
             });
         }
diff --git a/src/librustdoc/html/static/js/search.js b/src/librustdoc/html/static/js/search.js
index 121a43e3d92..ccbd6811b07 100644
--- a/src/librustdoc/html/static/js/search.js
+++ b/src/librustdoc/html/static/js/search.js
@@ -5318,8 +5318,9 @@ function registerSearchEvents() {
 
     // @ts-expect-error
     searchState.input.addEventListener("blur", () => {
-        // @ts-expect-error
-        searchState.input.placeholder = searchState.input.origPlaceholder;
+        if (window.searchState.input) {
+            window.searchState.input.placeholder = window.searchState.origPlaceholder;
+        }
     });
 
     // Push and pop states are used to add search results to the browser
diff --git a/src/librustdoc/html/static/js/settings.js b/src/librustdoc/html/static/js/settings.js
index bf33e0f17e5..5f1bbd27328 100644
--- a/src/librustdoc/html/static/js/settings.js
+++ b/src/librustdoc/html/static/js/settings.js
@@ -59,6 +59,14 @@
                 } else {
                     removeClass(document.documentElement, "sans-serif");
                 }
+                break;
+            case "word-wrap-source-code":
+                if (value === true) {
+                    addClass(document.documentElement, "word-wrap-source-code");
+                } else {
+                    removeClass(document.documentElement, "word-wrap-source-code");
+                }
+                break;
         }
     }
 
@@ -246,6 +254,11 @@
                 "js_name": "sans-serif-fonts",
                 "default": false,
             },
+            {
+                "name": "Word wrap source code",
+                "js_name": "word-wrap-source-code",
+                "default": false,
+            },
         ];
 
         // Then we build the DOM.
diff --git a/src/librustdoc/html/static/js/src-script.js b/src/librustdoc/html/static/js/src-script.js
index 8f712f4c20c..fc27241334b 100644
--- a/src/librustdoc/html/static/js/src-script.js
+++ b/src/librustdoc/html/static/js/src-script.js
@@ -138,10 +138,8 @@ function highlightSrcLines() {
     if (x) {
         x.scrollIntoView();
     }
-    onEachLazy(document.getElementsByClassName("src-line-numbers"), e => {
-        onEachLazy(e.getElementsByTagName("a"), i_e => {
-            removeClass(i_e, "line-highlighted");
-        });
+    onEachLazy(document.querySelectorAll("a[data-nosnippet]"), e => {
+        removeClass(e, "line-highlighted");
     });
     for (let i = from; i <= to; ++i) {
         elem = document.getElementById(i);
@@ -200,7 +198,7 @@ const handleSrcHighlight = (function() {
 
 window.addEventListener("hashchange", highlightSrcLines);
 
-onEachLazy(document.getElementsByClassName("src-line-numbers"), el => {
+onEachLazy(document.querySelectorAll("a[data-nosnippet]"), el => {
     el.addEventListener("click", handleSrcHighlight);
 });
 
diff --git a/src/librustdoc/html/static/js/storage.js b/src/librustdoc/html/static/js/storage.js
index 3042373fb09..425b915b5f9 100644
--- a/src/librustdoc/html/static/js/storage.js
+++ b/src/librustdoc/html/static/js/storage.js
@@ -286,6 +286,9 @@ if (getSettingValue("hide-modnav") === "true") {
 if (getSettingValue("sans-serif-fonts") === "true") {
     addClass(document.documentElement, "sans-serif");
 }
+if (getSettingValue("word-wrap-source-code") === "true") {
+    addClass(document.documentElement, "word-wrap-source-code");
+}
 function updateSidebarWidth() {
     const desktopSidebarWidth = getSettingValue("desktop-sidebar-width");
     if (desktopSidebarWidth && desktopSidebarWidth !== "null") {
diff --git a/src/librustdoc/html/templates/scraped_source.html b/src/librustdoc/html/templates/scraped_source.html
index bd54bbf58d5..3e69f1c8cad 100644
--- a/src/librustdoc/html/templates/scraped_source.html
+++ b/src/librustdoc/html/templates/scraped_source.html
@@ -2,17 +2,7 @@
     <div class="scraped-example-title">
        {{info.name +}} (<a href="{{info.url}}">{{info.title}}</a>) {# #}
     </div> {# #}
-    <div class="example-wrap">
-        {# https://developers.google.com/search/docs/crawling-indexing/robots-meta-tag#data-nosnippet-attr
-           Do not show "1 2 3 4 5 ..." in web search results. #}
-        <div class="src-line-numbers" data-nosnippet> {# #}
-            <pre>
-                {% for line in lines.clone() %}
-                    {# ~#}
-                    <span>{{line|safe}}</span>
-                {% endfor %}
-            </pre> {# #}
-        </div> {# #}
+    <div class="example-wrap digits-{{max_nb_digits}}"> {# #}
         <pre class="rust"> {# #}
             <code>
                 {{code_html|safe}}
diff --git a/src/librustdoc/html/templates/source.html b/src/librustdoc/html/templates/source.html
index ea530087e6f..454d4c27f1a 100644
--- a/src/librustdoc/html/templates/source.html
+++ b/src/librustdoc/html/templates/source.html
@@ -9,15 +9,7 @@
 </div>
 {% else %}
 {% endmatch %}
-<div class="example-wrap">
-    {# https://developers.google.com/search/docs/crawling-indexing/robots-meta-tag#data-nosnippet-attr
-       Do not show "1 2 3 4 5 ..." in web search results. #}
-    <div data-nosnippet><pre class="src-line-numbers">
-        {% for line in lines.clone() %}
-            {# ~#}
-            <a href="#{{line|safe}}" id="{{line|safe}}">{{line|safe}}</a>
-        {% endfor %}
-    </pre></div> {# #}
+<div class="example-wrap digits-{{max_nb_digits}}"> {# #}
     <pre class="rust"> {# #}
         <code>
             {{code_html|safe}}
diff --git a/src/librustdoc/json/conversions.rs b/src/librustdoc/json/conversions.rs
index fcee2960979..9606ba76991 100644
--- a/src/librustdoc/json/conversions.rs
+++ b/src/librustdoc/json/conversions.rs
@@ -846,7 +846,7 @@ fn convert_static(
         is_unsafe: safety.is_unsafe(),
         expr: stat
             .expr
-            .map(|e| rendered_const(tcx, tcx.hir().body(e), tcx.hir().body_owner_def_id(e)))
+            .map(|e| rendered_const(tcx, tcx.hir_body(e), tcx.hir_body_owner_def_id(e)))
             .unwrap_or_default(),
     }
 }
diff --git a/src/librustdoc/lib.rs b/src/librustdoc/lib.rs
index 1d5bac3d1bf..1f2f8f7d33a 100644
--- a/src/librustdoc/lib.rs
+++ b/src/librustdoc/lib.rs
@@ -7,12 +7,12 @@
 #![feature(box_patterns)]
 #![feature(debug_closure_helpers)]
 #![feature(file_buffered)]
+#![feature(format_args_nl)]
 #![feature(if_let_guard)]
 #![feature(impl_trait_in_assoc_type)]
 #![feature(iter_intersperse)]
 #![feature(let_chains)]
 #![feature(never_type)]
-#![feature(os_str_display)]
 #![feature(round_char_boundary)]
 #![feature(test)]
 #![feature(type_alias_impl_trait)]
@@ -105,6 +105,7 @@ macro_rules! map {
 mod clean;
 mod config;
 mod core;
+mod display;
 mod docfs;
 mod doctest;
 mod error;
@@ -113,7 +114,6 @@ mod fold;
 mod formats;
 // used by the error-index generator, so it needs to be public
 pub mod html;
-mod joined;
 mod json;
 pub(crate) mod lint;
 mod markdown;
diff --git a/src/librustdoc/passes/calculate_doc_coverage.rs b/src/librustdoc/passes/calculate_doc_coverage.rs
index 135aa799060..45b8dafa907 100644
--- a/src/librustdoc/passes/calculate_doc_coverage.rs
+++ b/src/librustdoc/passes/calculate_doc_coverage.rs
@@ -232,7 +232,7 @@ impl DocVisitor<'_> for CoverageCalculator<'_, '_> {
                     .item_id
                     .as_def_id()
                     .and_then(|def_id| self.ctx.tcx.opt_parent(def_id))
-                    .and_then(|def_id| self.ctx.tcx.hir().get_if_local(def_id))
+                    .and_then(|def_id| self.ctx.tcx.hir_get_if_local(def_id))
                     .map(|node| {
                         matches!(
                             node,
diff --git a/src/librustdoc/passes/collect_intra_doc_links.rs b/src/librustdoc/passes/collect_intra_doc_links.rs
index 9fe8b99e8af..97e6d314642 100644
--- a/src/librustdoc/passes/collect_intra_doc_links.rs
+++ b/src/librustdoc/passes/collect_intra_doc_links.rs
@@ -1983,7 +1983,7 @@ fn resolution_failure(
                                     .tcx
                                     .resolutions(())
                                     .all_macro_rules
-                                    .contains_key(&Symbol::intern(path_str))
+                                    .contains(&Symbol::intern(path_str))
                             {
                                 diag.note(format!(
                                     "`macro_rules` named `{path_str}` exists in this crate, \
diff --git a/src/librustdoc/passes/lint/check_code_block_syntax.rs b/src/librustdoc/passes/lint/check_code_block_syntax.rs
index 459bdd991db..9662dd85d67 100644
--- a/src/librustdoc/passes/lint/check_code_block_syntax.rs
+++ b/src/librustdoc/passes/lint/check_code_block_syntax.rs
@@ -1,5 +1,6 @@
 //! Validates syntax inside Rust code blocks (\`\`\`rust).
 
+use std::borrow::Cow;
 use std::sync::Arc;
 
 use rustc_data_structures::sync::Lock;
@@ -43,7 +44,11 @@ fn check_rust_syntax(
 
     let sm = Arc::new(SourceMap::new(FilePathMapping::empty()));
     let dcx = DiagCtxt::new(Box::new(emitter)).disable_warnings();
-    let source = dox[code_block.code].to_owned();
+    let source = dox[code_block.code]
+        .lines()
+        .map(|line| crate::html::markdown::map_line(line).for_code())
+        .intersperse(Cow::Borrowed("\n"))
+        .collect::<String>();
     let psess = ParseSess::with_dcx(dcx, sm);
 
     let edition = code_block.lang_string.edition.unwrap_or_else(|| cx.tcx.sess.edition());
diff --git a/src/librustdoc/passes/strip_aliased_non_local.rs b/src/librustdoc/passes/strip_aliased_non_local.rs
index fa7737bc143..7f5c7da3634 100644
--- a/src/librustdoc/passes/strip_aliased_non_local.rs
+++ b/src/librustdoc/passes/strip_aliased_non_local.rs
@@ -26,7 +26,7 @@ impl DocFolder for AliasedNonLocalStripper<'_> {
         Some(match i.kind {
             clean::TypeAliasItem(..) => {
                 let mut stripper = NonLocalStripper { tcx: self.tcx };
-                // don't call `fold_item` as that could strip the type-alias it-self
+                // don't call `fold_item` as that could strip the type alias itself
                 // which we don't want to strip out
                 stripper.fold_item_recur(i)
             }
diff --git a/src/librustdoc/scrape_examples.rs b/src/librustdoc/scrape_examples.rs
index 599671bd4d4..44551d75903 100644
--- a/src/librustdoc/scrape_examples.rs
+++ b/src/librustdoc/scrape_examples.rs
@@ -123,8 +123,8 @@ where
 {
     type NestedFilter = nested_filter::OnlyBodies;
 
-    fn nested_visit_map(&mut self) -> Self::Map {
-        self.cx.tcx().hir()
+    fn maybe_tcx(&mut self) -> Self::MaybeTyCtxt {
+        self.cx.tcx()
     }
 
     fn visit_expr(&mut self, ex: &'tcx hir::Expr<'tcx>) {
@@ -135,8 +135,7 @@ where
         // If we visit an item that contains an expression outside a function body,
         // then we need to exit before calling typeck (which will panic). See
         // test/run-make/rustdoc-scrape-examples-invalid-expr for an example.
-        let hir = tcx.hir();
-        if hir.maybe_body_owned_by(ex.hir_id.owner.def_id).is_none() {
+        if tcx.hir_maybe_body_owned_by(ex.hir_id.owner.def_id).is_none() {
             return;
         }
 
@@ -178,7 +177,7 @@ where
         // If the enclosing item has a span coming from a proc macro, then we also don't want to
         // include the example.
         let enclosing_item_span =
-            tcx.hir().span_with_body(tcx.hir().get_parent_item(ex.hir_id).into());
+            tcx.hir().span_with_body(tcx.hir_get_parent_item(ex.hir_id).into());
         if enclosing_item_span.from_expansion() {
             trace!("Rejecting expr ({call_span:?}) from macro item: {enclosing_item_span:?}");
             return;
@@ -302,7 +301,7 @@ pub(crate) fn run(
         // Run call-finder on all items
         let mut calls = FxIndexMap::default();
         let mut finder = FindCalls { calls: &mut calls, cx, target_crates, bin_crate };
-        tcx.hir().visit_all_item_likes_in_crate(&mut finder);
+        tcx.hir_visit_all_item_likes_in_crate(&mut finder);
 
         // The visitor might have found a type error, which we need to
         // promote to a fatal error
diff --git a/src/librustdoc/visit_ast.rs b/src/librustdoc/visit_ast.rs
index e4628e4f837..7b6921afa08 100644
--- a/src/librustdoc/visit_ast.rs
+++ b/src/librustdoc/visit_ast.rs
@@ -96,7 +96,7 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> {
         let om = Module::new(
             cx.tcx.crate_name(LOCAL_CRATE),
             CRATE_DEF_ID,
-            cx.tcx.hir().root_module().spans.inner_span,
+            cx.tcx.hir_root_module().spans.inner_span,
             None,
             None,
         );
@@ -119,7 +119,7 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> {
     }
 
     pub(crate) fn visit(mut self) -> Module<'tcx> {
-        let root_module = self.cx.tcx.hir().root_module();
+        let root_module = self.cx.tcx.hir_root_module();
         self.visit_mod_contents(CRATE_DEF_ID, root_module);
 
         let mut top_level_module = self.modules.pop().unwrap();
@@ -193,13 +193,13 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> {
         // Reimplementation of `walk_mod` because we need to do it in two passes (explanations in
         // the second loop):
         for &i in m.item_ids {
-            let item = self.cx.tcx.hir().item(i);
+            let item = self.cx.tcx.hir_item(i);
             if !matches!(item.kind, hir::ItemKind::Use(_, hir::UseKind::Glob)) {
                 self.visit_item(item);
             }
         }
         for &i in m.item_ids {
-            let item = self.cx.tcx.hir().item(i);
+            let item = self.cx.tcx.hir_item(i);
             // To match the way import precedence works, visit glob imports last.
             // Later passes in rustdoc will de-duplicate by name and kind, so if glob-
             // imported items appear last, then they'll be the ones that get discarded.
@@ -315,7 +315,7 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> {
             Node::Item(&hir::Item { kind: hir::ItemKind::Mod(m), .. }) if glob => {
                 let prev = mem::replace(&mut self.inlining, true);
                 for &i in m.item_ids {
-                    let i = tcx.hir().item(i);
+                    let i = tcx.hir_item(i);
                     self.visit_item_inner(i, None, Some(def_id));
                 }
                 self.inlining = prev;
@@ -433,13 +433,13 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> {
         match item.kind {
             hir::ItemKind::ForeignMod { items, .. } => {
                 for item in items {
-                    let item = tcx.hir().foreign_item(item.id);
+                    let item = tcx.hir_foreign_item(item.id);
                     self.visit_foreign_item_inner(item, None);
                 }
             }
             // If we're inlining, skip private items.
             _ if self.inlining && !is_pub => {}
-            hir::ItemKind::GlobalAsm(..) => {}
+            hir::ItemKind::GlobalAsm { .. } => {}
             hir::ItemKind::Use(_, hir::UseKind::ListStem) => {}
             hir::ItemKind::Use(path, kind) => {
                 for &res in &path.res {
@@ -563,8 +563,8 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> {
 impl<'tcx> Visitor<'tcx> for RustdocVisitor<'_, 'tcx> {
     type NestedFilter = nested_filter::All;
 
-    fn nested_visit_map(&mut self) -> Self::Map {
-        self.cx.tcx.hir()
+    fn maybe_tcx(&mut self) -> Self::MaybeTyCtxt {
+        self.cx.tcx
     }
 
     fn visit_item(&mut self, i: &'tcx hir::Item<'tcx>) {
diff --git a/src/llvm-project b/src/llvm-project
-Subproject 7e8c93c87c611f21d9bd95100563392f4c18bfe
+Subproject 92e80685d0d5dcea3ccf321995c43b72338639c
diff --git a/src/rustdoc-json-types/lib.rs b/src/rustdoc-json-types/lib.rs
index a92f3ded774..8f6496e9626 100644
--- a/src/rustdoc-json-types/lib.rs
+++ b/src/rustdoc-json-types/lib.rs
@@ -30,7 +30,7 @@ pub type FxHashMap<K, V> = HashMap<K, V>; // re-export for use in src/librustdoc
 /// 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 = 39;
+pub const FORMAT_VERSION: u32 = 40;
 
 /// The root of the emitted JSON blob.
 ///
@@ -120,7 +120,9 @@ pub struct Item {
     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: HashMap<String, Id>,
-    /// Stringified versions of the attributes on this item (e.g. `"#[inline]"`)
+    /// Stringified versions of parsed attributes on this item.
+    /// Essentially debug printed (e.g. `#[inline]` becomes something similar to `#[attr="Inline(Hint)"]`).
+    /// Equivalent to the hir pretty-printing of attributes.
     pub attrs: Vec<String>,
     /// Information about the item’s deprecation, if present.
     pub deprecation: Option<Deprecation>,
diff --git a/src/stage0 b/src/stage0
index 5c3e3a7cebc..9d6a08d378d 100644
--- a/src/stage0
+++ b/src/stage0
@@ -13,469 +13,469 @@ nightly_branch=master
 #
 # All changes below this comment will be overridden the next time the
 # tool is executed.
-            
-compiler_date=2025-02-08
+
+compiler_date=2025-02-18
 compiler_version=beta
-rustfmt_date=2025-02-08
+rustfmt_date=2025-02-18
 rustfmt_version=nightly
 
-dist/2025-02-08/rustc-beta-aarch64-apple-darwin.tar.gz=ca5440402ebfd3ba7ce3b30ecbcdc3e9341861996a5dd51601de3131d62d0bc2
-dist/2025-02-08/rustc-beta-aarch64-apple-darwin.tar.xz=bb7c79e5adb6998ac3a57ae7434a247029777bf3e1687c51ff3fdca938b72b3d
-dist/2025-02-08/rustc-beta-aarch64-pc-windows-msvc.tar.gz=596a586332d87beaf3d52fead6e6c286a1da6abc89b53da5f6874f5da134cded
-dist/2025-02-08/rustc-beta-aarch64-pc-windows-msvc.tar.xz=6828f88d3d576cdea838d975e0dc61ae2daf32746d7dbefce07f12684f5c0d97
-dist/2025-02-08/rustc-beta-aarch64-unknown-linux-gnu.tar.gz=86943f595d3ad4b4ee5b7c9db63df9addf9c5e50e25267f64daa504dc4bb63b6
-dist/2025-02-08/rustc-beta-aarch64-unknown-linux-gnu.tar.xz=13449944b13c4a8ef9aae649e1edf0bfbedc9816f7f32445ce9623098a21e51d
-dist/2025-02-08/rustc-beta-aarch64-unknown-linux-musl.tar.gz=ce7fc3bed538a938f11b1cc15da20dc7d68097cf7d68a37ecc969c225bf5dcaf
-dist/2025-02-08/rustc-beta-aarch64-unknown-linux-musl.tar.xz=a3b77720f7ff2a4707ecf44cab11ef088c3903ed6e123de14237b2149858f1a0
-dist/2025-02-08/rustc-beta-arm-unknown-linux-gnueabi.tar.gz=a9f02a5eb65c85f98b73dfd88ef4eb48e6782455b3ccb77032b2df5bbfa39e84
-dist/2025-02-08/rustc-beta-arm-unknown-linux-gnueabi.tar.xz=93c5ad3bcb8473a7ec0dcc81b788436c9881c9d9c276906dc09bada56b45180c
-dist/2025-02-08/rustc-beta-arm-unknown-linux-gnueabihf.tar.gz=90f46141b9590ad69f60ffabdceb262e0940081994bda97a74692b76fd5cf8c0
-dist/2025-02-08/rustc-beta-arm-unknown-linux-gnueabihf.tar.xz=364a0b19dd91fe47c7152bd5a495d5ebf10170e4e5896859b02aef81225fc72e
-dist/2025-02-08/rustc-beta-armv7-unknown-linux-gnueabihf.tar.gz=977901c4eaf17b2acc8f7b7dd4e2efe5c45b45f5763f94b2ca36beadcc1544fc
-dist/2025-02-08/rustc-beta-armv7-unknown-linux-gnueabihf.tar.xz=4f60ec3e8a48c97a98567338be479499dbfb48db328d3d208763a8e6ad36e80d
-dist/2025-02-08/rustc-beta-i686-pc-windows-gnu.tar.gz=ab1f6cf30ac8abf6dd130144355b79510633b24b06b416d82ba1fbccd318c2e5
-dist/2025-02-08/rustc-beta-i686-pc-windows-gnu.tar.xz=68327052940d99080217450553c42f3df81507385b5b90734c9b41a57e601ec6
-dist/2025-02-08/rustc-beta-i686-pc-windows-msvc.tar.gz=479053067ce64241d06a09d88b23cfb3da6dbcff46b5e8e33c2c1797ecdefb19
-dist/2025-02-08/rustc-beta-i686-pc-windows-msvc.tar.xz=12a4286b430fdd49e8806aa98a4395fc2ded6eae5ca254fb2cddc2c6db2cd893
-dist/2025-02-08/rustc-beta-i686-unknown-linux-gnu.tar.gz=865d88d2a9bb177851e901a56c5b3a295aa4ecdec8f73c72d4f9914b2e4aac60
-dist/2025-02-08/rustc-beta-i686-unknown-linux-gnu.tar.xz=3f17f91cd666e59efd5a47c2db28af8eee0d89d4ebbc6263f005e1d8d7d5b839
-dist/2025-02-08/rustc-beta-loongarch64-unknown-linux-gnu.tar.gz=a1c42bc975d9ccd10771aa83874d0ea382780a8a925016d966d50084fb45c267
-dist/2025-02-08/rustc-beta-loongarch64-unknown-linux-gnu.tar.xz=5452b62e9cccb5dac1cd36b01d31b11ac97c85bf4f82a559a15a08c278061d78
-dist/2025-02-08/rustc-beta-loongarch64-unknown-linux-musl.tar.gz=eb9d9ac867835710ef4dd301aed0afbfeb4221c818ef4d7f70f4bb2b5bc02790
-dist/2025-02-08/rustc-beta-loongarch64-unknown-linux-musl.tar.xz=0afbff667ada4671511c48d6c5558009f395f3c2f3ad3d10c716882f6c9860f6
-dist/2025-02-08/rustc-beta-powerpc-unknown-linux-gnu.tar.gz=db77ef515643b4ced71ab9ffce80338fe99b03abd6b7672f627f38e33e6642d9
-dist/2025-02-08/rustc-beta-powerpc-unknown-linux-gnu.tar.xz=cac3b683fb606be111d5788b1a4194ec0ea77e0226350454cb7d1904d6f2f78e
-dist/2025-02-08/rustc-beta-powerpc64-unknown-linux-gnu.tar.gz=a86bb2a0cd6f2c25f6f6c55b070a3f841c0a006588f759d0aa7c8d6b3401d904
-dist/2025-02-08/rustc-beta-powerpc64-unknown-linux-gnu.tar.xz=050e07098291a659baa3fb31e2a83c7e6e203614139dab3f3eb2c486c20b9f8b
-dist/2025-02-08/rustc-beta-powerpc64le-unknown-linux-gnu.tar.gz=c20d1b649bb28b8abba4a5dd8e881a03061ebe091e5cdbc44310706fc76b55b0
-dist/2025-02-08/rustc-beta-powerpc64le-unknown-linux-gnu.tar.xz=24c742968242e5bb3d81b865c0fdfdb021772a40fe4d25f3b83fdcf8ccb087bd
-dist/2025-02-08/rustc-beta-powerpc64le-unknown-linux-musl.tar.gz=2aa9e78148c44ae2c01f554449811993e592aa9c428de37fbdb42dead53eb60c
-dist/2025-02-08/rustc-beta-powerpc64le-unknown-linux-musl.tar.xz=2e6751dacf4ce4a60c61e1fa8afb746ac38285c62e567df78f2de27ce25e5770
-dist/2025-02-08/rustc-beta-riscv64gc-unknown-linux-gnu.tar.gz=d0d365fdba806bda1263767d40365da7b5c2e5d01cfe6e9e4c5a2fd66ad4608a
-dist/2025-02-08/rustc-beta-riscv64gc-unknown-linux-gnu.tar.xz=fef429294c81caa19fc0dac00eda00d55c087e94c7235e032b17c2b4f5556850
-dist/2025-02-08/rustc-beta-s390x-unknown-linux-gnu.tar.gz=75ea2ad9e2377d09c9517a932126f876eaf377ff0f38a382187bb4a8c31be34e
-dist/2025-02-08/rustc-beta-s390x-unknown-linux-gnu.tar.xz=908e6fff603ef6d0721efc24e8faf014f5cafa71fc0220da4dac5c870b974c04
-dist/2025-02-08/rustc-beta-x86_64-apple-darwin.tar.gz=ccea051b3787d3682258edcfa6a35f2d7a44ac5c3d8117fcd55cc0a97e7b9b92
-dist/2025-02-08/rustc-beta-x86_64-apple-darwin.tar.xz=3555ff1e179da6a249861086c8986039ab7dcdcf581beb1f27246f987da436f6
-dist/2025-02-08/rustc-beta-x86_64-pc-windows-gnu.tar.gz=227da932cd028d2c2415f4df4c14daf225e2a917a0879e1debcee2b8ebbfc790
-dist/2025-02-08/rustc-beta-x86_64-pc-windows-gnu.tar.xz=231c0fb2989746424f8ad394e9015ef13a6b991a50f62009e7bca3e010e9320e
-dist/2025-02-08/rustc-beta-x86_64-pc-windows-msvc.tar.gz=b75be89ce511758f7b9ab6b5701155349e5675e4b85975c33a91483d78666927
-dist/2025-02-08/rustc-beta-x86_64-pc-windows-msvc.tar.xz=bf529ca4409fefe3d38e2b0ee6b3102e9f8b7ace4352bea7e6d3148a84e98767
-dist/2025-02-08/rustc-beta-x86_64-unknown-freebsd.tar.gz=e2dcaf33887031f8d169ab734f3b3eb6858e5247097e46293c11ba1a2887a919
-dist/2025-02-08/rustc-beta-x86_64-unknown-freebsd.tar.xz=2cf5c49c04e57bcc34ada76ff22dd149c4d643b5af5766267663f2e0e4031d17
-dist/2025-02-08/rustc-beta-x86_64-unknown-illumos.tar.gz=c7572379985941a513b7b9e750043a74097655600cb34cb0460a34a5587b7e0d
-dist/2025-02-08/rustc-beta-x86_64-unknown-illumos.tar.xz=4e98bbcbae02618c3f95a21fdb22b4e067ce0bacd8370076a1bf8107964d2896
-dist/2025-02-08/rustc-beta-x86_64-unknown-linux-gnu.tar.gz=bcc22d77b53b54bcb95a5b92b19af7cf5d3e4b4418c3c9ed1d5ab9db3cd6c3a0
-dist/2025-02-08/rustc-beta-x86_64-unknown-linux-gnu.tar.xz=8f3eb1b74e06bbdbf1d09e5df205677db6f6ff78e94f80fc1fd1441deed86f80
-dist/2025-02-08/rustc-beta-x86_64-unknown-linux-musl.tar.gz=ccd5ba2c7c7d8c8eeaae2f7c7b462945d7c7ac552d6175dd33c0b77ee3d6730b
-dist/2025-02-08/rustc-beta-x86_64-unknown-linux-musl.tar.xz=ab198284a035b8b5ef584a925b5f7ef08228d1eeb43b17a33dab8c7ae93cbc96
-dist/2025-02-08/rustc-beta-x86_64-unknown-netbsd.tar.gz=5b52a1e3f591e6f65ac50d885cfa38e2bb655abc3db7f09ea860450b4b95d6e0
-dist/2025-02-08/rustc-beta-x86_64-unknown-netbsd.tar.xz=fad94b63a75e789df1e5d25e0bf44d1fc29146dea862006d5b1ffb5c20fdfc8d
-dist/2025-02-08/rust-std-beta-aarch64-apple-darwin.tar.gz=19c355276bb12522f3c8e94e843e6e54434c66c4e2e49bf68e084d43e1c83350
-dist/2025-02-08/rust-std-beta-aarch64-apple-darwin.tar.xz=c05a1e3e86e2846c4322df02f9b02a1bf16edbf39d3394da56e1a8b3b36d9a60
-dist/2025-02-08/rust-std-beta-aarch64-apple-ios.tar.gz=ade5b194bd6b7737f505d201f6560ec3aeb41345ea692b2d262317fe0a3bfc38
-dist/2025-02-08/rust-std-beta-aarch64-apple-ios.tar.xz=026a233fab1b27e70dd1455a9a9a0f097d0617b720f82549a413bc4b2d9a2755
-dist/2025-02-08/rust-std-beta-aarch64-apple-ios-macabi.tar.gz=fe743c3895565a7e641b8727a0e96172e0a735bb77f9ced18e201f54d5bab19e
-dist/2025-02-08/rust-std-beta-aarch64-apple-ios-macabi.tar.xz=66fd5b769ae7f0e4dd587fdf81ac31cb780cd20eadd082ef0252d7346354bfdb
-dist/2025-02-08/rust-std-beta-aarch64-apple-ios-sim.tar.gz=ccc55643bde9f89be185b560e44d379ef85a0cc7f087f2f2bf7738d523b7a32d
-dist/2025-02-08/rust-std-beta-aarch64-apple-ios-sim.tar.xz=b1d4c3de214fa207e987742ab73d316537ad9856eb3499d046f6aea4e9d3bc0c
-dist/2025-02-08/rust-std-beta-aarch64-linux-android.tar.gz=b17466c9da602c1c282826bdd878e6ba4592906f74b0e8bbe3df302c2358ede4
-dist/2025-02-08/rust-std-beta-aarch64-linux-android.tar.xz=4addf9a4ed3ae4c80b097027fef7237261d2e9ada641e549cc91595c83614d6e
-dist/2025-02-08/rust-std-beta-aarch64-pc-windows-gnullvm.tar.gz=391f0b460feb164d29ed222b322380586743b19ab03c217690e8053e22903d3e
-dist/2025-02-08/rust-std-beta-aarch64-pc-windows-gnullvm.tar.xz=d81567dc4e204f7080854d0a3a7e7e16424758ff122dfc14448fa9da630932aa
-dist/2025-02-08/rust-std-beta-aarch64-pc-windows-msvc.tar.gz=91ff95f6a56ee5db99e72b293ea2048be0fd5ac3788bd32d9fc97337e7ee8b68
-dist/2025-02-08/rust-std-beta-aarch64-pc-windows-msvc.tar.xz=d6eb64cdaa12fcc469687f99d03a30329847e89bf802361bb6acd61f65b0547e
-dist/2025-02-08/rust-std-beta-aarch64-unknown-fuchsia.tar.gz=c9f43315d352ecddbc18a9034723c58d5d8ec77fffa9e0e82322743d162e3db7
-dist/2025-02-08/rust-std-beta-aarch64-unknown-fuchsia.tar.xz=2356a2902f91323d1492ef12fa2f17d1e4934748cec03d6621d6ecfe272f1c3e
-dist/2025-02-08/rust-std-beta-aarch64-unknown-linux-gnu.tar.gz=ccc8c1777dc8d2e04d4d0640ece7b7820a8fea60a0fe371efcaa055972dcd7b3
-dist/2025-02-08/rust-std-beta-aarch64-unknown-linux-gnu.tar.xz=4208ef48af14c5feee97c16fe7e894777a479492760dc0eb838febf2eb92ece7
-dist/2025-02-08/rust-std-beta-aarch64-unknown-linux-musl.tar.gz=b67e319c5e447e9ebf4b9a7e51800e7d7aec3564f0829fa23df953d8ee714131
-dist/2025-02-08/rust-std-beta-aarch64-unknown-linux-musl.tar.xz=432ea33a49f0010125c960d7b93b585e7b4c91b34f9d07989cf1664bf7e71fe5
-dist/2025-02-08/rust-std-beta-aarch64-unknown-linux-ohos.tar.gz=53bbbc52c4f99733c7b4a1038fa014867c0d6ca8f1d7eccba651fab4f794247e
-dist/2025-02-08/rust-std-beta-aarch64-unknown-linux-ohos.tar.xz=6df101f4d5416cba695be66b975a4e4dcab2a3041d079f0a454f342a6d24d7db
-dist/2025-02-08/rust-std-beta-aarch64-unknown-none.tar.gz=f1ef3141ac9c634e7e6686cf3c13b83323eb168a249f596b1490f874372c1423
-dist/2025-02-08/rust-std-beta-aarch64-unknown-none.tar.xz=a709c52f479c73ed15709b3cb0b8e7641606cf86375a4c9a60b1c9d381c70183
-dist/2025-02-08/rust-std-beta-aarch64-unknown-none-softfloat.tar.gz=31b8fec1cd20dd1809bdf733209288d60de0f096fbd0cc2955982ce119703772
-dist/2025-02-08/rust-std-beta-aarch64-unknown-none-softfloat.tar.xz=afb3872ffa8ab3c79d08f713aeb76c658ea00a3b9d79d5516d89aa78ec1eab8e
-dist/2025-02-08/rust-std-beta-aarch64-unknown-uefi.tar.gz=d73b90fa9d3a330274b2004ec535d396f90a82ac5b10fb8688f82472bd2c5ac1
-dist/2025-02-08/rust-std-beta-aarch64-unknown-uefi.tar.xz=ae05fa6bf0638b0e26fcb8bcac5973835aa965a56507588fa3d43981f20eee89
-dist/2025-02-08/rust-std-beta-arm-linux-androideabi.tar.gz=c18354734b678f0665dd60bb8765df00bca5d3fb7bb6e6a4b83e3cde971c26cf
-dist/2025-02-08/rust-std-beta-arm-linux-androideabi.tar.xz=5f6bc44bbe3b24140fecad715cc5528249906bacf98fc3a0618a9c0cc92913e0
-dist/2025-02-08/rust-std-beta-arm-unknown-linux-gnueabi.tar.gz=d9a2ab83df4240431479fe27600ab1098333699e14103b8a627dc09a3dad6976
-dist/2025-02-08/rust-std-beta-arm-unknown-linux-gnueabi.tar.xz=4f95a441ff45c16fb8aa37432cffeef60f2e0711c9cb1015cf440f4da51d5425
-dist/2025-02-08/rust-std-beta-arm-unknown-linux-gnueabihf.tar.gz=dacdeceea6625041f75c3f2fad4382d2c957b9511666987695fed8e3a2216c88
-dist/2025-02-08/rust-std-beta-arm-unknown-linux-gnueabihf.tar.xz=e338c1d2cef57e4c4ef613e6b080371751b874f94a246ab32e872a008e9166ab
-dist/2025-02-08/rust-std-beta-arm-unknown-linux-musleabi.tar.gz=8bd4174934e5d0e4dd8e757db494a238c95024be5336e6062923b479c0275b6a
-dist/2025-02-08/rust-std-beta-arm-unknown-linux-musleabi.tar.xz=1637b8d23bc8052803d785f15031a75594860f2263808c9dee68dead29bcd37e
-dist/2025-02-08/rust-std-beta-arm-unknown-linux-musleabihf.tar.gz=db6eb6a2af3395e1eee0060d0c022c74c0741c765009f081c0646d696bd0b873
-dist/2025-02-08/rust-std-beta-arm-unknown-linux-musleabihf.tar.xz=ca21a4af4de66abf9d31ae6b3fbc33df152656feef7a269bf7d039d2766176ee
-dist/2025-02-08/rust-std-beta-arm64ec-pc-windows-msvc.tar.gz=118c52dfa7142c0ee281b126da5b043b2357b78e7e110000f490b20e6a1e7eed
-dist/2025-02-08/rust-std-beta-arm64ec-pc-windows-msvc.tar.xz=558fbb7453a2e633630ed33ad150dce87fa515b5b443ae66c5f85ee27e0e771f
-dist/2025-02-08/rust-std-beta-armebv7r-none-eabi.tar.gz=96ff6441b0577b25b83540dc803d692d3444bcd0b4f6128a7743970726facce9
-dist/2025-02-08/rust-std-beta-armebv7r-none-eabi.tar.xz=2621a9f82f73a3a2a2a0c6a7446549c5ad29a430a3e0b961909fe68da21b8491
-dist/2025-02-08/rust-std-beta-armebv7r-none-eabihf.tar.gz=0ef78d66dea96d1a6565fb3469bdcb3368f9e61a55a59a5d2508c54c091484e9
-dist/2025-02-08/rust-std-beta-armebv7r-none-eabihf.tar.xz=1c586301ae2a20ed1caaf0d62d985f9fd95bf2b937ebe7b6d738ca5812713722
-dist/2025-02-08/rust-std-beta-armv5te-unknown-linux-gnueabi.tar.gz=aaeb690b0a08e8f6546ac5608fade5852bb029db9189d6d036c69c4895c6ce32
-dist/2025-02-08/rust-std-beta-armv5te-unknown-linux-gnueabi.tar.xz=1d6ae672d5ebcd302a654c4347a386fabc3bf53aec9664cdf76b9c5a061d4eb1
-dist/2025-02-08/rust-std-beta-armv5te-unknown-linux-musleabi.tar.gz=5557c7444e72eb29c904436f0221cb40c3156c5289c39b7e8c1a057dfd56c394
-dist/2025-02-08/rust-std-beta-armv5te-unknown-linux-musleabi.tar.xz=ae9abe3580b1751854305200bd6a5dbf98aeef396e163534ac5521d04d7bfa7c
-dist/2025-02-08/rust-std-beta-armv7-linux-androideabi.tar.gz=c466403dfb8e15cd674859fad24e2e0dd1ba100946c662a9bf29f2440f02a938
-dist/2025-02-08/rust-std-beta-armv7-linux-androideabi.tar.xz=83ad7b8237ae7ab0ddd6f43b604b27677ec9e83453f3127c356936af12140ee2
-dist/2025-02-08/rust-std-beta-armv7-unknown-linux-gnueabi.tar.gz=12489ebd742dd611937c2942c6f897608d1c4acb0fcb103a2a18f70b9b2d0596
-dist/2025-02-08/rust-std-beta-armv7-unknown-linux-gnueabi.tar.xz=54231b6c641736b29317682be9d1de9463e23478a892c16e083bc1bf20c9c925
-dist/2025-02-08/rust-std-beta-armv7-unknown-linux-gnueabihf.tar.gz=e41b6582cc6eb2340208a3473d54d441e13fab87bfee3091a513da4039c224c6
-dist/2025-02-08/rust-std-beta-armv7-unknown-linux-gnueabihf.tar.xz=9ca2df0ba2afd630a94a3bac08f8c412fdb6f905333c8978ac9b6bb5be777774
-dist/2025-02-08/rust-std-beta-armv7-unknown-linux-musleabi.tar.gz=dd9f6f6d930d18dc5e1764691acd2e33cdd032ef757d534ea70202fcdc18e461
-dist/2025-02-08/rust-std-beta-armv7-unknown-linux-musleabi.tar.xz=9d0e4864ecb7e33b231dedd10b54ad7707d79c61ff455256fd693a46a4ecddb5
-dist/2025-02-08/rust-std-beta-armv7-unknown-linux-musleabihf.tar.gz=b508af76bcabed45442fc1587ea1c74bbbf85d75fbd2fb64d7078cd799485ae6
-dist/2025-02-08/rust-std-beta-armv7-unknown-linux-musleabihf.tar.xz=3f9916a37ac0b79586756f09986516ba08e1c24b4d4675bd17d66058e42494cd
-dist/2025-02-08/rust-std-beta-armv7-unknown-linux-ohos.tar.gz=a80bdd0178f0b9c98c2fe80e59a1b936115a509d9b1be0202bea09d275b4c5d0
-dist/2025-02-08/rust-std-beta-armv7-unknown-linux-ohos.tar.xz=6cb0340d4633e52f7a6f3419a0f18a9d0b75f604fbfe99ce5886186f81204264
-dist/2025-02-08/rust-std-beta-armv7a-none-eabi.tar.gz=f9ac794582c73c7e0a36990daa6c2e2d637213dc16a0063aed330bf68cf04d4d
-dist/2025-02-08/rust-std-beta-armv7a-none-eabi.tar.xz=7463c6f6e9ba1bc056ab528e00017db0d6ac2b0e06812f0b1d7b569a8a7e51ab
-dist/2025-02-08/rust-std-beta-armv7r-none-eabi.tar.gz=c676cfbcaae8c7686a231693b923f98e23ed3dfa174ecf42aab57d94e2bddd85
-dist/2025-02-08/rust-std-beta-armv7r-none-eabi.tar.xz=93b2802c8f555fdf03cd309539eabfa2f8e434114f8f750b89ec3005249e9b91
-dist/2025-02-08/rust-std-beta-armv7r-none-eabihf.tar.gz=077cf1bbe4fdb0c011bd625a59ebc4b3d2876a50b544b76934523504c59042b9
-dist/2025-02-08/rust-std-beta-armv7r-none-eabihf.tar.xz=3be6d9e04ad155e78be5ecce391b57b285c7675cd9a4f86e2ddb388c934c21f8
-dist/2025-02-08/rust-std-beta-i586-pc-windows-msvc.tar.gz=2e705d5660544cec738cda300b059dcc1d730f7ff3cb029980a1ac9c1f9e6831
-dist/2025-02-08/rust-std-beta-i586-pc-windows-msvc.tar.xz=4cc0760e20f53648ae5b171c6948af3950cda4c468f2bb437016d92a2eeb2465
-dist/2025-02-08/rust-std-beta-i586-unknown-linux-gnu.tar.gz=3a516cf5c265899c9a548f6b9305b78069cc03864dcf1b3fa0e247adfa978cb8
-dist/2025-02-08/rust-std-beta-i586-unknown-linux-gnu.tar.xz=25144f4fb500ab9ba438042dd24796b25660fc3d16d3e04b64561bd35f84e46b
-dist/2025-02-08/rust-std-beta-i586-unknown-linux-musl.tar.gz=70bf9429de2e11fe9e14216d3c1cc61a3923e1a7c7bc17d5b0d63785716a9e26
-dist/2025-02-08/rust-std-beta-i586-unknown-linux-musl.tar.xz=35cfef70aa5c59ecbe0d563959c62ec0162a8943d32cdc45dd0207b6ad3d16eb
-dist/2025-02-08/rust-std-beta-i686-linux-android.tar.gz=a17f82a5f41e38c5c9ccaf1b2cf5182ed2e3c459e87c841497f0594cda7f71bf
-dist/2025-02-08/rust-std-beta-i686-linux-android.tar.xz=06565daae363ed88f579f261a89f75d126785c894e439976ae47870cedd56378
-dist/2025-02-08/rust-std-beta-i686-pc-windows-gnu.tar.gz=986595d50c1c5799f67bd04575b248c5cd81dc03f318bd137b46248f75b2727f
-dist/2025-02-08/rust-std-beta-i686-pc-windows-gnu.tar.xz=9daf96aa939ec6ed4ac8ff6119da06fca827a7f160e5c7db658653d5839846af
-dist/2025-02-08/rust-std-beta-i686-pc-windows-gnullvm.tar.gz=f793d7a5ce20392287a7d178a2403d1d72dec2437401c447a7c5e3168c25bcf6
-dist/2025-02-08/rust-std-beta-i686-pc-windows-gnullvm.tar.xz=039a361f65fd610b14142f0d654895f4ac271cfb03918f7fcd8b2e9c5ffc3c98
-dist/2025-02-08/rust-std-beta-i686-pc-windows-msvc.tar.gz=6cdd125eafa981da12bff2a7e519866cfe40b7d6d7b6debabbd2415759fd9dc8
-dist/2025-02-08/rust-std-beta-i686-pc-windows-msvc.tar.xz=7f1a722bc007efe77dbd099ff36f2c682ec4bcdb7e8b60d6ad868e9d08a9a367
-dist/2025-02-08/rust-std-beta-i686-unknown-freebsd.tar.gz=b7dc78d6d4e6ce343ce90a7153082575a991eebd8acbc3cdfbcb56d1cc281b83
-dist/2025-02-08/rust-std-beta-i686-unknown-freebsd.tar.xz=91d19c3086de738b3d11a2bd0f0ad31c9f6a9c39ef800fdb2040638fc91c69ac
-dist/2025-02-08/rust-std-beta-i686-unknown-linux-gnu.tar.gz=fdd0f12816d4bfc15c441cd26c7ef0f9ac05f19b03f471f162e8781f04bfc392
-dist/2025-02-08/rust-std-beta-i686-unknown-linux-gnu.tar.xz=a1ba9ce61110a1a64cba2bad7274865979d8d10cf30dba44137cd0acd4b49bf1
-dist/2025-02-08/rust-std-beta-i686-unknown-linux-musl.tar.gz=3774b47f5d6748c32d634f0414391121f2a68d7e3e15162cce62ca42bfce1db3
-dist/2025-02-08/rust-std-beta-i686-unknown-linux-musl.tar.xz=fb0203eb986554773288b732feb65a76aa7e4510b30acf60a9a879614332f0e1
-dist/2025-02-08/rust-std-beta-i686-unknown-uefi.tar.gz=a388f43900f4ac040e2263f06e89ef92c3d630f2631b37147eb1234e3ff5555b
-dist/2025-02-08/rust-std-beta-i686-unknown-uefi.tar.xz=5fcc43194705468d31fc07ea2cca780fef717adc2d46bd9508d006adcd237c23
-dist/2025-02-08/rust-std-beta-loongarch64-unknown-linux-gnu.tar.gz=c1d25f88813fb558bf47478a7e2a43e735f2e7ceba87f3ab8e658200825ac6a5
-dist/2025-02-08/rust-std-beta-loongarch64-unknown-linux-gnu.tar.xz=66ae16763fba1c9e54519a34b70811f4b54c2a3985bd0faab4ae1a454f837556
-dist/2025-02-08/rust-std-beta-loongarch64-unknown-linux-musl.tar.gz=0cf97c80c7dc0e760d9aa2f6a6d6b82ecceed06b0c0e7f56b2f3fee98f558d3e
-dist/2025-02-08/rust-std-beta-loongarch64-unknown-linux-musl.tar.xz=7b8284890d5c90e9a3d2cee73ee6b77ed0f023321a547a9add7d25e64886c9b9
-dist/2025-02-08/rust-std-beta-loongarch64-unknown-none.tar.gz=67f97000b8786928eb7ff7f060fdaa2fa9e3805c23e5f3fc24350c6ab0954c17
-dist/2025-02-08/rust-std-beta-loongarch64-unknown-none.tar.xz=5660100854fbe9fd87c0854b560a91dd5b653f07a0c374ba6349c44ff54b3b76
-dist/2025-02-08/rust-std-beta-loongarch64-unknown-none-softfloat.tar.gz=a885c29e4eea19ada52cc22bfe395eb0f2e884932885c3978187a44405d5873a
-dist/2025-02-08/rust-std-beta-loongarch64-unknown-none-softfloat.tar.xz=acbfef3c94943ebc1a44e662a5eeffea6c3c0f8b2518568b03d2e1c5baf323ea
-dist/2025-02-08/rust-std-beta-nvptx64-nvidia-cuda.tar.gz=2bb3f9493afdd89c52bd62e2597c8687335e0d4fbe5a486782335a614881a516
-dist/2025-02-08/rust-std-beta-nvptx64-nvidia-cuda.tar.xz=0702664eceb39c21390e3ee8dbfc0a7a181877449642b1b47dc74e1f463d8ebc
-dist/2025-02-08/rust-std-beta-powerpc-unknown-linux-gnu.tar.gz=788b46512726f8350f8266f4dc7e3bd4108ea96b2c257876d42d6c62cef71507
-dist/2025-02-08/rust-std-beta-powerpc-unknown-linux-gnu.tar.xz=a746eeeb6c41696c95144068e348237bc7a3b254b1c3db626797dd2947194999
-dist/2025-02-08/rust-std-beta-powerpc64-unknown-linux-gnu.tar.gz=48f11a2d3aee9b3cb8d9e7f6480c8859427f446ebde617e2575a17dda6605fc7
-dist/2025-02-08/rust-std-beta-powerpc64-unknown-linux-gnu.tar.xz=5a3933b2465cfe93daab1b5d339a0da17bc364d5621d85e192283250b337e38f
-dist/2025-02-08/rust-std-beta-powerpc64le-unknown-linux-gnu.tar.gz=50bddd9780ebcb5bc081b41c071c6059618881e1cc384f30787caa6fd2f28127
-dist/2025-02-08/rust-std-beta-powerpc64le-unknown-linux-gnu.tar.xz=4630d67082a6c2211a8c59a6da45d260616bd026d7cf7d2bbee5ae6d24b218c1
-dist/2025-02-08/rust-std-beta-powerpc64le-unknown-linux-musl.tar.gz=4a0bcf103df6febd7b3e04bb24d54b30b6a2f4ffaf150f6774811c149f450588
-dist/2025-02-08/rust-std-beta-powerpc64le-unknown-linux-musl.tar.xz=6a37963463641ddc14e03c0f9d8b037cd62afe3af89aeaa73f1f1d3aea1a6f93
-dist/2025-02-08/rust-std-beta-riscv32i-unknown-none-elf.tar.gz=7e2c0f7ea78086cc2b8674aacf350b6de737ca68a51778a8a66c75932161a8a8
-dist/2025-02-08/rust-std-beta-riscv32i-unknown-none-elf.tar.xz=a575b31731a910d2db684a931ced914b4286b942ce878fde94f08bafae92903c
-dist/2025-02-08/rust-std-beta-riscv32im-unknown-none-elf.tar.gz=096407f438062be73bdd4f2efb6e06ddcb6c6c8e08b3c2db76388a4f4c4a731f
-dist/2025-02-08/rust-std-beta-riscv32im-unknown-none-elf.tar.xz=38b771d01c0a56330d9547692c424e92b243d3f2448c13b29873cb2e4fe70b17
-dist/2025-02-08/rust-std-beta-riscv32imac-unknown-none-elf.tar.gz=218d5db0b8427fcc0a81612134d830e6807db8d66eaa4365c4b101f29ab75ae2
-dist/2025-02-08/rust-std-beta-riscv32imac-unknown-none-elf.tar.xz=1227b4dbfe7f3a0c7715904d3ed1e9f9846c49e522bb579d7dd76de370ab83a2
-dist/2025-02-08/rust-std-beta-riscv32imafc-unknown-none-elf.tar.gz=8e5ad6f6116b0fef61650f2a3788cc9a9e6da1cd76dd0dc300d81060ef42ee6b
-dist/2025-02-08/rust-std-beta-riscv32imafc-unknown-none-elf.tar.xz=777cc9ec22cb9b92ea3569d6835770f45ea7793397be670edeafa65660d411eb
-dist/2025-02-08/rust-std-beta-riscv32imc-unknown-none-elf.tar.gz=16d6414fcbfd0356206cccb10f0d5ec1391300c8bd92029d9457fc3030cf3cff
-dist/2025-02-08/rust-std-beta-riscv32imc-unknown-none-elf.tar.xz=bd4ffe303e7b6a9031eae2dca7b4af4c7095e06961a55a6eff01db57178c1635
-dist/2025-02-08/rust-std-beta-riscv64gc-unknown-linux-gnu.tar.gz=facbe91da7b765e03b058682a1353153b89632f5f60692b25077ba4152105faf
-dist/2025-02-08/rust-std-beta-riscv64gc-unknown-linux-gnu.tar.xz=e130dbd1aa2f19c308ca3e72e0456769ab0898a9bacdc667261396d786a22fca
-dist/2025-02-08/rust-std-beta-riscv64gc-unknown-linux-musl.tar.gz=3849880a18e3176c4acb895dcee7f5befa64ad3413ac8476325d97a9a2bbc542
-dist/2025-02-08/rust-std-beta-riscv64gc-unknown-linux-musl.tar.xz=8a14b3cb0c9e20ec84ab686e6eca5acd201e71364ab5007c96b884feb4b59340
-dist/2025-02-08/rust-std-beta-riscv64gc-unknown-none-elf.tar.gz=f5f58c50d793cb26103f812bb32b2d3623c57319a141e151201764f20032262b
-dist/2025-02-08/rust-std-beta-riscv64gc-unknown-none-elf.tar.xz=db7ea697f41b8dc2610c6d71ded8dc0b80ec848c002cb6af8faa3105023ec424
-dist/2025-02-08/rust-std-beta-riscv64imac-unknown-none-elf.tar.gz=b081a9b131703cbc11658eb84e54e495fca021bdc6487d902c7aeb8126629437
-dist/2025-02-08/rust-std-beta-riscv64imac-unknown-none-elf.tar.xz=2a3327ffdd3bfb9b4ce6d1aa13202958587d0819dc49ce08e635970d8e608b6c
-dist/2025-02-08/rust-std-beta-s390x-unknown-linux-gnu.tar.gz=3b0abfeb0a6bda721e1faad0fe4ed2db072ae04beaef53d895f3902a26387522
-dist/2025-02-08/rust-std-beta-s390x-unknown-linux-gnu.tar.xz=5d6314a99746c5fd7f1d0eb0ac426848b4baafb9e2f4e08b7ce361091909a485
-dist/2025-02-08/rust-std-beta-sparc64-unknown-linux-gnu.tar.gz=01f85bdc840f6d354fbc765b2574cbf039a97e7a8cd4345f31d4a93f41ea2936
-dist/2025-02-08/rust-std-beta-sparc64-unknown-linux-gnu.tar.xz=350aa8e77ce705d04d996ae3911121c14213fed5db6d179ae4efeb54e64d5554
-dist/2025-02-08/rust-std-beta-sparcv9-sun-solaris.tar.gz=4cdb48054bcd8c596657c466145e1ec9d9a513d6019694895b7b1218183d7ed1
-dist/2025-02-08/rust-std-beta-sparcv9-sun-solaris.tar.xz=abda0bf8356c63fcf50fafb196c86a6c2275f3155362fe6b9eb63c8a145e07dc
-dist/2025-02-08/rust-std-beta-thumbv6m-none-eabi.tar.gz=87ce4c701797be511509773e6121d69470d604f143c4e46e18866105193d503e
-dist/2025-02-08/rust-std-beta-thumbv6m-none-eabi.tar.xz=d0af4c523a37df6ef2519d2abd680e2abc21e8806904cbbfa2bad3ddebc55252
-dist/2025-02-08/rust-std-beta-thumbv7em-none-eabi.tar.gz=9c183442ae8a7bf18303331e23c2263d180157a4fa9d000735b57f54f894fbf3
-dist/2025-02-08/rust-std-beta-thumbv7em-none-eabi.tar.xz=c9278b82123969a4f9c8536f6d27ceb6b6cae182335cee4cfc1ba8f297d503c7
-dist/2025-02-08/rust-std-beta-thumbv7em-none-eabihf.tar.gz=cda7f615c006369dea46c6d00660d596459d2ca809b27802c15a9e71e741f806
-dist/2025-02-08/rust-std-beta-thumbv7em-none-eabihf.tar.xz=7b2bf45033d1849ced101c696641a3e0037dc65029bea06fcedf3b11af1b21b8
-dist/2025-02-08/rust-std-beta-thumbv7m-none-eabi.tar.gz=bbe3db6ddb17830708086940294a8931054c09d781968f69c810a5a17cb4299d
-dist/2025-02-08/rust-std-beta-thumbv7m-none-eabi.tar.xz=4cf28e2656f8927cbb75c007aded9d0fcac8d8d34841e9ac5486ba8ad3a75a58
-dist/2025-02-08/rust-std-beta-thumbv7neon-linux-androideabi.tar.gz=6021afefa8b06c99e09e63c07a8d4580ef7a6d9e004205d85f8fea47fb7c77d7
-dist/2025-02-08/rust-std-beta-thumbv7neon-linux-androideabi.tar.xz=7fb5c89a7605b0b377fd3e11d3ac5e33dc59667557b690b02df5c852109cefe8
-dist/2025-02-08/rust-std-beta-thumbv7neon-unknown-linux-gnueabihf.tar.gz=f1cbb573556272077f7a119e019ee4c1f8c533cfcbdb2b12c858c7d72c05212a
-dist/2025-02-08/rust-std-beta-thumbv7neon-unknown-linux-gnueabihf.tar.xz=2d7c0f7ece15f6ad5de053a4b0292113efd8b45457a7ea4ac4a84534985a6e7c
-dist/2025-02-08/rust-std-beta-thumbv8m.base-none-eabi.tar.gz=fb5c1d99eeedb48edac78bce237351bb2a04b3ce1e028b47c488a28a0f5b258d
-dist/2025-02-08/rust-std-beta-thumbv8m.base-none-eabi.tar.xz=301ced2f4ca8766183cfbd84b94ca09f21c9c2fee62b3e1afca440f5872ec6f0
-dist/2025-02-08/rust-std-beta-thumbv8m.main-none-eabi.tar.gz=c720cd1896d201ecee177381386cf37ff58a0ad2e153efa8752dc11358489774
-dist/2025-02-08/rust-std-beta-thumbv8m.main-none-eabi.tar.xz=f2e14c2a08777200dcec2c18677a1bd68c57a7caaed8751e17d4a82af0f61e9c
-dist/2025-02-08/rust-std-beta-thumbv8m.main-none-eabihf.tar.gz=146bfeecf574cab3bc15396c2523f27cc463ba9030d9a8c9a07118781a78227c
-dist/2025-02-08/rust-std-beta-thumbv8m.main-none-eabihf.tar.xz=093364007abdcb942a1d095bba6b3f6a63cb891e3897764923828db5aa44f970
-dist/2025-02-08/rust-std-beta-wasm32-unknown-emscripten.tar.gz=f2e4ef8637a02510dc3edf78f7117dc16671946e1eebf3ee8911160bde53f3a2
-dist/2025-02-08/rust-std-beta-wasm32-unknown-emscripten.tar.xz=d30b8b9a7607de4cee24b144452439726eb8b4a436f53c058c2b0c21b686a391
-dist/2025-02-08/rust-std-beta-wasm32-unknown-unknown.tar.gz=242403cc6e3df3d47c566c63fccfb40e6a95d2f2295630bb13f4875c9e28ae62
-dist/2025-02-08/rust-std-beta-wasm32-unknown-unknown.tar.xz=8ba08c010e49593d417feb05f3efe407200421bc26d40b51a8d92ae6cbc46200
-dist/2025-02-08/rust-std-beta-wasm32-wasip1.tar.gz=25cdad2de0d599c5e90373765acba871e2facc7656ea1362b3e0d1132fe8805b
-dist/2025-02-08/rust-std-beta-wasm32-wasip1.tar.xz=0eb3ae579124a47f7e8b63e6933e703d00c535f8369c614678e35c431a5480ff
-dist/2025-02-08/rust-std-beta-wasm32-wasip1-threads.tar.gz=08d400d8ad036c2b240f4044adc6cf484cf996b5f1cca5e9c0fe81814a71635e
-dist/2025-02-08/rust-std-beta-wasm32-wasip1-threads.tar.xz=46aeb5066e5fe4bc613a704c760292a85d2cd5ae10d135412adf5cb42de24015
-dist/2025-02-08/rust-std-beta-wasm32-wasip2.tar.gz=9df47e7c8280b8780ca41ed3dac9326375c76955560491ade1c2fdc8682b70d4
-dist/2025-02-08/rust-std-beta-wasm32-wasip2.tar.xz=275cc332f6d29c685ecfc308e143edccf58fce0359f632b038733a7759ae7389
-dist/2025-02-08/rust-std-beta-wasm32v1-none.tar.gz=96ee2faa07796474ea348b83b626cfa0bd07d108ffd05aed8051b63960315ee5
-dist/2025-02-08/rust-std-beta-wasm32v1-none.tar.xz=965650429f8129c65a6180025fdb69c0890f5c6f9c1fc16566c08b055e4ed93c
-dist/2025-02-08/rust-std-beta-x86_64-apple-darwin.tar.gz=92ebd93de4f4da8267e13824db8455dbcb7bb95ffda8ae480c648c2eb8f820da
-dist/2025-02-08/rust-std-beta-x86_64-apple-darwin.tar.xz=00e4b5a44f69a2c9235e522eee91e972624c75e3ce773928b09e9ee20d2d51ba
-dist/2025-02-08/rust-std-beta-x86_64-apple-ios.tar.gz=dfb3d88798ff9b490c1a517d57e459a77cf2bb2e8b427493a3a612405534ebb3
-dist/2025-02-08/rust-std-beta-x86_64-apple-ios.tar.xz=7091bae8ccf328336261c2b096d1565c0dc9dca02b03d4e228a7c3f4c412a6df
-dist/2025-02-08/rust-std-beta-x86_64-apple-ios-macabi.tar.gz=79628c744d12c1bc5514801a530d903ec539efd16e82d0c871adfaa168b7f55b
-dist/2025-02-08/rust-std-beta-x86_64-apple-ios-macabi.tar.xz=2b12ae64c94a4f11591de8484998391d2825bc68535257dd305d31a1c51a7210
-dist/2025-02-08/rust-std-beta-x86_64-fortanix-unknown-sgx.tar.gz=54f4caf75ac93676fd61fec65e2ea6b73774d5de7d7abe8d47d9c2d8030d8fe0
-dist/2025-02-08/rust-std-beta-x86_64-fortanix-unknown-sgx.tar.xz=abc1feb82d4449c0d098a1ee0861ad991e79911cf0d5cc9ed9b5ca1e134248e6
-dist/2025-02-08/rust-std-beta-x86_64-linux-android.tar.gz=fdc1afd6334f725cfd913faf0915da711cdbcc45c05ec2672fae34d97e73ca60
-dist/2025-02-08/rust-std-beta-x86_64-linux-android.tar.xz=9451c74b695dce762d56354fac148263b0a7de75209c127bc7126da54ede7114
-dist/2025-02-08/rust-std-beta-x86_64-pc-solaris.tar.gz=d32731be8a9d3c5e8279a1b4971d105fe457717acbdfcfd720bc0ae55a34df26
-dist/2025-02-08/rust-std-beta-x86_64-pc-solaris.tar.xz=a5a08cfaae7eb3d77c137bf0d436beb7a84a19e351f529fe459861a8841df44c
-dist/2025-02-08/rust-std-beta-x86_64-pc-windows-gnu.tar.gz=7c41f46506f93132c5f90184d7ad1577edec98d47be17c6330f24cd172c673d5
-dist/2025-02-08/rust-std-beta-x86_64-pc-windows-gnu.tar.xz=e439c8a7368e70685b239ab7b84bffcd8081ddc80c2d9f6d2b686485316aed68
-dist/2025-02-08/rust-std-beta-x86_64-pc-windows-gnullvm.tar.gz=128c7e526e37551ba3b6a347cb870c4ceefb272d964512fa10635c8f1f6e239c
-dist/2025-02-08/rust-std-beta-x86_64-pc-windows-gnullvm.tar.xz=660409184e81625419b91a4fb5d84572abb0505d9bc11bacbed50121f9230790
-dist/2025-02-08/rust-std-beta-x86_64-pc-windows-msvc.tar.gz=10b37018a254f81e55f555628fcc535a54b0eae0097f02a148c443a3d7e762f9
-dist/2025-02-08/rust-std-beta-x86_64-pc-windows-msvc.tar.xz=d61e44948d2ea70b44c8d3f958a3657dae9eb8a551aedb3ef90327da0124d9d5
-dist/2025-02-08/rust-std-beta-x86_64-unknown-freebsd.tar.gz=7ed5b948a50946b518040170be16cba1b5ec0c02f823436c973e5afbd335d26b
-dist/2025-02-08/rust-std-beta-x86_64-unknown-freebsd.tar.xz=0bdce5b2f88448dbbe101f4ef48231763739533b52c32ab6ef858b6b37d98a50
-dist/2025-02-08/rust-std-beta-x86_64-unknown-fuchsia.tar.gz=49676d645ef9c49a421f3fa1d908830b61542541f1ea12c2d240ceaac3b977d4
-dist/2025-02-08/rust-std-beta-x86_64-unknown-fuchsia.tar.xz=1c0854c619e2a0dd7e508513fb68c22352ddc170572bfe6535d2f759de069c53
-dist/2025-02-08/rust-std-beta-x86_64-unknown-illumos.tar.gz=8ded61102c481e7c000292a8c6d39d84c016f81b00618947ac7357a7c72fb142
-dist/2025-02-08/rust-std-beta-x86_64-unknown-illumos.tar.xz=b76ed9b0896cfe863fd2aff88fd6d075e6091a651cb0d5fa65307948316f7ebd
-dist/2025-02-08/rust-std-beta-x86_64-unknown-linux-gnu.tar.gz=1cbcf5ae9dd2c61212b82bf27bf1b1b7d9504f077119c1fa04a978fa0ef3b2e6
-dist/2025-02-08/rust-std-beta-x86_64-unknown-linux-gnu.tar.xz=86f236dc4222f5f93f160ca847f43f364e1659b6d4c9f68a31c202f5346f2221
-dist/2025-02-08/rust-std-beta-x86_64-unknown-linux-gnux32.tar.gz=2225ae88e8cf954b7eef8fc2e4f391c0194fd7be6433928dbc8aa6e2235ac479
-dist/2025-02-08/rust-std-beta-x86_64-unknown-linux-gnux32.tar.xz=6017a3197b7a103a4444b81f8fa4f790a2b11bf1c3db0af297e2ed0c97f78172
-dist/2025-02-08/rust-std-beta-x86_64-unknown-linux-musl.tar.gz=e7b3c4d8efb8f56fa76c3cfe51eb0683fc41308691ac194b9eb70e13b1b2e976
-dist/2025-02-08/rust-std-beta-x86_64-unknown-linux-musl.tar.xz=f0ad48db569e21b8fa1053de3320d766cbb10962ebbd69ed56f170bdaf76fe45
-dist/2025-02-08/rust-std-beta-x86_64-unknown-linux-ohos.tar.gz=f1baa7f02c0969d74cd0ba420ae0a8d5e761cd5545c04fd2e432c889db8cb655
-dist/2025-02-08/rust-std-beta-x86_64-unknown-linux-ohos.tar.xz=e572e1a7db7e0127bf7072e5361b91dac84c019afa6433538b40dc984957f1d7
-dist/2025-02-08/rust-std-beta-x86_64-unknown-netbsd.tar.gz=c714b5f4a61041b5c68abc532203c88d65a852a813b7af8e11ab3207804886fc
-dist/2025-02-08/rust-std-beta-x86_64-unknown-netbsd.tar.xz=61d6c87b146f114d14fe637257b9feccbb1d787958a7291f7800c7541bd8e21b
-dist/2025-02-08/rust-std-beta-x86_64-unknown-none.tar.gz=5ab7c9985beb3f07520cecbb60c2fcb8bf60b3079b253267fb55d2139619331a
-dist/2025-02-08/rust-std-beta-x86_64-unknown-none.tar.xz=992257d8f922654977d1c1fdad2b57654b4ea7fd2c27d6a4bcb8f85b9a75fc5a
-dist/2025-02-08/rust-std-beta-x86_64-unknown-redox.tar.gz=e5090b881329fd94e4250cef92464bf3fc42ae6566ab3bf461e1f3c843d3d1fe
-dist/2025-02-08/rust-std-beta-x86_64-unknown-redox.tar.xz=89cc1ad229a453c15d4a88137646d6824c572eed602c9c131982e3ddc62abd75
-dist/2025-02-08/rust-std-beta-x86_64-unknown-uefi.tar.gz=f42383deea91ab9dfdffede178b4411d751772a482811058dfc0bcc33da2fab6
-dist/2025-02-08/rust-std-beta-x86_64-unknown-uefi.tar.xz=3b4e67edf9785c0eff0fd83e357fbd4c0840620d1b7380ba5d270729f751e4b1
-dist/2025-02-08/cargo-beta-aarch64-apple-darwin.tar.gz=f197e428af3ea3d409a43b54372c9f0f7f8f70ef8d048e066b5c609364211e35
-dist/2025-02-08/cargo-beta-aarch64-apple-darwin.tar.xz=e84b8ec6f87512fbf31c823703b94ebee8531675430224331845158b8997121f
-dist/2025-02-08/cargo-beta-aarch64-pc-windows-msvc.tar.gz=16b73927483117d158a3f3aa36d9cd14d74dce707071345fab9e6c359b68665b
-dist/2025-02-08/cargo-beta-aarch64-pc-windows-msvc.tar.xz=3fb1381b750a1ccf386602394af21130e9cc6384b962bd008ffb2e11bcac4c1b
-dist/2025-02-08/cargo-beta-aarch64-unknown-linux-gnu.tar.gz=2b0d90694987ee10a0f0e5b40c1f95c65c9bbeae6c921556b85067a1920f5988
-dist/2025-02-08/cargo-beta-aarch64-unknown-linux-gnu.tar.xz=b47667e97225ce7542c6e9a137adbc7c002ee1e4b1e0c1255b5dcb87743d886f
-dist/2025-02-08/cargo-beta-aarch64-unknown-linux-musl.tar.gz=843bea38aaeaf6629060a046e6654fde6f4a73221a996e56ea9a1f35466cc6cb
-dist/2025-02-08/cargo-beta-aarch64-unknown-linux-musl.tar.xz=147827cb32d5ff3628f347800a9202eb0b3caaa142d363dffde94bc6bf821352
-dist/2025-02-08/cargo-beta-arm-unknown-linux-gnueabi.tar.gz=0dd6585f3e93a60d4084a4f7f6c58748e82c03ea38fee164828d3911d024459b
-dist/2025-02-08/cargo-beta-arm-unknown-linux-gnueabi.tar.xz=2e8240d79a86de26e7782265bd6551c6357a025b7084a3ba88161c84e2fee54b
-dist/2025-02-08/cargo-beta-arm-unknown-linux-gnueabihf.tar.gz=323095df9d5c9f47463c5b05729960ee0db5f910619050ed548adb2b8c59fc35
-dist/2025-02-08/cargo-beta-arm-unknown-linux-gnueabihf.tar.xz=8f5be63da1f8e9a7d90116c9793e03021855dde170a356002f306180d4ba7ff7
-dist/2025-02-08/cargo-beta-armv7-unknown-linux-gnueabihf.tar.gz=63dfca05df7a8d14c27070ce882a96defa2e5c3134fe17e848273694b42b3c88
-dist/2025-02-08/cargo-beta-armv7-unknown-linux-gnueabihf.tar.xz=3881f42ce8e69953804cb0c566fe08f44ab8763cefe8c0d6a4461645dd44f518
-dist/2025-02-08/cargo-beta-i686-pc-windows-gnu.tar.gz=70aabf19aa3db89599131fc09fc005a9c8c0974d3f04267df0e41007f5bc46d7
-dist/2025-02-08/cargo-beta-i686-pc-windows-gnu.tar.xz=c57bfddc9285dd7c1151c3bfbde4f92da394fe6c8d0239c1f9ee8eceeb15e29c
-dist/2025-02-08/cargo-beta-i686-pc-windows-msvc.tar.gz=056e750115574ab4e008d76d1bda67aa30f3818a9f46cc88d7ad13bb4238d772
-dist/2025-02-08/cargo-beta-i686-pc-windows-msvc.tar.xz=663a9ec0ba07807ce289bd61c66e196262e991046da44dd583fdc1414759f5e8
-dist/2025-02-08/cargo-beta-i686-unknown-linux-gnu.tar.gz=95dee9910674196f2bf655e91a8443be5b46e36747bf1e980e1585fa3789375b
-dist/2025-02-08/cargo-beta-i686-unknown-linux-gnu.tar.xz=09af49a00e181084e7f766c9008c2d3446ff1cb801780feb79d0b7cc31a038b6
-dist/2025-02-08/cargo-beta-loongarch64-unknown-linux-gnu.tar.gz=ab1ac3686ed7b3b50ace61efe20ea6848370de691cf0f287337113d7ca52c61b
-dist/2025-02-08/cargo-beta-loongarch64-unknown-linux-gnu.tar.xz=bee237e641da0d804f2002d33910bfb199127c5006af39dd06d877ca5fd1ff82
-dist/2025-02-08/cargo-beta-loongarch64-unknown-linux-musl.tar.gz=9beb2a39f1ef7630f03ed1da5e1e68964a4b198fe7f18d0db9efb4f10d70b35f
-dist/2025-02-08/cargo-beta-loongarch64-unknown-linux-musl.tar.xz=f1528e4aec2095d55b98e3421d2310f3aa3fcf7c2493f83e75d3cbc80e18f923
-dist/2025-02-08/cargo-beta-powerpc-unknown-linux-gnu.tar.gz=2ee460d198339449e85ff8c7e522c47f33677e90f8b215c0a13514fe5f05cdb1
-dist/2025-02-08/cargo-beta-powerpc-unknown-linux-gnu.tar.xz=4a9fc5e31abfe582b580e395ada8d59a1aecf65b07b09b8ce526a84fdf61830b
-dist/2025-02-08/cargo-beta-powerpc64-unknown-linux-gnu.tar.gz=9e471ec06debbe078b8c92773a1c4380969e92dbbd9f20884986b2cc7e942106
-dist/2025-02-08/cargo-beta-powerpc64-unknown-linux-gnu.tar.xz=1cc6e9e448f036af3bd6f8e597ee242edc65811835e20545e84ab917744b50ca
-dist/2025-02-08/cargo-beta-powerpc64le-unknown-linux-gnu.tar.gz=08fb88ea0a4d904b28ef7a51275ccfd1f8480f1d33c7a5a83461d322143e81be
-dist/2025-02-08/cargo-beta-powerpc64le-unknown-linux-gnu.tar.xz=0809a5f718157763bbf67e8c3a3b9b868a6694bd88b87277b542fc3207c1f1d7
-dist/2025-02-08/cargo-beta-powerpc64le-unknown-linux-musl.tar.gz=dcbf18b645016eee37a23dce4126d0ad61f8886e8997b480609e8f8fffe0191a
-dist/2025-02-08/cargo-beta-powerpc64le-unknown-linux-musl.tar.xz=f4f9350ec459016cc0fe54414d75fee119b0051e3ae1b839971ec5b569be97ce
-dist/2025-02-08/cargo-beta-riscv64gc-unknown-linux-gnu.tar.gz=dbeee82ff5cec6c16ba873b40f986e85b55f831efbd4d09d0107b2e66037db87
-dist/2025-02-08/cargo-beta-riscv64gc-unknown-linux-gnu.tar.xz=2974493a708fd5c793b9f1920a11b66aa5ad6aaffecb0b9c597416b7200e411a
-dist/2025-02-08/cargo-beta-s390x-unknown-linux-gnu.tar.gz=ca157ff6dac3d680f63c99fc2efbc30376eb6ae54682f40369c4d2aa8647b77c
-dist/2025-02-08/cargo-beta-s390x-unknown-linux-gnu.tar.xz=ea5ebe153fbe3d1fdf514ebc050f46776b075268aaf7e7e3ef308f13d0ad545f
-dist/2025-02-08/cargo-beta-x86_64-apple-darwin.tar.gz=dd19dc295a1c430a52c223478a01610b359e2e0b40abce7111c9a02a51d4d313
-dist/2025-02-08/cargo-beta-x86_64-apple-darwin.tar.xz=f729d46cb4ca8945657671d5d0c0a55792a2ac9f5ea21b3e2991843004fc23ae
-dist/2025-02-08/cargo-beta-x86_64-pc-windows-gnu.tar.gz=9a7dbfaaafd177a725aef60ec6c4fe3c5b732bcbd69a2e25a91ad7461a76ef4c
-dist/2025-02-08/cargo-beta-x86_64-pc-windows-gnu.tar.xz=ef585bc9e5f23368fbb960959f80fd34961909e62cd65bb847db06a7178b174a
-dist/2025-02-08/cargo-beta-x86_64-pc-windows-msvc.tar.gz=5e396a15cc63f084467d9afcb30cb374c070856b0aa3fcf209d88bcccfc6436b
-dist/2025-02-08/cargo-beta-x86_64-pc-windows-msvc.tar.xz=78cdbf2cf0d7fec4d24308853e96a39bf5c417905abfcad34de4d1b2d498a617
-dist/2025-02-08/cargo-beta-x86_64-unknown-freebsd.tar.gz=1e485ce036055c7b3e1949d9e34e7d3dca436438b3f09bd75d36e49217d53d44
-dist/2025-02-08/cargo-beta-x86_64-unknown-freebsd.tar.xz=80c5d0d5860eebbb807a2e5cf8ccba8ed4461f932825439ec8182f6f0c8939c4
-dist/2025-02-08/cargo-beta-x86_64-unknown-illumos.tar.gz=5681331e1d29b476c4f458fee453f73efc86856caf1d30c49df9dec3e147f4b1
-dist/2025-02-08/cargo-beta-x86_64-unknown-illumos.tar.xz=9f7d97045851bb1ab334761cddb38cd8bf3bb0b26f2fca4903c09301c70fbca4
-dist/2025-02-08/cargo-beta-x86_64-unknown-linux-gnu.tar.gz=24bda5749883e133ac58ddcfdbd8dbeeb660c3b33d613852dd1d993cc13f6e8f
-dist/2025-02-08/cargo-beta-x86_64-unknown-linux-gnu.tar.xz=2304e5cde3efdb5c6e7319d8239bca7d1c69306846cc3eba84d70d49b8c24e51
-dist/2025-02-08/cargo-beta-x86_64-unknown-linux-musl.tar.gz=825a42ae4bab8be081c7320f06fc7ce6d0c0c31f2224fb0c13af4d01e04c3626
-dist/2025-02-08/cargo-beta-x86_64-unknown-linux-musl.tar.xz=656d148e383896d06483297d785d62c8e235020081acb4110a8bf698a3d6d6e5
-dist/2025-02-08/cargo-beta-x86_64-unknown-netbsd.tar.gz=1e6a750acd6c1f44e37b3d1b1dceef7e8a0d2b0806e1577b667573a6340e7cf9
-dist/2025-02-08/cargo-beta-x86_64-unknown-netbsd.tar.xz=c45bd23fd1004cea41f133a40bd102e55e9f7ed29c9e8cec83dcacc4653da7c7
-dist/2025-02-08/clippy-beta-aarch64-apple-darwin.tar.gz=cd22d4191ca0fb9d8ec3d627805f297ca4528018e34c4f627e7e4fc05d6b0954
-dist/2025-02-08/clippy-beta-aarch64-apple-darwin.tar.xz=0c408799a742b00dce013553a597e62d8f84232ede6883632dade6b3d024904f
-dist/2025-02-08/clippy-beta-aarch64-pc-windows-msvc.tar.gz=b4d902d9a096cdc6eb655ae2a0960db6fe4b83ccb681cc5bb5124dcfc5e1fdc0
-dist/2025-02-08/clippy-beta-aarch64-pc-windows-msvc.tar.xz=9e2514d7f304e3253e7205cec8189b8f9b1d49da5b14b2c03e69b8bb0223a65c
-dist/2025-02-08/clippy-beta-aarch64-unknown-linux-gnu.tar.gz=1496cc4bbd0edc18ce05bc2ca76d9ed78934f1d1c4749ff830dff7d1acc2535f
-dist/2025-02-08/clippy-beta-aarch64-unknown-linux-gnu.tar.xz=4577dce94261cdf07064f43404ade755a8906ed0b1a39a148333d9309e011916
-dist/2025-02-08/clippy-beta-aarch64-unknown-linux-musl.tar.gz=d28217818fb6c1a13456684bfdbf9caa687dcc257fadffcde2d2fd94dfbb228a
-dist/2025-02-08/clippy-beta-aarch64-unknown-linux-musl.tar.xz=bae2f1841776e321f5f950678516bb31c05dea8f825fda23f3a3cd423dc00249
-dist/2025-02-08/clippy-beta-arm-unknown-linux-gnueabi.tar.gz=e1698a07ab47d02954bdb67034de33a8414731e3d12a8937cdea97004d98e45f
-dist/2025-02-08/clippy-beta-arm-unknown-linux-gnueabi.tar.xz=631499b2ee136c428f1c6045eb6aefee3e6c765dec1baeb0c3e4da056d540d71
-dist/2025-02-08/clippy-beta-arm-unknown-linux-gnueabihf.tar.gz=248a15737924d07afee2108e6545cb4bd5cce91878dedb5368c358c16a0b016d
-dist/2025-02-08/clippy-beta-arm-unknown-linux-gnueabihf.tar.xz=6581c968d48e37baf99addf17baf8b4fdda1417c201da311959c62a51783209a
-dist/2025-02-08/clippy-beta-armv7-unknown-linux-gnueabihf.tar.gz=8ff9d93ea49d805ffd3534c6aa03692fa7dc8bf0460bba7c22259257dae863d1
-dist/2025-02-08/clippy-beta-armv7-unknown-linux-gnueabihf.tar.xz=b267e928b9708cc168d84c005036d155547274e8fe03f23b14232609b780d219
-dist/2025-02-08/clippy-beta-i686-pc-windows-gnu.tar.gz=957e8d14451557ce11de7303be61a50c68af7b90293a46e48c9a1bb3db05d154
-dist/2025-02-08/clippy-beta-i686-pc-windows-gnu.tar.xz=576732c661cd0998f7b47e99fe84c3dbcb919e787e3222e1c27b5377361083aa
-dist/2025-02-08/clippy-beta-i686-pc-windows-msvc.tar.gz=5a4ea7d3fd79823b2342ec9069e865000dc5ab58be0fcf3655bcb161cb195858
-dist/2025-02-08/clippy-beta-i686-pc-windows-msvc.tar.xz=fbe4f93a9f1ab718a5dd3995ce67b7b0768f7f211f8fc3f1e6b181c4e4f1f467
-dist/2025-02-08/clippy-beta-i686-unknown-linux-gnu.tar.gz=0c013dba1ac3db5ec21e672f357c6a86c379c29f7cac0d43df4857e0332abe22
-dist/2025-02-08/clippy-beta-i686-unknown-linux-gnu.tar.xz=0e585010d88a8f04f9e031fcef623a12da7f82ed9f0d827157b10f6be80eb920
-dist/2025-02-08/clippy-beta-loongarch64-unknown-linux-gnu.tar.gz=2716e252361ac72dfebc8521b3f2c0edaa6e0a0bb0b9a3ea3b9b888a5d747104
-dist/2025-02-08/clippy-beta-loongarch64-unknown-linux-gnu.tar.xz=3d400fda76fbc8a830e12f5f71e40d4993019e9e53d58434ca23e3d8761d530f
-dist/2025-02-08/clippy-beta-loongarch64-unknown-linux-musl.tar.gz=8d7cd64c2ec8ee5f25bbd6aa01ba22001a4ab2eae6ca481583f736a1da07dc31
-dist/2025-02-08/clippy-beta-loongarch64-unknown-linux-musl.tar.xz=f60bca644d441e36e537e896d4b122ca0f8cde0fbd886f12c3a312efd5225997
-dist/2025-02-08/clippy-beta-powerpc-unknown-linux-gnu.tar.gz=a3fc359554026ae39c7cc46c53c6928de0522387ad2327444745eefd0fdcc43b
-dist/2025-02-08/clippy-beta-powerpc-unknown-linux-gnu.tar.xz=8344b79f4a2a61cf68e526c144bbe797f1c60dec047845fc731f4c47a58b7be6
-dist/2025-02-08/clippy-beta-powerpc64-unknown-linux-gnu.tar.gz=05c2cd6e95a7c8ef36703b9bc39cedb8e5eb5b3c93a66a16deef2e18e96be489
-dist/2025-02-08/clippy-beta-powerpc64-unknown-linux-gnu.tar.xz=2a0c36d392c3b69fc437c8ab8358f87c6c50380e8c5ced6690c9a7d0be9c95e8
-dist/2025-02-08/clippy-beta-powerpc64le-unknown-linux-gnu.tar.gz=c9c6975ec5ef1e206a917d03841db90e1f4a3562b0afec32a4753134752454d3
-dist/2025-02-08/clippy-beta-powerpc64le-unknown-linux-gnu.tar.xz=efe920b25316d84c08693f28d0593d227ca18456be2dd9b426c8cbf49d81a352
-dist/2025-02-08/clippy-beta-powerpc64le-unknown-linux-musl.tar.gz=b133cb9709d11da1710b3b833a3e15d3fc216cacfd37e6e1c7c8fe1dbc202067
-dist/2025-02-08/clippy-beta-powerpc64le-unknown-linux-musl.tar.xz=7db8db7300b87bc7841ef59111c8f83fc6e6ffedbb6dd8fbb79431edb821e017
-dist/2025-02-08/clippy-beta-riscv64gc-unknown-linux-gnu.tar.gz=2fea1f879ca90478a2fb75566da4cbed2f2afec31af6d18160fe183772241658
-dist/2025-02-08/clippy-beta-riscv64gc-unknown-linux-gnu.tar.xz=668ef736714909588cae0bc77c30c3063d2911b0e1c28fc77678081099d38a83
-dist/2025-02-08/clippy-beta-s390x-unknown-linux-gnu.tar.gz=a05eb2ea42fc2ed7df974610b076adae42ec456bb80eaf9191521dda5c0cf12b
-dist/2025-02-08/clippy-beta-s390x-unknown-linux-gnu.tar.xz=5874e0cfa3b7f7c3b8d9d0c84a519b98908b6228fcd9f29d971b9c36d639a998
-dist/2025-02-08/clippy-beta-x86_64-apple-darwin.tar.gz=e259a0e7776430f1697153505f937c92e942fb5c436c00dd9da3b160ef9a7856
-dist/2025-02-08/clippy-beta-x86_64-apple-darwin.tar.xz=a311ec69770a431837a26e63591dba11dedbad411b688ee69e457bb7a4596c50
-dist/2025-02-08/clippy-beta-x86_64-pc-windows-gnu.tar.gz=2b8278419633f83ca22e2d1c54a170e04775d903076935ce13e91575d6122715
-dist/2025-02-08/clippy-beta-x86_64-pc-windows-gnu.tar.xz=13190a1e83debb9483abc7b0e4e3a4b8d53895542ffa56ebc012dbd72b8e7637
-dist/2025-02-08/clippy-beta-x86_64-pc-windows-msvc.tar.gz=9c8c8de0bd646bb0284e47b6adc1db6443d9da7c06ed6d237c421339a446fd83
-dist/2025-02-08/clippy-beta-x86_64-pc-windows-msvc.tar.xz=32dbce4ca3f3abf6da249e33297efea856b3b0ff39b3571531d71e1d0f90a46c
-dist/2025-02-08/clippy-beta-x86_64-unknown-freebsd.tar.gz=55b22726920214e2ca9c4e552b50401fb3eef872c63de554623aaeebf9e11e75
-dist/2025-02-08/clippy-beta-x86_64-unknown-freebsd.tar.xz=b67e699d65462020333c037f206287e609f35e448cffeed6ecaec6874ccb2233
-dist/2025-02-08/clippy-beta-x86_64-unknown-illumos.tar.gz=6bf570aa87c05050e5dffcc191db07b1b0abf18bce160637a1be39e5caa130e6
-dist/2025-02-08/clippy-beta-x86_64-unknown-illumos.tar.xz=52ae7e8906f27c020df67beae61c1c1faf6458a0ccb3e904b909841b2b504d89
-dist/2025-02-08/clippy-beta-x86_64-unknown-linux-gnu.tar.gz=fadc4ff0f2acc6d44a5a29d01101152c8570dcca69ea4fd60e8ca6cc2cfac1e9
-dist/2025-02-08/clippy-beta-x86_64-unknown-linux-gnu.tar.xz=fd8472509ffdb81ff5ce786784274f654eb213ce963caf6e5cb90baa9e641c3b
-dist/2025-02-08/clippy-beta-x86_64-unknown-linux-musl.tar.gz=c94e847aae4aa7a04981a4be424cdb2a1eae81a808858a5fc14a20134b7575e3
-dist/2025-02-08/clippy-beta-x86_64-unknown-linux-musl.tar.xz=b1f2a240069cda55c7c49afda6137822d7aee855557cf7269ac4f440d54a0ca9
-dist/2025-02-08/clippy-beta-x86_64-unknown-netbsd.tar.gz=86ada6c1406ff381a81624da25e65f415380da01252e6016bffe50ef7ca5b49d
-dist/2025-02-08/clippy-beta-x86_64-unknown-netbsd.tar.xz=2b24a66215cd93b84065cd35d00bb3e7c9482b118eaeaf089bb2a128450add88
-dist/2025-02-08/rustfmt-nightly-aarch64-apple-darwin.tar.gz=0e6740aa70ccffed1e2649619990b75586638b8eb34747aab58ba4cccfb09f29
-dist/2025-02-08/rustfmt-nightly-aarch64-apple-darwin.tar.xz=c5c166763202c35bb7844f0866f05cfb7e2666fe9416776a092372362e7bd8e3
-dist/2025-02-08/rustfmt-nightly-aarch64-pc-windows-msvc.tar.gz=cf025264c087d9339787db6f8cd4c76aaa9c3118b7875de7413dc1894b660fcd
-dist/2025-02-08/rustfmt-nightly-aarch64-pc-windows-msvc.tar.xz=f788a44b9542ddda3a88e3000c31644e1b9ee874ce523798b6638f7f181b8df0
-dist/2025-02-08/rustfmt-nightly-aarch64-unknown-linux-gnu.tar.gz=0a7d236cd45ef4f3f12e4516b202cf9b5190e70a19044c8b17c5eef16c45032e
-dist/2025-02-08/rustfmt-nightly-aarch64-unknown-linux-gnu.tar.xz=80cf271c8adc2192b212d1b40992d90aa21fcc695375774c17a4f70468c82c11
-dist/2025-02-08/rustfmt-nightly-aarch64-unknown-linux-musl.tar.gz=58d0eef426f8f0c4bf191118161db008db378ee3e77d973d7e15e39e88c3f7ea
-dist/2025-02-08/rustfmt-nightly-aarch64-unknown-linux-musl.tar.xz=7eedaef7100f7272f38fbc3cc14a1f4fee7deddd31a59f7db71c2a6f2f03f946
-dist/2025-02-08/rustfmt-nightly-arm-unknown-linux-gnueabi.tar.gz=df5190ea7d9bb84d5ffdda74cec7591388d3501c2a4084c667e0f29ca0a0ba9d
-dist/2025-02-08/rustfmt-nightly-arm-unknown-linux-gnueabi.tar.xz=96334a22d0534adc8521b6a000f422f1727f80235303137eac32f4e76e85194b
-dist/2025-02-08/rustfmt-nightly-arm-unknown-linux-gnueabihf.tar.gz=2944da8dab479b4892f512711ed6bb3b242602ab11c81c6b22352a467c898165
-dist/2025-02-08/rustfmt-nightly-arm-unknown-linux-gnueabihf.tar.xz=5aa59c9ded50503a2f05d5330ef41ae888254887559b31c4304422aa4ba9e73f
-dist/2025-02-08/rustfmt-nightly-armv7-unknown-linux-gnueabihf.tar.gz=bf17c1d62ae3f59b595c6536add64453d5584135e1d238bae55216c428416767
-dist/2025-02-08/rustfmt-nightly-armv7-unknown-linux-gnueabihf.tar.xz=c60925816b01596b846e56efa7cd0e186391e212da077b36d781b926799704ad
-dist/2025-02-08/rustfmt-nightly-i686-pc-windows-gnu.tar.gz=5c73dce13f7e0ad408f39f49ae647b0fbda00416027ba43228a443f311b33fb1
-dist/2025-02-08/rustfmt-nightly-i686-pc-windows-gnu.tar.xz=df005321b30d82854f97a3e68a702c64ded7f4cde18edc69c3b1d7c8927b53e0
-dist/2025-02-08/rustfmt-nightly-i686-pc-windows-msvc.tar.gz=2483fe75cd360607f29fc5f7cfc6d55074047291544f398946b5c263eef6b164
-dist/2025-02-08/rustfmt-nightly-i686-pc-windows-msvc.tar.xz=f2553f96bb513e999597c89d0655bf1b28a338d6510f9a146b3e47674c83c550
-dist/2025-02-08/rustfmt-nightly-i686-unknown-linux-gnu.tar.gz=9cbc32383a8b6025ab9b7d75bf95b831cba32826f2732597cc3749b1a44278d6
-dist/2025-02-08/rustfmt-nightly-i686-unknown-linux-gnu.tar.xz=07f16dcf8340feaa8ac400eb54833017d501f78f11f699c095febec148f35aef
-dist/2025-02-08/rustfmt-nightly-loongarch64-unknown-linux-gnu.tar.gz=03f7b562d15fb47e8e3ad244b127ed170c6296fbf5bf822fddd51eb5ab2a4f1d
-dist/2025-02-08/rustfmt-nightly-loongarch64-unknown-linux-gnu.tar.xz=834186eb75bff0e05f1a7d9ea63382c7b96a18519aa6815060554b06f3d7b90e
-dist/2025-02-08/rustfmt-nightly-loongarch64-unknown-linux-musl.tar.gz=45c7db895ae3751fe07dc7483d0c1ae6dae989ec0b3507eabdfaaf9b1f24af04
-dist/2025-02-08/rustfmt-nightly-loongarch64-unknown-linux-musl.tar.xz=8aef8265279fca165e1627639d64c1f980877744cbd8a927c6c6f3f1e9ef2fd8
-dist/2025-02-08/rustfmt-nightly-powerpc-unknown-linux-gnu.tar.gz=4f85245ee5c997a7d821ea3c397c5bc7315c8cb07605f2b5a4746f254ef567aa
-dist/2025-02-08/rustfmt-nightly-powerpc-unknown-linux-gnu.tar.xz=ec1326a520eb4bac792bce1e260b261e3cd638c123dc2da962bf2f25bf3a89a6
-dist/2025-02-08/rustfmt-nightly-powerpc64-unknown-linux-gnu.tar.gz=dd473534cd897998f6dd361f18a61ea12ed38bbb323c3d602b3124e832ce37fb
-dist/2025-02-08/rustfmt-nightly-powerpc64-unknown-linux-gnu.tar.xz=a3523178a5186ebd3847b1b0fbdf91f300b29f780fc7910fa867ccddaecbab73
-dist/2025-02-08/rustfmt-nightly-powerpc64le-unknown-linux-gnu.tar.gz=625777f6ccf6ede941a88252bea183f8f285f6d282f95a78153e5d45e63edaad
-dist/2025-02-08/rustfmt-nightly-powerpc64le-unknown-linux-gnu.tar.xz=7c57c7c0163c74beaa80378974cfbe222f70057e44a025e042989f3816cf02e0
-dist/2025-02-08/rustfmt-nightly-powerpc64le-unknown-linux-musl.tar.gz=b431d8683230d783641bbacf7fee8102ecccf527485e8a753781b49906cdfb56
-dist/2025-02-08/rustfmt-nightly-powerpc64le-unknown-linux-musl.tar.xz=637801af10b564fa8b2bc8b3075b3fc26efd69237702bdd95b60f6d55e7a92ea
-dist/2025-02-08/rustfmt-nightly-riscv64gc-unknown-linux-gnu.tar.gz=2532bededd1512c36f97ad91d065ccf681b4df47db86dc382635cbd55e6f1aaa
-dist/2025-02-08/rustfmt-nightly-riscv64gc-unknown-linux-gnu.tar.xz=16eb4fcdc66b86d45af649905b33eb1a9cb9a859eaf4becae10b443a747b790f
-dist/2025-02-08/rustfmt-nightly-s390x-unknown-linux-gnu.tar.gz=2c29615e4de5f4f9aac64102afee2f7775e961037c2b044ff5ea4690564a0af5
-dist/2025-02-08/rustfmt-nightly-s390x-unknown-linux-gnu.tar.xz=0f88b8da8d50b7bc1fef09a53c5cedb79d24b3a63b8ab8cc6960285acb7fde7a
-dist/2025-02-08/rustfmt-nightly-x86_64-apple-darwin.tar.gz=f46c2a065cf61ba644062b20efbbd6cfd0dcba5b15bc8f333c35e32dd4640e03
-dist/2025-02-08/rustfmt-nightly-x86_64-apple-darwin.tar.xz=9bd81b4ec3625d959f96d7b6b17d6c548673c10f7246b712f99fe40e8dcb4844
-dist/2025-02-08/rustfmt-nightly-x86_64-pc-windows-gnu.tar.gz=2d37451b9314b525d41817c432e3bff5e90028cb020f0331f39490975376bf82
-dist/2025-02-08/rustfmt-nightly-x86_64-pc-windows-gnu.tar.xz=e8ad4c33272708ba094a2a14e1a69d9071345351921b357ebd0df7d32ccfdcd3
-dist/2025-02-08/rustfmt-nightly-x86_64-pc-windows-msvc.tar.gz=1d8e24202a8b2111b78531e5313446c6d2e3be083aef77cbd95c09b909b7c49b
-dist/2025-02-08/rustfmt-nightly-x86_64-pc-windows-msvc.tar.xz=9217ed18bb34379079aa529e3ef2fcc899db75780db942b1b6a45cb8f9b317eb
-dist/2025-02-08/rustfmt-nightly-x86_64-unknown-freebsd.tar.gz=f82fdf61057bae8a38daa767f72c74efefaa6043ef48229bed9d8a86787387be
-dist/2025-02-08/rustfmt-nightly-x86_64-unknown-freebsd.tar.xz=60857a9f3518fa6e1a0b8f17a0391672af55bf37bf5fc965b18079060086f946
-dist/2025-02-08/rustfmt-nightly-x86_64-unknown-illumos.tar.gz=67384f76d40cb80dc79bf0221a4c021e33f11f283e793404421bd5391d75f0af
-dist/2025-02-08/rustfmt-nightly-x86_64-unknown-illumos.tar.xz=e1238f9c47666b623feca48243e88ad40ba70d3d762e5e0326dd29eef41f8c83
-dist/2025-02-08/rustfmt-nightly-x86_64-unknown-linux-gnu.tar.gz=cdf48fd39c450219ecbc0801aab198c74e31e543b663030bcff9d6df8829f7b0
-dist/2025-02-08/rustfmt-nightly-x86_64-unknown-linux-gnu.tar.xz=a5a461c01b8709335c7e2dfef9378ee49aee5f9491f4bf6173142ab87bee3d31
-dist/2025-02-08/rustfmt-nightly-x86_64-unknown-linux-musl.tar.gz=d9881dac0e6d10758cc5e28d3c63be557d596336685b5d095cb77f69853ad978
-dist/2025-02-08/rustfmt-nightly-x86_64-unknown-linux-musl.tar.xz=beec700c0c6829c8975abbd26f7330a3ddc21040a8f2d531f1991f99ac11363f
-dist/2025-02-08/rustfmt-nightly-x86_64-unknown-netbsd.tar.gz=188004d4990d1ca6f8fa7b00dfab510b0cbe840fda4e7318942bc2664c03588a
-dist/2025-02-08/rustfmt-nightly-x86_64-unknown-netbsd.tar.xz=ff25e65591a638072362122059d71888ddef0b9b81f0b0d7b2fc285fe82192f9
-dist/2025-02-08/rustc-nightly-aarch64-apple-darwin.tar.gz=c97c0921c6749ab5eebb2fcc65cc7fc98af43d1504fa1cf4ef360e299c74fdde
-dist/2025-02-08/rustc-nightly-aarch64-apple-darwin.tar.xz=51e12e1a1bb60b65145a5742899f0ba3c60840c60962c424351dd337de7b2906
-dist/2025-02-08/rustc-nightly-aarch64-pc-windows-msvc.tar.gz=469e68dc2e1546ca27117cb3d282a77bd783e84c351d911a2e45c91916fc5944
-dist/2025-02-08/rustc-nightly-aarch64-pc-windows-msvc.tar.xz=ca16024d975f6c4835d53a47cc12e728380ed9e9126f4f523854b5c8af148499
-dist/2025-02-08/rustc-nightly-aarch64-unknown-linux-gnu.tar.gz=75e9f08ced58ed868a1c5e727b632c1ea9ed8f483cadc68d30d7394ab3f60215
-dist/2025-02-08/rustc-nightly-aarch64-unknown-linux-gnu.tar.xz=c5d3906481bed31bfa563f216e8c17e8be971cf64d0a5d66820025900aa82893
-dist/2025-02-08/rustc-nightly-aarch64-unknown-linux-musl.tar.gz=a6dab5d9fd0ddb5fd83f5332e3608df71b62891c1bc02f73479cf0aa344c4d1a
-dist/2025-02-08/rustc-nightly-aarch64-unknown-linux-musl.tar.xz=9b4412cb694f46774b5642b972b0c149b5ba8d4081069cf078ee83e7f7ab1c45
-dist/2025-02-08/rustc-nightly-arm-unknown-linux-gnueabi.tar.gz=cda11234e3e2370fb2a9ae7d4f50a7c44c646c5ea9dee4d4a576383785ae993d
-dist/2025-02-08/rustc-nightly-arm-unknown-linux-gnueabi.tar.xz=bd6cece56d6559ae261d5494af10d665bb7293f439287f1d190cd959c598ab15
-dist/2025-02-08/rustc-nightly-arm-unknown-linux-gnueabihf.tar.gz=c29c0ace7fe5b160e9399813848e70865d4eb90536169d9c6089a90b74e4e0b7
-dist/2025-02-08/rustc-nightly-arm-unknown-linux-gnueabihf.tar.xz=bea382931c0b9804ab6ea0f3666679493c1c4390a2f732ad367b6540d4279614
-dist/2025-02-08/rustc-nightly-armv7-unknown-linux-gnueabihf.tar.gz=c83086892fb1ce1b4ca0c778e9efe57334bd18d74a961fccca78125d58f185b0
-dist/2025-02-08/rustc-nightly-armv7-unknown-linux-gnueabihf.tar.xz=30ac3e64449a919ccb70dd30350086bcd5e8f4d12e0da6851f685fb60cdb871b
-dist/2025-02-08/rustc-nightly-i686-pc-windows-gnu.tar.gz=f51df0f5c9a8a89fabfb347f1164000a99485bd85dd67ca7bc84e2a48f9c80e6
-dist/2025-02-08/rustc-nightly-i686-pc-windows-gnu.tar.xz=153a564d7930f3c217b25f8573f79b279af82b8ea5ad7ccf09f8c127d4cb6c33
-dist/2025-02-08/rustc-nightly-i686-pc-windows-msvc.tar.gz=2d9c05cb1d60804cfca6076f231ac03081d3b2072cdb87e5f1d9f036325a17a1
-dist/2025-02-08/rustc-nightly-i686-pc-windows-msvc.tar.xz=fbb95c6c97a5e4389b761a1ba712f111f808113ef30abbbf0e95bedc5344708a
-dist/2025-02-08/rustc-nightly-i686-unknown-linux-gnu.tar.gz=df09eea18c7be163abd768ede37cfdec306563d0b33219ae040d8f9eef263154
-dist/2025-02-08/rustc-nightly-i686-unknown-linux-gnu.tar.xz=3ba7cab1234713f4fffade6344b62fc6b89d264e3b8b993799e5e0051d869e08
-dist/2025-02-08/rustc-nightly-loongarch64-unknown-linux-gnu.tar.gz=ae94de4d5bcd8c265fb0297078d446fd84bb5ca7f9e61f87e956330d2c439ccd
-dist/2025-02-08/rustc-nightly-loongarch64-unknown-linux-gnu.tar.xz=be81df065f92bd25825af0935b3188418c97e377ed7ce05ae287f576ff2eec9c
-dist/2025-02-08/rustc-nightly-loongarch64-unknown-linux-musl.tar.gz=96ccf45347235ac893cb61aa0d61aedcc46ae7c93d26f3ad964a73ddd1c274f9
-dist/2025-02-08/rustc-nightly-loongarch64-unknown-linux-musl.tar.xz=1e19c00ef298e5cd1334aa60df8873c0925304bb2a996d0ca590d363443f2f20
-dist/2025-02-08/rustc-nightly-powerpc-unknown-linux-gnu.tar.gz=3e44c93e6be85dfcd8031fce279b1d1604859d149a979d91144272ed4da920fd
-dist/2025-02-08/rustc-nightly-powerpc-unknown-linux-gnu.tar.xz=c3651fdbfec98c155159d799a895917c0df3599dcda71a79bdcedee3c9cb616d
-dist/2025-02-08/rustc-nightly-powerpc64-unknown-linux-gnu.tar.gz=4875368f83906c9f447657ed70b0d53ae75ff563e6bd9b75110defbaf44ba134
-dist/2025-02-08/rustc-nightly-powerpc64-unknown-linux-gnu.tar.xz=069716dd18791fea69df0b97627bba5fc83ee466883080ef934f4cb6b6342ee7
-dist/2025-02-08/rustc-nightly-powerpc64le-unknown-linux-gnu.tar.gz=0aa826fe16a9245091b84972be39aae113f62c1a254b82ec91df81a2c2c2da82
-dist/2025-02-08/rustc-nightly-powerpc64le-unknown-linux-gnu.tar.xz=2324bf8323d784cd37ca9546295e252a0327e25386ba406409e5c4172412d356
-dist/2025-02-08/rustc-nightly-powerpc64le-unknown-linux-musl.tar.gz=2b5debffe5fd870828cf54365b21b1cb989bc5223b76a911299208c5ec105c2c
-dist/2025-02-08/rustc-nightly-powerpc64le-unknown-linux-musl.tar.xz=34342b7ac92698d15b53afa13897fa37401e33b591e2a9ebb00ea17bf2ad7741
-dist/2025-02-08/rustc-nightly-riscv64gc-unknown-linux-gnu.tar.gz=e189c8be0a75ebc0c65e1b467476c510415231c5bb67d742719b39c425ee98a6
-dist/2025-02-08/rustc-nightly-riscv64gc-unknown-linux-gnu.tar.xz=2407a0e42ff8280a16e944f430d2724bed6765295a64a53f463e341d35f9fb0b
-dist/2025-02-08/rustc-nightly-s390x-unknown-linux-gnu.tar.gz=ffbaaee6cb11d1cbed985601a58fa5e9d796161b0435b9a8ef547148f042200a
-dist/2025-02-08/rustc-nightly-s390x-unknown-linux-gnu.tar.xz=3c6dcdea50189f815b16033dfc8524687d7101eafb266be8f16681fcf348dbd5
-dist/2025-02-08/rustc-nightly-x86_64-apple-darwin.tar.gz=43b2f1b7d16b46419d8fcede2b5b354ab778a1f2c3f59e691b71db8cfa00d727
-dist/2025-02-08/rustc-nightly-x86_64-apple-darwin.tar.xz=6f18083854625d41b5ace4aa8b33f9c1aadfba764a6cb8ce1d5986f745ddfe3c
-dist/2025-02-08/rustc-nightly-x86_64-pc-windows-gnu.tar.gz=eba376122f9f9439aa2f6f08a08d496e07fd3e5ac99c82a69eab279e44755b8f
-dist/2025-02-08/rustc-nightly-x86_64-pc-windows-gnu.tar.xz=e5b67e358ae1068482f6e09cdacb6f212b6b6c5e87056909b54182d21c6ba5bb
-dist/2025-02-08/rustc-nightly-x86_64-pc-windows-msvc.tar.gz=310f2fc7bf1d32926d78e264638d2795718a1406209238c8c18ba76680c58fca
-dist/2025-02-08/rustc-nightly-x86_64-pc-windows-msvc.tar.xz=f8915c772b352fd7f25a7f1be36c94f7b383b813d9dae139831c9f7f2b960de8
-dist/2025-02-08/rustc-nightly-x86_64-unknown-freebsd.tar.gz=05bd6715082b006f6d43c369f37c262732e4bb1c44cabe50a201ceb8556ce651
-dist/2025-02-08/rustc-nightly-x86_64-unknown-freebsd.tar.xz=d03c251f78273d4a3de99e650634d30287c4868031ddd3cc929cef8d7cb5d215
-dist/2025-02-08/rustc-nightly-x86_64-unknown-illumos.tar.gz=16804058f8c88358ae2ab0a8302acb092c27a459ca7217d30fde504746be14f9
-dist/2025-02-08/rustc-nightly-x86_64-unknown-illumos.tar.xz=b76535703edb204daf577e42b0a1ae2a41bfa18e0e3205fb5773e7ec12ec9fc7
-dist/2025-02-08/rustc-nightly-x86_64-unknown-linux-gnu.tar.gz=5a63f9f4ee7c5a91252ae8b235780ed6349641c9b6593e9038021c3a3927374d
-dist/2025-02-08/rustc-nightly-x86_64-unknown-linux-gnu.tar.xz=0a53b3706ab2981fadb020362dd497917f8361cc778a11d9a7fa05a3b329eea2
-dist/2025-02-08/rustc-nightly-x86_64-unknown-linux-musl.tar.gz=ab4e3c5a4eaabf0f227b160efcf2f8d0e9a572d079ece83c6313fca9865a5dc3
-dist/2025-02-08/rustc-nightly-x86_64-unknown-linux-musl.tar.xz=dcd74640d883aa75df93b86b041f98ef99178d5f39e78de3ff6801077703da32
-dist/2025-02-08/rustc-nightly-x86_64-unknown-netbsd.tar.gz=cdb3d9ac0651e3d44dbb50b3e341e609e228cbc4c358b126547a2909b807901a
-dist/2025-02-08/rustc-nightly-x86_64-unknown-netbsd.tar.xz=f4227393819e678dd67c579fc00b52fc1a3974e270f10593d8b550f7362b5a63
\ No newline at end of file
+dist/2025-02-18/rustc-beta-aarch64-apple-darwin.tar.gz=1b51ca064350d8b15c7ab6c8ec996a497e912dc237cafc2c205066fc6416e0ff
+dist/2025-02-18/rustc-beta-aarch64-apple-darwin.tar.xz=e4b71f6456d9e62ada6909254606da7f6681f3da0f5bc7d2c3d5c387fea35743
+dist/2025-02-18/rustc-beta-aarch64-pc-windows-msvc.tar.gz=1ff70a5bd238d959d806c3b471a5b03c6cde784944a96a585e0ae0837d2a9c92
+dist/2025-02-18/rustc-beta-aarch64-pc-windows-msvc.tar.xz=5017b351bb90a08041757eae61386b224ec0161c5734293184a87d8903f30098
+dist/2025-02-18/rustc-beta-aarch64-unknown-linux-gnu.tar.gz=8a58b6cc4577615efc76bb9472229098d6f938c1f051ea540409e9dc812dbd8f
+dist/2025-02-18/rustc-beta-aarch64-unknown-linux-gnu.tar.xz=330e217dbd1c507c8706aef5fbd0baf9584495445743f38668cdc962adfb125e
+dist/2025-02-18/rustc-beta-aarch64-unknown-linux-musl.tar.gz=4060ec54281975880a9819b815120d6a450e4c31ddf1136ecce348e28875d50d
+dist/2025-02-18/rustc-beta-aarch64-unknown-linux-musl.tar.xz=0f92b9267a55ac0c764cde63b8cbc8a0a317f7e0817185d380fc2aa35a933687
+dist/2025-02-18/rustc-beta-arm-unknown-linux-gnueabi.tar.gz=4e989e00725e7cfb08ea834c74ec6dd352eda74c13218f7da423ed598af9f9df
+dist/2025-02-18/rustc-beta-arm-unknown-linux-gnueabi.tar.xz=b06181daf8842c2e544e5d54169f9bbfc70ee76115495de033ac4e341617a588
+dist/2025-02-18/rustc-beta-arm-unknown-linux-gnueabihf.tar.gz=93fe2efcde1032ad636ef509a73168f0ab7fadbca699a27e2882b3832539e8fa
+dist/2025-02-18/rustc-beta-arm-unknown-linux-gnueabihf.tar.xz=b49d0256381c928785035578e5b7624c37ba732ea7aefca37dbb66b5162c8090
+dist/2025-02-18/rustc-beta-armv7-unknown-linux-gnueabihf.tar.gz=56fd36c20d78085f39f0df40f15f7694e8744714104539865b9c3d7b06d47e2f
+dist/2025-02-18/rustc-beta-armv7-unknown-linux-gnueabihf.tar.xz=86587bea60c2b8a114ab33fe65a7152fcf8e1dcca14550712dca72af9fd65674
+dist/2025-02-18/rustc-beta-i686-pc-windows-gnu.tar.gz=32c95c78b223efea2ee8e02fbe3a58ac753ce9284e792bc87feaa051fcff5687
+dist/2025-02-18/rustc-beta-i686-pc-windows-gnu.tar.xz=d2e623f11aee7e81eceb6947e3f7150bfd727eb2f527e4abc6b10d3322959802
+dist/2025-02-18/rustc-beta-i686-pc-windows-msvc.tar.gz=f7ffd07eb2b5e83df513c6a313e28003e3ce923778583e78da9efbb5e62405dc
+dist/2025-02-18/rustc-beta-i686-pc-windows-msvc.tar.xz=cd2e61b548a6849b17183fcacb2ac8f94955d893be439793580c39080feb28be
+dist/2025-02-18/rustc-beta-i686-unknown-linux-gnu.tar.gz=fedeca202c1651fe4b15cfc411365ecf016376b3cc7c772d2e0d739e0ec02dc6
+dist/2025-02-18/rustc-beta-i686-unknown-linux-gnu.tar.xz=6389f10e2328bdfa81ef1f34406bb4ec8bcb6dcf64d39c95946d32c9fee7f0b7
+dist/2025-02-18/rustc-beta-loongarch64-unknown-linux-gnu.tar.gz=21b565bbaacc2be5d066c5d0c49b7c0f5b0bd24690ca35e9c88e6ec91846f744
+dist/2025-02-18/rustc-beta-loongarch64-unknown-linux-gnu.tar.xz=4a728e3ec41834775f0b288cdca5ae152314edcaf20d7ea46ea62fab1b9ef327
+dist/2025-02-18/rustc-beta-loongarch64-unknown-linux-musl.tar.gz=0a72f650aa70708f9a72886ea33b7a2e3ffe2a6e2cbcb1d2248f3d9eca74a9d4
+dist/2025-02-18/rustc-beta-loongarch64-unknown-linux-musl.tar.xz=877c3c447f4c25068f70bd7d6dd5078d75b0c194777b2f8a9311a66fc6eda701
+dist/2025-02-18/rustc-beta-powerpc-unknown-linux-gnu.tar.gz=67af5e05ab0a367d3f76c0656823b530a23fb26d9c86db2b433684b9191b8881
+dist/2025-02-18/rustc-beta-powerpc-unknown-linux-gnu.tar.xz=2da086d39eaa000ba629ee15bec740db57039ca3e5c7c55feb9cb9ca6d39c785
+dist/2025-02-18/rustc-beta-powerpc64-unknown-linux-gnu.tar.gz=b079df9a3e5be95a755d22f5ecf3ddeb43f94d96eaa3985770ae98ad0e7e15bb
+dist/2025-02-18/rustc-beta-powerpc64-unknown-linux-gnu.tar.xz=916fe3b67094bb351320371de9587f01bb65f9b9aed2c7aff7930e499822b660
+dist/2025-02-18/rustc-beta-powerpc64le-unknown-linux-gnu.tar.gz=356e3173c960aadfb91dcb607a26647895fb1ae11a7cb596b019c71c6dd808e7
+dist/2025-02-18/rustc-beta-powerpc64le-unknown-linux-gnu.tar.xz=9115c4c85d8c4b5254df41a5d3f55733648ba282711e18a692ee100ed13fb550
+dist/2025-02-18/rustc-beta-powerpc64le-unknown-linux-musl.tar.gz=b8a267f9ca2d23385c529924782f633b6b9f66b50cda5986eff91c223d715307
+dist/2025-02-18/rustc-beta-powerpc64le-unknown-linux-musl.tar.xz=84e65fc1d4282d737b6d0b1aaa1c2abd395af215983eb5b3700e925ca6ba3bc3
+dist/2025-02-18/rustc-beta-riscv64gc-unknown-linux-gnu.tar.gz=19b1d8618edb5d3a6cf620c814f6b76a462bc965f4aac2cde7aca5849b92cac9
+dist/2025-02-18/rustc-beta-riscv64gc-unknown-linux-gnu.tar.xz=9914408b5590fe7c43b2aa10c261bb5162b024e396c625b06546564a5eaddb89
+dist/2025-02-18/rustc-beta-s390x-unknown-linux-gnu.tar.gz=37ac73ea85f43a4859d8c4526cb9480e69bcaa778e842128852c8b21c6db6b45
+dist/2025-02-18/rustc-beta-s390x-unknown-linux-gnu.tar.xz=af1cab661b26cab1ade53f12ec08d13d60a74c2f370d9d3617154e0f01cb3719
+dist/2025-02-18/rustc-beta-x86_64-apple-darwin.tar.gz=aa9999ad56f8968e3e0f5417661ff42fbd1bd482a649d1a8c01d6ba5a6e78a72
+dist/2025-02-18/rustc-beta-x86_64-apple-darwin.tar.xz=bfd09bf8db9bbd7557df3f206207631cc4b10d59d0cf6b072e610646b5c7fa4a
+dist/2025-02-18/rustc-beta-x86_64-pc-windows-gnu.tar.gz=dabab346a003a5b13265da6bab96fc703c34f728c852092bec4cf08d13daeadc
+dist/2025-02-18/rustc-beta-x86_64-pc-windows-gnu.tar.xz=c2252dea69c8dcf6ba0213da8b05b8d9173676fb7448cde684da7b56d2c52577
+dist/2025-02-18/rustc-beta-x86_64-pc-windows-msvc.tar.gz=9ae9c3cb963872b2ef02650bcec15906e0b5e89cc6d3bee0aadfffcec7a1e6df
+dist/2025-02-18/rustc-beta-x86_64-pc-windows-msvc.tar.xz=bcbf75c92e9afe6b25bbde275bd38c3cdeda6324baf5b8c99173ca5738760a7f
+dist/2025-02-18/rustc-beta-x86_64-unknown-freebsd.tar.gz=5313d0780f6a9587e809da0f1ffc973721afad88a0ef8fb83005c383d79229d8
+dist/2025-02-18/rustc-beta-x86_64-unknown-freebsd.tar.xz=52ab3212d64b56a8da207fe976cbc8d266e962a61c742e6069137b10ff25c3c1
+dist/2025-02-18/rustc-beta-x86_64-unknown-illumos.tar.gz=527f839ddedc7bdff48527a276d3d7f64d475dd815b81c6664f1ce25668e0ce4
+dist/2025-02-18/rustc-beta-x86_64-unknown-illumos.tar.xz=3861d9928983a415cd44e5dc50a99af948fac392adfb6c2147b14fb98dd08890
+dist/2025-02-18/rustc-beta-x86_64-unknown-linux-gnu.tar.gz=0054d14cf00b25cfbcb2a1560887c825f703223312ca9cdd0ad51076bf54a3cc
+dist/2025-02-18/rustc-beta-x86_64-unknown-linux-gnu.tar.xz=75a9d69d13e50bb22ec721f9c64d08282d76f285482b285bb61bacabeecd710c
+dist/2025-02-18/rustc-beta-x86_64-unknown-linux-musl.tar.gz=305765ca6a413ea86360b970bd062a19f6bc52756551af43d74920fc2a021d95
+dist/2025-02-18/rustc-beta-x86_64-unknown-linux-musl.tar.xz=c7230b578a97fb234ac106c7333615074b9a7a8abc1422f149ad613c2af28134
+dist/2025-02-18/rustc-beta-x86_64-unknown-netbsd.tar.gz=881bd9b260b2c7838ea1b4de2cd6baf2ff4d317e0c159faba1a683c4c32ba267
+dist/2025-02-18/rustc-beta-x86_64-unknown-netbsd.tar.xz=0c4f2cc0bafbf8b41b257e851870b64d3b5fc112f02227f561c645dc439c96db
+dist/2025-02-18/rust-std-beta-aarch64-apple-darwin.tar.gz=958434edfc07bf6d10da3d41f01d1511b28d1178423a78bff2f60cd10046dbca
+dist/2025-02-18/rust-std-beta-aarch64-apple-darwin.tar.xz=80043af05fb96c497bce55063f2733e37f243f85084f9aa60ab504d7c15c5cce
+dist/2025-02-18/rust-std-beta-aarch64-apple-ios.tar.gz=509aa8803b29ccbb97a1a8c12e2ca6b27310d5af313650c7afff45ab1843106a
+dist/2025-02-18/rust-std-beta-aarch64-apple-ios.tar.xz=f0f05dafc9a3277b075023bb449675946706307f769590c065cb53ae615708d9
+dist/2025-02-18/rust-std-beta-aarch64-apple-ios-macabi.tar.gz=c37c0aee56c5858f91fb5aa60df28cc92649d4884d5edde57eb6690f494ba5f5
+dist/2025-02-18/rust-std-beta-aarch64-apple-ios-macabi.tar.xz=e5120697e4a118824fdebf6d2a644f8f338bb83e209303fc684095b8f6b4ab1c
+dist/2025-02-18/rust-std-beta-aarch64-apple-ios-sim.tar.gz=d5b851917a3f57703378596828e92068c28e00fe0b02331891737c2fc697b5ff
+dist/2025-02-18/rust-std-beta-aarch64-apple-ios-sim.tar.xz=ef33164320931007db65f4915b533e5078a7279a7a134fc80045751a5198834a
+dist/2025-02-18/rust-std-beta-aarch64-linux-android.tar.gz=4b94607d7c09b4f0f85236fb2237f1859150e12745ee2020cb134db904c1e05b
+dist/2025-02-18/rust-std-beta-aarch64-linux-android.tar.xz=3aa7807ef9da83e1a4deebe4a7085e4df1b60edd4e9f237870f827073100d09c
+dist/2025-02-18/rust-std-beta-aarch64-pc-windows-gnullvm.tar.gz=2d4c8a5a400c35443228087f8203f320253299a5018c1b92526f99023581c3e9
+dist/2025-02-18/rust-std-beta-aarch64-pc-windows-gnullvm.tar.xz=df8741055b3d4f7eeedec9e0101a1c184a37ffb75b2d4474bfbca577300355f2
+dist/2025-02-18/rust-std-beta-aarch64-pc-windows-msvc.tar.gz=8db9c07d4b68018cd3c67be1e7bc496078dfa8a6852a35c449db5ba19f6bf0df
+dist/2025-02-18/rust-std-beta-aarch64-pc-windows-msvc.tar.xz=f112e3d51013ceff37e8c05d80c3605b76ddec8e55474d55815b4860f96febc8
+dist/2025-02-18/rust-std-beta-aarch64-unknown-fuchsia.tar.gz=2bb6d934225823c2dcdb86dca91dd8e488f69b238b283607f75abe9b5de46486
+dist/2025-02-18/rust-std-beta-aarch64-unknown-fuchsia.tar.xz=99036fde94fe0cb3ab2d2f48cd9f9bbb4d2fa784f1bd21afaa71b032cd95b069
+dist/2025-02-18/rust-std-beta-aarch64-unknown-linux-gnu.tar.gz=c49fee34a02f5d3fe4e03ed7da90e045f7967c3e2e8f7a30804f4da5d535225c
+dist/2025-02-18/rust-std-beta-aarch64-unknown-linux-gnu.tar.xz=88170a13f9448b9671d252f0315aed94b6324716230db7307061d4890cfda70a
+dist/2025-02-18/rust-std-beta-aarch64-unknown-linux-musl.tar.gz=a5e7442d722f6742fd0436d3c7d75738bc6cbd936f3399190086a88ef311e34e
+dist/2025-02-18/rust-std-beta-aarch64-unknown-linux-musl.tar.xz=5d07a95e3203ebe98eed1b271a2e6ae44bead53e7bda019a8d256c8553c21bd1
+dist/2025-02-18/rust-std-beta-aarch64-unknown-linux-ohos.tar.gz=0af6b8fc7f46641d4de61ada9bc9ff01af7775d727121267177fa4c43cc9ed7b
+dist/2025-02-18/rust-std-beta-aarch64-unknown-linux-ohos.tar.xz=fd68b34e7aba25d8a834c018febbde6c3de7d967e014a738681642f41ec61603
+dist/2025-02-18/rust-std-beta-aarch64-unknown-none.tar.gz=46025b0892498de8e311be7bd6df261065f80a70720cb9285062161a30af5d76
+dist/2025-02-18/rust-std-beta-aarch64-unknown-none.tar.xz=b4cbf42364270c158d801ac7d4b6cfd7bf1f5f9e74a3044e6d959f1971f5bc34
+dist/2025-02-18/rust-std-beta-aarch64-unknown-none-softfloat.tar.gz=ae2a5c63cce6ce237c81ae78cabc6e1e0634c956789c2e2f39e3a6c0d864636f
+dist/2025-02-18/rust-std-beta-aarch64-unknown-none-softfloat.tar.xz=6bba5069c89d9a0d9a2013715814fb55ebcd77b342ed0dfc72115a18367c5e8c
+dist/2025-02-18/rust-std-beta-aarch64-unknown-uefi.tar.gz=0bef07ceece7523d6f41f1d640569c52ebe8a6b97b35da84240d873fd68250da
+dist/2025-02-18/rust-std-beta-aarch64-unknown-uefi.tar.xz=cff8b58e158786cee1a719552fb97cb2cd3a12b541641437809428a65eed42fa
+dist/2025-02-18/rust-std-beta-arm-linux-androideabi.tar.gz=a34fbf0d01cea60876a6d0aa4ee96587a5e31b6d4f84aa7c1ba5b7fed261b639
+dist/2025-02-18/rust-std-beta-arm-linux-androideabi.tar.xz=7d72d638f5984726fb4a61e81671d9557d0a9a876bf5bbf39b2df3c9983d2962
+dist/2025-02-18/rust-std-beta-arm-unknown-linux-gnueabi.tar.gz=9ef3aa1bcbe1a18658bd16359cbf3e94ae1b07f65bd5c69ffbfa964ad845baf5
+dist/2025-02-18/rust-std-beta-arm-unknown-linux-gnueabi.tar.xz=93020195c2ce07204179e2d2f900953707e341a71d9371551c4a727afc58378e
+dist/2025-02-18/rust-std-beta-arm-unknown-linux-gnueabihf.tar.gz=5b0a4c4831534de85a5ba5b0149bbd824ca83648bf66babe5dbf13291b06e03d
+dist/2025-02-18/rust-std-beta-arm-unknown-linux-gnueabihf.tar.xz=60f1ff95cc88330b6c9741520c02ec845e7b14943f2927091a9110b7fb1c4305
+dist/2025-02-18/rust-std-beta-arm-unknown-linux-musleabi.tar.gz=0f254a943b6e56873d2ab84e8a93fa7a3ab723df5a7926faeadae4696ed06121
+dist/2025-02-18/rust-std-beta-arm-unknown-linux-musleabi.tar.xz=0c682e7cbba8463682a0a20785ff7fd331d2bc7a32c0cf86b8159897fc5c5ee7
+dist/2025-02-18/rust-std-beta-arm-unknown-linux-musleabihf.tar.gz=6c5be45f79035f57ac41fd209f6e7d4439cab5acaa766f7bf001b24196411b1e
+dist/2025-02-18/rust-std-beta-arm-unknown-linux-musleabihf.tar.xz=bf25eb6650ad27377414d53af6857133a539530c841cf96e3cae4406bf4ad485
+dist/2025-02-18/rust-std-beta-arm64ec-pc-windows-msvc.tar.gz=39ec7fd0ecf47b36c6badc1fc71f1290e99d2edfa9d7cfa72446fbb32ba9c8b0
+dist/2025-02-18/rust-std-beta-arm64ec-pc-windows-msvc.tar.xz=f1d578e9251d95c94c20e3ee7888993ec7ea3d967a880f89963df7bea0bbe93b
+dist/2025-02-18/rust-std-beta-armebv7r-none-eabi.tar.gz=3990bce70402da79a27fd0f4adb169e4e9faf617bdbca2676bc41d9f85845dd7
+dist/2025-02-18/rust-std-beta-armebv7r-none-eabi.tar.xz=ac944400a3da94e32d7010e10b30879bbb5da456d3d54dfd0efc792884b035de
+dist/2025-02-18/rust-std-beta-armebv7r-none-eabihf.tar.gz=1196e2ae338b325ceb89edaab8b165478ec481270a7ce4c65f47f45d76e7b33b
+dist/2025-02-18/rust-std-beta-armebv7r-none-eabihf.tar.xz=fead57620d21252c958020e340fc793102d177f76fd90b3936eaf3a22a3b87c9
+dist/2025-02-18/rust-std-beta-armv5te-unknown-linux-gnueabi.tar.gz=e5e81e1501554c2b28745c4d905bbe50f909dce3b7806f6010a9d48cc528304e
+dist/2025-02-18/rust-std-beta-armv5te-unknown-linux-gnueabi.tar.xz=d3c9513cdb058685848de3d7864a7fa5a9b1e45f779a7ecf447ac7faae652772
+dist/2025-02-18/rust-std-beta-armv5te-unknown-linux-musleabi.tar.gz=6ca5598fbdbf77bcf971e62d5b3f486e25d01e95773670bdc600448f29b68a09
+dist/2025-02-18/rust-std-beta-armv5te-unknown-linux-musleabi.tar.xz=7e0a8798586538c4b322b58b9e1ac90e27dea4a0b5b750873f90b8ec9dcdbe2e
+dist/2025-02-18/rust-std-beta-armv7-linux-androideabi.tar.gz=cbd81d4f949248c0c48a60545648a384aa321ee5f590f52d50e8d3639a649745
+dist/2025-02-18/rust-std-beta-armv7-linux-androideabi.tar.xz=75216a970ba5a023717dbbd45b5a1a90ff9533f25deca241c11ebce80dc6229e
+dist/2025-02-18/rust-std-beta-armv7-unknown-linux-gnueabi.tar.gz=5acf37dc6035d3d83d003d70d3db3c196b20d461a70385e8e5d545be1f4392b4
+dist/2025-02-18/rust-std-beta-armv7-unknown-linux-gnueabi.tar.xz=8c75b0c815465126d6c7516883c478c78fc83cfb294bd5b9387d5bad65c9810f
+dist/2025-02-18/rust-std-beta-armv7-unknown-linux-gnueabihf.tar.gz=733fca96e3d6dd232c746a6034bd7cc864f06bfce3d5da3bfd72c5ca4cea221d
+dist/2025-02-18/rust-std-beta-armv7-unknown-linux-gnueabihf.tar.xz=e9ef142105d0073bf070a6de74e7173fbc09f3f22fd50eef0fea8ab6cf0ab4d7
+dist/2025-02-18/rust-std-beta-armv7-unknown-linux-musleabi.tar.gz=13f2b55f297628bea201f3caaae0178f0d898c67a696d4b60a37c3ba5af0582b
+dist/2025-02-18/rust-std-beta-armv7-unknown-linux-musleabi.tar.xz=455605ff7e88d69052a4b3798ba27a673807ac1be197a8ac9e57497b5bac1661
+dist/2025-02-18/rust-std-beta-armv7-unknown-linux-musleabihf.tar.gz=5b4b8eae2d86baeb470ad2a143f5d91f0dedeb225607189d9d0f8c8115e5251f
+dist/2025-02-18/rust-std-beta-armv7-unknown-linux-musleabihf.tar.xz=fd36a0f0a001436195eacdb52baee2462c43a1f9899b2e01ed60019f8285a95d
+dist/2025-02-18/rust-std-beta-armv7-unknown-linux-ohos.tar.gz=093900dfc07c4c4f59bd84aa8e9b505890cd8736a994798da8cfd7fb6210ab5b
+dist/2025-02-18/rust-std-beta-armv7-unknown-linux-ohos.tar.xz=e5cef28308348a378835ced449affa965f49d9b7382d28cbf32337ae94ebc9cd
+dist/2025-02-18/rust-std-beta-armv7a-none-eabi.tar.gz=210229d27f6d574670e9406b98748869212e66713a8ad37f2e5b67ebf27e43ab
+dist/2025-02-18/rust-std-beta-armv7a-none-eabi.tar.xz=323197f1dc3fe70e0a540df8a5f5d9475dcb390f1685bef54ba355ad3b48f316
+dist/2025-02-18/rust-std-beta-armv7r-none-eabi.tar.gz=f66d3a794ea7ea0df73b5df389738e6b3e1790b27c06187de2ed1888743ecb57
+dist/2025-02-18/rust-std-beta-armv7r-none-eabi.tar.xz=ae1465d82ea49e5ed33ac95dc0ece4c8fd0ce3df20b21a6a44ed93f33d131aca
+dist/2025-02-18/rust-std-beta-armv7r-none-eabihf.tar.gz=fa0d84655bfb7488c9c378ecf833edbde08c652d25fbc9092ed2707b320f657a
+dist/2025-02-18/rust-std-beta-armv7r-none-eabihf.tar.xz=251ba3b71c4a0bbdc327a84ee1b3c3deeeed3917fe55aadff9a52a44063f6270
+dist/2025-02-18/rust-std-beta-i586-pc-windows-msvc.tar.gz=9d9d89b206dc85323a7ee765447d1cafc2fab9189be88e1558709d94c51f2298
+dist/2025-02-18/rust-std-beta-i586-pc-windows-msvc.tar.xz=b124483bdffbb41b5c806f6bcc1003ba15d031cf5fe02eaead555abe15da20a6
+dist/2025-02-18/rust-std-beta-i586-unknown-linux-gnu.tar.gz=914925fb75c45cd9939c8692b02efd337b814040ca9bce369d812b97698a4c3e
+dist/2025-02-18/rust-std-beta-i586-unknown-linux-gnu.tar.xz=eefceae8f0d42a5e3524ac134afa9a13e938f1680edf605cca2e2d9dfbd33682
+dist/2025-02-18/rust-std-beta-i586-unknown-linux-musl.tar.gz=4baafd6924c5ab59c0c4dfd30272c08328ea1f31b70e8a9a3dababb94c1dee03
+dist/2025-02-18/rust-std-beta-i586-unknown-linux-musl.tar.xz=c75b6e4b50cd731d7f955956ce0afc02f04e2adc4268a1bec8b076eb1733ad28
+dist/2025-02-18/rust-std-beta-i686-linux-android.tar.gz=e067c5483336195763c2f1e9625644075fd93cc86180e6d24bee63aa26b22e99
+dist/2025-02-18/rust-std-beta-i686-linux-android.tar.xz=d4892feae881cc914b94b8bfd66b5e75cc4d62cbb697b8faa3f29d1d70d15f5f
+dist/2025-02-18/rust-std-beta-i686-pc-windows-gnu.tar.gz=ff6f43c40e2f8edc9ca21df771c3c28ab77bcb0e254adaa09d8c9433bd56fa97
+dist/2025-02-18/rust-std-beta-i686-pc-windows-gnu.tar.xz=8d6088d7ef7f13bbf576fe238e8a032091359a773845f35e3329b5d8273d1fcc
+dist/2025-02-18/rust-std-beta-i686-pc-windows-gnullvm.tar.gz=cc62a0e5c529bb041455ed15f646e5c9c918f20a644ed7306ad8beb1abf07c1d
+dist/2025-02-18/rust-std-beta-i686-pc-windows-gnullvm.tar.xz=9cdcd32f73d9839c5e6322f8b1e18e3abf825ddbbe9bb498616f0c058c5fb061
+dist/2025-02-18/rust-std-beta-i686-pc-windows-msvc.tar.gz=fecfb22d75b38e9c92c64790833c8ed8b4ea70c40d3d9c67a23277216c1246bb
+dist/2025-02-18/rust-std-beta-i686-pc-windows-msvc.tar.xz=a592d638118b0b95e8ef0e174b94c1825d0e3a3aab87d03737eb7415d1b32076
+dist/2025-02-18/rust-std-beta-i686-unknown-freebsd.tar.gz=e8546b2f4fe5746706d2b0d56fe174ee5993bd61a9fe451fd233236d7af0feee
+dist/2025-02-18/rust-std-beta-i686-unknown-freebsd.tar.xz=09efaf8f4f26ce6d466c1e20758901dc64348cdf390f4b878b4ee9542f50dbae
+dist/2025-02-18/rust-std-beta-i686-unknown-linux-gnu.tar.gz=e173f1ac1662deba8c719965d5d4c77e711cc0eac732d933b6c8ac021a44de5c
+dist/2025-02-18/rust-std-beta-i686-unknown-linux-gnu.tar.xz=600aa6b6ed1b49afdcc4978c897cd1e1f801193b804b0d1723319464c8378c88
+dist/2025-02-18/rust-std-beta-i686-unknown-linux-musl.tar.gz=38f7ee73e7067df2b98064b9f0ed00b8aa7c7eeae5955719fa0e2cf1f126ed19
+dist/2025-02-18/rust-std-beta-i686-unknown-linux-musl.tar.xz=6e639d9c45eda6b43ce0ce52e3213172df1875adfa0c13e52385b5aa9fa05da1
+dist/2025-02-18/rust-std-beta-i686-unknown-uefi.tar.gz=2ae2a44ad2610bbe3a97d176bbfd3e43efa22b5713c9591b9f3bcd7609f3a30b
+dist/2025-02-18/rust-std-beta-i686-unknown-uefi.tar.xz=d8fcf03e16ce9ada0253a2f8899ee9193083e1638d9e0da6cc76886b6ee14c91
+dist/2025-02-18/rust-std-beta-loongarch64-unknown-linux-gnu.tar.gz=cb0f09f7d81b1e49a761a94396dcb4e999dbbea7d70e044785992da9a2aa38e2
+dist/2025-02-18/rust-std-beta-loongarch64-unknown-linux-gnu.tar.xz=1cecfc8d91f5f3cb7bc67f49795d5e2c74638df0f7d66a3051a851522dae2860
+dist/2025-02-18/rust-std-beta-loongarch64-unknown-linux-musl.tar.gz=0369af7a8b56dac39448a22f1ad27df42c72ccdcb43cb96e7eaecf0c0d8b94e2
+dist/2025-02-18/rust-std-beta-loongarch64-unknown-linux-musl.tar.xz=a395fd16ea741c72b39db5d398a46a90cae8531be620a3468d0dc13f8a9979f0
+dist/2025-02-18/rust-std-beta-loongarch64-unknown-none.tar.gz=e2ce2c57ad140b9088052009efae96ed04ce14c258cd954ee049dfc9146242a7
+dist/2025-02-18/rust-std-beta-loongarch64-unknown-none.tar.xz=a055dd6cc47dad744fe9343d100750ea013cbe3a5bf4fcdac54e80809352a6d3
+dist/2025-02-18/rust-std-beta-loongarch64-unknown-none-softfloat.tar.gz=26f8ba1c9594f7336ad530eef0076f2d30752d2edcf96d944cc962dde3d3caff
+dist/2025-02-18/rust-std-beta-loongarch64-unknown-none-softfloat.tar.xz=32c80782a30c7bf300510e4fa29dad76d9a40bed2d5746c4d55b17fb261d788c
+dist/2025-02-18/rust-std-beta-nvptx64-nvidia-cuda.tar.gz=2cd25291aa0c7610f10ab04cdf4ba4d214e0c684bed73d2ef31ae84af2da0eaf
+dist/2025-02-18/rust-std-beta-nvptx64-nvidia-cuda.tar.xz=a7f02677a5c35388593142ef95cf56ffe1632df7fd42ff35bff89dafb34a0bdf
+dist/2025-02-18/rust-std-beta-powerpc-unknown-linux-gnu.tar.gz=64ee7c6fb36ea45490911ae5557f6a051728a05a7c88b1476489be87456be4bb
+dist/2025-02-18/rust-std-beta-powerpc-unknown-linux-gnu.tar.xz=250993b5a1915a8970d91f7a10f3f50c907cc9508d71d3b2c8e20914f90254a6
+dist/2025-02-18/rust-std-beta-powerpc64-unknown-linux-gnu.tar.gz=deb267998a725fb0288432a51aeac3004d6430fce8683c91d102b6708e5b99ea
+dist/2025-02-18/rust-std-beta-powerpc64-unknown-linux-gnu.tar.xz=49ba80f0a2970948699b78fc60ac09f77dda94e56931399c57c2c81fce8df9e4
+dist/2025-02-18/rust-std-beta-powerpc64le-unknown-linux-gnu.tar.gz=06ff5073e946ed2a5f80f9a5befd7b0c27e9a959cebf26b6cefc5762a0fcdb03
+dist/2025-02-18/rust-std-beta-powerpc64le-unknown-linux-gnu.tar.xz=27f12f5835f564b03f55d85121c9474b9fc93d3084c5b5d0fc63d33c8c9dcc64
+dist/2025-02-18/rust-std-beta-powerpc64le-unknown-linux-musl.tar.gz=78aff087bbef054b5a6a8d2a1649dd59f69b7f98635eb0c130c4ba87b3018850
+dist/2025-02-18/rust-std-beta-powerpc64le-unknown-linux-musl.tar.xz=e82b6bdfed001c91f8f88f6c37e31ef35f2f25cdca6fe8adfd90d024cc068e15
+dist/2025-02-18/rust-std-beta-riscv32i-unknown-none-elf.tar.gz=8dd6fdb684882ce4087bb96ec2d982c0d808c93c053c774b49cfc705959b4309
+dist/2025-02-18/rust-std-beta-riscv32i-unknown-none-elf.tar.xz=4d6db36241c3d8735e7c2ee84a37ae0cfbc2fc79c6fd259ca4b74e7edb68b8f0
+dist/2025-02-18/rust-std-beta-riscv32im-unknown-none-elf.tar.gz=32e8a2eb316b2a82f80e7268a318df89f732d7cbaba779debe10664aec7d40de
+dist/2025-02-18/rust-std-beta-riscv32im-unknown-none-elf.tar.xz=562030f1bdc1307bc0261a2e66c12a6f21fed0ed21fdb2140b1b1d55664aab00
+dist/2025-02-18/rust-std-beta-riscv32imac-unknown-none-elf.tar.gz=52e8c7ab87f5d2cbb712fb54ad8394b50a7f95961e1a7e5ffce981a962899a2c
+dist/2025-02-18/rust-std-beta-riscv32imac-unknown-none-elf.tar.xz=74f13bf5f0ff428c323c3041771b653d3ccf5a999105869378bf09a6ce6ca040
+dist/2025-02-18/rust-std-beta-riscv32imafc-unknown-none-elf.tar.gz=e2b8a784b4c6826d00a69de8e1891a40619f4b17c6e54c5cbed349fedefbd8f1
+dist/2025-02-18/rust-std-beta-riscv32imafc-unknown-none-elf.tar.xz=a231848519003a9ea1306a7da2139675516c0d94e5cbd8af140bb1a37515d7fa
+dist/2025-02-18/rust-std-beta-riscv32imc-unknown-none-elf.tar.gz=82d33d6527db7adaff8bec04c6edb4f3f7a1609fb5e5a91011a3b48dd47af93e
+dist/2025-02-18/rust-std-beta-riscv32imc-unknown-none-elf.tar.xz=bfe440ce80c4547afef2c6b8379bccde9f7206a5406c5a7ed5c06ab460ad8ba1
+dist/2025-02-18/rust-std-beta-riscv64gc-unknown-linux-gnu.tar.gz=728c559f432bf8487c345967a2ebc9e4eada676a6d56a68ec32d26ee9775857e
+dist/2025-02-18/rust-std-beta-riscv64gc-unknown-linux-gnu.tar.xz=51e7990ff209c27a15d40a684d634e5cad26d55913b897c2be6d92fcb849b7d8
+dist/2025-02-18/rust-std-beta-riscv64gc-unknown-linux-musl.tar.gz=ea66ba0e6af1fc7ce741c0838bc005abc04c6ad85c6831d2e05d04f30066281b
+dist/2025-02-18/rust-std-beta-riscv64gc-unknown-linux-musl.tar.xz=7429a295ccda168e41e9717647ba4639eceb81a73d66ad7ba01c9e7f1ca19c03
+dist/2025-02-18/rust-std-beta-riscv64gc-unknown-none-elf.tar.gz=01824c3ca49cfb433de4508282f0395bae8053866691869fb804a3058a403b02
+dist/2025-02-18/rust-std-beta-riscv64gc-unknown-none-elf.tar.xz=67e324c82c6a5c9fd279b9bf5ebabaecf35374c741e5728c2db3fbbb6eab1fd9
+dist/2025-02-18/rust-std-beta-riscv64imac-unknown-none-elf.tar.gz=466d4e5c1912937481fbd22d739987d663f3c771ff0efd676b34fc34e9266f87
+dist/2025-02-18/rust-std-beta-riscv64imac-unknown-none-elf.tar.xz=409c2870098222c09abcb1c89c7394633222bef2cba9756638e8cad058e3c524
+dist/2025-02-18/rust-std-beta-s390x-unknown-linux-gnu.tar.gz=b31e3389756f78bf680982d6c6415c3712567e5050beddae10d2df52de560e61
+dist/2025-02-18/rust-std-beta-s390x-unknown-linux-gnu.tar.xz=729b7433746947b88b8d4f488e2831bff1a92911b8c25f16a1208542e38d5834
+dist/2025-02-18/rust-std-beta-sparc64-unknown-linux-gnu.tar.gz=56caed9608e88e9ed6dec56ec3265d6464633ed57b14a455c626983defc87411
+dist/2025-02-18/rust-std-beta-sparc64-unknown-linux-gnu.tar.xz=9c9d16cac691185cf055117d6888a25f14c72cd110e3bc699c6e78b5d0dc70d8
+dist/2025-02-18/rust-std-beta-sparcv9-sun-solaris.tar.gz=6963215ffa1d2cf9650db8bc36f1eb5c97fb4d824a010c2a65f842fa00b4b4c0
+dist/2025-02-18/rust-std-beta-sparcv9-sun-solaris.tar.xz=9077823bd323a251347336f3c656c6847d5fefb90efed0cacadd510f42afbb74
+dist/2025-02-18/rust-std-beta-thumbv6m-none-eabi.tar.gz=f6d1b17e822403428a6ddf254caf78c75c2d91c208e035590779b51d45b7e081
+dist/2025-02-18/rust-std-beta-thumbv6m-none-eabi.tar.xz=1eb4e8ca0d8a52ddf754aec64b70e7a1f91d2746eef7e3c6d4580a0794cbfae3
+dist/2025-02-18/rust-std-beta-thumbv7em-none-eabi.tar.gz=7793950c2a0016ae90ee8fb70e881047a1f85c82f087fb02532009d6419ba6a8
+dist/2025-02-18/rust-std-beta-thumbv7em-none-eabi.tar.xz=14464141719b2b2c5d4a4b29685f3a2e336c2542ea33af01cfaec2ed7511baba
+dist/2025-02-18/rust-std-beta-thumbv7em-none-eabihf.tar.gz=62612b4da1872950af31bf3d9f0a595f019e6c4074874f4cdfa9166c5ff39434
+dist/2025-02-18/rust-std-beta-thumbv7em-none-eabihf.tar.xz=07ab29714f76693f69273a5567408307186b272061501a9ac83eebe944f66d31
+dist/2025-02-18/rust-std-beta-thumbv7m-none-eabi.tar.gz=b2da60ea5d746b607c505279b84aa2a8f3bac3e258ca867f6aeca958e50691c8
+dist/2025-02-18/rust-std-beta-thumbv7m-none-eabi.tar.xz=3eee9280fdd35f30ee9b4a2e5d8614ee916f36cd08629a574f6450721a1fe05b
+dist/2025-02-18/rust-std-beta-thumbv7neon-linux-androideabi.tar.gz=c5be169574de4fcd550b67d19834e064b81084e673764390b9f177631f3fb0ed
+dist/2025-02-18/rust-std-beta-thumbv7neon-linux-androideabi.tar.xz=bcd6ddf6493cbe17236a74c26da7e16a9a752fd39ab453866ae3301cff0c8375
+dist/2025-02-18/rust-std-beta-thumbv7neon-unknown-linux-gnueabihf.tar.gz=e52c2e8272a9ff9442ae6bafb7526cac424d15d4d6d1d8d210fe981b42da9b73
+dist/2025-02-18/rust-std-beta-thumbv7neon-unknown-linux-gnueabihf.tar.xz=81adef669c6f8f08b941b0befc6af30eb78b2717c3aacd52a1526d9545548c53
+dist/2025-02-18/rust-std-beta-thumbv8m.base-none-eabi.tar.gz=1036f47867b7d29ae468a040c50da016e20a6a3a291714df4193895234591c00
+dist/2025-02-18/rust-std-beta-thumbv8m.base-none-eabi.tar.xz=508b4f1e307d0b033eab345ab5106eb83ad8dad212f491f1ba92b9680f699bc6
+dist/2025-02-18/rust-std-beta-thumbv8m.main-none-eabi.tar.gz=41c806851db8e59dc9ad8a756c8efde7dc14e8ef5dc4feb81be9351d267b6157
+dist/2025-02-18/rust-std-beta-thumbv8m.main-none-eabi.tar.xz=f3bb9c783f4350e491fd683d057521f497314e85576b26808f4edc7d1d8612c6
+dist/2025-02-18/rust-std-beta-thumbv8m.main-none-eabihf.tar.gz=36a3e0675cc972fe6f4f860da47c9d4e0ac175191622c651fbb002207506b627
+dist/2025-02-18/rust-std-beta-thumbv8m.main-none-eabihf.tar.xz=c3287e3ebd4583a576f4bbdcfaa77fc21f6165b18573cbfdeb28593494ec14dc
+dist/2025-02-18/rust-std-beta-wasm32-unknown-emscripten.tar.gz=4a7128b50478a05d4fdad69715a0a7c9dcc745aab33cbc2e137cb670c6265001
+dist/2025-02-18/rust-std-beta-wasm32-unknown-emscripten.tar.xz=6e3b5981cca095c2414ad4d23f3e69a942a12c1d51dc9e055ad8409c5edbdcf9
+dist/2025-02-18/rust-std-beta-wasm32-unknown-unknown.tar.gz=3b0cbbb35daf13711e8f211da8e2ed4ade96f728be5e5ad3ca02e4d3e32b7262
+dist/2025-02-18/rust-std-beta-wasm32-unknown-unknown.tar.xz=4e50db69d0917f205a16fa3c4ee5e4012c000194ff75641ddeb49bbec9a2ec49
+dist/2025-02-18/rust-std-beta-wasm32-wasip1.tar.gz=d382d7f9787018480329518035fc4ce3247775633423e0f2940c3670cbdd4705
+dist/2025-02-18/rust-std-beta-wasm32-wasip1.tar.xz=e1d9c800db62bf606d9be9a75dd00ac7190acf3fd3592cbdeba67170ddb092aa
+dist/2025-02-18/rust-std-beta-wasm32-wasip1-threads.tar.gz=cc7cd664a483eace96710d341132de4f9cf2113f358722425ddcf84cfe821116
+dist/2025-02-18/rust-std-beta-wasm32-wasip1-threads.tar.xz=17156e3e57e304ab3ab10f8191a9952400182d2a46ebcd39d1cfde97b94c0756
+dist/2025-02-18/rust-std-beta-wasm32-wasip2.tar.gz=7ab89b39693bd1bc3f344dce6ad1c127ef38f64591c7be769d07603b2422800d
+dist/2025-02-18/rust-std-beta-wasm32-wasip2.tar.xz=2f53c60d0c0388ff644c17e341368e8b15d4c41b2827b03507d4efee100a1841
+dist/2025-02-18/rust-std-beta-wasm32v1-none.tar.gz=3277ec65149e708925ca7fc062cde92953d01569d2700d2831e6ff68768e5b30
+dist/2025-02-18/rust-std-beta-wasm32v1-none.tar.xz=24df037c2c4bcb3d593033d93b6e26aa1dc38161452220d7a73a23a696dc93bc
+dist/2025-02-18/rust-std-beta-x86_64-apple-darwin.tar.gz=3878e1506ee4642fdd1bd5e10025c9c289f8d03b7cea876d2348fabc2675b721
+dist/2025-02-18/rust-std-beta-x86_64-apple-darwin.tar.xz=46bdd522559b61848cfcbc8c55d95b190a134f2a0ac19e3c7ebfaea867f9780b
+dist/2025-02-18/rust-std-beta-x86_64-apple-ios.tar.gz=e914a0a4a2824f12764b0b91b0dd97a25416686839f35dea2aabde41c011f850
+dist/2025-02-18/rust-std-beta-x86_64-apple-ios.tar.xz=b44ac0cc8cab86ba9d000d2164786b5bdc115ace7990ead57aaba2b0e02750aa
+dist/2025-02-18/rust-std-beta-x86_64-apple-ios-macabi.tar.gz=497c75ec8b5f99a625d898b406d437b8902b8ad42ee1d629a0efd27cfee96e44
+dist/2025-02-18/rust-std-beta-x86_64-apple-ios-macabi.tar.xz=a53ed7bfd16d62a7f51509d32fb6cc6b1f3af331f4c4867cbc1eb57da5c3d521
+dist/2025-02-18/rust-std-beta-x86_64-fortanix-unknown-sgx.tar.gz=3cec5b6714f156e43c85b4b15377475bc38f65325f61de020bddfc2708b25a7b
+dist/2025-02-18/rust-std-beta-x86_64-fortanix-unknown-sgx.tar.xz=ea2cc73a03644647dec905100154a3238dd819fd0421f770dbc433cd6dc49f33
+dist/2025-02-18/rust-std-beta-x86_64-linux-android.tar.gz=2eaebfe572503b69e34674ed414357c514bcfbf013e6a2a7bb3fb14f501872e8
+dist/2025-02-18/rust-std-beta-x86_64-linux-android.tar.xz=66c4f03b907515831f9a48c8f5036d4c3115e814507a27abe1e423ef4a7e7c0b
+dist/2025-02-18/rust-std-beta-x86_64-pc-solaris.tar.gz=4c8f54f1709908a9edabd9e60d8492cda771db4b51fe766f2a2323a10f184e1e
+dist/2025-02-18/rust-std-beta-x86_64-pc-solaris.tar.xz=60d74169e80b3e1297b010501fa5a46a29a7541c84ceeff304b4c2ace7631540
+dist/2025-02-18/rust-std-beta-x86_64-pc-windows-gnu.tar.gz=9d043fbcd7e180052b903180114d50c71a2ccbeb22fff7020514b4b0a11a8123
+dist/2025-02-18/rust-std-beta-x86_64-pc-windows-gnu.tar.xz=f08905c0b8192f23f214465d757d93e30aee0250dda279d648e948961e0745ca
+dist/2025-02-18/rust-std-beta-x86_64-pc-windows-gnullvm.tar.gz=e35a42a9a6eccc4b738b389fdff24ba01ef23ff8d00fbd34100acafffb9c3763
+dist/2025-02-18/rust-std-beta-x86_64-pc-windows-gnullvm.tar.xz=a1fd2d1b1bb1fe9593df7835b5fb81e5c9453c0bbbc17b9ee5deb0512ad12550
+dist/2025-02-18/rust-std-beta-x86_64-pc-windows-msvc.tar.gz=feada263ce77cd0ec635ae78c8ce34099385a1f89d1eb680576a85a8adf0d75e
+dist/2025-02-18/rust-std-beta-x86_64-pc-windows-msvc.tar.xz=ee8def01984eba376773f7a055a18a4096750d26f96ab9150326dc60ece9fbe5
+dist/2025-02-18/rust-std-beta-x86_64-unknown-freebsd.tar.gz=7791a0155a0a3464d07b176383a4b61dcfaef4bf0247c58d447402fac3123186
+dist/2025-02-18/rust-std-beta-x86_64-unknown-freebsd.tar.xz=11bae401884cce0306b415bb1202c2c63f81395677057fbbe12e9302951a9d3d
+dist/2025-02-18/rust-std-beta-x86_64-unknown-fuchsia.tar.gz=4d05cd943f4e4250d50a5f732a64f32431a6cfef59b38bc087b1dbe1aa4b9561
+dist/2025-02-18/rust-std-beta-x86_64-unknown-fuchsia.tar.xz=7bdc7161d66b6c330f027f53b15f164c9ad4d55debe77b7c34971a877bf82caa
+dist/2025-02-18/rust-std-beta-x86_64-unknown-illumos.tar.gz=01edf8fdab00df25cfea2ade367dc4f66a1d08d11bfd9a7e56b65a723e14b517
+dist/2025-02-18/rust-std-beta-x86_64-unknown-illumos.tar.xz=9efccf8ba6fc68997123924cddd7008b15f1a57170d03597a370346fdc6c83d6
+dist/2025-02-18/rust-std-beta-x86_64-unknown-linux-gnu.tar.gz=9dbfabe77853e672d576e70332e07727882cd6af14dee9a00d5186b43763ce82
+dist/2025-02-18/rust-std-beta-x86_64-unknown-linux-gnu.tar.xz=04e31482a3ff18d8fdff40c4e95b22bc667bc0dd1ba06fadbe2479bae5d97288
+dist/2025-02-18/rust-std-beta-x86_64-unknown-linux-gnux32.tar.gz=ebb6e0c56d905ef064fa15449a72746daa9e3998f5aba5c6a3c132bc676228cd
+dist/2025-02-18/rust-std-beta-x86_64-unknown-linux-gnux32.tar.xz=818fc179c8380f0fb70c46e04086926a69b026591ab62cfbc1051abc5c80012a
+dist/2025-02-18/rust-std-beta-x86_64-unknown-linux-musl.tar.gz=c0a1c692a334821d61a9b92cd1332f047175d4335336db3e2129687ba96ce5bc
+dist/2025-02-18/rust-std-beta-x86_64-unknown-linux-musl.tar.xz=2c71b87db4cec49e3c620e6b4e0e07f1aaaffe69020b07c87546232ed1ad3b20
+dist/2025-02-18/rust-std-beta-x86_64-unknown-linux-ohos.tar.gz=c1bbf445dd0b187904af5871bf48a720151ef75fdd3dd13161dad01ef5e87bbc
+dist/2025-02-18/rust-std-beta-x86_64-unknown-linux-ohos.tar.xz=19963165ec2c35c15024869c16a707bdc90e72032ebf6e203cc67411ec8ba7c7
+dist/2025-02-18/rust-std-beta-x86_64-unknown-netbsd.tar.gz=63fbbd51047125fe30f1954ea19a05d6f051d2a77929952bab326c8ded6db8d3
+dist/2025-02-18/rust-std-beta-x86_64-unknown-netbsd.tar.xz=1c255d1de9c2629210575b862600bf369499a0eb8873178ceff4a337ba9dccfe
+dist/2025-02-18/rust-std-beta-x86_64-unknown-none.tar.gz=781f14a54c5e68acde1e68042905a48f687294e2bc61450f422a6e15bf6ab509
+dist/2025-02-18/rust-std-beta-x86_64-unknown-none.tar.xz=d45921b98740f3cce5833bc4fb1e36816f34b81e96c3ca2c76deff4744134c6c
+dist/2025-02-18/rust-std-beta-x86_64-unknown-redox.tar.gz=0cd76d3c5b051b0b2015cb08079ea3b5ecbb9b0b68779eb35946721b24c07008
+dist/2025-02-18/rust-std-beta-x86_64-unknown-redox.tar.xz=6dec719cdb7f35f6eafd901414525b40f030566bdf3fb55e3b05de162dbf0741
+dist/2025-02-18/rust-std-beta-x86_64-unknown-uefi.tar.gz=aad1e2cfd4f428acb67d9dac1fab4450eccfe9212f52f99f06c09899285b6b1e
+dist/2025-02-18/rust-std-beta-x86_64-unknown-uefi.tar.xz=4252b618ffc906949756ad28a5437d11b0353fe1d755ee1d23502a208bdee74c
+dist/2025-02-18/cargo-beta-aarch64-apple-darwin.tar.gz=2f7f459d2595d3f60bcef4ccbd3c72095a3ec54fa3f2221a647ae892c308d5b2
+dist/2025-02-18/cargo-beta-aarch64-apple-darwin.tar.xz=501feb9138f3c11a13fd649d6573924ead10f5e6fb1e759d880684bff11c12be
+dist/2025-02-18/cargo-beta-aarch64-pc-windows-msvc.tar.gz=b589177b0287a42ce17939dbbee92d883dfec88ebad33bf9a719a3cb2f446533
+dist/2025-02-18/cargo-beta-aarch64-pc-windows-msvc.tar.xz=d795082d304ecdde233a4b6bb3aa558392b2c5a1553fab213ee99648c49f7a51
+dist/2025-02-18/cargo-beta-aarch64-unknown-linux-gnu.tar.gz=8444d938514ad2d55769cf614774dfb4e5a27c34f8ba8b49329a2bcf0b000162
+dist/2025-02-18/cargo-beta-aarch64-unknown-linux-gnu.tar.xz=02e52fc13ae52066031ac40dad039ad752b83582e5f9e74ecef206aa9206ac16
+dist/2025-02-18/cargo-beta-aarch64-unknown-linux-musl.tar.gz=e89709bb552e7ee73de02b08c6d8820b07bccdc09777eff9716efabb2bdd50cd
+dist/2025-02-18/cargo-beta-aarch64-unknown-linux-musl.tar.xz=50944f0e51190f9db065764afc7a41a3a1e39767bd1ef5ec659c98fe9243cc98
+dist/2025-02-18/cargo-beta-arm-unknown-linux-gnueabi.tar.gz=0343dfc2f015d476b3aca59b33dd75f907f595bc24715e6c518194ab1a5dfec1
+dist/2025-02-18/cargo-beta-arm-unknown-linux-gnueabi.tar.xz=3dd6c378c10172b1abfdd1db2fa764d1b48153ac1b5065cf124399941d036e56
+dist/2025-02-18/cargo-beta-arm-unknown-linux-gnueabihf.tar.gz=6eac5ffb9adaf2591d3b1872249f4a959c04afdf6694e62dc850936aa8d35972
+dist/2025-02-18/cargo-beta-arm-unknown-linux-gnueabihf.tar.xz=484510e6b9519c2b047864584253c8c1f725ce843e8dd3117349032314737462
+dist/2025-02-18/cargo-beta-armv7-unknown-linux-gnueabihf.tar.gz=fcf4d4b2d401ea689ee32f38b4a61c77b52ff4d3d114b23d0262bd02c3d1ab5d
+dist/2025-02-18/cargo-beta-armv7-unknown-linux-gnueabihf.tar.xz=1a75296c42d6cf93a5e51ecb2b2bfbf0cdea350668d610e8b0c90937163b4f33
+dist/2025-02-18/cargo-beta-i686-pc-windows-gnu.tar.gz=d1b5147ec79466ab9441825b4761e89cebe57ad21a3199b0eca0779683c45ed6
+dist/2025-02-18/cargo-beta-i686-pc-windows-gnu.tar.xz=980850e06ccd90512baebbfe3baaa65b5c089edbae8146fd940c708fd7556abc
+dist/2025-02-18/cargo-beta-i686-pc-windows-msvc.tar.gz=78c67ba481cc7b855b54a2edc0297bfb5eadcd83d5a028b05291415417bc6947
+dist/2025-02-18/cargo-beta-i686-pc-windows-msvc.tar.xz=6ea74c750e6e0a794965c6616e5b1a82dee295815b0c24a3898952b1a5a02c8a
+dist/2025-02-18/cargo-beta-i686-unknown-linux-gnu.tar.gz=a0c8f67fe77b94674698b4816c081e2ef08500990c0f9eb069d0190df9c4f129
+dist/2025-02-18/cargo-beta-i686-unknown-linux-gnu.tar.xz=9b13f958ec4edff194858e90c54866a129e88769a76fe22582b07e17540708c3
+dist/2025-02-18/cargo-beta-loongarch64-unknown-linux-gnu.tar.gz=f4d2f1c8a7bec6d2b8378aa87a324c42f0f414962174b40b6f0d1dc21d0cacf4
+dist/2025-02-18/cargo-beta-loongarch64-unknown-linux-gnu.tar.xz=f25659ef3be1a28da8821eb178e02108f41cd5c0b87aa01a052a3a90e7a9da50
+dist/2025-02-18/cargo-beta-loongarch64-unknown-linux-musl.tar.gz=b687a7c3fac08680818804e034af6689f1bbcf8fc0e406c99b2a49ddae1e900d
+dist/2025-02-18/cargo-beta-loongarch64-unknown-linux-musl.tar.xz=e33818d2f94e8ba148dbf8cd53d6396e370157acba6d0865a5ac71d292939a0a
+dist/2025-02-18/cargo-beta-powerpc-unknown-linux-gnu.tar.gz=9cdc2115bd2022cb0b0ac4f1c71142c064957137decc028dde7585acabb8b780
+dist/2025-02-18/cargo-beta-powerpc-unknown-linux-gnu.tar.xz=f722447494f0a7af9c31b71364aa4a2fde79a9887440d8281e4561f7d71620d3
+dist/2025-02-18/cargo-beta-powerpc64-unknown-linux-gnu.tar.gz=d8a30f9e48e89cb1d18532fd7fbef8eeceef6bc602ffaf9e730c635e2fdbb156
+dist/2025-02-18/cargo-beta-powerpc64-unknown-linux-gnu.tar.xz=ec6b3b1595c5761b8092a04a412e79534c28910ed78ed792c9af7ddc4d92a389
+dist/2025-02-18/cargo-beta-powerpc64le-unknown-linux-gnu.tar.gz=4bf9c73173199fd3ca952d33702288deb483c7fd392179352135b64e7e8fadb4
+dist/2025-02-18/cargo-beta-powerpc64le-unknown-linux-gnu.tar.xz=12aa06644e07344a77d0c16dc1ed96bfba63de02df1f432d57b82f8a1e2282b6
+dist/2025-02-18/cargo-beta-powerpc64le-unknown-linux-musl.tar.gz=eab362146d449dcdd10740c8e6f9dec6e640ffcca162eb51febbc835e34acdfd
+dist/2025-02-18/cargo-beta-powerpc64le-unknown-linux-musl.tar.xz=867b716eae9225c66c0ab4642e4e497055ad2effd24343bdf80354ce2845d6f9
+dist/2025-02-18/cargo-beta-riscv64gc-unknown-linux-gnu.tar.gz=0503ba2e4b1d668fa3a3174f96ec3fcc12ff767574a7af8ef5fc824ca86dc965
+dist/2025-02-18/cargo-beta-riscv64gc-unknown-linux-gnu.tar.xz=478b9f1d4f517f93f15530d27a7d55ea29cdd06eccf01176ffd08c0ee3204c1f
+dist/2025-02-18/cargo-beta-s390x-unknown-linux-gnu.tar.gz=419a679aaf6e164649538916daf28081a063bd56c7b86ff0e2df58072f334fdc
+dist/2025-02-18/cargo-beta-s390x-unknown-linux-gnu.tar.xz=31b39e242035036c1101412b7c86387e1183063ff5f6ce67d311d0764435a588
+dist/2025-02-18/cargo-beta-x86_64-apple-darwin.tar.gz=d6d5224228dd54b6d16be30271111620c02fd88e656b8bbd22452b66b0d5cb56
+dist/2025-02-18/cargo-beta-x86_64-apple-darwin.tar.xz=cbeff7c031b4ab4732207b90ee8849b27f03cb28d3e085bf4d70444347dbfc99
+dist/2025-02-18/cargo-beta-x86_64-pc-windows-gnu.tar.gz=ae5922955ec579732aacd8112bc53bf5333438eb81d0f81dc5f387888ac979a3
+dist/2025-02-18/cargo-beta-x86_64-pc-windows-gnu.tar.xz=553c55a2bc8eae2c8ba831823a97f2232808af1f753642ec4cdd09c33dd3f6ae
+dist/2025-02-18/cargo-beta-x86_64-pc-windows-msvc.tar.gz=d9b2cf14565478429fba6266f0c86a58f3bbd1ce11a63268828b33ccb55759cf
+dist/2025-02-18/cargo-beta-x86_64-pc-windows-msvc.tar.xz=2e8c0560355d11a038219072d2503860f5c5b3cd137b89ad931f54d9bba60499
+dist/2025-02-18/cargo-beta-x86_64-unknown-freebsd.tar.gz=4b528361607845e6f8ece403bbdb8d77c6159ec137396319562ea2772502792f
+dist/2025-02-18/cargo-beta-x86_64-unknown-freebsd.tar.xz=c244ec4f97420c29c690e32bd6d8f14994bf1d990747f31a3dc0f2b37644493e
+dist/2025-02-18/cargo-beta-x86_64-unknown-illumos.tar.gz=7d3b49df93e87322a9e99e36c6df020d3311c5538ad2298d8b5d8f2d9ad3e0d3
+dist/2025-02-18/cargo-beta-x86_64-unknown-illumos.tar.xz=136ce90487448ee770472d4bd2c0d9c96200f0ec762419feb42fefa26ba36b58
+dist/2025-02-18/cargo-beta-x86_64-unknown-linux-gnu.tar.gz=510538965ea142405925d3f4f03a87783516407989b8aa7be07fb84c0680e9fa
+dist/2025-02-18/cargo-beta-x86_64-unknown-linux-gnu.tar.xz=a8c569398d71cab0b90c809b1d869a2e3c5037407b5af804df08c205775981c2
+dist/2025-02-18/cargo-beta-x86_64-unknown-linux-musl.tar.gz=1493f66e5cb12e8b955841804e7df745e26daea2278144aa57670192c4c62cef
+dist/2025-02-18/cargo-beta-x86_64-unknown-linux-musl.tar.xz=472bbe49415557b75b572879d0c33750731c1fe9e56cc6ef3b0fd5532e56446c
+dist/2025-02-18/cargo-beta-x86_64-unknown-netbsd.tar.gz=d3baa6019e13449c01fbeebebce42027ce4ba70841cf28733f6849e7c6ce5d89
+dist/2025-02-18/cargo-beta-x86_64-unknown-netbsd.tar.xz=0db9af42e9ad914a99db4924721c895cdf490bc5351bc8c2d8993e3569e952e4
+dist/2025-02-18/clippy-beta-aarch64-apple-darwin.tar.gz=6ce3b464744082131c99a213454225e5702564c83032e11dd0f3d95530a4e0af
+dist/2025-02-18/clippy-beta-aarch64-apple-darwin.tar.xz=925baff0d91b959a99c989a4ada70973baf3da03e1d7e7e8be1fa334c3acbc50
+dist/2025-02-18/clippy-beta-aarch64-pc-windows-msvc.tar.gz=76d265c2fb6280eb8ed399501964f301a50e09e548cee708ebacc978d8db538c
+dist/2025-02-18/clippy-beta-aarch64-pc-windows-msvc.tar.xz=e3d1d69cbc838ab639b2e41537b10266f1b6998a228882d4041f35cbb8984909
+dist/2025-02-18/clippy-beta-aarch64-unknown-linux-gnu.tar.gz=918191cad727d795b51f892abb6e4fc87ed735bfcb51433cfb4371cb8be8690c
+dist/2025-02-18/clippy-beta-aarch64-unknown-linux-gnu.tar.xz=157d6eb9e7dd80dda7daae5584d095a5e68d13ba8b1d1229e90b5b51f6670e8d
+dist/2025-02-18/clippy-beta-aarch64-unknown-linux-musl.tar.gz=fd4de290450e3d3c9188263d9506167e298f4c2d4c781fb3573806694b5ca1ba
+dist/2025-02-18/clippy-beta-aarch64-unknown-linux-musl.tar.xz=335c5fd24d1c118e4d1d8abc43d3529dafc9b30af39f6b009924cd0fea676182
+dist/2025-02-18/clippy-beta-arm-unknown-linux-gnueabi.tar.gz=209cdd2042e293d1061b149d97333515821dda0ffeb9b30f24dd2dbb4c1ad548
+dist/2025-02-18/clippy-beta-arm-unknown-linux-gnueabi.tar.xz=57dbfdc34ff4e74aaa8776b80d35aaf0317fdc314ee4b0f3bf58fc672963cf38
+dist/2025-02-18/clippy-beta-arm-unknown-linux-gnueabihf.tar.gz=a89a2562fe9ac6492a5de220c395f433b0e53a8b29c72b3a8d4843f996649ca6
+dist/2025-02-18/clippy-beta-arm-unknown-linux-gnueabihf.tar.xz=0e6ff6453df1397e7b12e7e356e7c7300cfb1f85bc3a6705735067128d2a8135
+dist/2025-02-18/clippy-beta-armv7-unknown-linux-gnueabihf.tar.gz=7764c2dbaf217f4b1d8ca4ed2ceaacbb91e8010d26ae1bb10b808afd5dc6a798
+dist/2025-02-18/clippy-beta-armv7-unknown-linux-gnueabihf.tar.xz=b7a042eb6830653a623c6299ef57492ae6c9b51a9cd8480f0a6fc2fb6d378bbb
+dist/2025-02-18/clippy-beta-i686-pc-windows-gnu.tar.gz=ff41e5b666b3531ccdc0ee836dceb270179d6a849e47024eedc6ea309d36791a
+dist/2025-02-18/clippy-beta-i686-pc-windows-gnu.tar.xz=98c6386b63ed89dd96beea223f4c720320b6ca460f11b77c46427e128034cbbb
+dist/2025-02-18/clippy-beta-i686-pc-windows-msvc.tar.gz=12bd371402e4d3251f60fa29b3b3aca88b4480229088b68791dffbb4ae1554ce
+dist/2025-02-18/clippy-beta-i686-pc-windows-msvc.tar.xz=62a22d1e9b4fdba254e549f5edb276bef5252754ba67a76526d5aeca85515646
+dist/2025-02-18/clippy-beta-i686-unknown-linux-gnu.tar.gz=04179b3a20a68ae2fd8444832ea4edd8155dc9296c7a1edf101053e3ff934891
+dist/2025-02-18/clippy-beta-i686-unknown-linux-gnu.tar.xz=c955e464e99097025fc7d0e42bf90342f74f475c5148526df2f51ca46ce8db4d
+dist/2025-02-18/clippy-beta-loongarch64-unknown-linux-gnu.tar.gz=6f58d326a1f2a35246384640b999f95a961a9fe1e5a3c4a3e67d7f96e1ef43fb
+dist/2025-02-18/clippy-beta-loongarch64-unknown-linux-gnu.tar.xz=583b1d55a38fdd473a3f3f3cc0fbeac808fbeb593b8b45c7a6b8a09dec0d68cf
+dist/2025-02-18/clippy-beta-loongarch64-unknown-linux-musl.tar.gz=80d73fe94ecbbcdb14b35e1616d52e0bb9b1f04dcdd4fc384e7103cc36325014
+dist/2025-02-18/clippy-beta-loongarch64-unknown-linux-musl.tar.xz=1bae908e4d3931eb4dc71ac1524f990bbccaadd9d30897bf516899baebd4c5d5
+dist/2025-02-18/clippy-beta-powerpc-unknown-linux-gnu.tar.gz=b882e7ea97e0d36b289a46ef84b14b2b44ccea93ebdc77b2ab3430361ad15b1f
+dist/2025-02-18/clippy-beta-powerpc-unknown-linux-gnu.tar.xz=91e52dc398dccbafd040e401f155c204f2a14d5f62aecfc24912910d5e45008d
+dist/2025-02-18/clippy-beta-powerpc64-unknown-linux-gnu.tar.gz=c81497f2b2d2670b9dea2e35804e10780c1631a8761d4bc3331767c1ccaad6b9
+dist/2025-02-18/clippy-beta-powerpc64-unknown-linux-gnu.tar.xz=6236e129f3e551a3126fa07662cc8ad512ad5fcf436a8da9e27a915ad4c234cf
+dist/2025-02-18/clippy-beta-powerpc64le-unknown-linux-gnu.tar.gz=1b031f055ea4ab4a9da0c8bfb0037e92b493caf7879351f8b404a96c447ec92c
+dist/2025-02-18/clippy-beta-powerpc64le-unknown-linux-gnu.tar.xz=2f07224e49f7cab10d673116d9dee9f964b4c44ac3db336e7ddbab9e0b1bc698
+dist/2025-02-18/clippy-beta-powerpc64le-unknown-linux-musl.tar.gz=84a8295a7066d258404c87d2269a01d89cc7efd0f143ab53689597f3fb351417
+dist/2025-02-18/clippy-beta-powerpc64le-unknown-linux-musl.tar.xz=019460a6b0e1c5c1331d5a54f3e45306ba0d1b1c2206e2e69f38c1c3501cf741
+dist/2025-02-18/clippy-beta-riscv64gc-unknown-linux-gnu.tar.gz=aba8f7bd0a89205961f9803418cdf9bb63a7d72bab4a1ff937f4a142921f4833
+dist/2025-02-18/clippy-beta-riscv64gc-unknown-linux-gnu.tar.xz=4607503ba131ca655dab86ad48c1737f216f8ec25ba6dfddbf1f0807e5a6dd2a
+dist/2025-02-18/clippy-beta-s390x-unknown-linux-gnu.tar.gz=b0170f9b9e13d4f6e016124fd40c858e00155479fb4f96c1edc896130bb76dcd
+dist/2025-02-18/clippy-beta-s390x-unknown-linux-gnu.tar.xz=397f0a8944beffb87c74717bd0f422836b3daccfa848d91c0a65875031bf12fa
+dist/2025-02-18/clippy-beta-x86_64-apple-darwin.tar.gz=11be90b9b09f4e24e56df2a602ed8161023314514121d9f76e424fb628df468c
+dist/2025-02-18/clippy-beta-x86_64-apple-darwin.tar.xz=16d313798f12d0e74f888b627e079606958de58eb2ca4c70d5f7de8cce72620c
+dist/2025-02-18/clippy-beta-x86_64-pc-windows-gnu.tar.gz=41ed081e4a7b205cde3fe958db18ffe1c1ac5a9c336f965a03fe89499a3eddbd
+dist/2025-02-18/clippy-beta-x86_64-pc-windows-gnu.tar.xz=daaa3267dcbb3daa0cae47861683f29da5b7419214ec5949e242aa93e3f87148
+dist/2025-02-18/clippy-beta-x86_64-pc-windows-msvc.tar.gz=6d9b8f549e34d8fb65987b39c01c27fbb0e18e8950a07ec6fd1888d01e253971
+dist/2025-02-18/clippy-beta-x86_64-pc-windows-msvc.tar.xz=1c26b51c484f5b10ee693212e258fb86c63a2b59e631542918799d480a3587d4
+dist/2025-02-18/clippy-beta-x86_64-unknown-freebsd.tar.gz=a42777c542773a4262ac9fcb07ed13f769957f58a795160441241ad4e4f5258a
+dist/2025-02-18/clippy-beta-x86_64-unknown-freebsd.tar.xz=9e809fa141ac68557f203aec2a6cc02a4ddd22169595c40ed4a964801ccadb1d
+dist/2025-02-18/clippy-beta-x86_64-unknown-illumos.tar.gz=4e65b5db249a18e73247029506f619393be7042f2c91a00301962effb76a18ea
+dist/2025-02-18/clippy-beta-x86_64-unknown-illumos.tar.xz=6714018d069fbce271c177f1941a6b77f18b67af462596aff273f7db1970ef4a
+dist/2025-02-18/clippy-beta-x86_64-unknown-linux-gnu.tar.gz=72a8ee3bad3d638af0cc3ba6c0c0ed1594abe609573de71d9c7a884a8ac7645c
+dist/2025-02-18/clippy-beta-x86_64-unknown-linux-gnu.tar.xz=f622befd60c3695d3d62642b953deabc3e3f04b32cae8a7cc228cb534244adbc
+dist/2025-02-18/clippy-beta-x86_64-unknown-linux-musl.tar.gz=8946e02ab98281b6949c4d725a5fe3fb1bfc86651a675e451f126fec8d053762
+dist/2025-02-18/clippy-beta-x86_64-unknown-linux-musl.tar.xz=425907bd4320c516647e5dd6df6154746d64854fe2e78f27283b7fcb27164de7
+dist/2025-02-18/clippy-beta-x86_64-unknown-netbsd.tar.gz=505208d2d73ed7278e5b29d8450c382a89e4c0992d6199243e07c8640c611b85
+dist/2025-02-18/clippy-beta-x86_64-unknown-netbsd.tar.xz=d4b0306e7d7db5cb7da977a7ca72fff2070f102ac385c3b35286da25033582af
+dist/2025-02-18/rustfmt-nightly-aarch64-apple-darwin.tar.gz=13854358645915af29d9337bb87301aa1a5e76ecc86a934dd640d02af3255ea2
+dist/2025-02-18/rustfmt-nightly-aarch64-apple-darwin.tar.xz=0cd190bd5a064235f9cd6f6e7eae4725b3b53ae36493b3ea54b8f190372ba3ee
+dist/2025-02-18/rustfmt-nightly-aarch64-pc-windows-msvc.tar.gz=20b3f8c4c7b02158181970804d419b16f85a1083943e1c384e0bcef441f32aff
+dist/2025-02-18/rustfmt-nightly-aarch64-pc-windows-msvc.tar.xz=1ace0f10060d2fc35a1f05a1d61adebf95212e8b46a2234c2e78030481c1b3e9
+dist/2025-02-18/rustfmt-nightly-aarch64-unknown-linux-gnu.tar.gz=e5ad64d67931df49f6b7d3448e9860699d6549c2b4a96abda1a03069b45f339b
+dist/2025-02-18/rustfmt-nightly-aarch64-unknown-linux-gnu.tar.xz=e8112ae80c8fd43452612b79dabf471be561b7e828c9a2141c698429d761a49b
+dist/2025-02-18/rustfmt-nightly-aarch64-unknown-linux-musl.tar.gz=8ad36bbe888e61b6d7e540ba4a2478652f14c1b28dbbcaab732003293b7c985b
+dist/2025-02-18/rustfmt-nightly-aarch64-unknown-linux-musl.tar.xz=1794af320a273f4a951942ff0cd73a359fd82a971c572af1ab7ff2961340791c
+dist/2025-02-18/rustfmt-nightly-arm-unknown-linux-gnueabi.tar.gz=67b9d1d3c46bcce0aa94d6cb4155cdf7376677caf2e19685553266f781eb7cc1
+dist/2025-02-18/rustfmt-nightly-arm-unknown-linux-gnueabi.tar.xz=9d7b97a85b1368cfc78f1df42afb08aa04eb17fbb91ceb3c379d59fab4294890
+dist/2025-02-18/rustfmt-nightly-arm-unknown-linux-gnueabihf.tar.gz=42396c7bd206d0fa07f3b44fcb894ac074e2af07eb158c1ef630bb5467151c8f
+dist/2025-02-18/rustfmt-nightly-arm-unknown-linux-gnueabihf.tar.xz=6881c9681342f1430fd9cc8c9786cee40522be3dcd0fc4dcf2b3957d9d53f03e
+dist/2025-02-18/rustfmt-nightly-armv7-unknown-linux-gnueabihf.tar.gz=6cb8973ac4215d67aad9bb56521024626d7883a6e00748623a728dd9107698db
+dist/2025-02-18/rustfmt-nightly-armv7-unknown-linux-gnueabihf.tar.xz=920d877e04260be6ccf9963bea12220e886df673cbc48b55925650f8f5b21d9f
+dist/2025-02-18/rustfmt-nightly-i686-pc-windows-gnu.tar.gz=ec5c7105f2c2a2055f704435fff9edbbee0a234e63050676c034f983d4e341ee
+dist/2025-02-18/rustfmt-nightly-i686-pc-windows-gnu.tar.xz=ab408b64db4bd47abf8e24c0f611f2c67a3c9ce7e2138a92772e0510c6dce5f9
+dist/2025-02-18/rustfmt-nightly-i686-pc-windows-msvc.tar.gz=d04250bc2e57fcb03f1d80722f2ac1117f797d768262d7630c9b53af808a8c9d
+dist/2025-02-18/rustfmt-nightly-i686-pc-windows-msvc.tar.xz=6d7be9f04a46040f068544e32eb84c98afc1f81b750beb229def84a7513150fb
+dist/2025-02-18/rustfmt-nightly-i686-unknown-linux-gnu.tar.gz=94f585a7c666dc9c95c3ae14744e614a3fbe37b3e31b476b48bc4ef3289bdd46
+dist/2025-02-18/rustfmt-nightly-i686-unknown-linux-gnu.tar.xz=83e82932245b3c1d3c275126184f942b9cb8bf20f0d40c7cb1efb6f918327b9d
+dist/2025-02-18/rustfmt-nightly-loongarch64-unknown-linux-gnu.tar.gz=9c8cb5a14294c48f8ee972d5f460751a8bae5b541bff3d872c85812b621c39d8
+dist/2025-02-18/rustfmt-nightly-loongarch64-unknown-linux-gnu.tar.xz=4f616740bef30599e6950ae2d8ae694513003e14122f6002a9d60bdc64f77cfc
+dist/2025-02-18/rustfmt-nightly-loongarch64-unknown-linux-musl.tar.gz=a668e7be7946f6a9099486a40256f17832288322c9237d931f172ed76caf678d
+dist/2025-02-18/rustfmt-nightly-loongarch64-unknown-linux-musl.tar.xz=b02c57c217f358ff4344ee2afcbb8353cffc0a012f970d9520ad233d188d59e2
+dist/2025-02-18/rustfmt-nightly-powerpc-unknown-linux-gnu.tar.gz=9baf04e268999020372b21484ecc4f5aa001b3bcee05619ae5b0a11571a3a45f
+dist/2025-02-18/rustfmt-nightly-powerpc-unknown-linux-gnu.tar.xz=b2c2c3a0eecca7b4c35eabf09e215158c48eb426899e365db1c91c1c560ad255
+dist/2025-02-18/rustfmt-nightly-powerpc64-unknown-linux-gnu.tar.gz=c42df8e575a24a5edbc58b5b46a89a226188d2aafc59da89d568acbb6e9cceb6
+dist/2025-02-18/rustfmt-nightly-powerpc64-unknown-linux-gnu.tar.xz=fee7c373bb8fee5c7d4e43d7a7046d3bb156d3e496ed21cddf20c21ffdef3e69
+dist/2025-02-18/rustfmt-nightly-powerpc64le-unknown-linux-gnu.tar.gz=a0ae21a3b56e20e667c72d92ad4bacd4abb77d1fea32949d7dd62a56d6b531d3
+dist/2025-02-18/rustfmt-nightly-powerpc64le-unknown-linux-gnu.tar.xz=fc7e21cbd61169b37e81c73d4d5353d7e54ca322fd01491d9f37ff3666557e7e
+dist/2025-02-18/rustfmt-nightly-powerpc64le-unknown-linux-musl.tar.gz=e08ceaa9241c2e53041b107af09674ca4fbc4e12b1371254869e3d80d5c907ab
+dist/2025-02-18/rustfmt-nightly-powerpc64le-unknown-linux-musl.tar.xz=9f208dc6e5938a128174263349345e9fe7f587689f6a53fe5d026ae3c6fbed2c
+dist/2025-02-18/rustfmt-nightly-riscv64gc-unknown-linux-gnu.tar.gz=44abe49dfc0492cb3ab28f0aae0d784c982063399a38f8f13016f3dde123948a
+dist/2025-02-18/rustfmt-nightly-riscv64gc-unknown-linux-gnu.tar.xz=72fa294f92ab244608c384d6ad6eea0e540e6fc58181fd65a4fce0c5029c39fd
+dist/2025-02-18/rustfmt-nightly-s390x-unknown-linux-gnu.tar.gz=556dcf393856c6d9c9c6731b02e34146f9f956f588025b6a614b8802032d125a
+dist/2025-02-18/rustfmt-nightly-s390x-unknown-linux-gnu.tar.xz=93906364910891f71e13e0ecb07f02b707e42e11a0f1b117c590804dfa16a9d1
+dist/2025-02-18/rustfmt-nightly-x86_64-apple-darwin.tar.gz=7022c760f457075fb353e59984dcb551600581a315fd17ea1320b892b1d99a55
+dist/2025-02-18/rustfmt-nightly-x86_64-apple-darwin.tar.xz=e085ee261acbaa41361a0263dd8f13644f8a4e3679eca6bb0851b03c647b80e8
+dist/2025-02-18/rustfmt-nightly-x86_64-pc-windows-gnu.tar.gz=2436e18fefae02554e620a1131455d923c1b67e00e878c75a941a6eedb4eae28
+dist/2025-02-18/rustfmt-nightly-x86_64-pc-windows-gnu.tar.xz=9f6e37b5973925d07115e517e6a3e490a221218f48fd101a20670da2c3d01add
+dist/2025-02-18/rustfmt-nightly-x86_64-pc-windows-msvc.tar.gz=78f492a26981e7a6f9f3e8bbd1e880682e55cede081f3cfb13a2ec5f736edbb2
+dist/2025-02-18/rustfmt-nightly-x86_64-pc-windows-msvc.tar.xz=d4d788fa3e3b9a598aa7baec47af3b56362224533be85dd68a411f35f8800b2d
+dist/2025-02-18/rustfmt-nightly-x86_64-unknown-freebsd.tar.gz=8922186dedf0b12e9aa2bb041b76be8fccd43b9322c3496c688355f34bbd7a59
+dist/2025-02-18/rustfmt-nightly-x86_64-unknown-freebsd.tar.xz=6325e43f2c28118a9525f5c5fc09de9fb9a09ffb4aaad31f28d394c233ee8398
+dist/2025-02-18/rustfmt-nightly-x86_64-unknown-illumos.tar.gz=7f317107f8319c650973cef52f745c2a8e57ea6a087910b6979404f2cb2f1cff
+dist/2025-02-18/rustfmt-nightly-x86_64-unknown-illumos.tar.xz=b273be4897e83f786020bce1dc5dd1caf01e2ea43f085391c0031277718feba0
+dist/2025-02-18/rustfmt-nightly-x86_64-unknown-linux-gnu.tar.gz=0373011266a9ab62f45fa137a4bfdb970ccad8bbbb74bf776466d203f28d226b
+dist/2025-02-18/rustfmt-nightly-x86_64-unknown-linux-gnu.tar.xz=62748525678370dbda7f1cbd12a384e95d4af76103de998785278f6d1f076177
+dist/2025-02-18/rustfmt-nightly-x86_64-unknown-linux-musl.tar.gz=30eeb980720d8c22bc45c012c4fd212e118195206b8b993630f074e526e6693a
+dist/2025-02-18/rustfmt-nightly-x86_64-unknown-linux-musl.tar.xz=2f3843d9544fa56dc87f1d13ef79c68c1e68d84bec007f0345159a7448368dfe
+dist/2025-02-18/rustfmt-nightly-x86_64-unknown-netbsd.tar.gz=a86eaeab8f5f45d16eaf355a46d8b19afcd8c46db732432091156e381aa79cb6
+dist/2025-02-18/rustfmt-nightly-x86_64-unknown-netbsd.tar.xz=144cd28da601c331f6501c634ad89286be213a69cf521435e35657c5f6fe2afd
+dist/2025-02-18/rustc-nightly-aarch64-apple-darwin.tar.gz=21101db859a550ab39de1ecdec75f4f058934ed8e0ab7dfadbb2efab57bc5e0a
+dist/2025-02-18/rustc-nightly-aarch64-apple-darwin.tar.xz=a40da26e908810701112b9d3f9ab83515c8e2f4b33219f8f773c5459366d37e1
+dist/2025-02-18/rustc-nightly-aarch64-pc-windows-msvc.tar.gz=5391bc993b7208c10a6f4256417c9f76a4a33d167b2fd547b57af614d3fc8458
+dist/2025-02-18/rustc-nightly-aarch64-pc-windows-msvc.tar.xz=d72a205f52173b1726d8341a9436e62439b1987fe0cd4d2bbff740e38f629b41
+dist/2025-02-18/rustc-nightly-aarch64-unknown-linux-gnu.tar.gz=185e052bf2ba1cda4b6ad1c49a9f65b906899ad1ca09cd864d6041941ad344dc
+dist/2025-02-18/rustc-nightly-aarch64-unknown-linux-gnu.tar.xz=640f7af3fef5f0abacdaa292ce17255433ee16f12bfc2d2e81d70afcf9fcdd8f
+dist/2025-02-18/rustc-nightly-aarch64-unknown-linux-musl.tar.gz=8cdee468a90606b21a8cc3305f2b6b3eb7e3d687263f4ca8319c709cda2a324f
+dist/2025-02-18/rustc-nightly-aarch64-unknown-linux-musl.tar.xz=358bbfb24816c781e07d5480a5606dce27068f856c0a606873ceba05c9540c3c
+dist/2025-02-18/rustc-nightly-arm-unknown-linux-gnueabi.tar.gz=b220757cc8ed39c429c49b55e293ded3e07239d2e2bab8a9054ce55581393c47
+dist/2025-02-18/rustc-nightly-arm-unknown-linux-gnueabi.tar.xz=18d3cbc49e44b7810b498517d21390e39bee7ca8351c8b321592e83301ad79e9
+dist/2025-02-18/rustc-nightly-arm-unknown-linux-gnueabihf.tar.gz=77ac127bae48859f57d9bd8337ac4a0bda95b1190e451abeaf7e77f1a240a647
+dist/2025-02-18/rustc-nightly-arm-unknown-linux-gnueabihf.tar.xz=79009bbf0325f9db0c905d74cb0f139c92478a0b2fb5edaf383366b0e654a172
+dist/2025-02-18/rustc-nightly-armv7-unknown-linux-gnueabihf.tar.gz=8264c7ed7bc47953ae76cf87236655562a55846abb195bc9281833a0da168db6
+dist/2025-02-18/rustc-nightly-armv7-unknown-linux-gnueabihf.tar.xz=c6a85e7f2a469fb05f0a84ceaea25dc0d95b57bd4d68dcee447918162cb36b2a
+dist/2025-02-18/rustc-nightly-i686-pc-windows-gnu.tar.gz=0f31c9ed4ba7e942806fc2927d305f9d3ad676cf81e53e0f7c4123aef375fedc
+dist/2025-02-18/rustc-nightly-i686-pc-windows-gnu.tar.xz=350f4b46dee2aa6ebfc31912cfae5c7c1db99b298c52834308e95c504cadbe7d
+dist/2025-02-18/rustc-nightly-i686-pc-windows-msvc.tar.gz=e12a40b7178995ac223eb7fa317e1114a988256d99fe615f70d64cf3f2891fa7
+dist/2025-02-18/rustc-nightly-i686-pc-windows-msvc.tar.xz=09fb4a4744a55bf8fd6371e5496f6a9c00b119ddf20b19855653d66d9a618214
+dist/2025-02-18/rustc-nightly-i686-unknown-linux-gnu.tar.gz=52361a34730f8bf14eefad7e51f66fc3ba3be405a364d4520103208fe8fdc056
+dist/2025-02-18/rustc-nightly-i686-unknown-linux-gnu.tar.xz=ea81964fc8bcb8b43968acb6a4f7fdec2ddea9f75a8be5446fb01b4267d0d75f
+dist/2025-02-18/rustc-nightly-loongarch64-unknown-linux-gnu.tar.gz=11e7a143112b6abf818652e425b6f092464cc9d8f5286e90180c7af8d5939643
+dist/2025-02-18/rustc-nightly-loongarch64-unknown-linux-gnu.tar.xz=86f5d40eecccb623ff744bbc1fb40458949db9b2f8501cadc6be9bca95f6c693
+dist/2025-02-18/rustc-nightly-loongarch64-unknown-linux-musl.tar.gz=1be7ecdf609958fb0ef945203b22a600f1e343ee7fc581f6451eecd39f1e0b7e
+dist/2025-02-18/rustc-nightly-loongarch64-unknown-linux-musl.tar.xz=03d3b67092da64f7b7be7ba4e115cfad4071c0bea3abb5e60ac166127713b169
+dist/2025-02-18/rustc-nightly-powerpc-unknown-linux-gnu.tar.gz=4e33311490c2dcf79a16307e1141ce37574ec3829a1e93d0f5b06ff68ad5c861
+dist/2025-02-18/rustc-nightly-powerpc-unknown-linux-gnu.tar.xz=504c102fa1c8dc5042cb354a60b87c0f6d47f9adab341f496af723f3ea8bd548
+dist/2025-02-18/rustc-nightly-powerpc64-unknown-linux-gnu.tar.gz=c559971ae42a5e0e999e2fe119033bffbfe8c64b767e697e0b95f5dfc271d13e
+dist/2025-02-18/rustc-nightly-powerpc64-unknown-linux-gnu.tar.xz=51159ab0ade5a62e128b6c794da5c9815324470c468e5808b50663c3a0df6d39
+dist/2025-02-18/rustc-nightly-powerpc64le-unknown-linux-gnu.tar.gz=a8134f8f7ea4531d54ab5c0a0894f140ce5182a384c57395edbe06ed551970ab
+dist/2025-02-18/rustc-nightly-powerpc64le-unknown-linux-gnu.tar.xz=8f2f5d88ba9f4ee946db5f00d56c4852213dcb0b46ce2793966af6c96d934dc6
+dist/2025-02-18/rustc-nightly-powerpc64le-unknown-linux-musl.tar.gz=7bfc6666757b57fa6160d076a9eb52852dcb5bf788bd12c0f014bbd552b8a7ef
+dist/2025-02-18/rustc-nightly-powerpc64le-unknown-linux-musl.tar.xz=e2c3293e2a90012ad48bbc2b6f285adf20f356ff1f4146b3f11543511bbb3c44
+dist/2025-02-18/rustc-nightly-riscv64gc-unknown-linux-gnu.tar.gz=f582a59b5a6cbe6d58400c69f1cad4f34d0f1541c1ffb9df3ce71846edd828fe
+dist/2025-02-18/rustc-nightly-riscv64gc-unknown-linux-gnu.tar.xz=244384bd94f7c3606a086abc8be9d36a855faf09d9bb6b121f42e168c526e717
+dist/2025-02-18/rustc-nightly-s390x-unknown-linux-gnu.tar.gz=327e29662140cb76b1ff362fe51f9a12fb4ec304929317ed8de7295fdb9c5b9f
+dist/2025-02-18/rustc-nightly-s390x-unknown-linux-gnu.tar.xz=a2c4e19b3a8dabd5c8fe72edd60d1925947cad5971c77c62f12fca764c6e4e7a
+dist/2025-02-18/rustc-nightly-x86_64-apple-darwin.tar.gz=b6812f92e08cff1b706380637fdd553b04b9cea746ffb69e6488fbec70680136
+dist/2025-02-18/rustc-nightly-x86_64-apple-darwin.tar.xz=f2502b2a810938c41e2302a9112c97d04998ada16583373e6488bcca00595761
+dist/2025-02-18/rustc-nightly-x86_64-pc-windows-gnu.tar.gz=9159f7323718c6a8b98f7de5186ed507b3dca1bfcce9bdfa6437313bd630a989
+dist/2025-02-18/rustc-nightly-x86_64-pc-windows-gnu.tar.xz=10d54c9b80570b6729d0e68f017d862c5126f09b42f150d004aa8fd5382b165c
+dist/2025-02-18/rustc-nightly-x86_64-pc-windows-msvc.tar.gz=1f29c1aef1dcc3ff9f2e3013b7ea0521829e6e474f9058cb70fea65180230b41
+dist/2025-02-18/rustc-nightly-x86_64-pc-windows-msvc.tar.xz=18cddaf7653f146dcc8a44ca0cc33c6dd3c24f28fff87aedca01fe3beaaa9915
+dist/2025-02-18/rustc-nightly-x86_64-unknown-freebsd.tar.gz=e57c1477cf9b75e727db48b8b75435d65aa0a6ef0eb566ac1890b576c330f64f
+dist/2025-02-18/rustc-nightly-x86_64-unknown-freebsd.tar.xz=725f9e9d25b7ad6f60d5596684964b0c3a63d98a62a2aeda6a134b9f02fdb681
+dist/2025-02-18/rustc-nightly-x86_64-unknown-illumos.tar.gz=86a9034fa275c0fc73d639fe6437fc4d2621bf12897c9f83a81ab6056173a95f
+dist/2025-02-18/rustc-nightly-x86_64-unknown-illumos.tar.xz=4aeef66ad8a908589ddf0d89581a6ca809b7c2a57f06bbda6fd3927b531fe07b
+dist/2025-02-18/rustc-nightly-x86_64-unknown-linux-gnu.tar.gz=907aa282fb9f3d96c9fb03dadd62f4ea766e1242fe19106ae331a4f49a9b20f0
+dist/2025-02-18/rustc-nightly-x86_64-unknown-linux-gnu.tar.xz=e63d13a6acd596ebfd7bf65b292eedd2a5e260b536ca11517a3fe1d8e6a6241f
+dist/2025-02-18/rustc-nightly-x86_64-unknown-linux-musl.tar.gz=b7bee098c32b321551f68e96e002f85e3a0323c864aa7f65641a58ae24f4238e
+dist/2025-02-18/rustc-nightly-x86_64-unknown-linux-musl.tar.xz=e5c22c2fab89c7ba548b11104c54e8f82cafb1979ba73a318682bb10ed9e4fb9
+dist/2025-02-18/rustc-nightly-x86_64-unknown-netbsd.tar.gz=7bf40bffe95437244f6f8a5fde69499f42d2b716e166115530fbcdbdcd837887
+dist/2025-02-18/rustc-nightly-x86_64-unknown-netbsd.tar.xz=0adeaa382f289233ffc9229e116a340ac03a861f0fdbb5bd35aaf5d0d7370877
\ No newline at end of file
diff --git a/src/tools/build-manifest/src/main.rs b/src/tools/build-manifest/src/main.rs
index c21bf82b852..cd5ca74f8ad 100644
--- a/src/tools/build-manifest/src/main.rs
+++ b/src/tools/build-manifest/src/main.rs
@@ -67,6 +67,7 @@ static TARGETS: &[&str] = &[
     "aarch64-unknown-none-softfloat",
     "aarch64-unknown-redox",
     "aarch64-unknown-uefi",
+    "amdgcn-amd-amdhsa",
     "arm64e-apple-darwin",
     "arm64e-apple-ios",
     "arm64e-apple-tvos",
@@ -99,6 +100,7 @@ static TARGETS: &[&str] = &[
     "i586-pc-windows-msvc",
     "i586-unknown-linux-gnu",
     "i586-unknown-linux-musl",
+    "i586-unknown-redox",
     "i686-apple-darwin",
     "i686-linux-android",
     "i686-pc-windows-gnu",
@@ -107,7 +109,6 @@ static TARGETS: &[&str] = &[
     "i686-unknown-freebsd",
     "i686-unknown-linux-gnu",
     "i686-unknown-linux-musl",
-    "i686-unknown-redox",
     "i686-unknown-uefi",
     "loongarch64-unknown-linux-gnu",
     "loongarch64-unknown-linux-musl",
diff --git a/src/tools/cargo b/src/tools/cargo
-Subproject 2928e32734b04925ee51e1ae88bea9a83d2fd45
+Subproject 1d1d646c06a84c1aa53967b394b7f1218f85db8
diff --git a/src/tools/clippy/clippy_lints/src/arbitrary_source_item_ordering.rs b/src/tools/clippy/clippy_lints/src/arbitrary_source_item_ordering.rs
index f519a65fc27..c0ae4960e10 100644
--- a/src/tools/clippy/clippy_lints/src/arbitrary_source_item_ordering.rs
+++ b/src/tools/clippy/clippy_lints/src/arbitrary_source_item_ordering.rs
@@ -338,7 +338,7 @@ impl<'tcx> LateLintPass<'tcx> for ArbitrarySourceItemOrdering {
             return;
         }
 
-        let items = module.item_ids.iter().map(|&id| cx.tcx.hir().item(id));
+        let items = module.item_ids.iter().map(|&id| cx.tcx.hir_item(id));
 
         // Iterates over the items within a module.
         //
@@ -362,7 +362,7 @@ impl<'tcx> LateLintPass<'tcx> for ArbitrarySourceItemOrdering {
                     }
                 } else if let ItemKind::ForeignMod { .. } = item.kind {
                     continue;
-                } else if let ItemKind::GlobalAsm(_) = item.kind {
+                } else if let ItemKind::GlobalAsm { .. } = item.kind {
                     continue;
                 } else if let ItemKind::Use(path, use_kind) = item.kind {
                     if path.segments.is_empty() {
@@ -467,7 +467,7 @@ fn convert_module_item_kind(value: &ItemKind<'_>) -> SourceItemOrderingModuleIte
         ItemKind::Macro(..) => Macro,
         ItemKind::Mod(..) => Mod,
         ItemKind::ForeignMod { .. } => ForeignMod,
-        ItemKind::GlobalAsm(..) => GlobalAsm,
+        ItemKind::GlobalAsm { .. } => GlobalAsm,
         ItemKind::TyAlias(..) => TyAlias,
         ItemKind::Enum(..) => Enum,
         ItemKind::Struct(..) => Struct,
diff --git a/src/tools/clippy/clippy_lints/src/assigning_clones.rs b/src/tools/clippy/clippy_lints/src/assigning_clones.rs
index c01155ca86e..348495f97a2 100644
--- a/src/tools/clippy/clippy_lints/src/assigning_clones.rs
+++ b/src/tools/clippy/clippy_lints/src/assigning_clones.rs
@@ -107,7 +107,7 @@ impl<'tcx> LateLintPass<'tcx> for AssigningClones {
             && !cx.tcx.is_builtin_derived(resolved_impl)
             // Don't suggest calling a function we're implementing.
             && resolved_impl.as_local().is_none_or(|block_id| {
-                cx.tcx.hir().parent_owner_iter(e.hir_id).all(|(id, _)| id.def_id != block_id)
+                cx.tcx.hir_parent_owner_iter(e.hir_id).all(|(id, _)| id.def_id != block_id)
             })
             && let resolved_assoc_items = cx.tcx.associated_items(resolved_impl)
             // Only suggest if `clone_from`/`clone_into` is explicitly implemented
diff --git a/src/tools/clippy/clippy_lints/src/async_yields_async.rs b/src/tools/clippy/clippy_lints/src/async_yields_async.rs
index eeaa3de3725..013819b0da8 100644
--- a/src/tools/clippy/clippy_lints/src/async_yields_async.rs
+++ b/src/tools/clippy/clippy_lints/src/async_yields_async.rs
@@ -60,12 +60,12 @@ impl<'tcx> LateLintPass<'tcx> for AsyncYieldsAsync {
                 // XXXkhuey maybe we should?
                 return;
             },
-            CoroutineSource::Block => cx.tcx.hir().body(*body_id).value,
+            CoroutineSource::Block => cx.tcx.hir_body(*body_id).value,
             CoroutineSource::Closure => {
                 // Like `async fn`, async closures are wrapped in an additional block
                 // to move all of the closure's arguments into the future.
 
-                let async_closure_body = cx.tcx.hir().body(*body_id).value;
+                let async_closure_body = cx.tcx.hir_body(*body_id).value;
                 let ExprKind::Block(block, _) = async_closure_body.kind else {
                     return;
                 };
diff --git a/src/tools/clippy/clippy_lints/src/attrs/inline_always.rs b/src/tools/clippy/clippy_lints/src/attrs/inline_always.rs
index 2325f914b0b..cb63fadb4e2 100644
--- a/src/tools/clippy/clippy_lints/src/attrs/inline_always.rs
+++ b/src/tools/clippy/clippy_lints/src/attrs/inline_always.rs
@@ -20,7 +20,7 @@ pub(super) fn check(cx: &LateContext<'_>, span: Span, name: Symbol, attrs: &[Att
                 span_lint(
                     cx,
                     INLINE_ALWAYS,
-                    attr.span,
+                    attr.span(),
                     format!("you have declared `#[inline(always)]` on `{name}`. This is usually a bad idea"),
                 );
             }
diff --git a/src/tools/clippy/clippy_lints/src/attrs/repr_attributes.rs b/src/tools/clippy/clippy_lints/src/attrs/repr_attributes.rs
index 6d1ab46aa0c..6cc47596bbb 100644
--- a/src/tools/clippy/clippy_lints/src/attrs/repr_attributes.rs
+++ b/src/tools/clippy/clippy_lints/src/attrs/repr_attributes.rs
@@ -1,6 +1,7 @@
+use rustc_attr_parsing::{find_attr, AttributeKind, ReprAttr};
 use rustc_hir::Attribute;
 use rustc_lint::LateContext;
-use rustc_span::{Span, sym};
+use rustc_span::Span;
 
 use clippy_utils::diagnostics::span_lint_and_then;
 use clippy_utils::msrvs;
@@ -14,30 +15,21 @@ pub(super) fn check(cx: &LateContext<'_>, item_span: Span, attrs: &[Attribute],
 }
 
 fn check_packed(cx: &LateContext<'_>, item_span: Span, attrs: &[Attribute]) {
-    if let Some(items) = attrs.iter().find_map(|attr| {
-        if attr.ident().is_some_and(|ident| matches!(ident.name, sym::repr)) {
-            attr.meta_item_list()
-        } else {
-            None
+    if let Some(reprs) = find_attr!(attrs, AttributeKind::Repr(r) => r) {
+        let packed_span = reprs.iter().find(|(r, _)| matches!(r, ReprAttr::ReprPacked(..))).map(|(_, s)| *s);
+
+        if let Some(packed_span) = packed_span && !reprs.iter().any(|(x, _)| *x == ReprAttr::ReprC || *x == ReprAttr::ReprRust) {
+            span_lint_and_then(
+                cx,
+                REPR_PACKED_WITHOUT_ABI,
+                item_span,
+                "item uses `packed` representation without ABI-qualification",
+                |diag| {
+                    diag.warn("unqualified `#[repr(packed)]` defaults to `#[repr(Rust, packed)]`, which has no stable ABI")
+                        .help("qualify the desired ABI explicity via `#[repr(C, packed)]` or `#[repr(Rust, packed)]`")
+                        .span_label(packed_span, "`packed` representation set here");
+                },
+            );
         }
-    }) && let Some(packed) = items
-        .iter()
-        .find(|item| item.ident().is_some_and(|ident| matches!(ident.name, sym::packed)))
-        && !items.iter().any(|item| {
-            item.ident()
-                .is_some_and(|ident| matches!(ident.name, sym::C | sym::Rust))
-        })
-    {
-        span_lint_and_then(
-            cx,
-            REPR_PACKED_WITHOUT_ABI,
-            item_span,
-            "item uses `packed` representation without ABI-qualification",
-            |diag| {
-                diag.warn("unqualified `#[repr(packed)]` defaults to `#[repr(Rust, packed)]`, which has no stable ABI")
-                    .help("qualify the desired ABI explicity via `#[repr(C, packed)]` or `#[repr(Rust, packed)]`")
-                    .span_label(packed.span(), "`packed` representation set here");
-            },
-        );
     }
 }
diff --git a/src/tools/clippy/clippy_lints/src/attrs/unnecessary_clippy_cfg.rs b/src/tools/clippy/clippy_lints/src/attrs/unnecessary_clippy_cfg.rs
index 478ba7a187b..6ee3290fa76 100644
--- a/src/tools/clippy/clippy_lints/src/attrs/unnecessary_clippy_cfg.rs
+++ b/src/tools/clippy/clippy_lints/src/attrs/unnecessary_clippy_cfg.rs
@@ -15,7 +15,7 @@ pub(super) fn check(
 ) {
     if cfg_attr.has_name(sym::clippy)
         && let Some(ident) = behind_cfg_attr.ident()
-        && Level::from_symbol(ident.name, Some(attr.id)).is_some()
+        && Level::from_symbol(ident.name, || Some(attr.id)).is_some()
         && let Some(items) = behind_cfg_attr.meta_item_list()
     {
         let nb_items = items.len();
diff --git a/src/tools/clippy/clippy_lints/src/attrs/utils.rs b/src/tools/clippy/clippy_lints/src/attrs/utils.rs
index 152e6ec70a1..0e650e49392 100644
--- a/src/tools/clippy/clippy_lints/src/attrs/utils.rs
+++ b/src/tools/clippy/clippy_lints/src/attrs/utils.rs
@@ -17,12 +17,12 @@ pub(super) fn is_word(nmi: &MetaItemInner, expected: Symbol) -> bool {
 }
 
 pub(super) fn is_lint_level(symbol: Symbol, attr_id: AttrId) -> bool {
-    Level::from_symbol(symbol, Some(attr_id)).is_some()
+    Level::from_symbol(symbol, || Some(attr_id)).is_some()
 }
 
 pub(super) fn is_relevant_item(cx: &LateContext<'_>, item: &Item<'_>) -> bool {
     if let ItemKind::Fn { body: eid, .. } = item.kind {
-        is_relevant_expr(cx, cx.tcx.typeck_body(eid), cx.tcx.hir().body(eid).value)
+        is_relevant_expr(cx, cx.tcx.typeck_body(eid), cx.tcx.hir_body(eid).value)
     } else {
         true
     }
@@ -30,7 +30,7 @@ pub(super) fn is_relevant_item(cx: &LateContext<'_>, item: &Item<'_>) -> bool {
 
 pub(super) fn is_relevant_impl(cx: &LateContext<'_>, item: &ImplItem<'_>) -> bool {
     match item.kind {
-        ImplItemKind::Fn(_, eid) => is_relevant_expr(cx, cx.tcx.typeck_body(eid), cx.tcx.hir().body(eid).value),
+        ImplItemKind::Fn(_, eid) => is_relevant_expr(cx, cx.tcx.typeck_body(eid), cx.tcx.hir_body(eid).value),
         _ => false,
     }
 }
@@ -39,7 +39,7 @@ pub(super) fn is_relevant_trait(cx: &LateContext<'_>, item: &TraitItem<'_>) -> b
     match item.kind {
         TraitItemKind::Fn(_, TraitFn::Required(_)) => true,
         TraitItemKind::Fn(_, TraitFn::Provided(eid)) => {
-            is_relevant_expr(cx, cx.tcx.typeck_body(eid), cx.tcx.hir().body(eid).value)
+            is_relevant_expr(cx, cx.tcx.typeck_body(eid), cx.tcx.hir_body(eid).value)
         },
         _ => false,
     }
diff --git a/src/tools/clippy/clippy_lints/src/booleans.rs b/src/tools/clippy/clippy_lints/src/booleans.rs
index f8c30d1c881..f30f16997d7 100644
--- a/src/tools/clippy/clippy_lints/src/booleans.rs
+++ b/src/tools/clippy/clippy_lints/src/booleans.rs
@@ -452,7 +452,7 @@ fn simplify_not(cx: &LateContext<'_>, curr_msrv: &Msrv, expr: &Expr<'_>) -> Opti
                 })
         },
         ExprKind::Closure(closure) => {
-            let body = cx.tcx.hir().body(closure.body);
+            let body = cx.tcx.hir_body(closure.body);
             let params = body
                 .params
                 .iter()
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 e4c0db5d9ef..57a135abc2e 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
@@ -66,7 +66,7 @@ fn is_used_as_unaligned(cx: &LateContext<'_>, e: &Expr<'_>) -> bool {
             if matches!(name.ident.as_str(), "read_unaligned" | "write_unaligned")
                 && let Some(def_id) = cx.typeck_results().type_dependent_def_id(parent.hir_id)
                 && let Some(def_id) = cx.tcx.impl_of_method(def_id)
-                && cx.tcx.type_of(def_id).instantiate_identity().is_unsafe_ptr()
+                && cx.tcx.type_of(def_id).instantiate_identity().is_raw_ptr()
             {
                 true
             } else {
diff --git a/src/tools/clippy/clippy_lints/src/collection_is_never_read.rs b/src/tools/clippy/clippy_lints/src/collection_is_never_read.rs
index 8276e53648c..1279be34ed8 100644
--- a/src/tools/clippy/clippy_lints/src/collection_is_never_read.rs
+++ b/src/tools/clippy/clippy_lints/src/collection_is_never_read.rs
@@ -118,7 +118,7 @@ fn has_no_read_access<'tcx, T: Visitable<'tcx>>(cx: &LateContext<'tcx>, id: HirI
             let is_read_in_closure_arg = args.iter().any(|arg| {
                 if let ExprKind::Closure(closure) = arg.kind
                     // To keep things simple, we only check the first param to see if its read.
-                    && let Body { params: [param, ..], value } = cx.tcx.hir().body(closure.body)
+                    && let Body { params: [param, ..], value } = cx.tcx.hir_body(closure.body)
                 {
                     !has_no_read_access(cx, param.hir_id, *value)
                 } else {
diff --git a/src/tools/clippy/clippy_lints/src/default_numeric_fallback.rs b/src/tools/clippy/clippy_lints/src/default_numeric_fallback.rs
index 772268e7899..784214c29af 100644
--- a/src/tools/clippy/clippy_lints/src/default_numeric_fallback.rs
+++ b/src/tools/clippy/clippy_lints/src/default_numeric_fallback.rs
@@ -52,11 +52,10 @@ declare_lint_pass!(DefaultNumericFallback => [DEFAULT_NUMERIC_FALLBACK]);
 
 impl<'tcx> LateLintPass<'tcx> for DefaultNumericFallback {
     fn check_body(&mut self, cx: &LateContext<'tcx>, body: &Body<'tcx>) {
-        let hir = cx.tcx.hir();
         // NOTE: this is different from `clippy_utils::is_inside_always_const_context`.
         // Inline const supports type inference.
         let is_parent_const = matches!(
-            hir.body_const_context(hir.body_owner_def_id(body.id())),
+            cx.tcx.hir_body_const_context(cx.tcx.hir_body_owner_def_id(body.id())),
             Some(ConstContext::Const { inline: false } | ConstContext::Static(_))
         );
         let mut visitor = NumericFallbackVisitor::new(cx, is_parent_const);
diff --git a/src/tools/clippy/clippy_lints/src/default_union_representation.rs b/src/tools/clippy/clippy_lints/src/default_union_representation.rs
index 9f020d3081c..6e6d81db11c 100644
--- a/src/tools/clippy/clippy_lints/src/default_union_representation.rs
+++ b/src/tools/clippy/clippy_lints/src/default_union_representation.rs
@@ -1,10 +1,10 @@
 use clippy_utils::diagnostics::span_lint_and_then;
+use rustc_attr_parsing::{find_attr, AttributeKind, ReprAttr};
 use rustc_hir::{HirId, Item, ItemKind};
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_middle::ty::layout::LayoutOf;
 use rustc_middle::ty::{self, FieldDef};
 use rustc_session::declare_lint_pass;
-use rustc_span::sym;
 
 declare_clippy_lint! {
     /// ### What it does
@@ -97,16 +97,7 @@ fn is_zst<'tcx>(cx: &LateContext<'tcx>, field: &FieldDef, args: ty::GenericArgsR
 }
 
 fn has_c_repr_attr(cx: &LateContext<'_>, hir_id: HirId) -> bool {
-    cx.tcx.hir().attrs(hir_id).iter().any(|attr| {
-        if attr.has_name(sym::repr) {
-            if let Some(items) = attr.meta_item_list() {
-                for item in items {
-                    if item.is_word() && matches!(item.name_or_empty(), sym::C) {
-                        return true;
-                    }
-                }
-            }
-        }
-        false
-    })
+    let attrs = cx.tcx.hir().attrs(hir_id);
+
+    find_attr!(attrs, AttributeKind::Repr(r) if r.iter().any(|(x, _)| *x == ReprAttr::ReprC))
 }
diff --git a/src/tools/clippy/clippy_lints/src/dereference.rs b/src/tools/clippy/clippy_lints/src/dereference.rs
index 233ebe00d8e..849c60b89b9 100644
--- a/src/tools/clippy/clippy_lints/src/dereference.rs
+++ b/src/tools/clippy/clippy_lints/src/dereference.rs
@@ -682,7 +682,7 @@ fn try_parse_ref_op<'tcx>(
             },
             [arg],
         ) => (true, typeck.qpath_res(path, *hir_id).opt_def_id()?, arg),
-        ExprKind::Unary(UnOp::Deref, sub_expr) if !typeck.expr_ty(sub_expr).is_unsafe_ptr() => {
+        ExprKind::Unary(UnOp::Deref, sub_expr) if !typeck.expr_ty(sub_expr).is_raw_ptr() => {
             return Some((RefOp::Deref, sub_expr));
         },
         ExprKind::AddrOf(BorrowKind::Ref, mutability, sub_expr) => return Some((RefOp::AddrOf(mutability), sub_expr)),
diff --git a/src/tools/clippy/clippy_lints/src/derivable_impls.rs b/src/tools/clippy/clippy_lints/src/derivable_impls.rs
index 9569081ad08..bb445e0155f 100644
--- a/src/tools/clippy/clippy_lints/src/derivable_impls.rs
+++ b/src/tools/clippy/clippy_lints/src/derivable_impls.rs
@@ -197,7 +197,7 @@ impl<'tcx> LateLintPass<'tcx> for DerivableImpls {
             && let impl_item_hir = child.id.hir_id()
             && let Node::ImplItem(impl_item) = cx.tcx.hir_node(impl_item_hir)
             && let ImplItemKind::Fn(_, b) = &impl_item.kind
-            && let Body { value: func_expr, .. } = cx.tcx.hir().body(*b)
+            && let Body { value: func_expr, .. } = cx.tcx.hir_body(*b)
             && let &ty::Adt(adt_def, args) = cx.tcx.type_of(item.owner_id).instantiate_identity().kind()
             && let attrs = cx.tcx.hir().attrs(item.hir_id())
             && !attrs.iter().any(|attr| attr.doc_str().is_some())
diff --git a/src/tools/clippy/clippy_lints/src/derive.rs b/src/tools/clippy/clippy_lints/src/derive.rs
index 91ddbb44ff8..db3e6034c5b 100644
--- a/src/tools/clippy/clippy_lints/src/derive.rs
+++ b/src/tools/clippy/clippy_lints/src/derive.rs
@@ -437,8 +437,8 @@ impl<'tcx> Visitor<'tcx> for UnsafeVisitor<'_, 'tcx> {
         walk_expr(self, expr)
     }
 
-    fn nested_visit_map(&mut self) -> Self::Map {
-        self.cx.tcx.hir()
+    fn maybe_tcx(&mut self) -> Self::MaybeTyCtxt {
+        self.cx.tcx
     }
 }
 
diff --git a/src/tools/clippy/clippy_lints/src/disallowed_macros.rs b/src/tools/clippy/clippy_lints/src/disallowed_macros.rs
index 4e8853821c3..6de16e306c9 100644
--- a/src/tools/clippy/clippy_lints/src/disallowed_macros.rs
+++ b/src/tools/clippy/clippy_lints/src/disallowed_macros.rs
@@ -1,3 +1,4 @@
+
 use clippy_config::Conf;
 use clippy_config::types::create_disallowed_map;
 use clippy_utils::diagnostics::{span_lint_and_then, span_lint_hir_and_then};
diff --git a/src/tools/clippy/clippy_lints/src/doc/include_in_doc_without_cfg.rs b/src/tools/clippy/clippy_lints/src/doc/include_in_doc_without_cfg.rs
index 4b40fc0b1ee..aa29705cf93 100644
--- a/src/tools/clippy/clippy_lints/src/doc/include_in_doc_without_cfg.rs
+++ b/src/tools/clippy/clippy_lints/src/doc/include_in_doc_without_cfg.rs
@@ -1,18 +1,17 @@
 use clippy_utils::diagnostics::span_lint_and_sugg;
 use clippy_utils::source::snippet_opt;
-use rustc_ast::AttrStyle;
 use rustc_errors::Applicability;
-use rustc_hir::{AttrArgs, AttrKind, Attribute};
-use rustc_lint::LateContext;
+use rustc_lint::EarlyContext;
+use rustc_ast::{Attribute, AttrKind, AttrArgs, AttrStyle};
 
 use super::DOC_INCLUDE_WITHOUT_CFG;
 
-pub fn check(cx: &LateContext<'_>, attrs: &[Attribute]) {
+pub fn check(cx: &EarlyContext<'_>, attrs: &[Attribute]) {
     for attr in attrs {
         if !attr.span.from_expansion()
             && let AttrKind::Normal(ref item) = attr.kind
             && attr.doc_str().is_some()
-            && let AttrArgs::Eq { expr: meta, .. } = &item.args
+            && let AttrArgs::Eq { expr: meta, .. } = &item.item.args
             && !attr.span.contains(meta.span)
             // Since the `include_str` is already expanded at this point, we can only take the
             // whole attribute snippet and then modify for our suggestion.
diff --git a/src/tools/clippy/clippy_lints/src/doc/missing_headers.rs b/src/tools/clippy/clippy_lints/src/doc/missing_headers.rs
index 8e2af6bf14a..e8638595c4b 100644
--- a/src/tools/clippy/clippy_lints/src/doc/missing_headers.rs
+++ b/src/tools/clippy/clippy_lints/src/doc/missing_headers.rs
@@ -24,8 +24,7 @@ pub fn check(
     if !check_private_items
         && cx
             .tcx
-            .hir()
-            .parent_iter(owner_id.into())
+            .hir_parent_iter(owner_id.into())
             .any(|(id, _node)| is_doc_hidden(cx.tcx.hir().attrs(id)))
     {
         return;
@@ -68,7 +67,7 @@ pub fn check(
         } else if let Some(body_id) = body_id
             && let Some(future) = cx.tcx.lang_items().future_trait()
             && let typeck = cx.tcx.typeck_body(body_id)
-            && let body = cx.tcx.hir().body(body_id)
+            && let body = cx.tcx.hir_body(body_id)
             && let ret_ty = typeck.expr_ty(body.value)
             && implements_trait_with_env(
                 cx.tcx,
diff --git a/src/tools/clippy/clippy_lints/src/doc/mod.rs b/src/tools/clippy/clippy_lints/src/doc/mod.rs
index 42e1f7fd950..42192801af7 100644
--- a/src/tools/clippy/clippy_lints/src/doc/mod.rs
+++ b/src/tools/clippy/clippy_lints/src/doc/mod.rs
@@ -20,7 +20,7 @@ use rustc_data_structures::fx::FxHashSet;
 use rustc_errors::Applicability;
 use rustc_hir::intravisit::{self, Visitor};
 use rustc_hir::{AnonConst, Attribute, Expr, ImplItemKind, ItemKind, Node, Safety, TraitItemKind};
-use rustc_lint::{LateContext, LateLintPass, LintContext};
+use rustc_lint::{EarlyContext, EarlyLintPass, LateContext, LateLintPass, LintContext};
 use rustc_middle::hir::nested_filter;
 use rustc_middle::ty;
 use rustc_resolve::rustdoc::{
@@ -577,6 +577,13 @@ impl_lint_pass!(Documentation => [
     DOC_INCLUDE_WITHOUT_CFG,
 ]);
 
+
+impl EarlyLintPass for Documentation {
+    fn check_attributes(&mut self, cx: &EarlyContext<'_>, attrs: &[rustc_ast::Attribute]) {
+        include_in_doc_without_cfg::check(cx, attrs);
+    }
+}
+
 impl<'tcx> LateLintPass<'tcx> for Documentation {
     fn check_attributes(&mut self, cx: &LateContext<'tcx>, attrs: &'tcx [Attribute]) {
         let Some(headers) = check_attrs(cx, &self.valid_idents, attrs) else {
@@ -597,7 +604,7 @@ impl<'tcx> LateLintPass<'tcx> for Documentation {
                         if !(is_entrypoint_fn(cx, item.owner_id.to_def_id())
                             || item.span.in_external_macro(cx.tcx.sess.source_map()))
                         {
-                            let body = cx.tcx.hir().body(body_id);
+                            let body = cx.tcx.hir_body(body_id);
 
                             let panic_info = FindPanicUnwrap::find_span(cx, cx.tcx.typeck(item.owner_id), body.value);
                             missing_headers::check(
@@ -649,7 +656,7 @@ impl<'tcx> LateLintPass<'tcx> for Documentation {
                     && !impl_item.span.in_external_macro(cx.tcx.sess.source_map())
                     && !is_trait_impl_item(cx, impl_item.hir_id())
                 {
-                    let body = cx.tcx.hir().body(body_id);
+                    let body = cx.tcx.hir_body(body_id);
 
                     let panic_span = FindPanicUnwrap::find_span(cx, cx.tcx.typeck(impl_item.owner_id), body.value);
                     missing_headers::check(
@@ -704,14 +711,13 @@ fn check_attrs(cx: &LateContext<'_>, valid_idents: &FxHashSet<String>, attrs: &[
         Some(("fake".into(), "fake".into()))
     }
 
-    include_in_doc_without_cfg::check(cx, attrs);
     if suspicious_doc_comments::check(cx, attrs) || is_doc_hidden(attrs) {
         return None;
     }
 
     let (fragments, _) = attrs_to_doc_fragments(
         attrs.iter().filter_map(|attr| {
-            if attr.span.in_external_macro(cx.sess().source_map()) {
+            if !attr.doc_str_and_comment_kind().is_some() || attr.span().in_external_macro(cx.sess().source_map()) {
                 None
             } else {
                 Some((attr, None))
@@ -1057,7 +1063,7 @@ impl<'tcx> Visitor<'tcx> for FindPanicUnwrap<'_, 'tcx> {
                     "assert" | "assert_eq" | "assert_ne"
                 )
             {
-                self.is_const = self.cx.tcx.hir().is_inside_const_context(expr.hir_id);
+                self.is_const = self.cx.tcx.hir_is_inside_const_context(expr.hir_id);
                 self.panic_span = Some(macro_call.span);
             }
         }
@@ -1079,8 +1085,8 @@ impl<'tcx> Visitor<'tcx> for FindPanicUnwrap<'_, 'tcx> {
     // Panics in const blocks will cause compilation to fail.
     fn visit_anon_const(&mut self, _: &'tcx AnonConst) {}
 
-    fn nested_visit_map(&mut self) -> Self::Map {
-        self.cx.tcx.hir()
+    fn maybe_tcx(&mut self) -> Self::MaybeTyCtxt {
+        self.cx.tcx
     }
 }
 
diff --git a/src/tools/clippy/clippy_lints/src/doc/suspicious_doc_comments.rs b/src/tools/clippy/clippy_lints/src/doc/suspicious_doc_comments.rs
index 84393213e6f..bfc36deea7b 100644
--- a/src/tools/clippy/clippy_lints/src/doc/suspicious_doc_comments.rs
+++ b/src/tools/clippy/clippy_lints/src/doc/suspicious_doc_comments.rs
@@ -3,6 +3,7 @@ use rustc_ast::AttrStyle;
 use rustc_ast::token::CommentKind;
 use rustc_errors::Applicability;
 use rustc_hir::Attribute;
+use rustc_attr_parsing::AttributeKind;
 use rustc_lint::LateContext;
 use rustc_span::Span;
 
@@ -36,15 +37,14 @@ fn collect_doc_replacements(attrs: &[Attribute]) -> Vec<(Span, String)> {
     attrs
         .iter()
         .filter_map(|attr| {
-            if let Some((sym, com_kind)) = attr.doc_str_and_comment_kind()
-                && let AttrStyle::Outer = attr.style
-                && let Some(com) = sym.as_str().strip_prefix('!')
+            if let Attribute::Parsed(AttributeKind::DocComment{ style: AttrStyle::Outer, kind, comment, ..}) = attr
+                && let Some(com) = comment.as_str().strip_prefix('!')
             {
-                let sugg = match com_kind {
+                let sugg = match kind {
                     CommentKind::Line => format!("//!{com}"),
                     CommentKind::Block => format!("/*!{com}*/"),
                 };
-                Some((attr.span, sugg))
+                Some((attr.span(), sugg))
             } else {
                 None
             }
diff --git a/src/tools/clippy/clippy_lints/src/doc/too_long_first_doc_paragraph.rs b/src/tools/clippy/clippy_lints/src/doc/too_long_first_doc_paragraph.rs
index 1f89cab9148..1eda73a9672 100644
--- a/src/tools/clippy/clippy_lints/src/doc/too_long_first_doc_paragraph.rs
+++ b/src/tools/clippy/clippy_lints/src/doc/too_long_first_doc_paragraph.rs
@@ -1,5 +1,6 @@
 use rustc_errors::Applicability;
 use rustc_hir::{Attribute, Item, ItemKind};
+use rustc_attr_parsing::AttributeKind;
 use rustc_lint::LateContext;
 
 use clippy_utils::diagnostics::span_lint_and_then;
@@ -43,9 +44,9 @@ pub(super) fn check(
     let mut should_suggest_empty_doc = false;
 
     for attr in attrs {
-        if let Some(doc) = attr.doc_str() {
-            spans.push(attr.span);
-            let doc = doc.as_str();
+        if let Attribute::Parsed(AttributeKind::DocComment {span, comment, ..}) = attr {
+            spans.push(span);
+            let doc = comment.as_str();
             let doc = doc.trim();
             if spans.len() == 1 {
                 // We make this suggestion only if the first doc line ends with a punctuation
@@ -78,7 +79,7 @@ pub(super) fn check(
                 && let new_span = first_span.with_hi(second_span.lo()).with_lo(first_span.hi())
                 && let Some(snippet) = snippet_opt(cx, new_span)
             {
-                let Some(first) = snippet_opt(cx, first_span) else {
+                let Some(first) = snippet_opt(cx, *first_span) else {
                     return;
                 };
                 let Some(comment_form) = first.get(..3) else {
diff --git a/src/tools/clippy/clippy_lints/src/empty_drop.rs b/src/tools/clippy/clippy_lints/src/empty_drop.rs
index 10a84b1b2ff..d557a36c7ac 100644
--- a/src/tools/clippy/clippy_lints/src/empty_drop.rs
+++ b/src/tools/clippy/clippy_lints/src/empty_drop.rs
@@ -44,7 +44,7 @@ impl LateLintPass<'_> for EmptyDrop {
             && let impl_item_hir = child.id.hir_id()
             && let Node::ImplItem(impl_item) = cx.tcx.hir_node(impl_item_hir)
             && let ImplItemKind::Fn(_, b) = &impl_item.kind
-            && let Body { value: func_expr, .. } = cx.tcx.hir().body(*b)
+            && let Body { value: func_expr, .. } = cx.tcx.hir_body(*b)
             && let func_expr = peel_blocks(func_expr)
             && let ExprKind::Block(block, _) = func_expr.kind
             && block.stmts.is_empty()
diff --git a/src/tools/clippy/clippy_lints/src/enum_clike.rs b/src/tools/clippy/clippy_lints/src/enum_clike.rs
index a090a987d4f..2e1f8ac615a 100644
--- a/src/tools/clippy/clippy_lints/src/enum_clike.rs
+++ b/src/tools/clippy/clippy_lints/src/enum_clike.rs
@@ -41,7 +41,7 @@ impl<'tcx> LateLintPass<'tcx> for UnportableVariant {
         if let ItemKind::Enum(def, _) = &item.kind {
             for var in def.variants {
                 if let Some(anon_const) = &var.disr_expr {
-                    let def_id = cx.tcx.hir().body_owner_def_id(anon_const.body);
+                    let def_id = cx.tcx.hir_body_owner_def_id(anon_const.body);
                     let mut ty = cx.tcx.type_of(def_id.to_def_id()).instantiate_identity();
                     let constant = cx
                         .tcx
diff --git a/src/tools/clippy/clippy_lints/src/escape.rs b/src/tools/clippy/clippy_lints/src/escape.rs
index c55d4387d69..0c06c9117d7 100644
--- a/src/tools/clippy/clippy_lints/src/escape.rs
+++ b/src/tools/clippy/clippy_lints/src/escape.rs
@@ -80,8 +80,7 @@ impl<'tcx> LateLintPass<'tcx> for BoxedLocal {
 
         let parent_id = cx
             .tcx
-            .hir()
-            .get_parent_item(cx.tcx.local_def_id_to_hir_id(fn_def_id))
+            .hir_get_parent_item(cx.tcx.local_def_id_to_hir_id(fn_def_id))
             .def_id;
 
         let mut trait_self_ty = None;
diff --git a/src/tools/clippy/clippy_lints/src/eta_reduction.rs b/src/tools/clippy/clippy_lints/src/eta_reduction.rs
index ae3acc1c4b1..d1782d582f4 100644
--- a/src/tools/clippy/clippy_lints/src/eta_reduction.rs
+++ b/src/tools/clippy/clippy_lints/src/eta_reduction.rs
@@ -97,7 +97,7 @@ fn check_closure<'tcx>(cx: &LateContext<'tcx>, outer_receiver: Option<&Expr<'tcx
         && matches!(c.fn_decl.output, FnRetTy::DefaultReturn(_))
         && !expr.span.from_expansion()
     {
-        cx.tcx.hir().body(c.body)
+        cx.tcx.hir_body(c.body)
     } else {
         return;
     };
diff --git a/src/tools/clippy/clippy_lints/src/exit.rs b/src/tools/clippy/clippy_lints/src/exit.rs
index e6ddcd107d9..cc8e4d7d9e2 100644
--- a/src/tools/clippy/clippy_lints/src/exit.rs
+++ b/src/tools/clippy/clippy_lints/src/exit.rs
@@ -47,7 +47,7 @@ impl<'tcx> LateLintPass<'tcx> for Exit {
             && 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)
-            && let parent = cx.tcx.hir().get_parent_item(e.hir_id)
+            && let parent = cx.tcx.hir_get_parent_item(e.hir_id)
             && let OwnerNode::Item(Item{kind: ItemKind::Fn{ .. }, ..}) = cx.tcx.hir_owner_node(parent)
             // If the next item up is a function we check if it is an entry point
             // and only then emit a linter warning
diff --git a/src/tools/clippy/clippy_lints/src/extra_unused_type_parameters.rs b/src/tools/clippy/clippy_lints/src/extra_unused_type_parameters.rs
index 5d93aceb33f..6a217b6182c 100644
--- a/src/tools/clippy/clippy_lints/src/extra_unused_type_parameters.rs
+++ b/src/tools/clippy/clippy_lints/src/extra_unused_type_parameters.rs
@@ -241,13 +241,13 @@ impl<'tcx> Visitor<'tcx> for TypeWalker<'_, 'tcx> {
         }
     }
 
-    fn nested_visit_map(&mut self) -> Self::Map {
-        self.cx.tcx.hir()
+    fn maybe_tcx(&mut self) -> Self::MaybeTyCtxt {
+        self.cx.tcx
     }
 }
 
 fn is_empty_body(cx: &LateContext<'_>, body: BodyId) -> bool {
-    matches!(cx.tcx.hir().body(body).value.kind, ExprKind::Block(b, _) if b.stmts.is_empty() && b.expr.is_none())
+    matches!(cx.tcx.hir_body(body).value.kind, ExprKind::Block(b, _) if b.stmts.is_empty() && b.expr.is_none())
 }
 
 impl<'tcx> LateLintPass<'tcx> for ExtraUnusedTypeParameters {
diff --git a/src/tools/clippy/clippy_lints/src/fallible_impl_from.rs b/src/tools/clippy/clippy_lints/src/fallible_impl_from.rs
index f822432cce6..f67d38d932b 100644
--- a/src/tools/clippy/clippy_lints/src/fallible_impl_from.rs
+++ b/src/tools/clippy/clippy_lints/src/fallible_impl_from.rs
@@ -98,10 +98,10 @@ fn lint_impl_body(cx: &LateContext<'_>, impl_span: Span, impl_items: &[hir::Impl
 
     for impl_item in impl_items {
         if impl_item.ident.name == sym::from
-            && let ImplItemKind::Fn(_, body_id) = cx.tcx.hir().impl_item(impl_item.id).kind
+            && let ImplItemKind::Fn(_, body_id) = cx.tcx.hir_impl_item(impl_item.id).kind
         {
             // check the body for `begin_panic` or `unwrap`
-            let body = cx.tcx.hir().body(body_id);
+            let body = cx.tcx.hir_body(body_id);
             let mut fpu = FindPanicUnwrap {
                 lcx: cx,
                 typeck_results: cx.tcx.typeck(impl_item.id.owner_id.def_id),
diff --git a/src/tools/clippy/clippy_lints/src/format_impl.rs b/src/tools/clippy/clippy_lints/src/format_impl.rs
index 5619cb0ab1b..ff75fcf2b41 100644
--- a/src/tools/clippy/clippy_lints/src/format_impl.rs
+++ b/src/tools/clippy/clippy_lints/src/format_impl.rs
@@ -262,7 +262,7 @@ fn is_format_trait_impl(cx: &LateContext<'_>, impl_item: &ImplItem<'_>) -> Optio
         && let Some(name) = cx.tcx.get_diagnostic_name(did)
         && matches!(name, sym::Debug | sym::Display)
     {
-        let body = cx.tcx.hir().body(body_id);
+        let body = cx.tcx.hir_body(body_id);
         let formatter_name = body
             .params
             .get(1)
diff --git a/src/tools/clippy/clippy_lints/src/four_forward_slashes.rs b/src/tools/clippy/clippy_lints/src/four_forward_slashes.rs
index 0599e08e6c0..0bdb99d7b9a 100644
--- a/src/tools/clippy/clippy_lints/src/four_forward_slashes.rs
+++ b/src/tools/clippy/clippy_lints/src/four_forward_slashes.rs
@@ -46,7 +46,8 @@ impl<'tcx> LateLintPass<'tcx> for FourForwardSlashes {
             .hir()
             .attrs(item.hir_id())
             .iter()
-            .fold(item.span.shrink_to_lo(), |span, attr| span.to(attr.span));
+            .filter(|i| i.is_doc_comment())
+            .fold(item.span.shrink_to_lo(), |span, attr| span.to(attr.span()));
         let (Some(file), _, _, end_line, _) = sm.span_to_location_info(span) else {
             return;
         };
diff --git a/src/tools/clippy/clippy_lints/src/from_over_into.rs b/src/tools/clippy/clippy_lints/src/from_over_into.rs
index 9a73d0c0993..41bf6e81916 100644
--- a/src/tools/clippy/clippy_lints/src/from_over_into.rs
+++ b/src/tools/clippy/clippy_lints/src/from_over_into.rs
@@ -134,8 +134,8 @@ impl<'tcx> Visitor<'tcx> for SelfFinder<'_, 'tcx> {
     type Result = ControlFlow<()>;
     type NestedFilter = OnlyBodies;
 
-    fn nested_visit_map(&mut self) -> Self::Map {
-        self.cx.tcx.hir()
+    fn maybe_tcx(&mut self) -> Self::MaybeTyCtxt {
+        self.cx.tcx
     }
 
     fn visit_path(&mut self, path: &Path<'tcx>, _id: HirId) -> Self::Result {
@@ -175,11 +175,11 @@ fn convert_to_from(
         // bad suggestion/fix.
         return None;
     }
-    let impl_item = cx.tcx.hir().impl_item(impl_item_ref.id);
+    let impl_item = cx.tcx.hir_impl_item(impl_item_ref.id);
     let ImplItemKind::Fn(ref sig, body_id) = impl_item.kind else {
         return None;
     };
-    let body = cx.tcx.hir().body(body_id);
+    let body = cx.tcx.hir_body(body_id);
     let [input] = body.params else { return None };
     let PatKind::Binding(.., self_ident, None) = input.pat.kind else {
         return None;
diff --git a/src/tools/clippy/clippy_lints/src/functions/impl_trait_in_params.rs b/src/tools/clippy/clippy_lints/src/functions/impl_trait_in_params.rs
index 752dbc0db4d..cb83b1395d2 100644
--- a/src/tools/clippy/clippy_lints/src/functions/impl_trait_in_params.rs
+++ b/src/tools/clippy/clippy_lints/src/functions/impl_trait_in_params.rs
@@ -39,7 +39,7 @@ fn report(cx: &LateContext<'_>, param: &GenericParam<'_>, generics: &Generics<'_
 
 pub(super) fn check_fn<'tcx>(cx: &LateContext<'_>, kind: &'tcx FnKind<'_>, body: &'tcx Body<'_>, hir_id: HirId) {
     if let FnKind::ItemFn(_, generics, _) = kind
-        && cx.tcx.visibility(cx.tcx.hir().body_owner_def_id(body.id())).is_public()
+        && cx.tcx.visibility(cx.tcx.hir_body_owner_def_id(body.id())).is_public()
         && !is_in_test(cx.tcx, hir_id)
     {
         for param in generics.params {
@@ -56,8 +56,8 @@ pub(super) fn check_impl_item(cx: &LateContext<'_>, impl_item: &ImplItem<'_>) {
         && let hir::ItemKind::Impl(impl_) = item.kind
         && let hir::Impl { of_trait, .. } = *impl_
         && of_trait.is_none()
-        && let body = cx.tcx.hir().body(body_id)
-        && cx.tcx.visibility(cx.tcx.hir().body_owner_def_id(body.id())).is_public()
+        && let body = cx.tcx.hir_body(body_id)
+        && cx.tcx.visibility(cx.tcx.hir_body_owner_def_id(body.id())).is_public()
         && !is_in_test(cx.tcx, impl_item.hir_id())
     {
         for param in impl_item.generics.params {
diff --git a/src/tools/clippy/clippy_lints/src/functions/must_use.rs b/src/tools/clippy/clippy_lints/src/functions/must_use.rs
index e480805cac2..0ed4426d6e9 100644
--- a/src/tools/clippy/clippy_lints/src/functions/must_use.rs
+++ b/src/tools/clippy/clippy_lints/src/functions/must_use.rs
@@ -37,7 +37,7 @@ pub(super) fn check_item<'tcx>(cx: &LateContext<'tcx>, item: &'tcx hir::Item<'_>
             check_must_use_candidate(
                 cx,
                 sig.decl,
-                cx.tcx.hir().body(*body_id),
+                cx.tcx.hir_body(*body_id),
                 item.span,
                 item.owner_id,
                 item.span.with_hi(sig.decl.output.span().hi()),
@@ -59,7 +59,7 @@ pub(super) fn check_impl_item<'tcx>(cx: &LateContext<'tcx>, item: &'tcx hir::Imp
             check_must_use_candidate(
                 cx,
                 sig.decl,
-                cx.tcx.hir().body(*body_id),
+                cx.tcx.hir_body(*body_id),
                 item.span,
                 item.owner_id,
                 item.span.with_hi(sig.decl.output.span().hi()),
@@ -79,7 +79,7 @@ pub(super) fn check_trait_item<'tcx>(cx: &LateContext<'tcx>, item: &'tcx hir::Tr
         if let Some(attr) = attr {
             check_needless_must_use(cx, sig.decl, item.owner_id, item.span, fn_header_span, attr, attrs, sig);
         } else if let hir::TraitFn::Provided(eid) = *eid {
-            let body = cx.tcx.hir().body(eid);
+            let body = cx.tcx.hir_body(eid);
             if attr.is_none() && is_public && !is_proc_macro(attrs) {
                 check_must_use_candidate(
                     cx,
@@ -95,6 +95,7 @@ pub(super) fn check_trait_item<'tcx>(cx: &LateContext<'tcx>, item: &'tcx hir::Tr
     }
 }
 
+// FIXME: needs to be an EARLY LINT. all attribute lints should be
 #[allow(clippy::too_many_arguments)]
 fn check_needless_must_use(
     cx: &LateContext<'_>,
@@ -117,38 +118,22 @@ fn check_needless_must_use(
                 fn_header_span,
                 "this unit-returning function has a `#[must_use]` attribute",
                 |diag| {
-                    diag.span_suggestion(attr.span, "remove the attribute", "", Applicability::MachineApplicable);
+                    diag.span_suggestion(attr.span(), "remove the attribute", "", Applicability::MachineApplicable);
                 },
             );
         } else {
             // When there are multiple attributes, it is not sufficient to simply make `must_use` empty, see
             // issue #12320.
-            span_lint_and_then(
+            // FIXME(jdonszelmann): this used to give a machine-applicable fix. However, it was super fragile,
+            // honestly looked incorrect, and is a little hard to support for a little bit now. Some day this could be
+            // re-added.
+            span_lint_and_help(
                 cx,
-                MUST_USE_UNIT,
+                DOUBLE_MUST_USE,
                 fn_header_span,
                 "this unit-returning function has a `#[must_use]` attribute",
-                |diag| {
-                    let mut attrs_without_must_use = attrs.to_vec();
-                    attrs_without_must_use.retain(|a| a.id != attr.id);
-                    let sugg_str = attrs_without_must_use
-                        .iter()
-                        .map(|a| {
-                            if a.value_str().is_none() {
-                                return a.name_or_empty().to_string();
-                            }
-                            format!("{} = \"{}\"", a.name_or_empty(), a.value_str().unwrap())
-                        })
-                        .collect::<Vec<_>>()
-                        .join(", ");
-
-                    diag.span_suggestion(
-                        attrs[0].span.with_hi(attrs[attrs.len() - 1].span.hi()),
-                        "change these attributes to",
-                        sugg_str,
-                        Applicability::MachineApplicable,
-                    );
-                },
+                Some(attr.span()),
+                "remove `must_use`",
             );
         }
     } else if attr.value_str().is_none() && is_must_use_ty(cx, return_ty(cx, item_id)) {
diff --git a/src/tools/clippy/clippy_lints/src/functions/not_unsafe_ptr_arg_deref.rs b/src/tools/clippy/clippy_lints/src/functions/not_unsafe_ptr_arg_deref.rs
index 8a74951ef63..906bbd006d4 100644
--- a/src/tools/clippy/clippy_lints/src/functions/not_unsafe_ptr_arg_deref.rs
+++ b/src/tools/clippy/clippy_lints/src/functions/not_unsafe_ptr_arg_deref.rs
@@ -30,7 +30,7 @@ pub(super) fn check_fn<'tcx>(
 
 pub(super) fn check_trait_item<'tcx>(cx: &LateContext<'tcx>, item: &'tcx hir::TraitItem<'_>) {
     if let hir::TraitItemKind::Fn(ref sig, hir::TraitFn::Provided(eid)) = item.kind {
-        let body = cx.tcx.hir().body(eid);
+        let body = cx.tcx.hir_body(eid);
         check_raw_ptr(cx, sig.header.safety(), sig.decl, body, item.owner_id.def_id);
     }
 }
diff --git a/src/tools/clippy/clippy_lints/src/functions/renamed_function_params.rs b/src/tools/clippy/clippy_lints/src/functions/renamed_function_params.rs
index ac2e866e4ff..5ad83f886e2 100644
--- a/src/tools/clippy/clippy_lints/src/functions/renamed_function_params.rs
+++ b/src/tools/clippy/clippy_lints/src/functions/renamed_function_params.rs
@@ -22,7 +22,7 @@ pub(super) fn check_impl_item(cx: &LateContext<'_>, item: &ImplItem<'_>, ignored
         && let Some(did) = trait_item_def_id_of_impl(items, item.owner_id)
         && !is_from_ignored_trait(trait_ref, ignored_traits)
     {
-        let mut param_idents_iter = cx.tcx.hir().body_param_names(body_id);
+        let mut param_idents_iter = cx.tcx.hir_body_param_names(body_id);
         let mut default_param_idents_iter = cx.tcx.fn_arg_names(did).iter().copied();
 
         let renames = RenamedFnArgs::new(&mut default_param_idents_iter, &mut param_idents_iter);
diff --git a/src/tools/clippy/clippy_lints/src/implicit_hasher.rs b/src/tools/clippy/clippy_lints/src/implicit_hasher.rs
index 47a5c19215b..d2545e57652 100644
--- a/src/tools/clippy/clippy_lints/src/implicit_hasher.rs
+++ b/src/tools/clippy/clippy_lints/src/implicit_hasher.rs
@@ -130,7 +130,7 @@ impl<'tcx> LateLintPass<'tcx> for ImplicitHasher {
                     });
 
                     let mut ctr_vis = ImplicitHasherConstructorVisitor::new(cx, target);
-                    for item in impl_.items.iter().map(|item| cx.tcx.hir().impl_item(item.id)) {
+                    for item in impl_.items.iter().map(|item| cx.tcx.hir_impl_item(item.id)) {
                         ctr_vis.visit_impl_item(item);
                     }
 
@@ -154,7 +154,7 @@ impl<'tcx> LateLintPass<'tcx> for ImplicitHasher {
                 body: body_id,
                 ..
             } => {
-                let body = cx.tcx.hir().body(body_id);
+                let body = cx.tcx.hir_body(body_id);
 
                 for ty in sig.decl.inputs {
                     let mut vis = ImplicitHasherTypeVisitor::new(cx);
@@ -363,7 +363,7 @@ impl<'tcx> Visitor<'tcx> for ImplicitHasherConstructorVisitor<'_, '_, 'tcx> {
         walk_expr(self, e);
     }
 
-    fn nested_visit_map(&mut self) -> Self::Map {
-        self.cx.tcx.hir()
+    fn maybe_tcx(&mut self) -> Self::MaybeTyCtxt {
+        self.cx.tcx
     }
 }
diff --git a/src/tools/clippy/clippy_lints/src/inconsistent_struct_constructor.rs b/src/tools/clippy/clippy_lints/src/inconsistent_struct_constructor.rs
index 39ff3c13bcc..5b58113169b 100644
--- a/src/tools/clippy/clippy_lints/src/inconsistent_struct_constructor.rs
+++ b/src/tools/clippy/clippy_lints/src/inconsistent_struct_constructor.rs
@@ -183,7 +183,7 @@ fn suggestion<'tcx>(
 
 fn field_with_attrs_span(tcx: TyCtxt<'_>, field: &hir::ExprField<'_>) -> Span {
     if let Some(attr) = tcx.hir().attrs(field.hir_id).first() {
-        field.span.with_lo(attr.span.lo())
+        field.span.with_lo(attr.span().lo())
     } else {
         field.span
     }
diff --git a/src/tools/clippy/clippy_lints/src/index_refutable_slice.rs b/src/tools/clippy/clippy_lints/src/index_refutable_slice.rs
index 15650c4f732..deac51ab4c4 100644
--- a/src/tools/clippy/clippy_lints/src/index_refutable_slice.rs
+++ b/src/tools/clippy/clippy_lints/src/index_refutable_slice.rs
@@ -223,8 +223,8 @@ struct SliceIndexLintingVisitor<'a, 'tcx> {
 impl<'tcx> Visitor<'tcx> for SliceIndexLintingVisitor<'_, 'tcx> {
     type NestedFilter = nested_filter::OnlyBodies;
 
-    fn nested_visit_map(&mut self) -> Self::Map {
-        self.cx.tcx.hir()
+    fn maybe_tcx(&mut self) -> Self::MaybeTyCtxt {
+        self.cx.tcx
     }
 
     fn visit_expr(&mut self, expr: &'tcx hir::Expr<'tcx>) {
diff --git a/src/tools/clippy/clippy_lints/src/indexing_slicing.rs b/src/tools/clippy/clippy_lints/src/indexing_slicing.rs
index f666ed0a440..33431385c7d 100644
--- a/src/tools/clippy/clippy_lints/src/indexing_slicing.rs
+++ b/src/tools/clippy/clippy_lints/src/indexing_slicing.rs
@@ -112,7 +112,7 @@ impl IndexingSlicing {
 impl<'tcx> LateLintPass<'tcx> for IndexingSlicing {
     fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
         if let ExprKind::Index(array, index, _) = &expr.kind
-            && (!self.suppress_restriction_lint_in_const || !cx.tcx.hir().is_inside_const_context(expr.hir_id))
+            && (!self.suppress_restriction_lint_in_const || !cx.tcx.hir_is_inside_const_context(expr.hir_id))
             && let expr_ty = cx.typeck_results().expr_ty(array)
             && let mut deref = deref_chain(cx, expr_ty)
             && deref.any(|l| {
@@ -181,7 +181,7 @@ impl<'tcx> LateLintPass<'tcx> for IndexingSlicing {
                 span_lint_and_then(cx, INDEXING_SLICING, expr.span, "slicing may panic", |diag| {
                     diag.help(help_msg);
 
-                    if cx.tcx.hir().is_inside_const_context(expr.hir_id) {
+                    if cx.tcx.hir_is_inside_const_context(expr.hir_id) {
                         diag.note(note);
                     }
                 });
@@ -223,7 +223,7 @@ impl<'tcx> LateLintPass<'tcx> for IndexingSlicing {
                 span_lint_and_then(cx, INDEXING_SLICING, expr.span, "indexing may panic", |diag| {
                     diag.help("consider using `.get(n)` or `.get_mut(n)` instead");
 
-                    if cx.tcx.hir().is_inside_const_context(expr.hir_id) {
+                    if cx.tcx.hir_is_inside_const_context(expr.hir_id) {
                         diag.note(note);
                     }
                 });
diff --git a/src/tools/clippy/clippy_lints/src/infinite_iter.rs b/src/tools/clippy/clippy_lints/src/infinite_iter.rs
index d3aade31f14..960b9aa032b 100644
--- a/src/tools/clippy/clippy_lints/src/infinite_iter.rs
+++ b/src/tools/clippy/clippy_lints/src/infinite_iter.rs
@@ -94,7 +94,6 @@ impl Finiteness {
 }
 
 impl From<bool> for Finiteness {
-    #[must_use]
     fn from(b: bool) -> Self {
         if b { Infinite } else { Finite }
     }
@@ -159,7 +158,7 @@ fn is_infinite(cx: &LateContext<'_>, expr: &Expr<'_>) -> Finiteness {
             }
             if method.ident.name.as_str() == "flat_map" && args.len() == 1 {
                 if let ExprKind::Closure(&Closure { body, .. }) = args[0].kind {
-                    let body = cx.tcx.hir().body(body);
+                    let body = cx.tcx.hir_body(body);
                     return is_infinite(cx, body.value);
                 }
             }
diff --git a/src/tools/clippy/clippy_lints/src/inline_fn_without_body.rs b/src/tools/clippy/clippy_lints/src/inline_fn_without_body.rs
index 1b900f6be8e..9b4a3b3f9c8 100644
--- a/src/tools/clippy/clippy_lints/src/inline_fn_without_body.rs
+++ b/src/tools/clippy/clippy_lints/src/inline_fn_without_body.rs
@@ -42,10 +42,10 @@ impl<'tcx> LateLintPass<'tcx> for InlineFnWithoutBody {
             span_lint_and_then(
                 cx,
                 INLINE_FN_WITHOUT_BODY,
-                attr.span,
+                attr.span(),
                 format!("use of `#[inline]` on trait method `{}` which has no body", item.ident),
                 |diag| {
-                    diag.suggest_remove_item(cx, attr.span, "remove", Applicability::MachineApplicable);
+                    diag.suggest_remove_item(cx, attr.span(), "remove", Applicability::MachineApplicable);
                 },
             );
         }
diff --git a/src/tools/clippy/clippy_lints/src/items_after_statements.rs b/src/tools/clippy/clippy_lints/src/items_after_statements.rs
index f5ad79a0027..021d43cefdd 100644
--- a/src/tools/clippy/clippy_lints/src/items_after_statements.rs
+++ b/src/tools/clippy/clippy_lints/src/items_after_statements.rs
@@ -59,7 +59,7 @@ impl LateLintPass<'_> for ItemsAfterStatements {
                 .iter()
                 .skip_while(|stmt| matches!(stmt.kind, StmtKind::Item(..)))
                 .filter_map(|stmt| match stmt.kind {
-                    StmtKind::Item(id) => Some(cx.tcx.hir().item(id)),
+                    StmtKind::Item(id) => Some(cx.tcx.hir_item(id)),
                     _ => None,
                 })
                 // Ignore macros since they can only see previously defined locals.
diff --git a/src/tools/clippy/clippy_lints/src/items_after_test_module.rs b/src/tools/clippy/clippy_lints/src/items_after_test_module.rs
index 1ac549b74ac..9df044f25eb 100644
--- a/src/tools/clippy/clippy_lints/src/items_after_test_module.rs
+++ b/src/tools/clippy/clippy_lints/src/items_after_test_module.rs
@@ -58,7 +58,7 @@ fn cfg_test_module<'tcx>(cx: &LateContext<'tcx>, item: &Item<'tcx>) -> bool {
 
 impl LateLintPass<'_> for ItemsAfterTestModule {
     fn check_mod(&mut self, cx: &LateContext<'_>, module: &Mod<'_>, _: HirId) {
-        let mut items = module.item_ids.iter().map(|&id| cx.tcx.hir().item(id));
+        let mut items = module.item_ids.iter().map(|&id| cx.tcx.hir_item(id));
 
         let Some((mod_pos, test_mod)) = items.by_ref().enumerate().find(|(_, item)| cfg_test_module(cx, item)) else {
             return;
@@ -91,7 +91,7 @@ impl LateLintPass<'_> for ItemsAfterTestModule {
                 "items after a test module",
                 |diag| {
                     if let Some(prev) = mod_pos.checked_sub(1)
-                        && let prev = cx.tcx.hir().item(module.item_ids[prev])
+                        && let prev = cx.tcx.hir_item(module.item_ids[prev])
                         && let items_span = last.span.with_lo(test_mod.span.hi())
                         && let Some(items) = items_span.get_source_text(cx)
                     {
diff --git a/src/tools/clippy/clippy_lints/src/iter_without_into_iter.rs b/src/tools/clippy/clippy_lints/src/iter_without_into_iter.rs
index 238f66d6675..173232c511a 100644
--- a/src/tools/clippy/clippy_lints/src/iter_without_into_iter.rs
+++ b/src/tools/clippy/clippy_lints/src/iter_without_into_iter.rs
@@ -142,7 +142,7 @@ impl LateLintPass<'_> for IterWithoutIntoIter {
             })
             && let Some(iter_assoc_span) = imp.items.iter().find_map(|item| {
                 if item.ident.name.as_str() == "IntoIter" {
-                    Some(cx.tcx.hir().impl_item(item.id).expect_type().span)
+                    Some(cx.tcx.hir_impl_item(item.id).expect_type().span)
                 } else {
                     None
                 }
diff --git a/src/tools/clippy/clippy_lints/src/large_include_file.rs b/src/tools/clippy/clippy_lints/src/large_include_file.rs
index f3d62b513e8..53dc070833b 100644
--- a/src/tools/clippy/clippy_lints/src/large_include_file.rs
+++ b/src/tools/clippy/clippy_lints/src/large_include_file.rs
@@ -3,10 +3,11 @@ use clippy_utils::diagnostics::span_lint_and_then;
 use clippy_utils::macros::root_macro_call_first_node;
 use clippy_utils::source::snippet_opt;
 use rustc_ast::LitKind;
-use rustc_hir::{AttrArgs, AttrKind, Attribute, Expr, ExprKind};
-use rustc_lint::{LateContext, LateLintPass};
+use rustc_hir::{Expr, ExprKind};
+use rustc_ast::{Attribute, AttrArgs, AttrKind};
+use rustc_lint::{EarlyContext, EarlyLintPass, LateContext, LateLintPass};
 use rustc_session::impl_lint_pass;
-use rustc_span::{Span, sym};
+use rustc_span::sym;
 
 declare_clippy_lint! {
     /// ### What it does
@@ -52,24 +53,6 @@ impl LargeIncludeFile {
 
 impl_lint_pass!(LargeIncludeFile => [LARGE_INCLUDE_FILE]);
 
-impl LargeIncludeFile {
-    fn emit_lint(&self, cx: &LateContext<'_>, span: Span) {
-        #[expect(clippy::collapsible_span_lint_calls, reason = "rust-clippy#7797")]
-        span_lint_and_then(
-            cx,
-            LARGE_INCLUDE_FILE,
-            span,
-            "attempted to include a large file",
-            |diag| {
-                diag.note(format!(
-                    "the configuration allows a maximum size of {} bytes",
-                    self.max_file_size
-                ));
-            },
-        );
-    }
-}
-
 impl LateLintPass<'_> for LargeIncludeFile {
     fn check_expr(&mut self, cx: &LateContext<'_>, expr: &'_ Expr<'_>) {
         if let ExprKind::Lit(lit) = &expr.kind
@@ -85,18 +68,32 @@ impl LateLintPass<'_> for LargeIncludeFile {
             && (cx.tcx.is_diagnostic_item(sym::include_bytes_macro, macro_call.def_id)
                 || cx.tcx.is_diagnostic_item(sym::include_str_macro, macro_call.def_id))
         {
-            self.emit_lint(cx, expr.span.source_callsite());
+            #[expect(clippy::collapsible_span_lint_calls, reason = "rust-clippy#7797")]
+            span_lint_and_then(
+                cx,
+                LARGE_INCLUDE_FILE,
+                expr.span.source_callsite(),
+                "attempted to include a large file",
+                |diag| {
+                    diag.note(format!(
+                        "the configuration allows a maximum size of {} bytes",
+                        self.max_file_size
+                    ));
+                },
+            );
         }
     }
+}
 
-    fn check_attribute(&mut self, cx: &LateContext<'_>, attr: &Attribute) {
+impl EarlyLintPass for LargeIncludeFile {
+    fn check_attribute(&mut self, cx: &EarlyContext<'_>, attr: &Attribute) {
         if !attr.span.from_expansion()
             // Currently, rustc limits the usage of macro at the top-level of attributes,
             // so we don't need to recurse into each level.
             && let AttrKind::Normal(ref item) = attr.kind
             && let Some(doc) = attr.doc_str()
             && doc.as_str().len() as u64 > self.max_file_size
-            && let AttrArgs::Eq { expr: meta, .. } = &item.args
+            && let AttrArgs::Eq { expr: meta, .. } = &item.item.args
             && !attr.span.contains(meta.span)
             // Since the `include_str` is already expanded at this point, we can only take the
             // whole attribute snippet and then modify for our suggestion.
@@ -113,7 +110,19 @@ impl LateLintPass<'_> for LargeIncludeFile {
             && let sub_snippet = sub_snippet.trim()
             && (sub_snippet.starts_with("include_str!") || sub_snippet.starts_with("include_bytes!"))
         {
-            self.emit_lint(cx, attr.span);
+            #[expect(clippy::collapsible_span_lint_calls, reason = "rust-clippy#7797")]
+            span_lint_and_then(
+                cx,
+                LARGE_INCLUDE_FILE,
+                attr.span,
+                "attempted to include a large file",
+                |diag| {
+                    diag.note(format!(
+                        "the configuration allows a maximum size of {} bytes",
+                        self.max_file_size
+                    ));
+                },
+            );
         }
     }
 }
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 c68499ce9f7..620e27fa67c 100644
--- a/src/tools/clippy/clippy_lints/src/large_stack_arrays.rs
+++ b/src/tools/clippy/clippy_lints/src/large_stack_arrays.rs
@@ -83,7 +83,7 @@ impl<'tcx> LateLintPass<'tcx> for LargeStackArrays {
             && let ty::Array(element_type, cst) = cx.typeck_results().expr_ty(expr).kind()
             && let Some(element_count) = cst.try_to_target_usize(cx.tcx)
             && let Ok(element_size) = cx.layout_of(*element_type).map(|l| l.size.bytes())
-            && !cx.tcx.hir().parent_iter(expr.hir_id).any(|(_, node)| {
+            && !cx.tcx.hir_parent_iter(expr.hir_id).any(|(_, node)| {
                 matches!(
                     node,
                     Node::Item(Item {
diff --git a/src/tools/clippy/clippy_lints/src/len_zero.rs b/src/tools/clippy/clippy_lints/src/len_zero.rs
index 26bea8d633a..98ba52f1270 100644
--- a/src/tools/clippy/clippy_lints/src/len_zero.rs
+++ b/src/tools/clippy/clippy_lints/src/len_zero.rs
@@ -316,7 +316,7 @@ enum LenOutput {
 
 fn extract_future_output<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Option<&'tcx PathSegment<'tcx>> {
     if let ty::Alias(_, alias_ty) = ty.kind()
-        && let Some(Node::OpaqueTy(opaque)) = cx.tcx.hir().get_if_local(alias_ty.def_id)
+        && let Some(Node::OpaqueTy(opaque)) = cx.tcx.hir_get_if_local(alias_ty.def_id)
         && let OpaqueTyOrigin::AsyncFn { .. } = opaque.origin
         && let [GenericBound::Trait(trait_ref)] = &opaque.bounds
         && let Some(segment) = trait_ref.trait_ref.path.segments.last()
diff --git a/src/tools/clippy/clippy_lints/src/lib.rs b/src/tools/clippy/clippy_lints/src/lib.rs
index 13218331a67..177f83921cd 100644
--- a/src/tools/clippy/clippy_lints/src/lib.rs
+++ b/src/tools/clippy/clippy_lints/src/lib.rs
@@ -407,9 +407,9 @@ mod zombie_processes;
 
 use clippy_config::{Conf, get_configuration_metadata, sanitize_explanation};
 use clippy_utils::macros::FormatArgsStorage;
+use utils::attr_collector::{AttrCollector, AttrStorage};
 use rustc_data_structures::fx::FxHashSet;
 use rustc_lint::{Lint, LintId};
-use utils::attr_collector::{AttrCollector, AttrStorage};
 
 /// Register all pre expansion lints
 ///
@@ -717,6 +717,7 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) {
     store.register_late_pass(move |_| Box::new(disallowed_names::DisallowedNames::new(conf)));
     store.register_late_pass(move |tcx| Box::new(functions::Functions::new(tcx, conf)));
     store.register_late_pass(move |_| Box::new(doc::Documentation::new(conf)));
+    store.register_early_pass(move || Box::new(doc::Documentation::new(conf)));
     store.register_late_pass(|_| Box::new(neg_multiply::NegMultiply));
     store.register_late_pass(|_| Box::new(let_if_seq::LetIfSeq));
     store.register_late_pass(|_| Box::new(mixed_read_write_in_expression::EvalOrderDependence));
@@ -860,6 +861,7 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) {
     store.register_early_pass(|| Box::new(pub_use::PubUse));
     store.register_late_pass(|_| Box::new(format_push_string::FormatPushString));
     store.register_late_pass(move |_| Box::new(large_include_file::LargeIncludeFile::new(conf)));
+    store.register_early_pass(move || Box::new(large_include_file::LargeIncludeFile::new(conf)));
     store.register_late_pass(|_| Box::new(strings::TrimSplitWhitespace));
     store.register_late_pass(|_| Box::new(rc_clone_in_vec_init::RcCloneInVecInit));
     store.register_early_pass(|| Box::<duplicate_mod::DuplicateMod>::default());
diff --git a/src/tools/clippy/clippy_lints/src/lifetimes.rs b/src/tools/clippy/clippy_lints/src/lifetimes.rs
index 860c0584acc..f08812017b9 100644
--- a/src/tools/clippy/clippy_lints/src/lifetimes.rs
+++ b/src/tools/clippy/clippy_lints/src/lifetimes.rs
@@ -19,8 +19,8 @@ use rustc_hir::{
     WherePredicateKind, lang_items,
 };
 use rustc_lint::{LateContext, LateLintPass, LintContext};
-use rustc_middle::hir::map::Map;
 use rustc_middle::hir::nested_filter as middle_nested_filter;
+use rustc_middle::ty::TyCtxt;
 use rustc_session::impl_lint_pass;
 use rustc_span::Span;
 use rustc_span::def_id::LocalDefId;
@@ -275,7 +275,7 @@ fn could_use_elision<'tcx>(
     }
 
     if let Some(body_id) = body {
-        let body = cx.tcx.hir().body(body_id);
+        let body = cx.tcx.hir_body(body_id);
 
         let first_ident = body.params.first().and_then(|param| param.pat.simple_ident());
         if non_elidable_self_type(cx, func, first_ident, msrv) {
@@ -582,7 +582,7 @@ impl<'tcx, F> Visitor<'tcx> for LifetimeChecker<'_, 'tcx, F>
 where
     F: NestedFilter<'tcx>,
 {
-    type Map = Map<'tcx>;
+    type MaybeTyCtxt = TyCtxt<'tcx>;
     type NestedFilter = F;
 
     // for lifetimes as parameters of generics
@@ -628,8 +628,8 @@ where
         self.lifetime_elision_impossible = false;
     }
 
-    fn nested_visit_map(&mut self) -> Self::Map {
-        self.cx.tcx.hir()
+    fn maybe_tcx(&mut self) -> Self::MaybeTyCtxt {
+        self.cx.tcx
     }
 }
 
diff --git a/src/tools/clippy/clippy_lints/src/lines_filter_map_ok.rs b/src/tools/clippy/clippy_lints/src/lines_filter_map_ok.rs
index 8206c75927b..f022598651b 100644
--- a/src/tools/clippy/clippy_lints/src/lines_filter_map_ok.rs
+++ b/src/tools/clippy/clippy_lints/src/lines_filter_map_ok.rs
@@ -101,7 +101,7 @@ fn should_lint(cx: &LateContext<'_>, args: &[Expr<'_>], method_str: &str) -> boo
                 ExprKind::Closure(Closure { body, .. }) => {
                     if let Body {
                         params: [param], value, ..
-                    } = cx.tcx.hir().body(*body)
+                    } = cx.tcx.hir_body(*body)
                         && let ExprKind::MethodCall(method, receiver, [], _) = value.kind
                         && path_to_local_id(receiver, param.pat.hir_id)
                         && let Some(method_did) = cx.typeck_results().type_dependent_def_id(value.hir_id)
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 4d206850c99..797ff1f3986 100644
--- a/src/tools/clippy/clippy_lints/src/loops/infinite_loop.rs
+++ b/src/tools/clippy/clippy_lints/src/loops/infinite_loop.rs
@@ -61,7 +61,7 @@ pub(super) fn check<'tcx>(
 }
 
 fn get_parent_fn_ret_ty<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'_>) -> Option<FnRetTy<'tcx>> {
-    for (_, parent_node) in cx.tcx.hir().parent_iter(expr.hir_id) {
+    for (_, parent_node) in cx.tcx.hir_parent_iter(expr.hir_id) {
         match parent_node {
             // Skip `Coroutine` closures, these are the body of `async fn`, not async closures.
             // This is because we still need to backtrack one parent node to get the `OpaqueDef` ty.
diff --git a/src/tools/clippy/clippy_lints/src/loops/manual_find.rs b/src/tools/clippy/clippy_lints/src/loops/manual_find.rs
index 1721f569541..aa8a2934f89 100644
--- a/src/tools/clippy/clippy_lints/src/loops/manual_find.rs
+++ b/src/tools/clippy/clippy_lints/src/loops/manual_find.rs
@@ -134,7 +134,7 @@ fn last_stmt_and_ret<'tcx>(
         }
         None
     }
-    let mut parent_iter = cx.tcx.hir().parent_iter(expr.hir_id);
+    let mut parent_iter = cx.tcx.hir_parent_iter(expr.hir_id);
     if let Some((node_hir, Node::Stmt(..))) = parent_iter.next()
         // This should be the loop
         // This should be the function body
diff --git a/src/tools/clippy/clippy_lints/src/loops/needless_range_loop.rs b/src/tools/clippy/clippy_lints/src/loops/needless_range_loop.rs
index 2e6442156ef..0f62183eb33 100644
--- a/src/tools/clippy/clippy_lints/src/loops/needless_range_loop.rs
+++ b/src/tools/clippy/clippy_lints/src/loops/needless_range_loop.rs
@@ -56,7 +56,7 @@ pub(super) fn check<'tcx>(
 
                 // ensure that the indexed variable was declared before the loop, see #601
                 if let Some(indexed_extent) = indexed_extent {
-                    let parent_def_id = cx.tcx.hir().get_parent_item(expr.hir_id);
+                    let parent_def_id = cx.tcx.hir_get_parent_item(expr.hir_id);
                     let region_scope_tree = cx.tcx.region_scope_tree(parent_def_id);
                     let pat_extent = region_scope_tree.var_scope(pat.hir_id.local_id).unwrap();
                     if region_scope_tree.is_subscope_of(indexed_extent, pat_extent) {
@@ -256,7 +256,7 @@ impl<'tcx> VarVisitor<'_, 'tcx> {
             let res = self.cx.qpath_res(seqpath, seqexpr.hir_id);
             match res {
                 Res::Local(hir_id) => {
-                    let parent_def_id = self.cx.tcx.hir().get_parent_item(expr.hir_id);
+                    let parent_def_id = self.cx.tcx.hir_get_parent_item(expr.hir_id);
                     let extent = self
                         .cx
                         .tcx
@@ -370,7 +370,7 @@ impl<'tcx> Visitor<'tcx> for VarVisitor<'_, 'tcx> {
                 }
             },
             ExprKind::Closure(&Closure { body, .. }) => {
-                let body = self.cx.tcx.hir().body(body);
+                let body = self.cx.tcx.hir_body(body);
                 self.visit_expr(body.value);
             },
             _ => walk_expr(self, expr),
diff --git a/src/tools/clippy/clippy_lints/src/loops/utils.rs b/src/tools/clippy/clippy_lints/src/loops/utils.rs
index 51fde5288ab..a5185d38e7c 100644
--- a/src/tools/clippy/clippy_lints/src/loops/utils.rs
+++ b/src/tools/clippy/clippy_lints/src/loops/utils.rs
@@ -240,8 +240,8 @@ impl<'tcx> Visitor<'tcx> for InitializeVisitor<'_, 'tcx> {
         }
     }
 
-    fn nested_visit_map(&mut self) -> Self::Map {
-        self.cx.tcx.hir()
+    fn maybe_tcx(&mut self) -> Self::MaybeTyCtxt {
+        self.cx.tcx
     }
 }
 
diff --git a/src/tools/clippy/clippy_lints/src/loops/while_let_on_iterator.rs b/src/tools/clippy/clippy_lints/src/loops/while_let_on_iterator.rs
index b7e37c1a876..6000ff7a360 100644
--- a/src/tools/clippy/clippy_lints/src/loops/while_let_on_iterator.rs
+++ b/src/tools/clippy/clippy_lints/src/loops/while_let_on_iterator.rs
@@ -245,8 +245,8 @@ fn needs_mutable_borrow(cx: &LateContext<'_>, iter_expr: &IterExpr, loop_expr: &
     impl<'tcx> Visitor<'tcx> for AfterLoopVisitor<'_, '_, 'tcx> {
         type NestedFilter = OnlyBodies;
         type Result = ControlFlow<()>;
-        fn nested_visit_map(&mut self) -> Self::Map {
-            self.cx.tcx.hir()
+        fn maybe_tcx(&mut self) -> Self::MaybeTyCtxt {
+            self.cx.tcx
         }
 
         fn visit_expr(&mut self, e: &'tcx Expr<'_>) -> Self::Result {
@@ -288,8 +288,8 @@ fn needs_mutable_borrow(cx: &LateContext<'_>, iter_expr: &IterExpr, loop_expr: &
     }
     impl<'tcx> Visitor<'tcx> for NestedLoopVisitor<'_, '_, 'tcx> {
         type NestedFilter = OnlyBodies;
-        fn nested_visit_map(&mut self) -> Self::Map {
-            self.cx.tcx.hir()
+        fn maybe_tcx(&mut self) -> Self::MaybeTyCtxt {
+            self.cx.tcx
         }
 
         fn visit_local(&mut self, l: &'tcx LetStmt<'_>) {
@@ -351,7 +351,7 @@ fn needs_mutable_borrow(cx: &LateContext<'_>, iter_expr: &IterExpr, loop_expr: &
             loop_id: loop_expr.hir_id,
             after_loop: false,
         };
-        v.visit_expr(cx.tcx.hir().body(cx.enclosing_body.unwrap()).value)
+        v.visit_expr(cx.tcx.hir_body(cx.enclosing_body.unwrap()).value)
             .is_break()
     }
 }
diff --git a/src/tools/clippy/clippy_lints/src/macro_use.rs b/src/tools/clippy/clippy_lints/src/macro_use.rs
index 37412866539..165e8c2ea05 100644
--- a/src/tools/clippy/clippy_lints/src/macro_use.rs
+++ b/src/tools/clippy/clippy_lints/src/macro_use.rs
@@ -94,7 +94,7 @@ impl LateLintPass<'_> for MacroUseImports {
         {
             for kid in cx.tcx.module_children(id) {
                 if let Res::Def(DefKind::Macro(_mac_type), mac_id) = kid.res {
-                    let span = mac_attr.span;
+                    let span = mac_attr.span();
                     let def_path = cx.tcx.def_path_str(mac_id);
                     self.imports.push((def_path, span, hir_id));
                 }
@@ -104,8 +104,8 @@ impl LateLintPass<'_> for MacroUseImports {
         }
     }
     fn check_attribute(&mut self, cx: &LateContext<'_>, attr: &hir::Attribute) {
-        if attr.span.from_expansion() {
-            self.push_unique_macro(cx, attr.span);
+        if attr.span().from_expansion() {
+            self.push_unique_macro(cx, attr.span());
         }
     }
     fn check_expr(&mut self, cx: &LateContext<'_>, expr: &hir::Expr<'_>) {
diff --git a/src/tools/clippy/clippy_lints/src/manual_async_fn.rs b/src/tools/clippy/clippy_lints/src/manual_async_fn.rs
index 9f3b0957eab..6f3a7d8cccc 100644
--- a/src/tools/clippy/clippy_lints/src/manual_async_fn.rs
+++ b/src/tools/clippy/clippy_lints/src/manual_async_fn.rs
@@ -168,7 +168,7 @@ fn desugared_async_block<'tcx>(cx: &LateContext<'tcx>, block: &'tcx Block<'tcx>)
         && let ClosureKind::Coroutine(CoroutineKind::Desugared(CoroutineDesugaring::Async, CoroutineSource::Block)) =
             kind
     {
-        return Some(cx.tcx.hir().body(body));
+        return Some(cx.tcx.hir_body(body));
     }
 
     None
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 052e6502da9..2a5aa12d126 100644
--- a/src/tools/clippy/clippy_lints/src/manual_float_methods.rs
+++ b/src/tools/clippy/clippy_lints/src/manual_float_methods.rs
@@ -143,7 +143,7 @@ impl<'tcx> LateLintPass<'tcx> for ManualFloatMethods {
             && exprs.iter_mut().partition_in_place(|i| path_to_local(i).is_some()) == 2
             && !expr.span.in_external_macro(cx.sess().source_map())
             && (
-                is_not_const(cx.tcx, cx.tcx.hir().enclosing_body_owner(expr.hir_id).into())
+                is_not_const(cx.tcx, cx.tcx.hir_enclosing_body_owner(expr.hir_id).into())
                     || self.msrv.meets(msrvs::CONST_FLOAT_CLASSIFY)
             )
             && let [first, second, const_1, const_2] = exprs
diff --git a/src/tools/clippy/clippy_lints/src/manual_non_exhaustive.rs b/src/tools/clippy/clippy_lints/src/manual_non_exhaustive.rs
index 00800231fe4..83d8a509390 100644
--- a/src/tools/clippy/clippy_lints/src/manual_non_exhaustive.rs
+++ b/src/tools/clippy/clippy_lints/src/manual_non_exhaustive.rs
@@ -116,7 +116,7 @@ impl<'tcx> LateLintPass<'tcx> for ManualNonExhaustive {
                             if let Some(non_exhaustive) =
                                 attr::find_by_name(cx.tcx.hir().attrs(item.hir_id()), sym::non_exhaustive)
                             {
-                                diag.span_note(non_exhaustive.span, "the struct is already non-exhaustive");
+                                diag.span_note(non_exhaustive.span(), "the struct is already non-exhaustive");
                             } else {
                                 let indent = snippet_indent(cx, item.span).unwrap_or_default();
                                 diag.span_suggestion_verbose(
diff --git a/src/tools/clippy/clippy_lints/src/manual_option_as_slice.rs b/src/tools/clippy/clippy_lints/src/manual_option_as_slice.rs
index 5c40c945c69..e4360518b66 100644
--- a/src/tools/clippy/clippy_lints/src/manual_option_as_slice.rs
+++ b/src/tools/clippy/clippy_lints/src/manual_option_as_slice.rs
@@ -191,7 +191,7 @@ fn check_arms(cx: &LateContext<'_>, none_arm: &Arm<'_>, some_arm: &Arm<'_>) -> b
 fn returns_empty_slice(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
     match expr.kind {
         ExprKind::Path(_) => clippy_utils::is_path_diagnostic_item(cx, expr, sym::default_fn),
-        ExprKind::Closure(cl) => is_empty_slice(cx, cx.tcx.hir().body(cl.body).value),
+        ExprKind::Closure(cl) => is_empty_slice(cx, cx.tcx.hir_body(cl.body).value),
         _ => false,
     }
 }
diff --git a/src/tools/clippy/clippy_lints/src/manual_retain.rs b/src/tools/clippy/clippy_lints/src/manual_retain.rs
index 708980ac503..0a4e756096e 100644
--- a/src/tools/clippy/clippy_lints/src/manual_retain.rs
+++ b/src/tools/clippy/clippy_lints/src/manual_retain.rs
@@ -92,7 +92,7 @@ fn check_into_iter(
         && SpanlessEq::new(cx).eq_expr(left_expr, struct_expr)
         && let hir::ExprKind::MethodCall(_, _, [closure_expr], _) = target_expr.kind
         && let hir::ExprKind::Closure(closure) = closure_expr.kind
-        && let filter_body = cx.tcx.hir().body(closure.body)
+        && let filter_body = cx.tcx.hir_body(closure.body)
         && let [filter_params] = filter_body.params
     {
         if match_map_type(cx, left_expr) {
@@ -139,7 +139,7 @@ fn check_iter(
         && SpanlessEq::new(cx).eq_expr(left_expr, struct_expr)
         && let hir::ExprKind::MethodCall(_, _, [closure_expr], _) = filter_expr.kind
         && let hir::ExprKind::Closure(closure) = closure_expr.kind
-        && let filter_body = cx.tcx.hir().body(closure.body)
+        && let filter_body = cx.tcx.hir_body(closure.body)
         && let [filter_params] = filter_body.params
     {
         match filter_params.pat.kind {
@@ -198,7 +198,7 @@ fn check_to_owned(
         && SpanlessEq::new(cx).eq_expr(left_expr, str_expr)
         && let hir::ExprKind::MethodCall(_, _, [closure_expr], _) = filter_expr.kind
         && let hir::ExprKind::Closure(closure) = closure_expr.kind
-        && let filter_body = cx.tcx.hir().body(closure.body)
+        && let filter_body = cx.tcx.hir_body(closure.body)
         && let [filter_params] = filter_body.params
     {
         if let hir::PatKind::Ref(pat, _) = filter_params.pat.kind {
diff --git a/src/tools/clippy/clippy_lints/src/map_unit_fn.rs b/src/tools/clippy/clippy_lints/src/map_unit_fn.rs
index 3221a04d2d0..56aead85e7c 100644
--- a/src/tools/clippy/clippy_lints/src/map_unit_fn.rs
+++ b/src/tools/clippy/clippy_lints/src/map_unit_fn.rs
@@ -163,7 +163,7 @@ fn unit_closure<'tcx>(
     expr: &hir::Expr<'_>,
 ) -> Option<(&'tcx hir::Param<'tcx>, &'tcx hir::Expr<'tcx>)> {
     if let hir::ExprKind::Closure(&hir::Closure { fn_decl, body, .. }) = expr.kind
-        && let body = cx.tcx.hir().body(body)
+        && let body = cx.tcx.hir_body(body)
         && let body_expr = &body.value
         && fn_decl.inputs.len() == 1
         && is_unit_expression(cx, body_expr)
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 39339966013..722ea7042dd 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
@@ -199,7 +199,7 @@ fn find_method_sugg_for_if_let<'tcx>(
     // type needs to be considered, not just the inner type of the branch being matched on.
     // Note the last expression in a block is dropped after all local bindings.
     let check_ty = if has_else
-        || (keyword == "if" && matches!(cx.tcx.hir().parent_iter(expr.hir_id).next(), Some((_, Node::Block(..)))))
+        || (keyword == "if" && matches!(cx.tcx.hir_parent_iter(expr.hir_id).next(), Some((_, Node::Block(..)))))
     {
         op_ty
     } else {
diff --git a/src/tools/clippy/clippy_lints/src/methods/bind_instead_of_map.rs b/src/tools/clippy/clippy_lints/src/methods/bind_instead_of_map.rs
index 91a5de16e96..1e9b29f567f 100644
--- a/src/tools/clippy/clippy_lints/src/methods/bind_instead_of_map.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/bind_instead_of_map.rs
@@ -163,7 +163,7 @@ impl BindInsteadOfMap {
 
         match arg.kind {
             hir::ExprKind::Closure(&hir::Closure { body, fn_decl_span, .. }) => {
-                let closure_body = cx.tcx.hir().body(body);
+                let closure_body = cx.tcx.hir_body(body);
                 let closure_expr = peel_blocks(closure_body.value);
 
                 if self.lint_closure_autofixable(cx, expr, recv, closure_expr, fn_decl_span) {
diff --git a/src/tools/clippy/clippy_lints/src/methods/bytecount.rs b/src/tools/clippy/clippy_lints/src/methods/bytecount.rs
index 687272e550b..0498f317442 100644
--- a/src/tools/clippy/clippy_lints/src/methods/bytecount.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/bytecount.rs
@@ -18,7 +18,7 @@ pub(super) fn check<'tcx>(
     filter_arg: &'tcx Expr<'_>,
 ) {
     if let ExprKind::Closure(&Closure { body, .. }) = filter_arg.kind
-        && let body = cx.tcx.hir().body(body)
+        && let body = cx.tcx.hir_body(body)
         && let [param] = body.params
         && let PatKind::Binding(_, arg_id, _, _) = strip_pat_refs(param.pat).kind
         && let ExprKind::Binary(ref op, l, r) = body.value.kind
diff --git a/src/tools/clippy/clippy_lints/src/methods/filter_map.rs b/src/tools/clippy/clippy_lints/src/methods/filter_map.rs
index c1653b65e98..5b9df6c2bfd 100644
--- a/src/tools/clippy/clippy_lints/src/methods/filter_map.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/filter_map.rs
@@ -22,7 +22,7 @@ fn is_method(cx: &LateContext<'_>, expr: &Expr<'_>, method_name: Symbol) -> bool
         ExprKind::Path(QPath::Resolved(_, segments)) => segments.segments.last().unwrap().ident.name == method_name,
         ExprKind::MethodCall(segment, _, _, _) => segment.ident.name == method_name,
         ExprKind::Closure(Closure { body, .. }) => {
-            let body = cx.tcx.hir().body(*body);
+            let body = cx.tcx.hir_body(*body);
             let closure_expr = peel_blocks(body.value);
             match closure_expr.kind {
                 ExprKind::MethodCall(PathSegment { ident, .. }, receiver, ..) => {
@@ -404,7 +404,7 @@ fn is_find_or_filter<'a>(
     if is_trait_method(cx, map_recv, sym::Iterator)
         // filter(|x| ...is_some())...
         && let ExprKind::Closure(&Closure { body: filter_body_id, .. }) = filter_arg.kind
-        && let filter_body = cx.tcx.hir().body(filter_body_id)
+        && let filter_body = cx.tcx.hir_body(filter_body_id)
         && let [filter_param] = filter_body.params
         // optional ref pattern: `filter(|&x| ..)`
         && let (filter_pat, is_filter_param_ref) = if let PatKind::Ref(ref_pat, _) = filter_param.pat.kind {
@@ -417,7 +417,7 @@ fn is_find_or_filter<'a>(
         && let Some(mut offending_expr) = OffendingFilterExpr::hir(cx, filter_body.value, filter_param_id)
 
         && let ExprKind::Closure(&Closure { body: map_body_id, .. }) = map_arg.kind
-        && let map_body = cx.tcx.hir().body(map_body_id)
+        && let map_body = cx.tcx.hir_body(map_body_id)
         && let [map_param] = map_body.params
         && let PatKind::Binding(_, map_param_id, map_param_ident, None) = map_param.pat.kind
 
diff --git a/src/tools/clippy/clippy_lints/src/methods/filter_map_bool_then.rs b/src/tools/clippy/clippy_lints/src/methods/filter_map_bool_then.rs
index d550c145466..f7e116c5310 100644
--- a/src/tools/clippy/clippy_lints/src/methods/filter_map_bool_then.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/filter_map_bool_then.rs
@@ -14,7 +14,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>, arg: &
     if !expr.span.in_external_macro(cx.sess().source_map())
         && is_trait_method(cx, expr, sym::Iterator)
         && let ExprKind::Closure(closure) = arg.kind
-        && let body = cx.tcx.hir().body(closure.body)
+        && let body = cx.tcx.hir_body(closure.body)
         && let value = peel_blocks(body.value)
         // Indexing should be fine as `filter_map` always has 1 input, we unfortunately need both
         // `inputs` and `params` here as we need both the type and the span
@@ -31,7 +31,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>, arg: &
         && is_copy(cx, param_ty)
         && let ExprKind::MethodCall(_, recv, [then_arg], _) = value.kind
         && let ExprKind::Closure(then_closure) = then_arg.kind
-        && let then_body = peel_blocks(cx.tcx.hir().body(then_closure.body).value)
+        && let then_body = peel_blocks(cx.tcx.hir_body(then_closure.body).value)
         && let Some(def_id) = cx.typeck_results().type_dependent_def_id(value.hir_id)
         && cx.tcx.is_diagnostic_item(sym::bool_then, def_id)
         && !is_from_proc_macro(cx, expr)
diff --git a/src/tools/clippy/clippy_lints/src/methods/format_collect.rs b/src/tools/clippy/clippy_lints/src/methods/format_collect.rs
index 3e5162ef458..1b28596d50d 100644
--- a/src/tools/clippy/clippy_lints/src/methods/format_collect.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/format_collect.rs
@@ -19,7 +19,7 @@ fn peel_non_expn_blocks<'tcx>(expr: &'tcx Expr<'tcx>) -> Option<&'tcx Expr<'tcx>
 pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, map_arg: &Expr<'_>, map_span: Span) {
     if is_type_lang_item(cx, cx.typeck_results().expr_ty(expr), LangItem::String)
         && let ExprKind::Closure(closure) = map_arg.kind
-        && let body = cx.tcx.hir().body(closure.body)
+        && let body = cx.tcx.hir_body(closure.body)
         && let Some(value) = peel_non_expn_blocks(body.value)
         && let Some(mac) = root_macro_call_first_node(cx, value)
         && is_format_macro(cx, mac.def_id)
diff --git a/src/tools/clippy/clippy_lints/src/methods/is_empty.rs b/src/tools/clippy/clippy_lints/src/methods/is_empty.rs
index 92c81b3c49d..1c64f78678a 100644
--- a/src/tools/clippy/clippy_lints/src/methods/is_empty.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/is_empty.rs
@@ -40,8 +40,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &'_ Expr<'_>, receiver: &Expr<'_
 
 fn is_under_cfg(cx: &LateContext<'_>, id: HirId) -> bool {
     cx.tcx
-        .hir()
-        .parent_id_iter(id)
+        .hir_parent_id_iter(id)
         .any(|id| cx.tcx.hir().attrs(id).iter().any(|attr| attr.has_name(sym::cfg)))
 }
 
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 30387ba62a7..76a0c0061e5 100644
--- a/src/tools/clippy/clippy_lints/src/methods/iter_filter.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/iter_filter.rs
@@ -95,7 +95,7 @@ fn is_method(
             false
         },
         ExprKind::Closure(&hir::Closure { body, .. }) => {
-            let body = cx.tcx.hir().body(body);
+            let body = cx.tcx.hir_body(body);
             let closure_expr = peel_blocks(body.value);
             let params = body.params.iter().map(|param| param.pat).collect::<Vec<_>>();
             is_method(cx, closure_expr, type_symbol, method_name, params.as_slice())
diff --git a/src/tools/clippy/clippy_lints/src/methods/iter_kv_map.rs b/src/tools/clippy/clippy_lints/src/methods/iter_kv_map.rs
index 299f6d10112..518041177e9 100644
--- a/src/tools/clippy/clippy_lints/src/methods/iter_kv_map.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/iter_kv_map.rs
@@ -30,7 +30,7 @@ pub(super) fn check<'tcx>(
         && let Body {
             params: [p],
             value: body_expr,
-        } = cx.tcx.hir().body(c.body)
+        } = cx.tcx.hir_body(c.body)
         && let PatKind::Tuple([key_pat, val_pat], _) = p.pat.kind
         && let (replacement_kind, annotation, bound_ident) = match (&key_pat.kind, &val_pat.kind) {
             (key, PatKind::Binding(ann, _, value, _)) if pat_is_wild(cx, key, m_arg) => ("value", ann, value),
diff --git a/src/tools/clippy/clippy_lints/src/methods/iter_nth_zero.rs b/src/tools/clippy/clippy_lints/src/methods/iter_nth_zero.rs
index 9ff6eaa3487..4bdf589f487 100644
--- a/src/tools/clippy/clippy_lints/src/methods/iter_nth_zero.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/iter_nth_zero.rs
@@ -11,7 +11,7 @@ use rustc_span::sym;
 use super::ITER_NTH_ZERO;
 
 pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, recv: &hir::Expr<'_>, arg: &hir::Expr<'_>) {
-    if let OwnerNode::Item(item) = cx.tcx.hir_owner_node(cx.tcx.hir().get_parent_item(expr.hir_id))
+    if let OwnerNode::Item(item) = cx.tcx.hir_owner_node(cx.tcx.hir_get_parent_item(expr.hir_id))
         && let def_id = item.owner_id.to_def_id()
         && is_trait_method(cx, expr, sym::Iterator)
         && let Some(Constant::Int(0)) = ConstEvalCtxt::new(cx).eval(arg)
diff --git a/src/tools/clippy/clippy_lints/src/methods/iter_overeager_cloned.rs b/src/tools/clippy/clippy_lints/src/methods/iter_overeager_cloned.rs
index 5ccb5243e90..a80977459f2 100644
--- a/src/tools/clippy/clippy_lints/src/methods/iter_overeager_cloned.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/iter_overeager_cloned.rs
@@ -62,7 +62,7 @@ pub(super) fn check<'tcx>(
             let ExprKind::Closure(closure) = expr.kind else {
                 return;
             };
-            let body @ Body { params: [p], .. } = cx.tcx.hir().body(closure.body) else {
+            let body @ Body { params: [p], .. } = cx.tcx.hir_body(closure.body) else {
                 return;
             };
             let mut delegate = MoveDelegate {
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 7d5ebdedd0c..e1ebca0b09d 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
@@ -187,7 +187,7 @@ fn peel_ptr_cast<'tcx>(e: &'tcx Expr<'tcx>) -> &'tcx Expr<'tcx> {
 ///      ^ given this `x` expression, returns the `foo(...)` expression
 fn peel_ptr_cast_ancestors<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'tcx>) -> &'tcx Expr<'tcx> {
     let mut prev = e;
-    for (_, node) in cx.tcx.hir().parent_iter(e.hir_id) {
+    for (_, node) in cx.tcx.hir_parent_iter(e.hir_id) {
         if let Node::Expr(e) = node
             && get_cast_target(e).is_some()
         {
diff --git a/src/tools/clippy/clippy_lints/src/methods/manual_inspect.rs b/src/tools/clippy/clippy_lints/src/methods/manual_inspect.rs
index 20e4d233525..09ccb386a20 100644
--- a/src/tools/clippy/clippy_lints/src/methods/manual_inspect.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/manual_inspect.rs
@@ -22,7 +22,7 @@ pub(crate) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, arg: &Expr<'_>, name:
         && (is_diag_trait_item(cx, fn_id, sym::Iterator)
             || (msrv.meets(msrvs::OPTION_RESULT_INSPECT)
                 && (is_diag_item_method(cx, fn_id, sym::Option) || is_diag_item_method(cx, fn_id, sym::Result))))
-        && let body = cx.tcx.hir().body(c.body)
+        && let body = cx.tcx.hir_body(c.body)
         && let [param] = body.params
         && let PatKind::Binding(BindingMode(ByRef::No, Mutability::Not), arg_id, _, None) = param.pat.kind
         && let arg_ty = typeck.node_type(arg_id)
@@ -45,7 +45,7 @@ pub(crate) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, arg: &Expr<'_>, name:
         let can_lint = for_each_expr_without_closures(block.stmts, |e| {
             if let ExprKind::Closure(c) = e.kind {
                 // Nested closures don't need to treat returns specially.
-                let _: Option<!> = for_each_expr(cx, cx.tcx.hir().body(c.body).value, |e| {
+                let _: Option<!> = for_each_expr(cx, cx.tcx.hir_body(c.body).value, |e| {
                     if path_to_local_id(e, arg_id) {
                         let (kind, same_ctxt) = check_use(cx, e);
                         match (kind, same_ctxt && e.span.ctxt() == ctxt) {
@@ -123,7 +123,7 @@ pub(crate) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, arg: &Expr<'_>, name:
                     };
                     let mut prev_expr = e;
 
-                    for (_, parent) in cx.tcx.hir().parent_iter(e.hir_id) {
+                    for (_, parent) in cx.tcx.hir_parent_iter(e.hir_id) {
                         if let Node::Expr(e) = parent {
                             match e.kind {
                                 ExprKind::Field(_, name)
diff --git a/src/tools/clippy/clippy_lints/src/methods/manual_ok_or.rs b/src/tools/clippy/clippy_lints/src/methods/manual_ok_or.rs
index 4321dd6b0e0..8265c93bfe9 100644
--- a/src/tools/clippy/clippy_lints/src/methods/manual_ok_or.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/manual_ok_or.rs
@@ -44,7 +44,7 @@ fn is_ok_wrapping(cx: &LateContext<'_>, map_expr: &Expr<'_>) -> bool {
     match map_expr.kind {
         ExprKind::Path(ref qpath) if is_res_lang_ctor(cx, cx.qpath_res(qpath, map_expr.hir_id), ResultOk) => true,
         ExprKind::Closure(closure) => {
-            let body = cx.tcx.hir().body(closure.body);
+            let body = cx.tcx.hir_body(closure.body);
             if let PatKind::Binding(_, param_id, ..) = body.params[0].pat.kind
                 && let ExprKind::Call(callee, [ok_arg]) = body.value.kind
                 && is_res_lang_ctor(cx, path_res(cx, callee), ResultOk)
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 1252f7ccd35..b2705e1ffc2 100644
--- a/src/tools/clippy/clippy_lints/src/methods/map_clone.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/map_clone.rs
@@ -47,7 +47,7 @@ pub(super) fn check(cx: &LateContext<'_>, e: &hir::Expr<'_>, recv: &hir::Expr<'_
     {
         match arg.kind {
             hir::ExprKind::Closure(&hir::Closure { body, .. }) => {
-                let closure_body = cx.tcx.hir().body(body);
+                let closure_body = cx.tcx.hir_body(body);
                 let closure_expr = peel_blocks(closure_body.value);
                 match closure_body.params[0].pat.kind {
                     hir::PatKind::Ref(inner, Mutability::Not) => {
diff --git a/src/tools/clippy/clippy_lints/src/methods/map_err_ignore.rs b/src/tools/clippy/clippy_lints/src/methods/map_err_ignore.rs
index 162f0ac564d..5d0d4dae35f 100644
--- a/src/tools/clippy/clippy_lints/src/methods/map_err_ignore.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/map_err_ignore.rs
@@ -16,7 +16,7 @@ pub(super) fn check(cx: &LateContext<'_>, e: &Expr<'_>, arg: &Expr<'_>) {
             fn_decl_span,
             ..
         }) = arg.kind
-        && let closure_body = cx.tcx.hir().body(body)
+        && let closure_body = cx.tcx.hir_body(body)
         && let [param] = closure_body.params
         && let PatKind::Wild = param.pat.kind
     {
diff --git a/src/tools/clippy/clippy_lints/src/methods/map_with_unused_argument_over_ranges.rs b/src/tools/clippy/clippy_lints/src/methods/map_with_unused_argument_over_ranges.rs
index 78656ace831..35dd7c082c9 100644
--- a/src/tools/clippy/clippy_lints/src/methods/map_with_unused_argument_over_ranges.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/map_with_unused_argument_over_ranges.rs
@@ -68,7 +68,7 @@ pub(super) fn check(
     let mut applicability = Applicability::MaybeIncorrect;
     if let Some(range) = higher::Range::hir(receiver)
         && let ExprKind::Closure(Closure { body, .. }) = arg.kind
-        && let body_hir = cx.tcx.hir().body(*body)
+        && let body_hir = cx.tcx.hir_body(*body)
         && let Body {
             params: [param],
             value: body_expr,
diff --git a/src/tools/clippy/clippy_lints/src/methods/mod.rs b/src/tools/clippy/clippy_lints/src/methods/mod.rs
index 0a8eafad0e8..b58e8ba32e7 100644
--- a/src/tools/clippy/clippy_lints/src/methods/mod.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/mod.rs
@@ -4671,7 +4671,7 @@ impl<'tcx> LateLintPass<'tcx> for Methods {
             return;
         }
         let name = impl_item.ident.name.as_str();
-        let parent = cx.tcx.hir().get_parent_item(impl_item.hir_id()).def_id;
+        let parent = cx.tcx.hir_get_parent_item(impl_item.hir_id()).def_id;
         let item = cx.tcx.hir().expect_item(parent);
         let self_ty = cx.tcx.type_of(item.owner_id).instantiate_identity();
 
@@ -4716,7 +4716,7 @@ impl<'tcx> LateLintPass<'tcx> for Methods {
             if sig.decl.implicit_self.has_implicit_self()
                 && !(self.avoid_breaking_exported_api
                     && cx.effective_visibilities.is_exported(impl_item.owner_id.def_id))
-                && let Some(first_arg) = iter_input_pats(sig.decl, cx.tcx.hir().body(id)).next()
+                && let Some(first_arg) = iter_input_pats(sig.decl, cx.tcx.hir_body(id)).next()
                 && let Some(first_arg_ty) = first_arg_ty_opt
             {
                 wrong_self_convention::check(
@@ -4852,7 +4852,7 @@ impl Methods {
                         ),
                         Some(("chars", recv, _, _, _))
                             if let ExprKind::Closure(arg) = arg.kind
-                                && let body = cx.tcx.hir().body(arg.body)
+                                && let body = cx.tcx.hir_body(arg.body)
                                 && let [param] = body.params =>
                         {
                             string_lit_chars_any::check(cx, expr, recv, param, peel_blocks(body.value), &self.msrv);
diff --git a/src/tools/clippy/clippy_lints/src/methods/needless_character_iteration.rs b/src/tools/clippy/clippy_lints/src/methods/needless_character_iteration.rs
index 6993150fb57..743aacf0588 100644
--- a/src/tools/clippy/clippy_lints/src/methods/needless_character_iteration.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/needless_character_iteration.rs
@@ -99,7 +99,7 @@ fn handle_expr(
 
 pub(super) fn check(cx: &LateContext<'_>, call_expr: &Expr<'_>, recv: &Expr<'_>, closure_arg: &Expr<'_>, is_all: bool) {
     if let ExprKind::Closure(&Closure { body, .. }) = closure_arg.kind
-        && let body = cx.tcx.hir().body(body)
+        && let body = cx.tcx.hir_body(body)
         && let Some(first_param) = body.params.first()
         && let ExprKind::MethodCall(method, mut recv, [], _) = recv.kind
         && method.ident.name.as_str() == "chars"
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 2780c3f8af5..45f79dd44f2 100644
--- a/src/tools/clippy/clippy_lints/src/methods/needless_collect.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/needless_collect.rs
@@ -456,8 +456,8 @@ impl<'tcx> Visitor<'tcx> for UsedCountVisitor<'_, 'tcx> {
         }
     }
 
-    fn nested_visit_map(&mut self) -> Self::Map {
-        self.cx.tcx.hir()
+    fn maybe_tcx(&mut self) -> Self::MaybeTyCtxt {
+        self.cx.tcx
     }
 }
 
@@ -498,7 +498,7 @@ fn get_captured_ids(cx: &LateContext<'_>, ty: Ty<'_>) -> HirIdSet {
                 }
             },
             ty::Closure(def_id, _) => {
-                let closure_hir_node = cx.tcx.hir().get_if_local(*def_id).unwrap();
+                let closure_hir_node = cx.tcx.hir_get_if_local(*def_id).unwrap();
                 if let Node::Expr(closure_expr) = closure_hir_node {
                     can_move_expr_to_closure(cx, closure_expr)
                         .unwrap()
diff --git a/src/tools/clippy/clippy_lints/src/methods/obfuscated_if_else.rs b/src/tools/clippy/clippy_lints/src/methods/obfuscated_if_else.rs
index b71f79f8482..e0905374dda 100644
--- a/src/tools/clippy/clippy_lints/src/methods/obfuscated_if_else.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/obfuscated_if_else.rs
@@ -27,7 +27,7 @@ pub(super) fn check<'tcx>(
 
         let if_then = match then_method_name {
             "then" if let ExprKind::Closure(closure) = then_arg.kind => {
-                let body = cx.tcx.hir().body(closure.body);
+                let body = cx.tcx.hir_body(closure.body);
                 snippet_with_applicability(cx, body.value.span, "..", &mut applicability)
             },
             "then_some" => snippet_with_applicability(cx, then_arg.span, "..", &mut applicability),
diff --git a/src/tools/clippy/clippy_lints/src/methods/option_as_ref_deref.rs b/src/tools/clippy/clippy_lints/src/methods/option_as_ref_deref.rs
index 8d97d1c72a6..469fcccbe4f 100644
--- a/src/tools/clippy/clippy_lints/src/methods/option_as_ref_deref.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/option_as_ref_deref.rs
@@ -54,7 +54,7 @@ pub(super) fn check(
                 })
         },
         hir::ExprKind::Closure(&hir::Closure { body, .. }) => {
-            let closure_body = cx.tcx.hir().body(body);
+            let closure_body = cx.tcx.hir_body(body);
             let closure_expr = peel_blocks(closure_body.value);
 
             match &closure_expr.kind {
diff --git a/src/tools/clippy/clippy_lints/src/methods/option_map_or_none.rs b/src/tools/clippy/clippy_lints/src/methods/option_map_or_none.rs
index 193deafccf6..1a273f77fb7 100644
--- a/src/tools/clippy/clippy_lints/src/methods/option_map_or_none.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/option_map_or_none.rs
@@ -60,7 +60,7 @@ pub(super) fn check<'tcx>(
         let self_snippet = snippet(cx, recv.span, "..");
         if let hir::ExprKind::Closure(&hir::Closure { body, fn_decl_span, .. }) = map_arg.kind
             && let arg_snippet = snippet(cx, fn_decl_span, "..")
-            && let body = cx.tcx.hir().body(body)
+            && let body = cx.tcx.hir_body(body)
             && let Some((func, [arg_char])) = reduce_unit_expression(body.value)
             && let Some(id) = path_def_id(cx, func).map(|ctor_id| cx.tcx.parent(ctor_id))
             && Some(id) == cx.tcx.lang_items().option_some_variant()
diff --git a/src/tools/clippy/clippy_lints/src/methods/option_map_unwrap_or.rs b/src/tools/clippy/clippy_lints/src/methods/option_map_unwrap_or.rs
index 7c4dc4ffb20..b1107d8cc72 100644
--- a/src/tools/clippy/clippy_lints/src/methods/option_map_unwrap_or.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/option_map_unwrap_or.rs
@@ -58,8 +58,7 @@ pub(super) fn check<'tcx>(
                 unwrap_or_span: unwrap_arg.span,
             };
 
-            let map = cx.tcx.hir();
-            let body = map.body_owned_by(map.enclosing_body_owner(expr.hir_id));
+            let body = cx.tcx.hir_body_owned_by(cx.tcx.hir_enclosing_body_owner(expr.hir_id));
 
             // Visit the body, and return if we've found a reference
             if reference_visitor.visit_body(body).is_break() {
@@ -143,8 +142,8 @@ impl<'tcx> Visitor<'tcx> for UnwrapVisitor<'_, 'tcx> {
         walk_path(self, path);
     }
 
-    fn nested_visit_map(&mut self) -> Self::Map {
-        self.cx.tcx.hir()
+    fn maybe_tcx(&mut self) -> Self::MaybeTyCtxt {
+        self.cx.tcx
     }
 }
 
@@ -174,7 +173,7 @@ impl<'tcx> Visitor<'tcx> for ReferenceVisitor<'_, 'tcx> {
         rustc_hir::intravisit::walk_expr(self, expr)
     }
 
-    fn nested_visit_map(&mut self) -> Self::Map {
-        self.cx.tcx.hir()
+    fn maybe_tcx(&mut self) -> Self::MaybeTyCtxt {
+        self.cx.tcx
     }
 }
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 6b39b753885..69f933fee68 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
@@ -92,7 +92,7 @@ pub(super) fn check<'tcx>(
         let in_sugg_method_implementation = {
             matches!(
                 suggested_method_def_id.as_local(),
-                Some(local_def_id) if local_def_id == cx.tcx.hir().get_parent_item(receiver.hir_id).def_id
+                Some(local_def_id) if local_def_id == cx.tcx.hir_get_parent_item(receiver.hir_id).def_id
             )
         };
         if in_sugg_method_implementation {
@@ -253,7 +253,7 @@ pub(super) fn check<'tcx>(
 
 fn closure_body_returns_empty_to_string(cx: &LateContext<'_>, e: &hir::Expr<'_>) -> bool {
     if let hir::ExprKind::Closure(&hir::Closure { body, .. }) = e.kind {
-        let body = cx.tcx.hir().body(body);
+        let body = cx.tcx.hir_body(body);
 
         if body.params.is_empty()
             && let hir::Expr { kind, .. } = &body.value
diff --git a/src/tools/clippy/clippy_lints/src/methods/result_map_or_else_none.rs b/src/tools/clippy/clippy_lints/src/methods/result_map_or_else_none.rs
index 3b0dc506305..af619c9e3bb 100644
--- a/src/tools/clippy/clippy_lints/src/methods/result_map_or_else_none.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/result_map_or_else_none.rs
@@ -23,7 +23,7 @@ pub(super) fn check<'tcx>(
         // We check that it is mapped as `Some`.
         && is_res_lang_ctor(cx, path_res(cx, map_arg), OptionSome)
         && let hir::ExprKind::Closure(&hir::Closure { body, .. }) = def_arg.kind
-        && let body = cx.tcx.hir().body(body)
+        && let body = cx.tcx.hir_body(body)
         // And finally we check that we return a `None` in the "else case".
         && is_res_lang_ctor(cx, path_res(cx, peel_blocks(body.value)), OptionNone)
     {
diff --git a/src/tools/clippy/clippy_lints/src/methods/return_and_then.rs b/src/tools/clippy/clippy_lints/src/methods/return_and_then.rs
index 7b1199ad1e2..68ffa81a278 100644
--- a/src/tools/clippy/clippy_lints/src/methods/return_and_then.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/return_and_then.rs
@@ -44,7 +44,7 @@ pub(super) fn check<'tcx>(
     };
 
     let closure_arg = fn_decl.inputs[0];
-    let closure_expr = peel_blocks(cx.tcx.hir().body(body).value);
+    let closure_expr = peel_blocks(cx.tcx.hir_body(body).value);
 
     let mut applicability = Applicability::MachineApplicable;
     let arg_snip = snippet_with_applicability(cx, closure_arg.span, "_", &mut applicability);
diff --git a/src/tools/clippy/clippy_lints/src/methods/search_is_some.rs b/src/tools/clippy/clippy_lints/src/methods/search_is_some.rs
index 4ab165a5528..97c8ce2bcdd 100644
--- a/src/tools/clippy/clippy_lints/src/methods/search_is_some.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/search_is_some.rs
@@ -37,7 +37,7 @@ pub(super) fn check<'tcx>(
             let mut applicability = Applicability::MachineApplicable;
             let any_search_snippet = if search_method == "find"
                 && let ExprKind::Closure(&hir::Closure { body, .. }) = search_arg.kind
-                && let closure_body = cx.tcx.hir().body(body)
+                && let closure_body = cx.tcx.hir_body(body)
                 && let Some(closure_arg) = closure_body.params.first()
             {
                 if let PatKind::Ref(..) = closure_arg.pat.kind {
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 8a99974394c..8389c2e3f98 100644
--- a/src/tools/clippy/clippy_lints/src/methods/str_splitn.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/str_splitn.rs
@@ -35,7 +35,7 @@ pub(super) fn check(
     };
     let manual = count == 2 && msrv.meets(msrvs::STR_SPLIT_ONCE);
 
-    match parse_iter_usage(cx, expr.span.ctxt(), cx.tcx.hir().parent_iter(expr.hir_id)) {
+    match parse_iter_usage(cx, expr.span.ctxt(), cx.tcx.hir_parent_iter(expr.hir_id)) {
         Some(usage) if needless(usage.kind) => lint_needless(cx, method_name, expr, self_arg, pat_arg),
         Some(usage) if manual => check_manual_split_once(cx, method_name, expr, self_arg, pat_arg, &usage),
         None if manual => {
@@ -127,7 +127,7 @@ fn check_manual_split_once_indirect(
     pat_arg: &Expr<'_>,
 ) -> Option<()> {
     let ctxt = expr.span.ctxt();
-    let mut parents = cx.tcx.hir().parent_iter(expr.hir_id);
+    let mut parents = cx.tcx.hir_parent_iter(expr.hir_id);
     if let (_, Node::LetStmt(local)) = parents.next()?
         && let PatKind::Binding(BindingMode::MUT, iter_binding_id, _, None) = local.pat.kind
         && let (iter_stmt_id, Node::Stmt(_)) = parents.next()?
@@ -220,7 +220,7 @@ fn indirect_usage<'tcx>(
             ControlFlow::Continue(Descend::from(path_to_binding.is_none()))
         });
 
-        let mut parents = cx.tcx.hir().parent_iter(path_to_binding?.hir_id);
+        let mut parents = cx.tcx.hir_parent_iter(path_to_binding?.hir_id);
         let iter_usage = parse_iter_usage(cx, ctxt, &mut parents)?;
 
         let (parent_id, _) = parents.find(|(_, node)| {
diff --git a/src/tools/clippy/clippy_lints/src/methods/suspicious_map.rs b/src/tools/clippy/clippy_lints/src/methods/suspicious_map.rs
index ed49233acb7..1bd48525f12 100644
--- a/src/tools/clippy/clippy_lints/src/methods/suspicious_map.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/suspicious_map.rs
@@ -10,7 +10,7 @@ use super::SUSPICIOUS_MAP;
 pub fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, count_recv: &hir::Expr<'_>, map_arg: &hir::Expr<'_>) {
     if is_trait_method(cx, count_recv, sym::Iterator)
         && let hir::ExprKind::Closure(closure) = expr_or_init(cx, map_arg).kind
-        && let closure_body = cx.tcx.hir().body(closure.body)
+        && let closure_body = cx.tcx.hir_body(closure.body)
         && !cx.typeck_results().expr_ty(closure_body.value).is_unit()
     {
         if let Some(map_mutated_vars) = mutated_variables(closure_body.value, cx) {
diff --git a/src/tools/clippy/clippy_lints/src/methods/unnecessary_filter_map.rs b/src/tools/clippy/clippy_lints/src/methods/unnecessary_filter_map.rs
index 5b9e9e70e47..ca42a9ac04e 100644
--- a/src/tools/clippy/clippy_lints/src/methods/unnecessary_filter_map.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/unnecessary_filter_map.rs
@@ -20,7 +20,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'tcx>, a
     }
 
     if let hir::ExprKind::Closure(&hir::Closure { body, .. }) = arg.kind {
-        let body = cx.tcx.hir().body(body);
+        let body = cx.tcx.hir_body(body);
         let arg_id = body.params[0].pat.hir_id;
         let mutates_arg = mutated_variables(body.value, cx).is_none_or(|used_mutably| used_mutably.contains(&arg_id));
         let (clone_or_copy_needed, _) = clone_or_copy_needed(cx, body.params[0].pat, body.value);
diff --git a/src/tools/clippy/clippy_lints/src/methods/unnecessary_fold.rs b/src/tools/clippy/clippy_lints/src/methods/unnecessary_fold.rs
index e7adf3b43ba..8e3cc9abe83 100644
--- a/src/tools/clippy/clippy_lints/src/methods/unnecessary_fold.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/unnecessary_fold.rs
@@ -62,7 +62,7 @@ fn check_fold_with_op(
 ) {
     if let hir::ExprKind::Closure(&hir::Closure { body, .. }) = acc.kind
         // Extract the body of the closure passed to fold
-        && let closure_body = cx.tcx.hir().body(body)
+        && let closure_body = cx.tcx.hir_body(body)
         && let closure_expr = peel_blocks(closure_body.value)
 
         // Check if the closure body is of the form `acc <op> some_expr(x)`
diff --git a/src/tools/clippy/clippy_lints/src/methods/unnecessary_lazy_eval.rs b/src/tools/clippy/clippy_lints/src/methods/unnecessary_lazy_eval.rs
index 7af550fa7c6..9f4080100da 100644
--- a/src/tools/clippy/clippy_lints/src/methods/unnecessary_lazy_eval.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/unnecessary_lazy_eval.rs
@@ -25,7 +25,7 @@ pub(super) fn check<'tcx>(
 
     if is_option || is_result || is_bool {
         if let hir::ExprKind::Closure(&hir::Closure { body, fn_decl, .. }) = arg.kind {
-            let body = cx.tcx.hir().body(body);
+            let body = cx.tcx.hir_body(body);
             let body_expr = &body.value;
 
             if usage::BindingUsageFinder::are_params_used(cx, body) || is_from_proc_macro(cx, expr) {
diff --git a/src/tools/clippy/clippy_lints/src/methods/unnecessary_literal_unwrap.rs b/src/tools/clippy/clippy_lints/src/methods/unnecessary_literal_unwrap.rs
index 10112b62878..00690aca6d1 100644
--- a/src/tools/clippy/clippy_lints/src/methods/unnecessary_literal_unwrap.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/unnecessary_literal_unwrap.rs
@@ -99,7 +99,7 @@ pub(super) fn check(
             ("None", "unwrap_or_else", _) => match args[0].kind {
                 hir::ExprKind::Closure(hir::Closure { body, .. }) => Some(vec![
                     (
-                        expr.span.with_hi(cx.tcx.hir().body(*body).value.span.lo()),
+                        expr.span.with_hi(cx.tcx.hir_body(*body).value.span.lo()),
                         String::new(),
                     ),
                     (expr.span.with_lo(args[0].span.hi()), String::new()),
diff --git a/src/tools/clippy/clippy_lints/src/methods/unnecessary_map_or.rs b/src/tools/clippy/clippy_lints/src/methods/unnecessary_map_or.rs
index 6dea1506d0e..5f88a7fd31f 100644
--- a/src/tools/clippy/clippy_lints/src/methods/unnecessary_map_or.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/unnecessary_map_or.rs
@@ -63,7 +63,7 @@ pub(super) fn check<'a>(
     let ext_def_span = def.span.until(map.span);
 
     let (sugg, method, applicability) = if let ExprKind::Closure(map_closure) = map.kind
-            && let closure_body = cx.tcx.hir().body(map_closure.body)
+            && let closure_body = cx.tcx.hir_body(map_closure.body)
             && let closure_body_value = closure_body.value.peel_blocks()
             && let ExprKind::Binary(op, l, r) = closure_body_value.kind
             && let Some(param) = closure_body.params.first()
diff --git a/src/tools/clippy/clippy_lints/src/methods/unnecessary_result_map_or_else.rs b/src/tools/clippy/clippy_lints/src/methods/unnecessary_result_map_or_else.rs
index dc50717112d..f84d0d6dff0 100644
--- a/src/tools/clippy/clippy_lints/src/methods/unnecessary_result_map_or_else.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/unnecessary_result_map_or_else.rs
@@ -53,7 +53,7 @@ pub(super) fn check<'tcx>(
     // lint if the caller of `map_or_else()` is a `Result`
     if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(recv), sym::Result)
         && let ExprKind::Closure(&Closure { body, .. }) = map_arg.kind
-        && let body = cx.tcx.hir().body(body)
+        && let body = cx.tcx.hir_body(body)
         && let Some(first_param) = body.params.first()
     {
         let body_expr = peel_blocks(body.value);
diff --git a/src/tools/clippy/clippy_lints/src/methods/unnecessary_sort_by.rs b/src/tools/clippy/clippy_lints/src/methods/unnecessary_sort_by.rs
index f0b29213e1e..fb4984914eb 100644
--- a/src/tools/clippy/clippy_lints/src/methods/unnecessary_sort_by.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/unnecessary_sort_by.rs
@@ -117,7 +117,7 @@ fn detect_lint(cx: &LateContext<'_>, expr: &Expr<'_>, recv: &Expr<'_>, arg: &Exp
         && let Some(impl_id) = cx.tcx.impl_of_method(method_id)
         && cx.tcx.type_of(impl_id).instantiate_identity().is_slice()
         && let ExprKind::Closure(&Closure { body, .. }) = arg.kind
-        && let closure_body = cx.tcx.hir().body(body)
+        && let closure_body = cx.tcx.hir_body(body)
         && let &[
             Param {
                 pat:
diff --git a/src/tools/clippy/clippy_lints/src/methods/unnecessary_to_owned.rs b/src/tools/clippy/clippy_lints/src/methods/unnecessary_to_owned.rs
index 7d72310c1c4..ea134c05705 100644
--- a/src/tools/clippy/clippy_lints/src/methods/unnecessary_to_owned.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/unnecessary_to_owned.rs
@@ -494,7 +494,7 @@ fn get_input_traits_and_projections<'tcx>(
 
 #[expect(clippy::too_many_lines)]
 fn can_change_type<'a>(cx: &LateContext<'a>, mut expr: &'a Expr<'a>, mut ty: Ty<'a>) -> bool {
-    for (_, node) in cx.tcx.hir().parent_iter(expr.hir_id) {
+    for (_, node) in cx.tcx.hir_parent_iter(expr.hir_id) {
         match node {
             Node::Stmt(_) => return true,
             Node::Block(..) => {},
@@ -506,7 +506,7 @@ fn can_change_type<'a>(cx: &LateContext<'a>, mut expr: &'a Expr<'a>, mut ty: Ty<
                     if has_lifetime(output_ty) && has_lifetime(ty) {
                         return false;
                     }
-                    let body = cx.tcx.hir().body(*body_id);
+                    let body = cx.tcx.hir_body(*body_id);
                     let body_expr = &body.value;
                     let mut count = 0;
                     return find_all_ret_expressions(cx, body_expr, |_| {
diff --git a/src/tools/clippy/clippy_lints/src/methods/unused_enumerate_index.rs b/src/tools/clippy/clippy_lints/src/methods/unused_enumerate_index.rs
index 0aec26f1011..af466fe091c 100644
--- a/src/tools/clippy/clippy_lints/src/methods/unused_enumerate_index.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/unused_enumerate_index.rs
@@ -46,7 +46,7 @@ pub(super) fn check(cx: &LateContext<'_>, call_expr: &Expr<'_>, recv: &Expr<'_>,
         && is_trait_method(cx, call_expr, sym::Iterator)
         // And the map argument is a closure
         && let ExprKind::Closure(closure) = closure_arg.kind
-        && let closure_body = cx.tcx.hir().body(closure.body)
+        && let closure_body = cx.tcx.hir_body(closure.body)
         // And that closure has one argument ...
         && let [closure_param] = closure_body.params
         // .. which is a tuple of 2 elements
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 82313257e5c..19152362fb5 100644
--- a/src/tools/clippy/clippy_lints/src/methods/useless_asref.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/useless_asref.rs
@@ -113,7 +113,7 @@ fn is_calling_clone(cx: &LateContext<'_>, arg: &hir::Expr<'_>) -> bool {
     match arg.kind {
         hir::ExprKind::Closure(&hir::Closure { body, .. })
             // If it's a closure, we need to check what is called.
-            if let closure_body = cx.tcx.hir().body(body)
+            if let closure_body = cx.tcx.hir_body(body)
                 && let [param] = closure_body.params
                 && let hir::PatKind::Binding(_, local_id, ..) = strip_pat_refs(param.pat).kind =>
         {
diff --git a/src/tools/clippy/clippy_lints/src/methods/utils.rs b/src/tools/clippy/clippy_lints/src/methods/utils.rs
index 6e39e7be2c4..3611b341897 100644
--- a/src/tools/clippy/clippy_lints/src/methods/utils.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/utils.rs
@@ -89,8 +89,8 @@ struct CloneOrCopyVisitor<'cx, 'tcx> {
 impl<'tcx> Visitor<'tcx> for CloneOrCopyVisitor<'_, 'tcx> {
     type NestedFilter = nested_filter::OnlyBodies;
 
-    fn nested_visit_map(&mut self) -> Self::Map {
-        self.cx.tcx.hir()
+    fn maybe_tcx(&mut self) -> Self::MaybeTyCtxt {
+        self.cx.tcx
     }
 
     fn visit_expr(&mut self, expr: &'tcx Expr<'tcx>) {
diff --git a/src/tools/clippy/clippy_lints/src/min_ident_chars.rs b/src/tools/clippy/clippy_lints/src/min_ident_chars.rs
index 4119b1d1051..e5801124db4 100644
--- a/src/tools/clippy/clippy_lints/src/min_ident_chars.rs
+++ b/src/tools/clippy/clippy_lints/src/min_ident_chars.rs
@@ -121,9 +121,9 @@ impl Visitor<'_> for IdentVisitor<'_, '_> {
             // Check whether the node is part of a `use` statement. We don't want to emit a warning if the user
             // has no control over the type.
             let usenode = opt_as_use_node(node).or_else(|| {
-                cx.tcx
-                    .hir()
-                    .parent_iter(hir_id)
+                cx
+                    .tcx
+                    .hir_parent_iter(hir_id)
                     .find_map(|(_, node)| opt_as_use_node(node))
             });
 
diff --git a/src/tools/clippy/clippy_lints/src/missing_const_for_fn.rs b/src/tools/clippy/clippy_lints/src/missing_const_for_fn.rs
index ba4af134ccd..8d751c2c0ac 100644
--- a/src/tools/clippy/clippy_lints/src/missing_const_for_fn.rs
+++ b/src/tools/clippy/clippy_lints/src/missing_const_for_fn.rs
@@ -143,7 +143,7 @@ impl<'tcx> LateLintPass<'tcx> for MissingConstForFn {
 
         // Const fns are not allowed as methods in a trait.
         {
-            let parent = cx.tcx.hir().get_parent_item(hir_id).def_id;
+            let parent = cx.tcx.hir_get_parent_item(hir_id).def_id;
             if parent != CRATE_DEF_ID {
                 if let hir::Node::Item(item) = cx.tcx.hir_node_by_def_id(parent) {
                     if let hir::ItemKind::Trait(..) = &item.kind {
diff --git a/src/tools/clippy/clippy_lints/src/missing_const_for_thread_local.rs b/src/tools/clippy/clippy_lints/src/missing_const_for_thread_local.rs
index e2ca4458eda..d4181c677af 100644
--- a/src/tools/clippy/clippy_lints/src/missing_const_for_thread_local.rs
+++ b/src/tools/clippy/clippy_lints/src/missing_const_for_thread_local.rs
@@ -81,7 +81,7 @@ fn is_unreachable(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
                 | sym::core_panic_2015_macro
                 | sym::std_panic_2015_macro
                 | sym::core_panic_2021_macro
-        ) && !cx.tcx.hir().is_inside_const_context(expr.hir_id))
+        ) && !cx.tcx.hir_is_inside_const_context(expr.hir_id))
             || matches!(
                 diag_name,
                 sym::unimplemented_macro | sym::todo_macro | sym::unreachable_macro | sym::unreachable_2015_macro
diff --git a/src/tools/clippy/clippy_lints/src/missing_doc.rs b/src/tools/clippy/clippy_lints/src/missing_doc.rs
index 06e92985e66..47a9e17b3cf 100644
--- a/src/tools/clippy/clippy_lints/src/missing_doc.rs
+++ b/src/tools/clippy/clippy_lints/src/missing_doc.rs
@@ -217,7 +217,7 @@ impl<'tcx> LateLintPass<'tcx> for MissingDoc {
             | hir::ItemKind::Union(..) => {},
             hir::ItemKind::ExternCrate(..)
             | hir::ItemKind::ForeignMod { .. }
-            | hir::ItemKind::GlobalAsm(..)
+            | hir::ItemKind::GlobalAsm { .. }
             | hir::ItemKind::Impl { .. }
             | hir::ItemKind::Use(..) => note_prev_span_then_ret!(self.prev_span, it.span),
         }
diff --git a/src/tools/clippy/clippy_lints/src/missing_fields_in_debug.rs b/src/tools/clippy/clippy_lints/src/missing_fields_in_debug.rs
index e9ec23b1efa..675989156ca 100644
--- a/src/tools/clippy/clippy_lints/src/missing_fields_in_debug.rs
+++ b/src/tools/clippy/clippy_lints/src/missing_fields_in_debug.rs
@@ -213,8 +213,8 @@ impl<'tcx> LateLintPass<'tcx> for MissingFieldsInDebug {
             && !item.span.from_expansion()
             // find `Debug::fmt` function
             && let Some(fmt_item) = items.iter().find(|i| i.ident.name == sym::fmt)
-            && let ImplItem { kind: ImplItemKind::Fn(_, body_id), .. } = cx.tcx.hir().impl_item(fmt_item.id)
-            && let body = cx.tcx.hir().body(*body_id)
+            && let ImplItem { kind: ImplItemKind::Fn(_, body_id), .. } = cx.tcx.hir_impl_item(fmt_item.id)
+            && let body = cx.tcx.hir_body(*body_id)
             && let ExprKind::Block(block, _) = body.value.kind
             // inspect `self`
             && let self_ty = cx.tcx.type_of(self_path_did).skip_binder().peel_refs()
diff --git a/src/tools/clippy/clippy_lints/src/missing_inline.rs b/src/tools/clippy/clippy_lints/src/missing_inline.rs
index 18385ac9269..3cf1a80607e 100644
--- a/src/tools/clippy/clippy_lints/src/missing_inline.rs
+++ b/src/tools/clippy/clippy_lints/src/missing_inline.rs
@@ -105,7 +105,7 @@ impl<'tcx> LateLintPass<'tcx> for MissingInline {
                 // note: we need to check if the trait is exported so we can't use
                 // `LateLintPass::check_trait_item` here.
                 for tit in trait_items {
-                    let tit_ = cx.tcx.hir().trait_item(tit.id);
+                    let tit_ = cx.tcx.hir_trait_item(tit.id);
                     match tit_.kind {
                         hir::TraitItemKind::Const(..) | hir::TraitItemKind::Type(..) => {},
                         hir::TraitItemKind::Fn(..) => {
@@ -113,7 +113,7 @@ impl<'tcx> LateLintPass<'tcx> for MissingInline {
                                 // trait method with default body needs inline in case
                                 // an impl is not provided
                                 let desc = "a default trait method";
-                                let item = cx.tcx.hir().trait_item(tit.id);
+                                let item = cx.tcx.hir_trait_item(tit.id);
                                 let attrs = cx.tcx.hir().attrs(item.hir_id());
                                 check_missing_inline_attrs(cx, attrs, item.span, desc);
                             }
@@ -128,7 +128,7 @@ impl<'tcx> LateLintPass<'tcx> for MissingInline {
             | hir::ItemKind::Static(..)
             | hir::ItemKind::Struct(..)
             | hir::ItemKind::TraitAlias(..)
-            | hir::ItemKind::GlobalAsm(..)
+            | hir::ItemKind::GlobalAsm { .. }
             | hir::ItemKind::TyAlias(..)
             | hir::ItemKind::Union(..)
             | hir::ItemKind::ExternCrate(..)
diff --git a/src/tools/clippy/clippy_lints/src/multiple_unsafe_ops_per_block.rs b/src/tools/clippy/clippy_lints/src/multiple_unsafe_ops_per_block.rs
index 9acede4f32d..2adc27c0b70 100644
--- a/src/tools/clippy/clippy_lints/src/multiple_unsafe_ops_per_block.rs
+++ b/src/tools/clippy/clippy_lints/src/multiple_unsafe_ops_per_block.rs
@@ -122,7 +122,7 @@ fn collect_unsafe_exprs<'tcx>(
                 unsafe_ops.push(("access of a mutable static occurs here", expr.span));
             },
 
-            ExprKind::Unary(UnOp::Deref, e) if cx.typeck_results().expr_ty_adjusted(e).is_unsafe_ptr() => {
+            ExprKind::Unary(UnOp::Deref, e) if cx.typeck_results().expr_ty_adjusted(e).is_raw_ptr() => {
                 unsafe_ops.push(("raw pointer dereference occurs here", expr.span));
             },
 
diff --git a/src/tools/clippy/clippy_lints/src/mutable_debug_assertion.rs b/src/tools/clippy/clippy_lints/src/mutable_debug_assertion.rs
index 152635a5c35..13a23a13b9c 100644
--- a/src/tools/clippy/clippy_lints/src/mutable_debug_assertion.rs
+++ b/src/tools/clippy/clippy_lints/src/mutable_debug_assertion.rs
@@ -119,7 +119,7 @@ impl<'tcx> Visitor<'tcx> for MutArgVisitor<'_, 'tcx> {
         walk_expr(self, expr);
     }
 
-    fn nested_visit_map(&mut self) -> Self::Map {
-        self.cx.tcx.hir()
+    fn maybe_tcx(&mut self) -> Self::MaybeTyCtxt {
+        self.cx.tcx
     }
 }
diff --git a/src/tools/clippy/clippy_lints/src/needless_borrowed_ref.rs b/src/tools/clippy/clippy_lints/src/needless_borrowed_ref.rs
index 098098718af..1b6896827fe 100644
--- a/src/tools/clippy/clippy_lints/src/needless_borrowed_ref.rs
+++ b/src/tools/clippy/clippy_lints/src/needless_borrowed_ref.rs
@@ -41,8 +41,7 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessBorrowedRef {
             && !ref_pat.span.from_expansion()
             && cx
                 .tcx
-                .hir()
-                .parent_iter(ref_pat.hir_id)
+                .hir_parent_iter(ref_pat.hir_id)
                 .map_while(|(_, parent)| if let Node::Pat(pat) = parent { Some(pat) } else { None })
                 // Do not lint patterns that are part of an OR `|` pattern, the binding mode must match in all arms
                 .all(|pat| !matches!(pat.kind, PatKind::Or(_)))
diff --git a/src/tools/clippy/clippy_lints/src/needless_borrows_for_generic_args.rs b/src/tools/clippy/clippy_lints/src/needless_borrows_for_generic_args.rs
index 7f91e555054..ea1d7e5d438 100644
--- a/src/tools/clippy/clippy_lints/src/needless_borrows_for_generic_args.rs
+++ b/src/tools/clippy/clippy_lints/src/needless_borrows_for_generic_args.rs
@@ -137,7 +137,7 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessBorrowsForGenericArgs<'tcx> {
         if self
             .possible_borrowers
             .last()
-            .is_some_and(|&(local_def_id, _)| local_def_id == cx.tcx.hir().body_owner_def_id(body.id()))
+            .is_some_and(|&(local_def_id, _)| local_def_id == cx.tcx.hir_body_owner_def_id(body.id()))
         {
             self.possible_borrowers.pop();
         }
@@ -359,7 +359,7 @@ fn referent_used_exactly_once<'tcx>(
         && let StatementKind::Assign(box (_, Rvalue::Ref(_, _, place))) = statement.kind
         && !place.is_indirect_first_projection()
     {
-        let body_owner_local_def_id = cx.tcx.hir().enclosing_body_owner(reference.hir_id);
+        let body_owner_local_def_id = cx.tcx.hir_enclosing_body_owner(reference.hir_id);
         if possible_borrowers
             .last()
             .is_none_or(|&(local_def_id, _)| local_def_id != body_owner_local_def_id)
diff --git a/src/tools/clippy/clippy_lints/src/needless_for_each.rs b/src/tools/clippy/clippy_lints/src/needless_for_each.rs
index 93e20f37ef8..90b27f5dbac 100644
--- a/src/tools/clippy/clippy_lints/src/needless_for_each.rs
+++ b/src/tools/clippy/clippy_lints/src/needless_for_each.rs
@@ -71,7 +71,7 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessForEach {
             // Skip the lint if the body is not block because this is simpler than `for` loop.
             // e.g. `v.iter().for_each(f)` is simpler and clearer than using `for` loop.
             && let ExprKind::Closure(&Closure { body, .. }) = for_each_arg.kind
-            && let body = cx.tcx.hir().body(body)
+            && let body = cx.tcx.hir_body(body)
             // Skip the lint if the body is not safe, so as not to suggest `for … in … unsafe {}`
             // and suggesting `for … in … { unsafe { } }` is a little ugly.
             && let ExprKind::Block(Block { rules: BlockCheckMode::DefaultBlock, .. }, ..) = body.value.kind
diff --git a/src/tools/clippy/clippy_lints/src/needless_late_init.rs b/src/tools/clippy/clippy_lints/src/needless_late_init.rs
index 4e19a2f409d..863a1f895c9 100644
--- a/src/tools/clippy/clippy_lints/src/needless_late_init.rs
+++ b/src/tools/clippy/clippy_lints/src/needless_late_init.rs
@@ -347,7 +347,7 @@ fn check<'tcx>(
 
 impl<'tcx> LateLintPass<'tcx> for NeedlessLateInit {
     fn check_local(&mut self, cx: &LateContext<'tcx>, local: &'tcx LetStmt<'tcx>) {
-        let mut parents = cx.tcx.hir().parent_iter(local.hir_id);
+        let mut parents = cx.tcx.hir_parent_iter(local.hir_id);
         if let LetStmt {
             init: None,
             pat:
diff --git a/src/tools/clippy/clippy_lints/src/needless_pass_by_ref_mut.rs b/src/tools/clippy/clippy_lints/src/needless_pass_by_ref_mut.rs
index 996251fdf16..36a0738cbc9 100644
--- a/src/tools/clippy/clippy_lints/src/needless_pass_by_ref_mut.rs
+++ b/src/tools/clippy/clippy_lints/src/needless_pass_by_ref_mut.rs
@@ -103,7 +103,6 @@ fn check_closures<'tcx>(
     checked_closures: &mut FxHashSet<LocalDefId>,
     closures: FxIndexSet<LocalDefId>,
 ) {
-    let hir = cx.tcx.hir();
     for closure in closures {
         if !checked_closures.insert(closure) {
             continue;
@@ -114,7 +113,7 @@ fn check_closures<'tcx>(
             .tcx
             .hir_node_by_def_id(closure)
             .associated_body()
-            .map(|(_, body_id)| hir.body(body_id))
+            .map(|(_, body_id)| cx.tcx.hir_body(body_id))
         {
             euv::ExprUseVisitor::for_clippy(cx, closure, &mut *ctx)
                 .consume_body(body)
@@ -351,9 +350,8 @@ impl MutablyUsedVariablesCtxt<'_> {
     // The goal here is to find if the current scope is unsafe or not. It stops when it finds
     // a function or an unsafe block.
     fn is_in_unsafe_block(&self, item: HirId) -> bool {
-        let hir = self.tcx.hir();
-        for (parent, node) in hir.parent_iter(item) {
-            if let Some(fn_sig) = hir.fn_sig_by_hir_id(parent) {
+        for (parent, node) in self.tcx.hir_parent_iter(item) {
+            if let Some(fn_sig) = self.tcx.hir_fn_sig_by_hir_id(parent) {
                 return fn_sig.header.is_unsafe();
             } else if let Node::Block(block) = node {
                 if matches!(block.rules, BlockCheckMode::UnsafeBlock(_)) {
diff --git a/src/tools/clippy/clippy_lints/src/new_without_default.rs b/src/tools/clippy/clippy_lints/src/new_without_default.rs
index cc56df3a23d..f0ee613791f 100644
--- a/src/tools/clippy/clippy_lints/src/new_without_default.rs
+++ b/src/tools/clippy/clippy_lints/src/new_without_default.rs
@@ -67,7 +67,7 @@ impl<'tcx> LateLintPass<'tcx> for NewWithoutDefault {
         {
             for assoc_item in *items {
                 if assoc_item.kind == (hir::AssocItemKind::Fn { has_self: false }) {
-                    let impl_item = cx.tcx.hir().impl_item(assoc_item.id);
+                    let impl_item = cx.tcx.hir_impl_item(assoc_item.id);
                     if impl_item.span.in_external_macro(cx.sess().source_map()) {
                         return;
                     }
@@ -90,7 +90,7 @@ impl<'tcx> LateLintPass<'tcx> for NewWithoutDefault {
                         if sig.decl.inputs.is_empty()
                             && name == sym::new
                             && cx.effective_visibilities.is_reachable(impl_item.owner_id.def_id)
-                            && let self_def_id = cx.tcx.hir().get_parent_item(id.into())
+                            && let self_def_id = cx.tcx.hir_get_parent_item(id.into())
                             && let self_ty = cx.tcx.type_of(self_def_id).instantiate_identity()
                             && self_ty == return_ty(cx, id)
                             && let Some(default_trait_id) = cx.tcx.get_diagnostic_item(sym::Default)
diff --git a/src/tools/clippy/clippy_lints/src/no_effect.rs b/src/tools/clippy/clippy_lints/src/no_effect.rs
index 4d3e6aa79d0..7187a8f2c11 100644
--- a/src/tools/clippy/clippy_lints/src/no_effect.rs
+++ b/src/tools/clippy/clippy_lints/src/no_effect.rs
@@ -141,7 +141,7 @@ impl NoEffect {
                     stmt.span,
                     "statement with no effect",
                     |diag| {
-                        for parent in cx.tcx.hir().parent_iter(stmt.hir_id) {
+                        for parent in cx.tcx.hir_parent_iter(stmt.hir_id) {
                             if let Node::Item(item) = parent.1
                                 && let ItemKind::Fn { .. } = item.kind
                                 && let Node::Block(block) = cx.tcx.parent_hir_node(stmt.hir_id)
diff --git a/src/tools/clippy/clippy_lints/src/no_mangle_with_rust_abi.rs b/src/tools/clippy/clippy_lints/src/no_mangle_with_rust_abi.rs
index b73b9083a99..6eca3f12cf2 100644
--- a/src/tools/clippy/clippy_lints/src/no_mangle_with_rust_abi.rs
+++ b/src/tools/clippy/clippy_lints/src/no_mangle_with_rust_abi.rs
@@ -54,7 +54,7 @@ impl<'tcx> LateLintPass<'tcx> for NoMangleWithRustAbi {
                         .span
                         .with_lo(fn_sig.span.lo() + BytePos::from_usize(fn_attrs.len()))
                         .shrink_to_lo();
-                    let attr_snippet = snippet(cx, attr.span, "..");
+                    let attr_snippet = snippet(cx, attr.span(), "..");
 
                     span_lint_and_then(
                         cx,
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 dad1e8a3d6a..448bb603cf2 100644
--- a/src/tools/clippy/clippy_lints/src/non_canonical_impls.rs
+++ b/src/tools/clippy/clippy_lints/src/non_canonical_impls.rs
@@ -121,10 +121,10 @@ impl LateLintPass<'_> for NonCanonicalImpls {
         if cx.tcx.is_automatically_derived(item.owner_id.to_def_id()) {
             return;
         }
-        let ImplItemKind::Fn(_, impl_item_id) = cx.tcx.hir().impl_item(impl_item.impl_item_id()).kind else {
+        let ImplItemKind::Fn(_, impl_item_id) = cx.tcx.hir_impl_item(impl_item.impl_item_id()).kind else {
             return;
         };
-        let body = cx.tcx.hir().body(impl_item_id);
+        let body = cx.tcx.hir_body(impl_item_id);
         let ExprKind::Block(block, ..) = body.value.kind else {
             return;
         };
diff --git a/src/tools/clippy/clippy_lints/src/non_copy_const.rs b/src/tools/clippy/clippy_lints/src/non_copy_const.rs
index 405bbfc9c6f..4007ca88a00 100644
--- a/src/tools/clippy/clippy_lints/src/non_copy_const.rs
+++ b/src/tools/clippy/clippy_lints/src/non_copy_const.rs
@@ -179,8 +179,8 @@ impl<'tcx> NonCopyConst<'tcx> {
     }
 
     fn is_value_unfrozen_raw_inner(cx: &LateContext<'tcx>, val: ty::ValTree<'tcx>, ty: Ty<'tcx>) -> bool {
-        // No branch that we check (yet) should continue if val isn't a ValTree::Branch
-        let ty::ValTree::Branch(val) = val else { return false };
+        // No branch that we check (yet) should continue if val isn't a branch
+        let Some(val) = val.try_to_branch() else { return false };
         match *ty.kind() {
             // the fact that we have to dig into every structs to search enums
             // leads us to the point checking `UnsafeCell` directly is the only option.
@@ -192,9 +192,10 @@ impl<'tcx> NonCopyConst<'tcx> {
                 .iter()
                 .any(|field| Self::is_value_unfrozen_raw_inner(cx, *field, ty)),
             ty::Adt(def, args) if def.is_enum() => {
-                let Some((&ty::ValTree::Leaf(variant_index), fields)) = val.split_first() else {
+                let Some((&variant_valtree, fields)) = val.split_first() else {
                     return false;
                 };
+                let variant_index = variant_valtree.unwrap_leaf();
                 let variant_index = VariantIdx::from_u32(variant_index.to_u32());
                 fields
                     .iter()
@@ -343,7 +344,7 @@ impl<'tcx> LateLintPass<'tcx> for NonCopyConst<'tcx> {
 
     fn check_impl_item(&mut self, cx: &LateContext<'tcx>, impl_item: &'tcx ImplItem<'_>) {
         if let ImplItemKind::Const(_, body_id) = &impl_item.kind {
-            let item_def_id = cx.tcx.hir().get_parent_item(impl_item.hir_id()).def_id;
+            let item_def_id = cx.tcx.hir_get_parent_item(impl_item.hir_id()).def_id;
             let item = cx.tcx.hir().expect_item(item_def_id);
 
             match &item.kind {
diff --git a/src/tools/clippy/clippy_lints/src/non_std_lazy_statics.rs b/src/tools/clippy/clippy_lints/src/non_std_lazy_statics.rs
index 22116505a1c..774a182d089 100644
--- a/src/tools/clippy/clippy_lints/src/non_std_lazy_statics.rs
+++ b/src/tools/clippy/clippy_lints/src/non_std_lazy_statics.rs
@@ -214,7 +214,7 @@ impl LazyInfo {
             && state.once_cell_sync_lazy.contains(&path_def_id)
         {
             let ty_span_no_args = path_span_without_args(path);
-            let body = cx.tcx.hir().body(body_id);
+            let body = cx.tcx.hir_body(body_id);
 
             // visit body to collect `Lazy::new` calls
             let mut new_fn_calls = FxIndexMap::default();
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 f6ce1d1d586..16c4391c0fb 100644
--- a/src/tools/clippy/clippy_lints/src/non_zero_suggestions.rs
+++ b/src/tools/clippy/clippy_lints/src/non_zero_suggestions.rs
@@ -55,7 +55,7 @@ impl<'tcx> LateLintPass<'tcx> for NonZeroSuggestions {
             check_non_zero_conversion(cx, rhs, Applicability::MachineApplicable);
         } else {
             // Check if the parent expression is a binary operation
-            let parent_is_binary = cx.tcx.hir().parent_iter(expr.hir_id).any(|(_, node)| {
+            let parent_is_binary = cx.tcx.hir_parent_iter(expr.hir_id).any(|(_, node)| {
                 matches!(node, rustc_hir::Node::Expr(parent_expr) if matches!(parent_expr.kind, ExprKind::Binary(..)))
             });
 
diff --git a/src/tools/clippy/clippy_lints/src/operators/arithmetic_side_effects.rs b/src/tools/clippy/clippy_lints/src/operators/arithmetic_side_effects.rs
index 9d07a14718d..594101427f5 100644
--- a/src/tools/clippy/clippy_lints/src/operators/arithmetic_side_effects.rs
+++ b/src/tools/clippy/clippy_lints/src/operators/arithmetic_side_effects.rs
@@ -349,10 +349,10 @@ impl<'tcx> LateLintPass<'tcx> for ArithmeticSideEffects {
     }
 
     fn check_body(&mut self, cx: &LateContext<'_>, body: &hir::Body<'_>) {
-        let body_owner = cx.tcx.hir().body_owner(body.id());
-        let body_owner_def_id = cx.tcx.hir().body_owner_def_id(body.id());
+        let body_owner = cx.tcx.hir_body_owner(body.id());
+        let body_owner_def_id = cx.tcx.hir_body_owner_def_id(body.id());
 
-        let body_owner_kind = cx.tcx.hir().body_owner_kind(body_owner_def_id);
+        let body_owner_kind = cx.tcx.hir_body_owner_kind(body_owner_def_id);
         if let hir::BodyOwnerKind::Const { .. } | hir::BodyOwnerKind::Static(_) = body_owner_kind {
             let body_span = cx.tcx.hir().span_with_body(body_owner);
             if let Some(span) = self.const_span
@@ -365,7 +365,7 @@ impl<'tcx> LateLintPass<'tcx> for ArithmeticSideEffects {
     }
 
     fn check_body_post(&mut self, cx: &LateContext<'_>, body: &hir::Body<'_>) {
-        let body_owner = cx.tcx.hir().body_owner(body.id());
+        let body_owner = cx.tcx.hir_body_owner(body.id());
         let body_span = cx.tcx.hir().span(body_owner);
         if let Some(span) = self.const_span
             && span.contains(body_span)
diff --git a/src/tools/clippy/clippy_lints/src/operators/assign_op_pattern.rs b/src/tools/clippy/clippy_lints/src/operators/assign_op_pattern.rs
index 1315c3dfc12..5737a91031d 100644
--- a/src/tools/clippy/clippy_lints/src/operators/assign_op_pattern.rs
+++ b/src/tools/clippy/clippy_lints/src/operators/assign_op_pattern.rs
@@ -26,7 +26,7 @@ pub(super) fn check<'tcx>(
             let rty = cx.typeck_results().expr_ty(rhs);
             if let Some((_, lang_item)) = binop_traits(op.node)
                 && let Some(trait_id) = cx.tcx.lang_items().get(lang_item)
-                && let parent_fn = cx.tcx.hir().get_parent_item(e.hir_id).def_id
+                && let parent_fn = cx.tcx.hir_get_parent_item(e.hir_id).def_id
                 && trait_ref_of_method(cx, parent_fn).is_none_or(|t| t.path.res.def_id() != trait_id)
                 && implements_trait(cx, ty, trait_id, &[rty.into()])
             {
diff --git a/src/tools/clippy/clippy_lints/src/operators/identity_op.rs b/src/tools/clippy/clippy_lints/src/operators/identity_op.rs
index 1c2d6e90fc9..03582322827 100644
--- a/src/tools/clippy/clippy_lints/src/operators/identity_op.rs
+++ b/src/tools/clippy/clippy_lints/src/operators/identity_op.rs
@@ -120,7 +120,7 @@ fn needs_parenthesis(cx: &LateContext<'_>, binary: &Expr<'_>, child: &Expr<'_>)
             // the parent HIR node is an expression, or if the parent HIR node
             // is a Block or Stmt, and the new left hand side would need
             // parenthesis be treated as a statement rather than an expression.
-            if let Some((_, parent)) = cx.tcx.hir().parent_iter(binary.hir_id).next() {
+            if let Some((_, parent)) = cx.tcx.hir_parent_iter(binary.hir_id).next() {
                 match parent {
                     Node::Expr(_) => return Parens::Needed,
                     Node::Block(_) | Node::Stmt(_) => {
@@ -142,7 +142,7 @@ fn needs_parenthesis(cx: &LateContext<'_>, binary: &Expr<'_>, child: &Expr<'_>)
             // This would mean that the rustfix suggestion will appear at the start of a line, which causes
             // these expressions to be interpreted as statements if they do not have parenthesis.
             let mut prev_id = binary.hir_id;
-            for (_, parent) in cx.tcx.hir().parent_iter(binary.hir_id) {
+            for (_, parent) in cx.tcx.hir_parent_iter(binary.hir_id) {
                 if let Node::Expr(expr) = parent
                     && let ExprKind::Binary(_, lhs, _) | ExprKind::Cast(lhs, _) | ExprKind::Unary(_, lhs) = expr.kind
                     && lhs.hir_id == prev_id
diff --git a/src/tools/clippy/clippy_lints/src/operators/numeric_arithmetic.rs b/src/tools/clippy/clippy_lints/src/operators/numeric_arithmetic.rs
index 2083f2bf628..c261fd9bd9c 100644
--- a/src/tools/clippy/clippy_lints/src/operators/numeric_arithmetic.rs
+++ b/src/tools/clippy/clippy_lints/src/operators/numeric_arithmetic.rs
@@ -68,10 +68,10 @@ impl Context {
     }
 
     pub fn enter_body(&mut self, cx: &LateContext<'_>, body: &hir::Body<'_>) {
-        let body_owner = cx.tcx.hir().body_owner(body.id());
-        let body_owner_def_id = cx.tcx.hir().body_owner_def_id(body.id());
+        let body_owner = cx.tcx.hir_body_owner(body.id());
+        let body_owner_def_id = cx.tcx.hir_body_owner_def_id(body.id());
 
-        match cx.tcx.hir().body_owner_kind(body_owner_def_id) {
+        match cx.tcx.hir_body_owner_kind(body_owner_def_id) {
             hir::BodyOwnerKind::Static(_) | hir::BodyOwnerKind::Const { .. } => {
                 let body_span = cx.tcx.hir().span_with_body(body_owner);
 
@@ -82,12 +82,12 @@ impl Context {
                 }
                 self.const_span = Some(body_span);
             },
-            hir::BodyOwnerKind::Fn | hir::BodyOwnerKind::Closure => (),
+            hir::BodyOwnerKind::Fn | hir::BodyOwnerKind::Closure | hir::BodyOwnerKind::GlobalAsm => (),
         }
     }
 
     pub fn body_post(&mut self, cx: &LateContext<'_>, body: &hir::Body<'_>) {
-        let body_owner = cx.tcx.hir().body_owner(body.id());
+        let body_owner = cx.tcx.hir_body_owner(body.id());
         let body_span = cx.tcx.hir().span_with_body(body_owner);
 
         if let Some(span) = self.const_span {
diff --git a/src/tools/clippy/clippy_lints/src/operators/ptr_eq.rs b/src/tools/clippy/clippy_lints/src/operators/ptr_eq.rs
index 861564d5456..8118ad59bb7 100644
--- a/src/tools/clippy/clippy_lints/src/operators/ptr_eq.rs
+++ b/src/tools/clippy/clippy_lints/src/operators/ptr_eq.rs
@@ -53,7 +53,7 @@ fn expr_as_cast_to_usize<'tcx>(cx: &LateContext<'tcx>, cast_expr: &'tcx Expr<'_>
 // If the given expression is a cast to a `*const` pointer, return the lhs of the cast
 // E.g., `foo as *const _` returns `foo`.
 fn expr_as_cast_to_raw_pointer<'tcx>(cx: &LateContext<'tcx>, cast_expr: &'tcx Expr<'_>) -> Option<&'tcx Expr<'tcx>> {
-    if cx.typeck_results().expr_ty(cast_expr).is_unsafe_ptr() {
+    if cx.typeck_results().expr_ty(cast_expr).is_raw_ptr() {
         if let ExprKind::Cast(expr, _) = cast_expr.kind {
             return Some(expr);
         }
diff --git a/src/tools/clippy/clippy_lints/src/panic_unimplemented.rs b/src/tools/clippy/clippy_lints/src/panic_unimplemented.rs
index fa5b02a5a41..c9bdeed660e 100644
--- a/src/tools/clippy/clippy_lints/src/panic_unimplemented.rs
+++ b/src/tools/clippy/clippy_lints/src/panic_unimplemented.rs
@@ -98,7 +98,7 @@ impl<'tcx> LateLintPass<'tcx> for PanicUnimplemented {
     fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
         if let Some(macro_call) = root_macro_call_first_node(cx, expr) {
             if is_panic(cx, macro_call.def_id) {
-                if cx.tcx.hir().is_inside_const_context(expr.hir_id)
+                if cx.tcx.hir_is_inside_const_context(expr.hir_id)
                     || self.allow_panic_in_tests && is_in_test(cx.tcx, expr.hir_id)
                 {
                     return;
@@ -139,7 +139,7 @@ impl<'tcx> LateLintPass<'tcx> for PanicUnimplemented {
             && let Res::Def(DefKind::Fn, def_id) = expr_path.res
             && match_def_path(cx, def_id, &paths::PANIC_ANY)
         {
-            if cx.tcx.hir().is_inside_const_context(expr.hir_id)
+            if cx.tcx.hir_is_inside_const_context(expr.hir_id)
                 || self.allow_panic_in_tests && is_in_test(cx.tcx, expr.hir_id)
             {
                 return;
diff --git a/src/tools/clippy/clippy_lints/src/pass_by_ref_or_value.rs b/src/tools/clippy/clippy_lints/src/pass_by_ref_or_value.rs
index a3e89671eec..73c31b83b51 100644
--- a/src/tools/clippy/clippy_lints/src/pass_by_ref_or_value.rs
+++ b/src/tools/clippy/clippy_lints/src/pass_by_ref_or_value.rs
@@ -136,7 +136,7 @@ impl PassByRefOrValue {
         }
 
         let fn_sig = cx.tcx.fn_sig(def_id).instantiate_identity();
-        let fn_body = cx.enclosing_body.map(|id| cx.tcx.hir().body(id));
+        let fn_body = cx.enclosing_body.map(|id| cx.tcx.hir_body(id));
 
         // Gather all the lifetimes found in the output type which may affect whether
         // `TRIVIALLY_COPY_PASS_BY_REF` should be linted.
@@ -179,10 +179,10 @@ impl PassByRefOrValue {
                         && let hir::TyKind::Ref(_, MutTy { ty: decl_ty, .. }) = input.kind
                     {
                         if let Some(typeck) = cx.maybe_typeck_results() {
-                            // Don't lint if an unsafe pointer is created.
-                            // TODO: Limit the check only to unsafe pointers to the argument (or part of the argument)
+                            // Don't lint if a raw pointer is created.
+                            // TODO: Limit the check only to raw pointers to the argument (or part of the argument)
                             //       which escape the current function.
-                            if typeck.node_types().items().any(|(_, &ty)| ty.is_unsafe_ptr())
+                            if typeck.node_types().items().any(|(_, &ty)| ty.is_raw_ptr())
                                 || typeck
                                     .adjustments()
                                     .items()
diff --git a/src/tools/clippy/clippy_lints/src/ptr.rs b/src/tools/clippy/clippy_lints/src/ptr.rs
index 7fba4b6a6c8..ef4948a05b7 100644
--- a/src/tools/clippy/clippy_lints/src/ptr.rs
+++ b/src/tools/clippy/clippy_lints/src/ptr.rs
@@ -186,8 +186,7 @@ impl<'tcx> LateLintPass<'tcx> for Ptr {
     }
 
     fn check_body(&mut self, cx: &LateContext<'tcx>, body: &Body<'tcx>) {
-        let hir = cx.tcx.hir();
-        let mut parents = hir.parent_iter(body.value.hir_id);
+        let mut parents = cx.tcx.hir_parent_iter(body.value.hir_id);
         let (item_id, sig, is_trait_item) = match parents.next() {
             Some((_, Node::Item(i))) => {
                 if let ItemKind::Fn { sig, .. } = &i.kind {
@@ -583,8 +582,8 @@ fn check_ptr_arg_usage<'tcx>(cx: &LateContext<'tcx>, body: &Body<'tcx>, args: &[
     }
     impl<'tcx> Visitor<'tcx> for V<'_, 'tcx> {
         type NestedFilter = nested_filter::OnlyBodies;
-        fn nested_visit_map(&mut self) -> Self::Map {
-            self.cx.tcx.hir()
+        fn maybe_tcx(&mut self) -> Self::MaybeTyCtxt {
+            self.cx.tcx
         }
 
         fn visit_anon_const(&mut self, _: &'tcx AnonConst) {}
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 808a7e005c6..68ae575c906 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
@@ -111,7 +111,7 @@ fn is_expr_ty_usize(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
 
 // Is the type of the expression a raw pointer?
 fn is_expr_ty_raw_ptr(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
-    cx.typeck_results().expr_ty(expr).is_unsafe_ptr()
+    cx.typeck_results().expr_ty(expr).is_raw_ptr()
 }
 
 fn build_suggestion(
diff --git a/src/tools/clippy/clippy_lints/src/redundant_async_block.rs b/src/tools/clippy/clippy_lints/src/redundant_async_block.rs
index 65fd312b3a0..bc5e8fd2c25 100644
--- a/src/tools/clippy/clippy_lints/src/redundant_async_block.rs
+++ b/src/tools/clippy/clippy_lints/src/redundant_async_block.rs
@@ -75,7 +75,7 @@ impl<'tcx> LateLintPass<'tcx> for RedundantAsyncBlock {
 /// any variable by ref.
 fn desugar_async_block<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option<&'tcx Expr<'tcx>> {
     if let ExprKind::Closure(Closure { body, def_id, kind, .. }) = expr.kind
-        && let body = cx.tcx.hir().body(*body)
+        && let body = cx.tcx.hir_body(*body)
         && matches!(
             kind,
             ClosureKind::Coroutine(CoroutineKind::Desugared(
diff --git a/src/tools/clippy/clippy_lints/src/redundant_clone.rs b/src/tools/clippy/clippy_lints/src/redundant_clone.rs
index fb1bc494bd9..cfa622aea58 100644
--- a/src/tools/clippy/clippy_lints/src/redundant_clone.rs
+++ b/src/tools/clippy/clippy_lints/src/redundant_clone.rs
@@ -205,7 +205,7 @@ impl<'tcx> LateLintPass<'tcx> for RedundantClone {
             let node = mir.source_scopes[scope]
                 .local_data
                 .as_ref()
-                .assert_crate_local()
+                .unwrap_crate_local()
                 .lint_root;
 
             if let Some(snip) = span.get_source_text(cx)
diff --git a/src/tools/clippy/clippy_lints/src/redundant_closure_call.rs b/src/tools/clippy/clippy_lints/src/redundant_closure_call.rs
index 91d023500ca..1498a49a7a4 100644
--- a/src/tools/clippy/clippy_lints/src/redundant_closure_call.rs
+++ b/src/tools/clippy/clippy_lints/src/redundant_closure_call.rs
@@ -90,7 +90,7 @@ fn find_innermost_closure<'tcx>(
     let mut data = None;
 
     while let ExprKind::Closure(closure) = expr.kind
-        && let body = cx.tcx.hir().body(closure.body)
+        && let body = cx.tcx.hir_body(closure.body)
         && {
             let mut visitor = ReturnVisitor;
             !visitor.visit_expr(body.value).is_break()
@@ -179,7 +179,7 @@ impl<'tcx> LateLintPass<'tcx> for RedundantClosureCall {
                             // Like `async fn`, async closures are wrapped in an additional block
                             // to move all of the closure's arguments into the future.
 
-                            let async_closure_body = cx.tcx.hir().body(closure.body).value;
+                            let async_closure_body = cx.tcx.hir_body(closure.body).value;
                             let ExprKind::Block(block, _) = async_closure_body.kind else {
                                 return;
                             };
@@ -241,8 +241,8 @@ impl<'tcx> LateLintPass<'tcx> for RedundantClosureCall {
                     hir_visit::walk_expr(self, expr);
                 }
 
-                fn nested_visit_map(&mut self) -> Self::Map {
-                    self.cx.tcx.hir()
+                fn maybe_tcx(&mut self) -> Self::MaybeTyCtxt {
+                    self.cx.tcx
                 }
             }
             let mut closure_usage_count = ClosureUsageCount { cx, path, count: 0 };
diff --git a/src/tools/clippy/clippy_lints/src/redundant_locals.rs b/src/tools/clippy/clippy_lints/src/redundant_locals.rs
index ebe3e7c2019..f3ccc9e38f4 100644
--- a/src/tools/clippy/clippy_lints/src/redundant_locals.rs
+++ b/src/tools/clippy/clippy_lints/src/redundant_locals.rs
@@ -98,7 +98,7 @@ impl<'tcx> LateLintPass<'tcx> for RedundantLocals {
 /// assert_static(closure);
 /// ```
 fn is_by_value_closure_capture(cx: &LateContext<'_>, redefinition: HirId, root_variable: HirId) -> bool {
-    let closure_def_id = cx.tcx.hir().enclosing_body_owner(redefinition);
+    let closure_def_id = cx.tcx.hir_enclosing_body_owner(redefinition);
 
     cx.tcx.is_closure_like(closure_def_id.to_def_id())
         && cx.tcx.closure_captures(closure_def_id).iter().any(|c| {
@@ -122,8 +122,7 @@ fn find_binding(pat: &Pat<'_>, name: Ident) -> Option<BindingMode> {
 
 /// Check if a rebinding of a local changes the effect of assignments to the binding.
 fn affects_assignments(cx: &LateContext<'_>, mutability: Mutability, bind: HirId, rebind: HirId) -> bool {
-    let hir = cx.tcx.hir();
-
     // the binding is mutable and the rebinding is in a different scope than the original binding
-    mutability == Mutability::Mut && hir.get_enclosing_scope(bind) != hir.get_enclosing_scope(rebind)
+    mutability == Mutability::Mut
+        && cx.tcx.hir_get_enclosing_scope(bind) != cx.tcx.hir_get_enclosing_scope(rebind)
 }
diff --git a/src/tools/clippy/clippy_lints/src/returns.rs b/src/tools/clippy/clippy_lints/src/returns.rs
index a1cf16e6ce9..152739c2973 100644
--- a/src/tools/clippy/clippy_lints/src/returns.rs
+++ b/src/tools/clippy/clippy_lints/src/returns.rs
@@ -177,8 +177,7 @@ declare_lint_pass!(Return => [LET_AND_RETURN, NEEDLESS_RETURN, NEEDLESS_RETURN_W
 /// because of the never-ness of `return` expressions
 fn stmt_needs_never_type(cx: &LateContext<'_>, stmt_hir_id: HirId) -> bool {
     cx.tcx
-        .hir()
-        .parent_iter(stmt_hir_id)
+        .hir_parent_iter(stmt_hir_id)
         .find_map(|(_, node)| if let Node::Expr(expr) = node { Some(expr) } else { None })
         .is_some_and(|e| {
             cx.typeck_results()
@@ -203,9 +202,9 @@ impl<'tcx> LateLintPass<'tcx> for Return {
             && is_res_lang_ctor(cx, path_res(cx, maybe_constr), ResultErr)
 
             // Ensure this is not the final stmt, otherwise removing it would cause a compile error
-            && let OwnerNode::Item(item) = cx.tcx.hir_owner_node(cx.tcx.hir().get_parent_item(expr.hir_id))
+            && let OwnerNode::Item(item) = cx.tcx.hir_owner_node(cx.tcx.hir_get_parent_item(expr.hir_id))
             && let ItemKind::Fn { body, .. } = item.kind
-            && let block = cx.tcx.hir().body(body).value
+            && let block = cx.tcx.hir_body(body).value
             && let ExprKind::Block(block, _) = block.kind
             && !is_inside_let_else(cx.tcx, expr)
             && let [.., final_stmt] = block.stmts
diff --git a/src/tools/clippy/clippy_lints/src/same_name_method.rs b/src/tools/clippy/clippy_lints/src/same_name_method.rs
index 29914d4379f..552135b15fd 100644
--- a/src/tools/clippy/clippy_lints/src/same_name_method.rs
+++ b/src/tools/clippy/clippy_lints/src/same_name_method.rs
@@ -50,9 +50,9 @@ impl<'tcx> LateLintPass<'tcx> for SameNameMethod {
     fn check_crate_post(&mut self, cx: &LateContext<'tcx>) {
         let mut map = FxHashMap::<Res, ExistingName>::default();
 
-        for id in cx.tcx.hir().items() {
+        for id in cx.tcx.hir_free_items() {
             if matches!(cx.tcx.def_kind(id.owner_id), DefKind::Impl { .. })
-                && let item = cx.tcx.hir().item(id)
+                && let item = cx.tcx.hir_item(id)
                 && let ItemKind::Impl(Impl {
                     items,
                     of_trait,
diff --git a/src/tools/clippy/clippy_lints/src/self_named_constructors.rs b/src/tools/clippy/clippy_lints/src/self_named_constructors.rs
index 23b47606f8a..fc02c3a5171 100644
--- a/src/tools/clippy/clippy_lints/src/self_named_constructors.rs
+++ b/src/tools/clippy/clippy_lints/src/self_named_constructors.rs
@@ -51,7 +51,7 @@ impl<'tcx> LateLintPass<'tcx> for SelfNamedConstructors {
             _ => return,
         }
 
-        let parent = cx.tcx.hir().get_parent_item(impl_item.hir_id()).def_id;
+        let parent = cx.tcx.hir_get_parent_item(impl_item.hir_id()).def_id;
         let item = cx.tcx.hir().expect_item(parent);
         let self_ty = cx.tcx.type_of(item.owner_id).instantiate_identity();
         let ret_ty = return_ty(cx, impl_item.owner_id);
diff --git a/src/tools/clippy/clippy_lints/src/shadow.rs b/src/tools/clippy/clippy_lints/src/shadow.rs
index 83199ba0f70..ee282ee1dfb 100644
--- a/src/tools/clippy/clippy_lints/src/shadow.rs
+++ b/src/tools/clippy/clippy_lints/src/shadow.rs
@@ -149,17 +149,15 @@ impl<'tcx> LateLintPass<'tcx> for Shadow {
     }
 
     fn check_body(&mut self, cx: &LateContext<'_>, body: &Body<'_>) {
-        let hir = cx.tcx.hir();
-        let owner_id = hir.body_owner_def_id(body.id());
-        if !matches!(hir.body_owner_kind(owner_id), BodyOwnerKind::Closure) {
+        let owner_id = cx.tcx.hir_body_owner_def_id(body.id());
+        if !matches!(cx.tcx.hir_body_owner_kind(owner_id), BodyOwnerKind::Closure) {
             self.bindings.push((FxHashMap::default(), owner_id));
         }
     }
 
     fn check_body_post(&mut self, cx: &LateContext<'_>, body: &Body<'_>) {
-        let hir = cx.tcx.hir();
         if !matches!(
-            hir.body_owner_kind(hir.body_owner_def_id(body.id())),
+            cx.tcx.hir_body_owner_kind(cx.tcx.hir_body_owner_def_id(body.id())),
             BodyOwnerKind::Closure
         ) {
             self.bindings.pop();
@@ -226,9 +224,9 @@ fn lint_shadow(cx: &LateContext<'_>, pat: &Pat<'_>, shadowed: HirId, span: Span)
 
 /// Returns true if the expression is a simple transformation of a local binding such as `&x`
 fn is_self_shadow(cx: &LateContext<'_>, pat: &Pat<'_>, mut expr: &Expr<'_>, hir_id: HirId) -> bool {
-    let hir = cx.tcx.hir();
-    let is_direct_binding = hir
-        .parent_iter(pat.hir_id)
+    let is_direct_binding = cx
+        .tcx
+        .hir_parent_iter(pat.hir_id)
         .map_while(|(_id, node)| match node {
             Node::Pat(pat) => Some(pat),
             _ => None,
@@ -261,14 +259,14 @@ fn is_self_shadow(cx: &LateContext<'_>, pat: &Pat<'_>, mut expr: &Expr<'_>, hir_
 /// For closure arguments passed to a method call, returns the method call, and the `HirId` of the
 /// closure (which will later be skipped). This is for <https://github.com/rust-lang/rust-clippy/issues/10780>
 fn find_init<'tcx>(cx: &LateContext<'tcx>, hir_id: HirId) -> Option<(&'tcx Expr<'tcx>, Option<HirId>)> {
-    for (hir_id, node) in cx.tcx.hir().parent_iter(hir_id) {
+    for (hir_id, node) in cx.tcx.hir_parent_iter(hir_id) {
         let init = match node {
             Node::Arm(_) | Node::Pat(_) | Node::PatField(_) | Node::Param(_) => continue,
             Node::Expr(expr) => match expr.kind {
                 ExprKind::Match(e, _, _) | ExprKind::Let(&LetExpr { init: e, .. }) => Some((e, None)),
                 // If we're a closure argument, then a parent call is also an associated item.
                 ExprKind::Closure(_) => {
-                    if let Some((_, node)) = cx.tcx.hir().parent_iter(hir_id).next() {
+                    if let Some((_, node)) = cx.tcx.hir_parent_iter(hir_id).next() {
                         match node {
                             Node::Expr(expr) => match expr.kind {
                                 ExprKind::MethodCall(_, _, _, _) | ExprKind::Call(_, _) => Some((expr, Some(hir_id))),
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 597bfddecbc..e9db7c9d031 100644
--- a/src/tools/clippy/clippy_lints/src/significant_drop_tightening.rs
+++ b/src/tools/clippy/clippy_lints/src/significant_drop_tightening.rs
@@ -232,8 +232,7 @@ impl<'ap, 'lc, 'others, 'stmt, 'tcx> StmtsChecker<'ap, 'lc, 'others, 'stmt, 'tcx
                 let block_is_ancestor = self
                     .cx
                     .tcx
-                    .hir()
-                    .parent_iter(self.ap.curr_block_hir_id)
+                    .hir_parent_iter(self.ap.curr_block_hir_id)
                     .any(|(id, _)| id == apa.first_block_hir_id);
                 if last_stmt_is_not_dummy && last_stmt_is_not_curr && (block_equals_curr || block_is_ancestor) {
                     apa.has_expensive_expr_after_last_attr = true;
diff --git a/src/tools/clippy/clippy_lints/src/single_call_fn.rs b/src/tools/clippy/clippy_lints/src/single_call_fn.rs
index fdbccbaa8a5..1a2fb77acc1 100644
--- a/src/tools/clippy/clippy_lints/src/single_call_fn.rs
+++ b/src/tools/clippy/clippy_lints/src/single_call_fn.rs
@@ -90,8 +90,7 @@ impl SingleCallFn {
             || fn_span.in_external_macro(cx.sess().source_map())
             || cx
                 .tcx
-                .hir()
-                .maybe_body_owned_by(fn_def_id)
+                .hir_maybe_body_owned_by(fn_def_id)
                 .is_none_or(|body| is_in_test_function(cx.tcx, body.value.hir_id))
             || match cx.tcx.hir_node(fn_hir_id) {
                 Node::Item(item) => is_from_proc_macro(cx, item),
diff --git a/src/tools/clippy/clippy_lints/src/string_patterns.rs b/src/tools/clippy/clippy_lints/src/string_patterns.rs
index 3834087f797..694ad4f6347 100644
--- a/src/tools/clippy/clippy_lints/src/string_patterns.rs
+++ b/src/tools/clippy/clippy_lints/src/string_patterns.rs
@@ -138,7 +138,7 @@ fn get_char_span<'tcx>(cx: &'_ LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Optio
 
 fn check_manual_pattern_char_comparison(cx: &LateContext<'_>, method_arg: &Expr<'_>, msrv: &Msrv) {
     if let ExprKind::Closure(closure) = method_arg.kind
-        && let body = cx.tcx.hir().body(closure.body)
+        && let body = cx.tcx.hir_body(closure.body)
         && let Some(PatKind::Binding(_, binding, ..)) = body.params.first().map(|p| p.pat.kind)
     {
         let mut set_char_spans: Vec<Span> = Vec::new();
diff --git a/src/tools/clippy/clippy_lints/src/strings.rs b/src/tools/clippy/clippy_lints/src/strings.rs
index 6164a6191db..4a5f143a2d3 100644
--- a/src/tools/clippy/clippy_lints/src/strings.rs
+++ b/src/tools/clippy/clippy_lints/src/strings.rs
@@ -2,8 +2,8 @@ use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg, span_lint_and_the
 use clippy_utils::source::{snippet, snippet_with_applicability};
 use clippy_utils::ty::is_type_lang_item;
 use clippy_utils::{
-    SpanlessEq, get_expr_use_or_unification_node, get_parent_expr, is_lint_allowed, is_path_diagnostic_item,
-    method_calls, peel_blocks,
+    SpanlessEq, get_expr_use_or_unification_node, get_parent_expr, is_lint_allowed, method_calls, path_def_id,
+    peel_blocks,
 };
 use rustc_errors::Applicability;
 use rustc_hir::def_id::DefId;
@@ -253,8 +253,9 @@ impl<'tcx> LateLintPass<'tcx> for StringLitAsBytes {
         use rustc_ast::LitKind;
 
         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 `std::str::converts::from_utf8` or `std::primitive::str::from_utf8`
+            && let Some(sym::str_from_utf8 | sym::str_inherent_from_utf8) =
+                path_def_id(cx, fun).and_then(|id| cx.tcx.get_diagnostic_name(id))
 
             // Find string::as_bytes
             && let ExprKind::AddrOf(BorrowKind::Ref, _, args) = bytes_arg.kind
diff --git a/src/tools/clippy/clippy_lints/src/suspicious_trait_impl.rs b/src/tools/clippy/clippy_lints/src/suspicious_trait_impl.rs
index e9779d437d4..fb426e91bf0 100644
--- a/src/tools/clippy/clippy_lints/src/suspicious_trait_impl.rs
+++ b/src/tools/clippy/clippy_lints/src/suspicious_trait_impl.rs
@@ -63,11 +63,11 @@ impl<'tcx> LateLintPass<'tcx> for SuspiciousImpl {
 
             // Check for more than one binary operation in the implemented function
             // Linting when multiple operations are involved can result in false positives
-            && let parent_fn = cx.tcx.hir().get_parent_item(expr.hir_id).def_id
+            && let parent_fn = cx.tcx.hir_get_parent_item(expr.hir_id).def_id
             && let hir::Node::ImplItem(impl_item) = cx.tcx.hir_node_by_def_id(parent_fn)
             && let hir::ImplItemKind::Fn(_, body_id) = impl_item.kind
-            && let body = cx.tcx.hir().body(body_id)
-            && let parent_fn = cx.tcx.hir().get_parent_item(expr.hir_id).def_id
+            && let body = cx.tcx.hir_body(body_id)
+            && let parent_fn = cx.tcx.hir_get_parent_item(expr.hir_id).def_id
             && let Some(trait_ref) = trait_ref_of_method(cx, parent_fn)
             && let trait_id = trait_ref.path.res.def_id()
             && ![binop_trait_id, op_assign_trait_id].contains(&trait_id)
diff --git a/src/tools/clippy/clippy_lints/src/swap_ptr_to_ref.rs b/src/tools/clippy/clippy_lints/src/swap_ptr_to_ref.rs
index 8c5cf93ab6e..ff196355a2e 100644
--- a/src/tools/clippy/clippy_lints/src/swap_ptr_to_ref.rs
+++ b/src/tools/clippy/clippy_lints/src/swap_ptr_to_ref.rs
@@ -76,7 +76,7 @@ impl LateLintPass<'_> for SwapPtrToRef {
 fn is_ptr_to_ref(cx: &LateContext<'_>, e: &Expr<'_>, ctxt: SyntaxContext) -> (bool, Option<Span>) {
     if let ExprKind::AddrOf(BorrowKind::Ref, Mutability::Mut, borrowed_expr) = e.kind
         && let ExprKind::Unary(UnOp::Deref, derefed_expr) = borrowed_expr.kind
-        && cx.typeck_results().expr_ty(derefed_expr).is_unsafe_ptr()
+        && cx.typeck_results().expr_ty(derefed_expr).is_raw_ptr()
     {
         (
             true,
diff --git a/src/tools/clippy/clippy_lints/src/trait_bounds.rs b/src/tools/clippy/clippy_lints/src/trait_bounds.rs
index 790e0965198..cbf7b126632 100644
--- a/src/tools/clippy/clippy_lints/src/trait_bounds.rs
+++ b/src/tools/clippy/clippy_lints/src/trait_bounds.rs
@@ -135,7 +135,7 @@ impl<'tcx> LateLintPass<'tcx> for TraitBounds {
                 && let Some(Node::Item(Item {
                     kind: ItemKind::Trait(_, _, _, self_bounds, _),
                     ..
-                })) = cx.tcx.hir().get_if_local(*def_id)
+                })) = cx.tcx.hir_get_if_local(*def_id)
             {
                 if self_bounds_map.is_empty() {
                     for bound in *self_bounds {
diff --git a/src/tools/clippy/clippy_lints/src/transmute/eager_transmute.rs b/src/tools/clippy/clippy_lints/src/transmute/eager_transmute.rs
index 1209bd5b34f..81c0a57083e 100644
--- a/src/tools/clippy/clippy_lints/src/transmute/eager_transmute.rs
+++ b/src/tools/clippy/clippy_lints/src/transmute/eager_transmute.rs
@@ -10,7 +10,7 @@ use rustc_middle::ty::Ty;
 use super::EAGER_TRANSMUTE;
 
 fn peel_parent_unsafe_blocks<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> Option<&'tcx Expr<'tcx>> {
-    for (_, parent) in cx.tcx.hir().parent_iter(expr.hir_id) {
+    for (_, parent) in cx.tcx.hir_parent_iter(expr.hir_id) {
         match parent {
             Node::Block(_) => {},
             Node::Expr(e) if let ExprKind::Block(..) = e.kind => {},
diff --git a/src/tools/clippy/clippy_lints/src/transmute/missing_transmute_annotations.rs b/src/tools/clippy/clippy_lints/src/transmute/missing_transmute_annotations.rs
index 4961dd6b280..96286fcf73d 100644
--- a/src/tools/clippy/clippy_lints/src/transmute/missing_transmute_annotations.rs
+++ b/src/tools/clippy/clippy_lints/src/transmute/missing_transmute_annotations.rs
@@ -7,7 +7,7 @@ use rustc_middle::ty::Ty;
 use crate::transmute::MISSING_TRANSMUTE_ANNOTATIONS;
 
 fn get_parent_local_binding_ty<'tcx>(cx: &LateContext<'tcx>, expr_hir_id: HirId) -> Option<LetStmt<'tcx>> {
-    let mut parent_iter = cx.tcx.hir().parent_iter(expr_hir_id);
+    let mut parent_iter = cx.tcx.hir_parent_iter(expr_hir_id);
     if let Some((_, node)) = parent_iter.next() {
         match node {
             Node::LetStmt(local) => Some(*local),
@@ -28,8 +28,8 @@ fn get_parent_local_binding_ty<'tcx>(cx: &LateContext<'tcx>, expr_hir_id: HirId)
 }
 
 fn is_function_block(cx: &LateContext<'_>, expr_hir_id: HirId) -> bool {
-    let def_id = cx.tcx.hir().enclosing_body_owner(expr_hir_id);
-    if let Some(body) = cx.tcx.hir().maybe_body_owned_by(def_id) {
+    let def_id = cx.tcx.hir_enclosing_body_owner(expr_hir_id);
+    if let Some(body) = cx.tcx.hir_maybe_body_owned_by(def_id) {
         return body.value.peel_blocks().hir_id == expr_hir_id;
     }
     false
diff --git a/src/tools/clippy/clippy_lints/src/types/borrowed_box.rs b/src/tools/clippy/clippy_lints/src/types/borrowed_box.rs
index 2e97772407f..004ad03e708 100644
--- a/src/tools/clippy/clippy_lints/src/types/borrowed_box.rs
+++ b/src/tools/clippy/clippy_lints/src/types/borrowed_box.rs
@@ -96,10 +96,10 @@ fn is_any_trait(cx: &LateContext<'_>, t: &hir::Ty<'_>) -> bool {
 
 fn get_bounds_if_impl_trait<'tcx>(cx: &LateContext<'tcx>, qpath: &QPath<'_>, id: HirId) -> Option<GenericBounds<'tcx>> {
     if let Some(did) = cx.qpath_res(qpath, id).opt_def_id()
-        && let Some(Node::GenericParam(generic_param)) = cx.tcx.hir().get_if_local(did)
+        && let Some(Node::GenericParam(generic_param)) = cx.tcx.hir_get_if_local(did)
         && let GenericParamKind::Type { synthetic, .. } = generic_param.kind
         && synthetic
-        && let Some(generics) = cx.tcx.hir().get_generics(id.owner.def_id)
+        && let Some(generics) = cx.tcx.hir_get_generics(id.owner.def_id)
         && let Some(pred) = generics.bounds_for_param(did.expect_local()).next()
     {
         Some(pred.bounds)
diff --git a/src/tools/clippy/clippy_lints/src/types/mod.rs b/src/tools/clippy/clippy_lints/src/types/mod.rs
index 579cbf447a2..71e6e75c1bd 100644
--- a/src/tools/clippy/clippy_lints/src/types/mod.rs
+++ b/src/tools/clippy/clippy_lints/src/types/mod.rs
@@ -375,8 +375,7 @@ impl<'tcx> LateLintPass<'tcx> for Types {
     ) {
         let is_in_trait_impl = if let hir::Node::Item(item) = cx.tcx.hir_node_by_def_id(
             cx.tcx
-                .hir()
-                .get_parent_item(cx.tcx.local_def_id_to_hir_id(def_id))
+                .hir_get_parent_item(cx.tcx.local_def_id_to_hir_id(def_id))
                 .def_id,
         ) {
             matches!(item.kind, ItemKind::Impl(hir::Impl { of_trait: Some(_), .. }))
@@ -420,7 +419,7 @@ impl<'tcx> LateLintPass<'tcx> for Types {
             ImplItemKind::Const(ty, _) => {
                 let is_in_trait_impl = if let hir::Node::Item(item) = cx
                     .tcx
-                    .hir_node_by_def_id(cx.tcx.hir().get_parent_item(item.hir_id()).def_id)
+                    .hir_node_by_def_id(cx.tcx.hir_get_parent_item(item.hir_id()).def_id)
                 {
                     matches!(item.kind, ItemKind::Impl(hir::Impl { of_trait: Some(_), .. }))
                 } else {
diff --git a/src/tools/clippy/clippy_lints/src/unconditional_recursion.rs b/src/tools/clippy/clippy_lints/src/unconditional_recursion.rs
index 207f2ef4563..a443043bef9 100644
--- a/src/tools/clippy/clippy_lints/src/unconditional_recursion.rs
+++ b/src/tools/clippy/clippy_lints/src/unconditional_recursion.rs
@@ -9,7 +9,6 @@ use rustc_hir::intravisit::{FnKind, Visitor, walk_body, walk_expr};
 use rustc_hir::{Body, Expr, ExprKind, FnDecl, HirId, Item, ItemKind, Node, QPath, TyKind};
 use rustc_hir_analysis::lower_ty;
 use rustc_lint::{LateContext, LateLintPass};
-use rustc_middle::hir::map::Map;
 use rustc_middle::hir::nested_filter;
 use rustc_middle::ty::{self, AssocKind, Ty, TyCtxt};
 use rustc_session::impl_lint_pass;
@@ -112,7 +111,7 @@ fn get_impl_trait_def_id(cx: &LateContext<'_>, method_def_id: LocalDefId) -> Opt
             owner_id,
             ..
         }),
-    )) = cx.tcx.hir().parent_iter(hir_id).next()
+    )) = cx.tcx.hir_parent_iter(hir_id).next()
         // We exclude `impl` blocks generated from rustc's proc macros.
         && !cx.tcx.has_attr(*owner_id, sym::automatically_derived)
         // It is a implementation of a trait.
@@ -217,7 +216,7 @@ fn check_to_string(cx: &LateContext<'_>, method_span: Span, method_def_id: Local
                 owner_id,
                 ..
             }),
-        )) = cx.tcx.hir().parent_iter(hir_id).next()
+        )) = cx.tcx.hir_parent_iter(hir_id).next()
         // We exclude `impl` blocks generated from rustc's proc macros.
         && !cx.tcx.has_attr(*owner_id, sym::automatically_derived)
         // It is a implementation of a trait.
@@ -275,7 +274,6 @@ fn is_default_method_on_current_ty<'tcx>(tcx: TyCtxt<'tcx>, qpath: QPath<'tcx>,
 
 struct CheckCalls<'a, 'tcx> {
     cx: &'a LateContext<'tcx>,
-    map: Map<'tcx>,
     implemented_ty_id: DefId,
     method_span: Span,
 }
@@ -287,8 +285,8 @@ where
     type NestedFilter = nested_filter::OnlyBodies;
     type Result = ControlFlow<()>;
 
-    fn nested_visit_map(&mut self) -> Self::Map {
-        self.map
+    fn maybe_tcx(&mut self) -> Self::MaybeTyCtxt {
+        self.cx.tcx
     }
 
     fn visit_expr(&mut self, expr: &'tcx Expr<'tcx>) -> ControlFlow<()> {
@@ -326,15 +324,15 @@ impl UnconditionalRecursion {
                             .find(|item| {
                                 item.kind == AssocKind::Fn && item.def_id.is_local() && item.name == kw::Default
                             })
-                        && let Some(body_node) = cx.tcx.hir().get_if_local(assoc_item.def_id)
+                        && let Some(body_node) = cx.tcx.hir_get_if_local(assoc_item.def_id)
                         && let Some(body_id) = body_node.body_id()
-                        && let body = cx.tcx.hir().body(body_id)
+                        && let body = cx.tcx.hir_body(body_id)
                         // We don't want to keep it if it has conditional return.
                         && let [return_expr] = get_return_calls_in_body(body).as_slice()
                         && let ExprKind::Call(call_expr, _) = return_expr.kind
                         // We need to use typeck here to infer the actual function being called.
-                        && let body_def_id = cx.tcx.hir().enclosing_body_owner(call_expr.hir_id)
-                        && let Some(body_owner) = cx.tcx.hir().maybe_body_owned_by(body_def_id)
+                        && let body_def_id = cx.tcx.hir_enclosing_body_owner(call_expr.hir_id)
+                        && let Some(body_owner) = cx.tcx.hir_maybe_body_owned_by(body_def_id)
                         && let typeck = cx.tcx.typeck_body(body_owner.id())
                         && let Some(call_def_id) = typeck.type_dependent_def_id(call_expr.hir_id)
                     {
@@ -369,7 +367,7 @@ impl UnconditionalRecursion {
                 kind: ItemKind::Impl(impl_),
                 ..
             }),
-        )) = cx.tcx.hir().parent_iter(hir_id).next()
+        )) = cx.tcx.hir_parent_iter(hir_id).next()
             && let Some(implemented_ty_id) = get_hir_ty_def_id(cx.tcx, *impl_.self_ty)
             && {
                 self.init_default_impl_for_type_if_needed(cx);
@@ -380,7 +378,6 @@ impl UnconditionalRecursion {
         {
             let mut c = CheckCalls {
                 cx,
-                map: cx.tcx.hir(),
                 implemented_ty_id,
                 method_span,
             };
diff --git a/src/tools/clippy/clippy_lints/src/undocumented_unsafe_blocks.rs b/src/tools/clippy/clippy_lints/src/undocumented_unsafe_blocks.rs
index b3d26908093..16916e3aaad 100644
--- a/src/tools/clippy/clippy_lints/src/undocumented_unsafe_blocks.rs
+++ b/src/tools/clippy/clippy_lints/src/undocumented_unsafe_blocks.rs
@@ -245,7 +245,7 @@ impl<'tcx> LateLintPass<'tcx> for UndocumentedUnsafeBlocks {
             // const and static items only need a safety comment if their body is an unsafe block, lint otherwise
             (&ItemKind::Const(.., body) | &ItemKind::Static(.., body), HasSafetyComment::Yes(pos)) => {
                 if !is_lint_allowed(cx, UNNECESSARY_SAFETY_COMMENT, body.hir_id) {
-                    let body = cx.tcx.hir().body(body);
+                    let body = cx.tcx.hir_body(body);
                     if !matches!(
                         body.value.kind, hir::ExprKind::Block(block, _)
                         if block.rules == BlockCheckMode::UnsafeBlock(UnsafeSource::UserProvided)
@@ -291,7 +291,7 @@ fn expr_has_unnecessary_safety_comment<'tcx>(
     expr: &'tcx hir::Expr<'tcx>,
     comment_pos: BytePos,
 ) -> Option<Span> {
-    if cx.tcx.hir().parent_iter(expr.hir_id).any(|(_, ref node)| {
+    if cx.tcx.hir_parent_iter(expr.hir_id).any(|(_, ref node)| {
         matches!(
             node,
             Node::Block(Block {
@@ -432,7 +432,7 @@ fn include_attrs_in_span(cx: &LateContext<'_>, hir_id: HirId, span: Span) -> Spa
         .hir()
         .attrs(hir_id)
         .iter()
-        .fold(span, |acc, attr| acc.to(attr.span)))
+        .fold(span, |acc, attr| acc.to(attr.span())))
 }
 
 enum HasSafetyComment {
@@ -558,7 +558,7 @@ fn comment_start_before_item_in_mod(
                 // some_item /* comment */ unsafe impl T {}
                 // ^-------^ returns the end of this span
                 //         ^---------------^ finally checks comments in this range
-                let prev_item = cx.tcx.hir().item(parent_mod.item_ids[idx - 1]);
+                let prev_item = cx.tcx.hir_item(parent_mod.item_ids[idx - 1]);
                 if let Some(sp) = walk_span_to_context(prev_item.span, SyntaxContext::root()) {
                     return Some(sp.hi());
                 }
@@ -604,10 +604,9 @@ fn span_from_macro_expansion_has_safety_comment(cx: &LateContext<'_>, span: Span
 
 fn get_body_search_span(cx: &LateContext<'_>) -> Option<Span> {
     let body = cx.enclosing_body?;
-    let map = cx.tcx.hir();
-    let mut span = map.body(body).value.span;
+    let mut span = cx.tcx.hir_body(body).value.span;
     let mut maybe_global_var = false;
-    for (_, node) in map.parent_iter(body.hir_id) {
+    for (_, node) in cx.tcx.hir_parent_iter(body.hir_id) {
         match node {
             Node::Expr(e) => span = e.span,
             Node::Block(_) | Node::Arm(_) | Node::Stmt(_) | Node::LetStmt(_) => (),
diff --git a/src/tools/clippy/clippy_lints/src/unit_return_expecting_ord.rs b/src/tools/clippy/clippy_lints/src/unit_return_expecting_ord.rs
index 87478a120dd..67ceac92dbc 100644
--- a/src/tools/clippy/clippy_lints/src/unit_return_expecting_ord.rs
+++ b/src/tools/clippy/clippy_lints/src/unit_return_expecting_ord.rs
@@ -123,7 +123,7 @@ fn check_arg<'tcx>(cx: &LateContext<'tcx>, arg: &'tcx Expr<'tcx>) -> Option<(Spa
         && let ty = cx.tcx.instantiate_bound_regions_with_erased(ret_ty)
         && ty.is_unit()
     {
-        let body = cx.tcx.hir().body(body);
+        let body = cx.tcx.hir_body(body);
         if let ExprKind::Block(block, _) = body.value.kind
             && block.expr.is_none()
             && let Some(stmt) = block.stmts.last()
diff --git a/src/tools/clippy/clippy_lints/src/unit_types/let_unit_value.rs b/src/tools/clippy/clippy_lints/src/unit_types/let_unit_value.rs
index 00b80e827d8..87f184e13ce 100644
--- a/src/tools/clippy/clippy_lints/src/unit_types/let_unit_value.rs
+++ b/src/tools/clippy/clippy_lints/src/unit_types/let_unit_value.rs
@@ -84,7 +84,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, local: &'tcx LetStmt<'_>) {
                     if let PatKind::Binding(_, binding_hir_id, ..) = local.pat.kind
                         && let Some(body_id) = cx.enclosing_body.as_ref()
                     {
-                        let body = cx.tcx.hir().body(*body_id);
+                        let body = cx.tcx.hir_body(*body_id);
 
                         // Collect variable usages
                         let mut visitor = UnitVariableCollector::new(binding_hir_id);
diff --git a/src/tools/clippy/clippy_lints/src/unused_async.rs b/src/tools/clippy/clippy_lints/src/unused_async.rs
index d00bd7f2b3d..1c1c841e964 100644
--- a/src/tools/clippy/clippy_lints/src/unused_async.rs
+++ b/src/tools/clippy/clippy_lints/src/unused_async.rs
@@ -101,8 +101,8 @@ impl<'tcx> Visitor<'tcx> for AsyncFnVisitor<'_, 'tcx> {
         }
     }
 
-    fn nested_visit_map(&mut self) -> Self::Map {
-        self.cx.tcx.hir()
+    fn maybe_tcx(&mut self) -> Self::MaybeTyCtxt {
+        self.cx.tcx
     }
 }
 
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 e65123b8a94..0687fc319af 100644
--- a/src/tools/clippy/clippy_lints/src/unused_io_amount.rs
+++ b/src/tools/clippy/clippy_lints/src/unused_io_amount.rs
@@ -224,7 +224,7 @@ fn is_unreachable_or_panic(cx: &LateContext<'_>, expr: &hir::Expr<'_>) -> bool {
         return false;
     };
     if is_panic(cx, macro_call.def_id) {
-        return !cx.tcx.hir().is_inside_const_context(expr.hir_id);
+        return !cx.tcx.hir_is_inside_const_context(expr.hir_id);
     }
     matches!(cx.tcx.item_name(macro_call.def_id).as_str(), "unreachable")
 }
diff --git a/src/tools/clippy/clippy_lints/src/unused_peekable.rs b/src/tools/clippy/clippy_lints/src/unused_peekable.rs
index 71aa57e0a14..7487e273caa 100644
--- a/src/tools/clippy/clippy_lints/src/unused_peekable.rs
+++ b/src/tools/clippy/clippy_lints/src/unused_peekable.rs
@@ -112,13 +112,13 @@ impl<'tcx> Visitor<'tcx> for PeekableVisitor<'_, 'tcx> {
     type NestedFilter = OnlyBodies;
     type Result = ControlFlow<()>;
 
-    fn nested_visit_map(&mut self) -> Self::Map {
-        self.cx.tcx.hir()
+    fn maybe_tcx(&mut self) -> Self::MaybeTyCtxt {
+        self.cx.tcx
     }
 
     fn visit_expr(&mut self, ex: &'tcx Expr<'tcx>) -> ControlFlow<()> {
         if path_to_local_id(ex, self.expected_hir_id) {
-            for (_, node) in self.cx.tcx.hir().parent_iter(ex.hir_id) {
+            for (_, node) in self.cx.tcx.hir_parent_iter(ex.hir_id) {
                 match node {
                     Node::Expr(expr) => {
                         match expr.kind {
diff --git a/src/tools/clippy/clippy_lints/src/unused_self.rs b/src/tools/clippy/clippy_lints/src/unused_self.rs
index 781f51aa9b0..2c6c7569316 100644
--- a/src/tools/clippy/clippy_lints/src/unused_self.rs
+++ b/src/tools/clippy/clippy_lints/src/unused_self.rs
@@ -56,7 +56,7 @@ impl<'tcx> LateLintPass<'tcx> for UnusedSelf {
         if impl_item.span.from_expansion() {
             return;
         }
-        let parent = cx.tcx.hir().get_parent_item(impl_item.hir_id()).def_id;
+        let parent = cx.tcx.hir_get_parent_item(impl_item.hir_id()).def_id;
         let parent_item = cx.tcx.hir().expect_item(parent);
         let assoc_item = cx.tcx.associated_item(impl_item.owner_id);
         let contains_todo = |cx, body: &'_ Body<'_>| -> bool {
@@ -77,7 +77,7 @@ impl<'tcx> LateLintPass<'tcx> for UnusedSelf {
             && assoc_item.fn_has_self_parameter
             && let ImplItemKind::Fn(.., body_id) = &impl_item.kind
             && (!cx.effective_visibilities.is_exported(impl_item.owner_id.def_id) || !self.avoid_breaking_exported_api)
-            && let body = cx.tcx.hir().body(*body_id)
+            && let body = cx.tcx.hir_body(*body_id)
             && let [self_param, ..] = body.params
             && !is_local_used(cx, body, self_param.pat.hir_id)
             && !contains_todo(cx, body)
diff --git a/src/tools/clippy/clippy_lints/src/unwrap.rs b/src/tools/clippy/clippy_lints/src/unwrap.rs
index 6a952c0d97a..76b9bbbd32f 100644
--- a/src/tools/clippy/clippy_lints/src/unwrap.rs
+++ b/src/tools/clippy/clippy_lints/src/unwrap.rs
@@ -374,8 +374,8 @@ impl<'tcx> Visitor<'tcx> for UnwrappableVariablesVisitor<'_, 'tcx> {
         }
     }
 
-    fn nested_visit_map(&mut self) -> Self::Map {
-        self.cx.tcx.hir()
+    fn maybe_tcx(&mut self) -> Self::MaybeTyCtxt {
+        self.cx.tcx
     }
 }
 
diff --git a/src/tools/clippy/clippy_lints/src/unwrap_in_result.rs b/src/tools/clippy/clippy_lints/src/unwrap_in_result.rs
index 9b9a2ffbbc8..f870eb71e19 100644
--- a/src/tools/clippy/clippy_lints/src/unwrap_in_result.rs
+++ b/src/tools/clippy/clippy_lints/src/unwrap_in_result.rs
@@ -74,7 +74,7 @@ impl<'tcx> LateLintPass<'tcx> for UnwrapInResult {
 
 fn lint_impl_body<'tcx>(cx: &LateContext<'tcx>, impl_span: Span, impl_item: &'tcx hir::ImplItem<'_>) {
     if let ImplItemKind::Fn(_, body_id) = impl_item.kind {
-        let body = cx.tcx.hir().body(body_id);
+        let body = cx.tcx.hir_body(body_id);
         let typeck = cx.tcx.typeck(impl_item.owner_id.def_id);
         let mut result = Vec::new();
         let _: Option<!> = for_each_expr(cx, body.value, |e| {
diff --git a/src/tools/clippy/clippy_lints/src/utils/author.rs b/src/tools/clippy/clippy_lints/src/utils/author.rs
index 6bad78cf871..5fc166438e8 100644
--- a/src/tools/clippy/clippy_lints/src/utils/author.rs
+++ b/src/tools/clippy/clippy_lints/src/utils/author.rs
@@ -132,8 +132,7 @@ impl<'tcx> LateLintPass<'tcx> for Author {
 }
 
 fn check_item(cx: &LateContext<'_>, hir_id: HirId) {
-    let hir = cx.tcx.hir();
-    if let Some(body) = hir.maybe_body_owned_by(hir_id.expect_owner().def_id) {
+    if let Some(body) = cx.tcx.hir_maybe_body_owned_by(hir_id.expect_owner().def_id) {
         check_node(cx, hir_id, |v| {
             v.expr(&v.bind("expr", body.value));
         });
@@ -637,9 +636,9 @@ impl<'a, 'tcx> PrintVisitor<'a, 'tcx> {
     }
 
     fn body(&self, body_id: &Binding<hir::BodyId>) {
-        let expr = self.cx.tcx.hir().body(body_id.value).value;
+        let expr = self.cx.tcx.hir_body(body_id.value).value;
         bind!(self, expr);
-        chain!(self, "{expr} = &cx.tcx.hir().body({body_id}).value");
+        chain!(self, "{expr} = &cx.tcx.hir_body({body_id}).value");
         self.expr(expr);
     }
 
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 eaeb754a23f..2e6fb7c4ce4 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
@@ -80,7 +80,7 @@ impl<'tcx> LateLintPass<'tcx> for CollapsibleCalls {
         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"])
             && let ExprKind::Closure(&Closure { body, .. }) = call_f.kind
-            && let body = cx.tcx.hir().body(body)
+            && 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
diff --git a/src/tools/clippy/clippy_lints/src/utils/internal_lints/invalid_paths.rs b/src/tools/clippy/clippy_lints/src/utils/internal_lints/invalid_paths.rs
index 08c178ed229..252ac5e6768 100644
--- a/src/tools/clippy/clippy_lints/src/utils/internal_lints/invalid_paths.rs
+++ b/src/tools/clippy/clippy_lints/src/utils/internal_lints/invalid_paths.rs
@@ -37,7 +37,7 @@ impl<'tcx> LateLintPass<'tcx> for InvalidPaths {
                 ty::TypingEnv::post_analysis(cx.tcx, item.owner_id),
                 cx.tcx.typeck(item.owner_id),
             )
-            .eval_simple(cx.tcx.hir().body(body_id).value)
+            .eval_simple(cx.tcx.hir_body(body_id).value)
             && let Some(path) = path
                 .iter()
                 .map(|x| {
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 dac1951489c..d6f10f1e4b8 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
@@ -108,7 +108,7 @@ impl<'tcx> LateLintPass<'tcx> for LintWithoutLintPass {
             if is_lint_ref_type(cx, ty) {
                 check_invalid_clippy_version_attribute(cx, item);
 
-                let expr = &cx.tcx.hir().body(body_id).value;
+                let expr = &cx.tcx.hir_body(body_id).value;
                 let fields = if let ExprKind::AddrOf(_, _, inner_exp) = expr.kind
                     && let ExprKind::Struct(_, struct_fields, _) = inner_exp.kind
                 {
@@ -156,7 +156,7 @@ impl<'tcx> LateLintPass<'tcx> for LintWithoutLintPass {
                     output: &mut self.registered_lints,
                     cx,
                 };
-                let body = cx.tcx.hir().body_owned_by(
+                let body = cx.tcx.hir_body_owned_by(
                     impl_item_refs
                         .iter()
                         .find(|iiref| iiref.ident.as_str() == "lint_vec")
@@ -277,7 +277,7 @@ impl<'tcx> Visitor<'tcx> for LintCollector<'_, 'tcx> {
         }
     }
 
-    fn nested_visit_map(&mut self) -> Self::Map {
+    fn maybe_tcx(&mut self) -> Self::MaybeTyCtxt {
         self.cx.tcx.hir()
     }
 }
diff --git a/src/tools/clippy/clippy_lints/src/zero_repeat_side_effects.rs b/src/tools/clippy/clippy_lints/src/zero_repeat_side_effects.rs
index 05f85650769..30fdf22fdbb 100644
--- a/src/tools/clippy/clippy_lints/src/zero_repeat_side_effects.rs
+++ b/src/tools/clippy/clippy_lints/src/zero_repeat_side_effects.rs
@@ -47,7 +47,6 @@ declare_lint_pass!(ZeroRepeatSideEffects => [ZERO_REPEAT_SIDE_EFFECTS]);
 
 impl LateLintPass<'_> for ZeroRepeatSideEffects {
     fn check_expr(&mut self, cx: &LateContext<'_>, expr: &rustc_hir::Expr<'_>) {
-        let hir_map = cx.tcx.hir();
         if let Some(args) = VecArgs::hir(cx, expr)
             && let VecArgs::Repeat(inner_expr, len) = args
             && let ExprKind::Lit(l) = len.kind
@@ -62,7 +61,7 @@ impl LateLintPass<'_> for ZeroRepeatSideEffects {
         // sessions).
         else if let ExprKind::Repeat(inner_expr, const_arg) = expr.kind
             && let ConstArgKind::Anon(anon_const) = const_arg.kind
-            && let length_expr = hir_map.body(anon_const.body).value
+            && let length_expr = cx.tcx.hir_body(anon_const.body).value
             && !length_expr.span.from_expansion()
             && let ExprKind::Lit(literal) = length_expr.kind
             && let LitKind::Int(Pu128(0), _) = literal.node
diff --git a/src/tools/clippy/clippy_lints/src/zero_sized_map_values.rs b/src/tools/clippy/clippy_lints/src/zero_sized_map_values.rs
index 1221abec1ab..4c9a7f0e16d 100644
--- a/src/tools/clippy/clippy_lints/src/zero_sized_map_values.rs
+++ b/src/tools/clippy/clippy_lints/src/zero_sized_map_values.rs
@@ -73,8 +73,8 @@ impl LateLintPass<'_> for ZeroSizedMapValues {
 }
 
 fn in_trait_impl(cx: &LateContext<'_>, hir_id: HirId) -> bool {
-    let parent_id = cx.tcx.hir().get_parent_item(hir_id);
-    let second_parent_id = cx.tcx.hir().get_parent_item(parent_id.into()).def_id;
+    let parent_id = cx.tcx.hir_get_parent_item(hir_id);
+    let second_parent_id = cx.tcx.hir_get_parent_item(parent_id.into()).def_id;
     if let Node::Item(item) = cx.tcx.hir_node_by_def_id(second_parent_id) {
         if let ItemKind::Impl(hir::Impl { of_trait: Some(_), .. }) = item.kind {
             return true;
diff --git a/src/tools/clippy/clippy_lints/src/zombie_processes.rs b/src/tools/clippy/clippy_lints/src/zombie_processes.rs
index 4df34891a2b..9bd00b1e5c8 100644
--- a/src/tools/clippy/clippy_lints/src/zombie_processes.rs
+++ b/src/tools/clippy/clippy_lints/src/zombie_processes.rs
@@ -249,8 +249,8 @@ impl<'tcx> Visitor<'tcx> for WaitFinder<'_, 'tcx> {
         walk_expr(self, ex)
     }
 
-    fn nested_visit_map(&mut self) -> Self::Map {
-        self.cx.tcx.hir()
+    fn maybe_tcx(&mut self) -> Self::MaybeTyCtxt {
+        self.cx.tcx
     }
 }
 
diff --git a/src/tools/clippy/clippy_utils/src/check_proc_macro.rs b/src/tools/clippy/clippy_utils/src/check_proc_macro.rs
index 59aaaa3d9fb..4f48fb3b8a9 100644
--- a/src/tools/clippy/clippy_utils/src/check_proc_macro.rs
+++ b/src/tools/clippy/clippy_utils/src/check_proc_macro.rs
@@ -192,7 +192,7 @@ fn expr_search_pat(tcx: TyCtxt<'_>, e: &Expr<'_>) -> (Pat, Pat) {
             },
             ExprKind::Closure(&Closure { body, .. }) => (
                 Pat::Str(""),
-                expr_search_pat_inner(tcx, tcx.hir().body(body).value, outer_span).1,
+                expr_search_pat_inner(tcx, tcx.hir_body(body).value, outer_span).1,
             ),
             ExprKind::Block(
                 Block {
diff --git a/src/tools/clippy/clippy_utils/src/consts.rs b/src/tools/clippy/clippy_utils/src/consts.rs
index db82c458f70..dd149c4a29b 100644
--- a/src/tools/clippy/clippy_utils/src/consts.rs
+++ b/src/tools/clippy/clippy_utils/src/consts.rs
@@ -351,21 +351,18 @@ pub enum FullInt {
 }
 
 impl PartialEq for FullInt {
-    #[must_use]
     fn eq(&self, other: &Self) -> bool {
         self.cmp(other) == Ordering::Equal
     }
 }
 
 impl PartialOrd for FullInt {
-    #[must_use]
     fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
         Some(self.cmp(other))
     }
 }
 
 impl Ord for FullInt {
-    #[must_use]
     fn cmp(&self, other: &Self) -> Ordering {
         use FullInt::{S, U};
 
@@ -455,7 +452,7 @@ impl<'tcx> ConstEvalCtxt<'tcx> {
                     Some(val)
                 }
             },
-            PatExprKind::ConstBlock(ConstBlock { body, .. }) => self.expr(self.tcx.hir().body(*body).value),
+            PatExprKind::ConstBlock(ConstBlock { body, .. }) => self.expr(self.tcx.hir_body(*body).value),
             PatExprKind::Path(qpath) => self.qpath(qpath, pat_expr.hir_id),
         }
     }
@@ -483,7 +480,7 @@ impl<'tcx> ConstEvalCtxt<'tcx> {
     /// Simple constant folding: Insert an expression, get a constant or none.
     fn expr(&self, e: &Expr<'_>) -> Option<Constant<'tcx>> {
         match e.kind {
-            ExprKind::ConstBlock(ConstBlock { body, .. }) => self.expr(self.tcx.hir().body(body).value),
+            ExprKind::ConstBlock(ConstBlock { body, .. }) => self.expr(self.tcx.hir_body(body).value),
             ExprKind::DropTemps(e) => self.expr(e),
             ExprKind::Path(ref qpath) => self.qpath(qpath, e.hir_id),
             ExprKind::Block(block, _) => self.block(block),
@@ -550,7 +547,7 @@ impl<'tcx> ConstEvalCtxt<'tcx> {
     /// leaves the local crate.
     pub fn eval_is_empty(&self, e: &Expr<'_>) -> Option<bool> {
         match e.kind {
-            ExprKind::ConstBlock(ConstBlock { body, .. }) => self.eval_is_empty(self.tcx.hir().body(body).value),
+            ExprKind::ConstBlock(ConstBlock { body, .. }) => self.eval_is_empty(self.tcx.hir_body(body).value),
             ExprKind::DropTemps(e) => self.eval_is_empty(e),
             ExprKind::Path(ref qpath) => {
                 if !self
@@ -645,7 +642,7 @@ impl<'tcx> ConstEvalCtxt<'tcx> {
             Res::Def(DefKind::Const | DefKind::AssocConst, def_id) => {
                 // Check if this constant is based on `cfg!(..)`,
                 // which is NOT constant for our purposes.
-                if let Some(node) = self.tcx.hir().get_if_local(def_id)
+                if let Some(node) = self.tcx.hir_get_if_local(def_id)
                     && let Node::Item(Item {
                         kind: ItemKind::Const(.., body_id),
                         ..
diff --git a/src/tools/clippy/clippy_utils/src/eager_or_lazy.rs b/src/tools/clippy/clippy_utils/src/eager_or_lazy.rs
index b5bb174e737..aaea8d71efb 100644
--- a/src/tools/clippy/clippy_utils/src/eager_or_lazy.rs
+++ b/src/tools/clippy/clippy_utils/src/eager_or_lazy.rs
@@ -217,7 +217,7 @@ fn expr_eagerness<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> EagernessS
                     self.eagerness |= NoChange;
                 },
                 // Dereferences should be cheap, but dereferencing a raw pointer earlier may not be safe.
-                ExprKind::Unary(UnOp::Deref, e) if !self.cx.typeck_results().expr_ty(e).is_unsafe_ptr() => (),
+                ExprKind::Unary(UnOp::Deref, e) if !self.cx.typeck_results().expr_ty(e).is_raw_ptr() => (),
                 ExprKind::Unary(UnOp::Deref, _) => self.eagerness |= NoChange,
                 ExprKind::Unary(_, e)
                     if matches!(
diff --git a/src/tools/clippy/clippy_utils/src/higher.rs b/src/tools/clippy/clippy_utils/src/higher.rs
index 6bb876322f2..c4d00002292 100644
--- a/src/tools/clippy/clippy_utils/src/higher.rs
+++ b/src/tools/clippy/clippy_utils/src/higher.rs
@@ -117,7 +117,7 @@ impl<'hir> IfLet<'hir> {
             if_else,
         ) = expr.kind
         {
-            let mut iter = cx.tcx.hir().parent_iter(expr.hir_id);
+            let mut iter = cx.tcx.hir_parent_iter(expr.hir_id);
             if let Some((_, Node::Block(Block { stmts: [], .. }))) = iter.next() {
                 if let Some((
                     _,
diff --git a/src/tools/clippy/clippy_utils/src/hir_utils.rs b/src/tools/clippy/clippy_utils/src/hir_utils.rs
index 4bbf28115a6..0ac675345ae 100644
--- a/src/tools/clippy/clippy_utils/src/hir_utils.rs
+++ b/src/tools/clippy/clippy_utils/src/hir_utils.rs
@@ -273,8 +273,8 @@ impl HirEqInterExpr<'_, '_, '_> {
             self.inner.cx.tcx.typeck_body(right),
         ));
         let res = self.eq_expr(
-            self.inner.cx.tcx.hir().body(left).value,
-            self.inner.cx.tcx.hir().body(right).value,
+            self.inner.cx.tcx.hir_body(left).value,
+            self.inner.cx.tcx.hir_body(right).value,
         );
         self.inner.maybe_typeck_results = old_maybe_typeck_results;
         res
@@ -906,7 +906,7 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> {
             }) => {
                 std::mem::discriminant(&capture_clause).hash(&mut self.s);
                 // closures inherit TypeckResults
-                self.hash_expr(self.cx.tcx.hir().body(body).value);
+                self.hash_expr(self.cx.tcx.hir_body(body).value);
             },
             ExprKind::ConstBlock(ref l_id) => {
                 self.hash_body(l_id.body);
@@ -968,7 +968,10 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> {
                                 self.hash_expr(out_expr);
                             }
                         },
-                        InlineAsmOperand::Const { anon_const } | InlineAsmOperand::SymFn { anon_const } => {
+                        InlineAsmOperand::SymFn { expr } => {
+                            self.hash_expr(expr);
+                        }
+                        InlineAsmOperand::Const { anon_const } => {
                             self.hash_body(anon_const.body);
                         },
                         InlineAsmOperand::SymStatic { path, def_id: _ } => self.hash_qpath(path),
@@ -1316,7 +1319,7 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> {
     pub fn hash_body(&mut self, body_id: BodyId) {
         // swap out TypeckResults when hashing a body
         let old_maybe_typeck_results = self.maybe_typeck_results.replace(self.cx.tcx.typeck_body(body_id));
-        self.hash_expr(self.cx.tcx.hir().body(body_id).value);
+        self.hash_expr(self.cx.tcx.hir_body(body_id).value);
         self.maybe_typeck_results = old_maybe_typeck_results;
     }
 
diff --git a/src/tools/clippy/clippy_utils/src/lib.rs b/src/tools/clippy/clippy_utils/src/lib.rs
index 79cc5066580..7fc25e3617d 100644
--- a/src/tools/clippy/clippy_utils/src/lib.rs
+++ b/src/tools/clippy/clippy_utils/src/lib.rs
@@ -93,6 +93,7 @@ use std::sync::{Mutex, MutexGuard, OnceLock};
 
 use itertools::Itertools;
 use rustc_ast::ast::{self, LitKind, RangeLimits};
+use rustc_attr_parsing::{find_attr, AttributeKind};
 use rustc_data_structures::fx::FxHashMap;
 use rustc_data_structures::packed::Pu128;
 use rustc_data_structures::unhash::UnhashMap;
@@ -212,7 +213,7 @@ pub fn find_binding_init<'tcx>(cx: &LateContext<'tcx>, hir_id: HirId) -> Option<
 ///
 /// e.g. returns true for `x` in `fn f(x: usize) { .. }` and `let x = 1;` but false for `let x;`
 pub fn local_is_initialized(cx: &LateContext<'_>, local: HirId) -> bool {
-    for (_, node) in cx.tcx.hir().parent_iter(local) {
+    for (_, node) in cx.tcx.hir_parent_iter(local) {
         match node {
             Node::Pat(..) | Node::PatField(..) => {},
             Node::LetStmt(let_stmt) => return let_stmt.init.is_some(),
@@ -227,7 +228,7 @@ pub fn local_is_initialized(cx: &LateContext<'_>, local: HirId) -> bool {
 ///
 /// The current context is determined based on the current body which is set before calling a lint's
 /// entry point (any function on `LateLintPass`). If you need to check in a different context use
-/// `tcx.hir().is_inside_const_context(_)`.
+/// `tcx.hir_is_inside_const_context(_)`.
 ///
 /// Do not call this unless the `LateContext` has an enclosing body. For release build this case
 /// will safely return `false`, but debug builds will ICE. Note that `check_expr`, `check_block`,
@@ -237,8 +238,7 @@ pub fn is_in_const_context(cx: &LateContext<'_>) -> bool {
     debug_assert!(cx.enclosing_body.is_some(), "`LateContext` has no enclosing body");
     cx.enclosing_body.is_some_and(|id| {
         cx.tcx
-            .hir()
-            .body_const_context(cx.tcx.hir().body_owner_def_id(id))
+            .hir_body_const_context(cx.tcx.hir_body_owner_def_id(id))
             .is_some()
     })
 }
@@ -251,8 +251,7 @@ pub fn is_in_const_context(cx: &LateContext<'_>) -> bool {
 ///  * associated constants
 pub fn is_inside_always_const_context(tcx: TyCtxt<'_>, hir_id: HirId) -> bool {
     use ConstContext::{Const, ConstFn, Static};
-    let hir = tcx.hir();
-    let Some(ctx) = hir.body_const_context(hir.enclosing_body_owner(hir_id)) else {
+    let Some(ctx) = tcx.hir_body_const_context(tcx.hir_enclosing_body_owner(hir_id)) else {
         return false;
     };
     match ctx {
@@ -652,8 +651,6 @@ fn non_local_item_children_by_name(tcx: TyCtxt<'_>, def_id: DefId, name: Symbol)
 }
 
 fn local_item_children_by_name(tcx: TyCtxt<'_>, local_id: LocalDefId, name: Symbol) -> Vec<Res> {
-    let hir = tcx.hir();
-
     let root_mod;
     let item_kind = match tcx.hir_node_by_def_id(local_id) {
         Node::Crate(r#mod) => {
@@ -677,7 +674,7 @@ fn local_item_children_by_name(tcx: TyCtxt<'_>, local_id: LocalDefId, name: Symb
         ItemKind::Mod(r#mod) => r#mod
             .item_ids
             .iter()
-            .filter_map(|&item_id| res(hir.item(item_id).ident, item_id.owner_id))
+            .filter_map(|&item_id| res(tcx.hir_item(item_id).ident, item_id.owner_id))
             .collect(),
         ItemKind::Impl(r#impl) => r#impl
             .items
@@ -810,7 +807,7 @@ pub fn get_trait_def_id(tcx: TyCtxt<'_>, path: &[&str]) -> Option<DefId> {
 pub fn trait_ref_of_method<'tcx>(cx: &LateContext<'tcx>, def_id: LocalDefId) -> Option<&'tcx TraitRef<'tcx>> {
     // Get the implemented trait for the current function
     let hir_id = cx.tcx.local_def_id_to_hir_id(def_id);
-    let parent_impl = cx.tcx.hir().get_parent_item(hir_id);
+    let parent_impl = cx.tcx.hir_get_parent_item(hir_id);
     if parent_impl != hir::CRATE_OWNER_ID
         && let Node::Item(item) = cx.tcx.hir_node_by_def_id(parent_impl.def_id)
         && let ItemKind::Impl(impl_) = &item.kind
@@ -944,7 +941,7 @@ pub fn is_default_equivalent(cx: &LateContext<'_>, e: &Expr<'_>) -> bool {
         ExprKind::Tup(items) | ExprKind::Array(items) => items.iter().all(|x| is_default_equivalent(cx, x)),
         ExprKind::Repeat(x, len) => {
             if let ConstArgKind::Anon(anon_const) = len.kind
-                && let ExprKind::Lit(const_lit) = cx.tcx.hir().body(anon_const.body).value.kind
+                && let ExprKind::Lit(const_lit) = cx.tcx.hir_body(anon_const.body).value.kind
                 && let LitKind::Int(v, _) = const_lit.node
                 && v <= 32
                 && is_default_equivalent(cx, x)
@@ -974,7 +971,7 @@ fn is_default_equivalent_from(cx: &LateContext<'_>, from_func: &Expr<'_>, arg: &
             ExprKind::Array([]) => return is_path_diagnostic_item(cx, ty, sym::Vec),
             ExprKind::Repeat(_, len) => {
                 if let ConstArgKind::Anon(anon_const) = len.kind
-                    && let ExprKind::Lit(const_lit) = cx.tcx.hir().body(anon_const.body).value.kind
+                    && let ExprKind::Lit(const_lit) = cx.tcx.hir_body(anon_const.body).value.kind
                     && let LitKind::Int(v, _) = const_lit.node
                 {
                     return v == 0 && is_path_diagnostic_item(cx, ty, sym::Vec);
@@ -1121,7 +1118,7 @@ pub fn capture_local_usage(cx: &LateContext<'_>, e: &Expr<'_>) -> CaptureKind {
     let mut capture = CaptureKind::Value;
     let mut capture_expr_ty = e;
 
-    for (parent_id, parent) in cx.tcx.hir().parent_iter(e.hir_id) {
+    for (parent_id, parent) in cx.tcx.hir_parent_iter(e.hir_id) {
         if let [
             Adjustment {
                 kind: Adjust::Deref(_) | Adjust::Borrow(AutoBorrow::Ref(..)),
@@ -1340,13 +1337,13 @@ pub fn is_entrypoint_fn(cx: &LateContext<'_>, def_id: DefId) -> bool {
 
 /// Returns `true` if the expression is in the program's `#[panic_handler]`.
 pub fn is_in_panic_handler(cx: &LateContext<'_>, e: &Expr<'_>) -> bool {
-    let parent = cx.tcx.hir().get_parent_item(e.hir_id);
+    let parent = cx.tcx.hir_get_parent_item(e.hir_id);
     Some(parent.to_def_id()) == cx.tcx.lang_items().panic_impl()
 }
 
 /// Gets the name of the item the expression is in, if available.
 pub fn get_item_name(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<Symbol> {
-    let parent_id = cx.tcx.hir().get_parent_item(expr.hir_id).def_id;
+    let parent_id = cx.tcx.hir_get_parent_item(expr.hir_id).def_id;
     match cx.tcx.hir_node_by_def_id(parent_id) {
         Node::Item(Item { ident, .. })
         | Node::TraitItem(TraitItem { ident, .. })
@@ -1372,8 +1369,8 @@ impl<'tcx> Visitor<'tcx> for ContainsName<'_, 'tcx> {
         }
     }
 
-    fn nested_visit_map(&mut self) -> Self::Map {
-        self.cx.tcx.hir()
+    fn maybe_tcx(&mut self) -> Self::MaybeTyCtxt {
+        self.cx.tcx
     }
 }
 
@@ -1411,9 +1408,9 @@ pub fn get_parent_expr_for_hir<'tcx>(cx: &LateContext<'tcx>, hir_id: HirId) -> O
 
 /// Gets the enclosing block, if any.
 pub fn get_enclosing_block<'tcx>(cx: &LateContext<'tcx>, hir_id: HirId) -> Option<&'tcx Block<'tcx>> {
-    let map = &cx.tcx.hir();
-    let enclosing_node = map
-        .get_enclosing_scope(hir_id)
+    let enclosing_node = cx
+        .tcx
+        .hir_get_enclosing_scope(hir_id)
         .map(|enclosing_id| cx.tcx.hir_node(enclosing_id));
     enclosing_node.and_then(|node| match node {
         Node::Block(block) => Some(block),
@@ -1424,7 +1421,7 @@ pub fn get_enclosing_block<'tcx>(cx: &LateContext<'tcx>, hir_id: HirId) -> Optio
         | Node::ImplItem(&ImplItem {
             kind: ImplItemKind::Fn(_, eid),
             ..
-        }) => match cx.tcx.hir().body(eid).value.kind {
+        }) => match cx.tcx.hir_body(eid).value.kind {
             ExprKind::Block(block, _) => Some(block),
             _ => None,
         },
@@ -1437,7 +1434,7 @@ pub fn get_enclosing_loop_or_multi_call_closure<'tcx>(
     cx: &LateContext<'tcx>,
     expr: &Expr<'_>,
 ) -> Option<&'tcx Expr<'tcx>> {
-    for (_, node) in cx.tcx.hir().parent_iter(expr.hir_id) {
+    for (_, node) in cx.tcx.hir_parent_iter(expr.hir_id) {
         match node {
             Node::Expr(e) => match e.kind {
                 ExprKind::Closure { .. }
@@ -1457,7 +1454,7 @@ pub fn get_enclosing_loop_or_multi_call_closure<'tcx>(
 
 /// Gets the parent node if it's an impl block.
 pub fn get_parent_as_impl(tcx: TyCtxt<'_>, id: HirId) -> Option<&Impl<'_>> {
-    match tcx.hir().parent_iter(id).next() {
+    match tcx.hir_parent_iter(id).next() {
         Some((
             _,
             Node::Item(Item {
@@ -1535,7 +1532,7 @@ pub fn peel_blocks_with_stmt<'a>(mut expr: &'a Expr<'a>) -> &'a Expr<'a> {
 
 /// Checks if the given expression is the else clause of either an `if` or `if let` expression.
 pub fn is_else_clause(tcx: TyCtxt<'_>, expr: &Expr<'_>) -> bool {
-    let mut iter = tcx.hir().parent_iter(expr.hir_id);
+    let mut iter = tcx.hir_parent_iter(expr.hir_id);
     match iter.next() {
         Some((
             _,
@@ -1552,7 +1549,7 @@ pub fn is_else_clause(tcx: TyCtxt<'_>, expr: &Expr<'_>) -> bool {
 /// returns `true` for both the `init` and the `else` part
 pub fn is_inside_let_else(tcx: TyCtxt<'_>, expr: &Expr<'_>) -> bool {
     let mut child_id = expr.hir_id;
-    for (parent_id, node) in tcx.hir().parent_iter(child_id) {
+    for (parent_id, node) in tcx.hir_parent_iter(child_id) {
         if let Node::LetStmt(LetStmt {
             init: Some(init),
             els: Some(els),
@@ -1572,7 +1569,7 @@ pub fn is_inside_let_else(tcx: TyCtxt<'_>, expr: &Expr<'_>) -> bool {
 /// Checks if the given expression is the else clause of a `let else` expression
 pub fn is_else_clause_in_let_else(tcx: TyCtxt<'_>, expr: &Expr<'_>) -> bool {
     let mut child_id = expr.hir_id;
-    for (parent_id, node) in tcx.hir().parent_iter(child_id) {
+    for (parent_id, node) in tcx.hir_parent_iter(child_id) {
         if let Node::LetStmt(LetStmt { els: Some(els), .. }) = node
             && els.hir_id == child_id
         {
@@ -1650,7 +1647,7 @@ pub fn is_integer_const(cx: &LateContext<'_>, e: &Expr<'_>, value: u128) -> bool
     if is_integer_literal(e, value) {
         return true;
     }
-    let enclosing_body = cx.tcx.hir().enclosing_body_owner(e.hir_id);
+    let enclosing_body = cx.tcx.hir_enclosing_body_owner(e.hir_id);
     if let Some(Constant::Int(v)) =
         ConstEvalCtxt::with_env(cx.tcx, cx.typing_env(), cx.tcx.typeck(enclosing_body)).eval(e)
     {
@@ -1953,7 +1950,7 @@ pub fn has_attr(attrs: &[hir::Attribute], symbol: Symbol) -> bool {
 }
 
 pub fn has_repr_attr(cx: &LateContext<'_>, hir_id: HirId) -> bool {
-    has_attr(cx.tcx.hir().attrs(hir_id), sym::repr)
+    find_attr!(cx.tcx.hir().attrs(hir_id), AttributeKind::Repr(..))
 }
 
 pub fn any_parent_has_attr(tcx: TyCtxt<'_>, node: HirId, symbol: Symbol) -> bool {
@@ -1965,7 +1962,7 @@ pub fn any_parent_has_attr(tcx: TyCtxt<'_>, node: HirId, symbol: Symbol) -> bool
             return true;
         }
         prev_enclosing_node = Some(enclosing_node);
-        enclosing_node = map.get_parent_item(enclosing_node).into();
+        enclosing_node = tcx.hir_get_parent_item(enclosing_node).into();
     }
 
     false
@@ -1974,8 +1971,8 @@ pub fn any_parent_has_attr(tcx: TyCtxt<'_>, node: HirId, symbol: Symbol) -> bool
 /// Checks if the given HIR node is inside an `impl` block with the `automatically_derived`
 /// attribute.
 pub fn in_automatically_derived(tcx: TyCtxt<'_>, id: HirId) -> bool {
-    tcx.hir()
-        .parent_owner_iter(id)
+    tcx
+        .hir_parent_owner_iter(id)
         .filter(|(_, node)| matches!(node, OwnerNode::Item(item) if matches!(item.kind, ItemKind::Impl(_))))
         .any(|(id, _)| {
             has_attr(
@@ -2067,7 +2064,7 @@ pub fn get_async_fn_body<'tcx>(tcx: TyCtxt<'tcx>, body: &Body<'_>) -> Option<&'t
                 ..
             },
             _,
-        ) = tcx.hir().body(body).value.kind
+        ) = tcx.hir_body(body).value.kind
         {
             return Some(expr);
         }
@@ -2175,7 +2172,7 @@ pub fn is_expr_untyped_identity_function(cx: &LateContext<'_>, expr: &Expr<'_>)
         ExprKind::Closure(&Closure { body, fn_decl, .. })
             if fn_decl.inputs.iter().all(|ty| matches!(ty.kind, TyKind::Infer(()))) =>
         {
-            is_body_identity_function(cx, cx.tcx.hir().body(body))
+            is_body_identity_function(cx, cx.tcx.hir_body(body))
         },
         ExprKind::Path(QPath::Resolved(_, path))
             if path.segments.iter().all(|seg| seg.infer_args)
@@ -2197,7 +2194,7 @@ pub fn is_expr_untyped_identity_function(cx: &LateContext<'_>, expr: &Expr<'_>)
 /// errors.
 pub fn is_expr_identity_function(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
     match expr.kind {
-        ExprKind::Closure(&Closure { body, .. }) => is_body_identity_function(cx, cx.tcx.hir().body(body)),
+        ExprKind::Closure(&Closure { body, .. }) => is_body_identity_function(cx, cx.tcx.hir_body(body)),
         _ => path_def_id(cx, expr).is_some_and(|id| cx.tcx.is_diagnostic_item(sym::convert_identity, id)),
     }
 }
@@ -2206,7 +2203,7 @@ pub fn is_expr_identity_function(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool
 /// Returns both the node and the `HirId` of the closest child node.
 pub fn get_expr_use_or_unification_node<'tcx>(tcx: TyCtxt<'tcx>, expr: &Expr<'_>) -> Option<(Node<'tcx>, HirId)> {
     let mut child_id = expr.hir_id;
-    let mut iter = tcx.hir().parent_iter(child_id);
+    let mut iter = tcx.hir_parent_iter(child_id);
     loop {
         match iter.next() {
             None => break None,
@@ -2552,9 +2549,9 @@ fn with_test_item_names(tcx: TyCtxt<'_>, module: LocalModDefId, f: impl Fn(&[Sym
         Entry::Occupied(entry) => f(entry.get()),
         Entry::Vacant(entry) => {
             let mut names = Vec::new();
-            for id in tcx.hir().module_items(module) {
+            for id in tcx.hir_module_free_items(module) {
                 if matches!(tcx.def_kind(id.owner_id), DefKind::Const)
-                    && let item = tcx.hir().item(id)
+                    && let item = tcx.hir_item(id)
                     && let ItemKind::Const(ty, _generics, _body) = item.kind
                 {
                     if let TyKind::Path(QPath::Resolved(_, path)) = ty.kind {
@@ -2585,7 +2582,7 @@ pub fn is_in_test_function(tcx: TyCtxt<'_>, id: HirId) -> bool {
     with_test_item_names(tcx, tcx.parent_module(id), |names| {
         let node = tcx.hir_node(id);
         once((id, node))
-            .chain(tcx.hir().parent_iter(id))
+            .chain(tcx.hir_parent_iter(id))
             // Since you can nest functions we need to collect all until we leave
             // function scope
             .any(|(_id, node)| {
@@ -2621,8 +2618,8 @@ pub fn is_cfg_test(tcx: TyCtxt<'_>, id: HirId) -> bool {
 
 /// Checks if any parent node of `HirId` has `#[cfg(test)]` attribute applied
 pub fn is_in_cfg_test(tcx: TyCtxt<'_>, id: HirId) -> bool {
-    tcx.hir()
-        .parent_id_iter(id)
+    tcx
+        .hir_parent_id_iter(id)
         .any(|parent_id| is_cfg_test(tcx, parent_id))
 }
 
@@ -2636,8 +2633,8 @@ pub fn inherits_cfg(tcx: TyCtxt<'_>, def_id: LocalDefId) -> bool {
     let hir = tcx.hir();
 
     tcx.has_attr(def_id, sym::cfg)
-        || hir
-            .parent_iter(tcx.local_def_id_to_hir_id(def_id))
+        || tcx
+            .hir_parent_iter(tcx.local_def_id_to_hir_id(def_id))
             .flat_map(|(parent_id, _)| hir.attrs(parent_id))
             .any(|attr| attr.has_name(sym::cfg))
 }
@@ -2657,8 +2654,7 @@ pub fn walk_to_expr_usage<'tcx, T>(
     e: &Expr<'tcx>,
     mut f: impl FnMut(HirId, Node<'tcx>, HirId) -> ControlFlow<T>,
 ) -> Option<ControlFlow<T, (Node<'tcx>, HirId)>> {
-    let map = cx.tcx.hir();
-    let mut iter = map.parent_iter(e.hir_id);
+    let mut iter = cx.tcx.hir_parent_iter(e.hir_id);
     let mut child_id = e.hir_id;
 
     while let Some((parent_id, parent)) = iter.next() {
@@ -2681,7 +2677,7 @@ pub fn walk_to_expr_usage<'tcx, T>(
             ExprKind::If(child, ..) | ExprKind::Match(child, ..) if child.hir_id != child_id => child_id = parent_id,
             ExprKind::Break(Destination { target_id: Ok(id), .. }, _) => {
                 child_id = id;
-                iter = map.parent_iter(id);
+                iter = cx.tcx.hir_parent_iter(id);
             },
             ExprKind::Block(..) | ExprKind::DropTemps(_) => child_id = parent_id,
             _ => return Some(ControlFlow::Continue((parent, child_id))),
@@ -2764,7 +2760,7 @@ impl<'tcx> ExprUseCtxt<'tcx> {
 
             Node::Expr(use_expr) => match use_expr.kind {
                 ExprKind::Ret(_) => ExprUseNode::Return(OwnerId {
-                    def_id: cx.tcx.hir().body_owner_def_id(cx.enclosing_body.unwrap()),
+                    def_id: cx.tcx.hir_body_owner_def_id(cx.enclosing_body.unwrap()),
                 }),
 
                 ExprKind::Closure(closure) => ExprUseNode::Return(OwnerId { def_id: closure.def_id }),
@@ -2932,7 +2928,7 @@ pub fn expr_use_ctxt<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'tcx>) -> ExprU
         #[allow(unreachable_patterns)]
         Some(ControlFlow::Break(_)) => unreachable!("type of node is ControlFlow<!>"),
         None => ExprUseCtxt {
-            node: Node::Crate(cx.tcx.hir().root_module()),
+            node: Node::Crate(cx.tcx.hir_root_module()),
             child_id: HirId::INVALID,
             adjustments: &[],
             is_ty_unified: true,
diff --git a/src/tools/clippy/clippy_utils/src/macros.rs b/src/tools/clippy/clippy_utils/src/macros.rs
index 30fd48fc060..9ce0fd8318f 100644
--- a/src/tools/clippy/clippy_utils/src/macros.rs
+++ b/src/tools/clippy/clippy_utils/src/macros.rs
@@ -178,7 +178,7 @@ pub fn first_node_in_macro(cx: &LateContext<'_>, node: &impl HirNode) -> Option<
     // get the parent node, possibly skipping over a statement
     // if the parent is not found, it is sensible to return `Some(root)`
     let hir = cx.tcx.hir();
-    let mut parent_iter = hir.parent_iter(node.hir_id());
+    let mut parent_iter = cx.tcx.hir_parent_iter(node.hir_id());
     let (parent_id, _) = match parent_iter.next() {
         None => return Some(ExpnId::root()),
         Some((_, Node::Stmt(_))) => match parent_iter.next() {
diff --git a/src/tools/clippy/clippy_utils/src/mir/mod.rs b/src/tools/clippy/clippy_utils/src/mir/mod.rs
index 85250f81dc4..ffcfcd240ea 100644
--- a/src/tools/clippy/clippy_utils/src/mir/mod.rs
+++ b/src/tools/clippy/clippy_utils/src/mir/mod.rs
@@ -30,7 +30,7 @@ pub fn visit_local_usage(locals: &[Local], mir: &Body<'_>, location: Location) -
         locals.len()
     ];
 
-    traversal::Postorder::new(&mir.basic_blocks, location.block, ())
+    traversal::Postorder::new(&mir.basic_blocks, location.block, None)
         .collect::<Vec<_>>()
         .into_iter()
         .rev()
@@ -136,8 +136,8 @@ pub fn used_exactly_once(mir: &Body<'_>, local: Local) -> Option<bool> {
 /// Returns the `mir::Body` containing the node associated with `hir_id`.
 #[allow(clippy::module_name_repetitions)]
 pub fn enclosing_mir(tcx: TyCtxt<'_>, hir_id: HirId) -> Option<&Body<'_>> {
-    let body_owner_local_def_id = tcx.hir().enclosing_body_owner(hir_id);
-    if tcx.hir().body_owner_kind(body_owner_local_def_id).is_fn_or_closure() {
+    let body_owner_local_def_id = tcx.hir_enclosing_body_owner(hir_id);
+    if tcx.hir_body_owner_kind(body_owner_local_def_id).is_fn_or_closure() {
         Some(tcx.optimized_mir(body_owner_local_def_id.to_def_id()))
     } else {
         None
diff --git a/src/tools/clippy/clippy_utils/src/ptr.rs b/src/tools/clippy/clippy_utils/src/ptr.rs
index 273c1b0defa..360c6251a57 100644
--- a/src/tools/clippy/clippy_utils/src/ptr.rs
+++ b/src/tools/clippy/clippy_utils/src/ptr.rs
@@ -13,7 +13,7 @@ pub fn get_spans(
     idx: usize,
     replacements: &[(&'static str, &'static str)],
 ) -> Option<Vec<(Span, Cow<'static, str>)>> {
-    if let Some(body) = opt_body_id.map(|id| cx.tcx.hir().body(id)) {
+    if let Some(body) = opt_body_id.map(|id| cx.tcx.hir_body(id)) {
         if let PatKind::Binding(_, binding_id, _, _) = strip_pat_refs(body.params[idx].pat).kind {
             extract_clone_suggestions(cx, binding_id, replacements, body)
         } else {
diff --git a/src/tools/clippy/clippy_utils/src/sugg.rs b/src/tools/clippy/clippy_utils/src/sugg.rs
index 088abd7c479..d5e0e2e3436 100644
--- a/src/tools/clippy/clippy_utils/src/sugg.rs
+++ b/src/tools/clippy/clippy_utils/src/sugg.rs
@@ -809,7 +809,7 @@ pub fn deref_closure_args(cx: &LateContext<'_>, closure: &hir::Expr<'_>) -> Opti
         fn_decl, def_id, body, ..
     }) = closure.kind
     {
-        let closure_body = cx.tcx.hir().body(body);
+        let closure_body = cx.tcx.hir_body(body);
         // is closure arg a type annotated double reference (i.e.: `|x: &&i32| ...`)
         // a type annotation is present if param `kind` is different from `TyKind::Infer`
         let closure_arg_is_type_annotated_double_ref = if let TyKind::Ref(_, MutTy { ty, .. }) = fn_decl.inputs[0].kind
diff --git a/src/tools/clippy/clippy_utils/src/ty/mod.rs b/src/tools/clippy/clippy_utils/src/ty/mod.rs
index a5374f6904e..8eef6a7f57e 100644
--- a/src/tools/clippy/clippy_utils/src/ty/mod.rs
+++ b/src/tools/clippy/clippy_utils/src/ty/mod.rs
@@ -267,7 +267,7 @@ pub fn implements_trait_with_env_from_iter<'tcx>(
     // through calling `body_owner_kind`, which would panic if the callee
     // does not have a body.
     if let Some(callee_id) = callee_id {
-        let _ = tcx.hir().body_owner_kind(callee_id);
+        let _ = tcx.hir_body_owner_kind(callee_id);
     }
 
     let ty = tcx.erase_regions(ty);
@@ -705,7 +705,7 @@ pub fn ty_sig<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Option<ExprFnSig<'t
         ty::Closure(id, subs) => {
             let decl = id
                 .as_local()
-                .and_then(|id| cx.tcx.hir().fn_decl_by_hir_id(cx.tcx.local_def_id_to_hir_id(id)));
+                .and_then(|id| cx.tcx.hir_fn_decl_by_hir_id(cx.tcx.local_def_id_to_hir_id(id)));
             Some(ExprFnSig::Closure(decl, subs.as_closure().sig()))
         },
         ty::FnDef(id, subs) => Some(ExprFnSig::Sig(cx.tcx.fn_sig(id).instantiate(cx.tcx, subs), Some(id))),
diff --git a/src/tools/clippy/clippy_utils/src/usage.rs b/src/tools/clippy/clippy_utils/src/usage.rs
index 37f72966892..3bf518f7fe7 100644
--- a/src/tools/clippy/clippy_utils/src/usage.rs
+++ b/src/tools/clippy/clippy_utils/src/usage.rs
@@ -133,8 +133,8 @@ impl<'tcx> Visitor<'tcx> for BindingUsageFinder<'_, 'tcx> {
         ControlFlow::Continue(())
     }
 
-    fn nested_visit_map(&mut self) -> Self::Map {
-        self.cx.tcx.hir()
+    fn maybe_tcx(&mut self) -> Self::MaybeTyCtxt {
+        self.cx.tcx
     }
 }
 
diff --git a/src/tools/clippy/clippy_utils/src/visitors.rs b/src/tools/clippy/clippy_utils/src/visitors.rs
index 99984c41714..2ac0efd7e39 100644
--- a/src/tools/clippy/clippy_utils/src/visitors.rs
+++ b/src/tools/clippy/clippy_utils/src/visitors.rs
@@ -154,8 +154,8 @@ pub fn for_each_expr<'tcx, B, C: Continue>(
         type NestedFilter = nested_filter::OnlyBodies;
         type Result = ControlFlow<B>;
 
-        fn nested_visit_map(&mut self) -> Self::Map {
-            self.tcx.hir()
+        fn maybe_tcx(&mut self) -> Self::MaybeTyCtxt {
+            self.tcx
         }
 
         fn visit_expr(&mut self, e: &'tcx Expr<'tcx>) -> Self::Result {
@@ -296,7 +296,7 @@ where
 
 /// Checks if the given resolved path is used in the given body.
 pub fn is_res_used(cx: &LateContext<'_>, res: Res, body: BodyId) -> bool {
-    for_each_expr(cx, cx.tcx.hir().body(body).value, |e| {
+    for_each_expr(cx, cx.tcx.hir_body(body).value, |e| {
         if let ExprKind::Path(p) = &e.kind {
             if cx.qpath_res(p, e.hir_id) == res {
                 return ControlFlow::Break(());
@@ -412,12 +412,12 @@ pub fn is_expr_unsafe<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> bool {
         type NestedFilter = nested_filter::OnlyBodies;
         type Result = ControlFlow<()>;
 
-        fn nested_visit_map(&mut self) -> Self::Map {
-            self.cx.tcx.hir()
+        fn maybe_tcx(&mut self) -> Self::MaybeTyCtxt {
+            self.cx.tcx
         }
         fn visit_expr(&mut self, e: &'tcx Expr<'_>) -> Self::Result {
             match e.kind {
-                ExprKind::Unary(UnOp::Deref, e) if self.cx.typeck_results().expr_ty(e).is_unsafe_ptr() => {
+                ExprKind::Unary(UnOp::Deref, e) if self.cx.typeck_results().expr_ty(e).is_raw_ptr() => {
                     ControlFlow::Break(())
                 },
                 ExprKind::MethodCall(..)
@@ -456,7 +456,7 @@ pub fn is_expr_unsafe<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> bool {
             }
         }
         fn visit_nested_item(&mut self, id: ItemId) -> Self::Result {
-            if let ItemKind::Impl(i) = &self.cx.tcx.hir().item(id).kind
+            if let ItemKind::Impl(i) = &self.cx.tcx.hir_item(id).kind
                 && i.safety.is_unsafe()
             {
                 ControlFlow::Break(())
@@ -477,8 +477,8 @@ pub fn contains_unsafe_block<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'tcx>)
     impl<'tcx> Visitor<'tcx> for V<'_, 'tcx> {
         type Result = ControlFlow<()>;
         type NestedFilter = nested_filter::OnlyBodies;
-        fn nested_visit_map(&mut self) -> Self::Map {
-            self.cx.tcx.hir()
+        fn maybe_tcx(&mut self) -> Self::MaybeTyCtxt {
+            self.cx.tcx
         }
 
         fn visit_block(&mut self, b: &'tcx Block<'_>) -> Self::Result {
@@ -544,8 +544,8 @@ pub fn for_each_local_use_after_expr<'tcx, B>(
     }
     impl<'tcx, F: FnMut(&'tcx Expr<'tcx>) -> ControlFlow<B>, B> Visitor<'tcx> for V<'_, 'tcx, F, B> {
         type NestedFilter = nested_filter::OnlyBodies;
-        fn nested_visit_map(&mut self) -> Self::Map {
-            self.cx.tcx.hir()
+        fn maybe_tcx(&mut self) -> Self::MaybeTyCtxt {
+            self.cx.tcx
         }
 
         fn visit_expr(&mut self, e: &'tcx Expr<'tcx>) {
@@ -729,8 +729,8 @@ pub fn for_each_local_assignment<'tcx, B>(
     }
     impl<'tcx, F: FnMut(&'tcx Expr<'tcx>) -> ControlFlow<B>, B> Visitor<'tcx> for V<'_, 'tcx, F, B> {
         type NestedFilter = nested_filter::OnlyBodies;
-        fn nested_visit_map(&mut self) -> Self::Map {
-            self.cx.tcx.hir()
+        fn maybe_tcx(&mut self) -> Self::MaybeTyCtxt {
+            self.cx.tcx
         }
 
         fn visit_expr(&mut self, e: &'tcx Expr<'tcx>) {
diff --git a/src/tools/clippy/tests/ui-toml/dbg_macro/dbg_macro.stderr b/src/tools/clippy/tests/ui-toml/dbg_macro/dbg_macro.stderr
index 129fab5ff97..f0d7104a57d 100644
--- a/src/tools/clippy/tests/ui-toml/dbg_macro/dbg_macro.stderr
+++ b/src/tools/clippy/tests/ui-toml/dbg_macro/dbg_macro.stderr
@@ -8,8 +8,9 @@ LL |     if let Some(n) = dbg!(n.checked_sub(4)) { n } else { n }
    = help: to override `-D warnings` add `#[allow(clippy::dbg_macro)]`
 help: remove the invocation before committing it to a version control system
    |
-LL |     if let Some(n) = n.checked_sub(4) { n } else { n }
-   |                      ~~~~~~~~~~~~~~~~
+LL -     if let Some(n) = dbg!(n.checked_sub(4)) { n } else { n }
+LL +     if let Some(n) = n.checked_sub(4) { n } else { n }
+   |
 
 error: the `dbg!` macro is intended as a debugging tool
   --> tests/ui-toml/dbg_macro/dbg_macro.rs:10:8
@@ -19,8 +20,9 @@ LL |     if dbg!(n <= 1) {
    |
 help: remove the invocation before committing it to a version control system
    |
-LL |     if n <= 1 {
-   |        ~~~~~~
+LL -     if dbg!(n <= 1) {
+LL +     if n <= 1 {
+   |
 
 error: the `dbg!` macro is intended as a debugging tool
   --> tests/ui-toml/dbg_macro/dbg_macro.rs:11:9
@@ -30,7 +32,8 @@ LL |         dbg!(1)
    |
 help: remove the invocation before committing it to a version control system
    |
-LL |         1
+LL -         dbg!(1)
+LL +         1
    |
 
 error: the `dbg!` macro is intended as a debugging tool
@@ -41,7 +44,8 @@ LL |         dbg!(n * factorial(n - 1))
    |
 help: remove the invocation before committing it to a version control system
    |
-LL |         n * factorial(n - 1)
+LL -         dbg!(n * factorial(n - 1))
+LL +         n * factorial(n - 1)
    |
 
 error: the `dbg!` macro is intended as a debugging tool
@@ -52,8 +56,9 @@ LL |     dbg!(42);
    |
 help: remove the invocation before committing it to a version control system
    |
-LL |     42;
-   |     ~~
+LL -     dbg!(42);
+LL +     42;
+   |
 
 error: the `dbg!` macro is intended as a debugging tool
   --> tests/ui-toml/dbg_macro/dbg_macro.rs:19:14
@@ -63,8 +68,9 @@ LL |     foo(3) + dbg!(factorial(4));
    |
 help: remove the invocation before committing it to a version control system
    |
-LL |     foo(3) + factorial(4);
-   |              ~~~~~~~~~~~~
+LL -     foo(3) + dbg!(factorial(4));
+LL +     foo(3) + factorial(4);
+   |
 
 error: the `dbg!` macro is intended as a debugging tool
   --> tests/ui-toml/dbg_macro/dbg_macro.rs:20:5
@@ -74,8 +80,9 @@ LL |     dbg!(1, 2, 3, 4, 5);
    |
 help: remove the invocation before committing it to a version control system
    |
-LL |     (1, 2, 3, 4, 5);
-   |     ~~~~~~~~~~~~~~~
+LL -     dbg!(1, 2, 3, 4, 5);
+LL +     (1, 2, 3, 4, 5);
+   |
 
 error: aborting due to 7 previous errors
 
diff --git a/src/tools/clippy/tests/ui-toml/doc_valid_idents_append/doc_markdown.stderr b/src/tools/clippy/tests/ui-toml/doc_valid_idents_append/doc_markdown.stderr
index a6e0ad0f804..8ba237ee75c 100644
--- a/src/tools/clippy/tests/ui-toml/doc_valid_idents_append/doc_markdown.stderr
+++ b/src/tools/clippy/tests/ui-toml/doc_valid_idents_append/doc_markdown.stderr
@@ -8,8 +8,9 @@ LL | /// TestItemThingyOfCoolness might sound cool but is not on the list and sh
    = help: to override `-D warnings` add `#[allow(clippy::doc_markdown)]`
 help: try
    |
-LL | /// `TestItemThingyOfCoolness` might sound cool but is not on the list and should be linted.
-   |     ~~~~~~~~~~~~~~~~~~~~~~~~~~
+LL - /// TestItemThingyOfCoolness might sound cool but is not on the list and should be linted.
+LL + /// `TestItemThingyOfCoolness` might sound cool but is not on the list and should be linted.
+   |
 
 error: aborting due to 1 previous error
 
diff --git a/src/tools/clippy/tests/ui-toml/doc_valid_idents_replace/doc_markdown.stderr b/src/tools/clippy/tests/ui-toml/doc_valid_idents_replace/doc_markdown.stderr
index d4d8a579798..9f2d7cf54e0 100644
--- a/src/tools/clippy/tests/ui-toml/doc_valid_idents_replace/doc_markdown.stderr
+++ b/src/tools/clippy/tests/ui-toml/doc_valid_idents_replace/doc_markdown.stderr
@@ -8,8 +8,9 @@ LL | /// OAuth and LaTeX are inside Clippy's default list.
    = help: to override `-D warnings` add `#[allow(clippy::doc_markdown)]`
 help: try
    |
-LL | /// `OAuth` and LaTeX are inside Clippy's default list.
-   |     ~~~~~~~
+LL - /// OAuth and LaTeX are inside Clippy's default list.
+LL + /// `OAuth` and LaTeX are inside Clippy's default list.
+   |
 
 error: item in documentation is missing backticks
   --> tests/ui-toml/doc_valid_idents_replace/doc_markdown.rs:6:15
@@ -19,8 +20,9 @@ LL | /// OAuth and LaTeX are inside Clippy's default list.
    |
 help: try
    |
-LL | /// OAuth and `LaTeX` are inside Clippy's default list.
-   |               ~~~~~~~
+LL - /// OAuth and LaTeX are inside Clippy's default list.
+LL + /// OAuth and `LaTeX` are inside Clippy's default list.
+   |
 
 error: item in documentation is missing backticks
   --> tests/ui-toml/doc_valid_idents_replace/doc_markdown.rs:9:5
@@ -30,8 +32,9 @@ LL | /// TestItemThingyOfCoolness might sound cool but is not on the list and sh
    |
 help: try
    |
-LL | /// `TestItemThingyOfCoolness` might sound cool but is not on the list and should be linted.
-   |     ~~~~~~~~~~~~~~~~~~~~~~~~~~
+LL - /// TestItemThingyOfCoolness might sound cool but is not on the list and should be linted.
+LL + /// `TestItemThingyOfCoolness` might sound cool but is not on the list and should be linted.
+   |
 
 error: aborting due to 3 previous errors
 
diff --git a/src/tools/clippy/tests/ui-toml/enum_variant_size/enum_variant_size.stderr b/src/tools/clippy/tests/ui-toml/enum_variant_size/enum_variant_size.stderr
index 8f7ebbd9546..020b3cc7878 100644
--- a/src/tools/clippy/tests/ui-toml/enum_variant_size/enum_variant_size.stderr
+++ b/src/tools/clippy/tests/ui-toml/enum_variant_size/enum_variant_size.stderr
@@ -14,8 +14,9 @@ LL | | }
    = help: to override `-D warnings` add `#[allow(clippy::large_enum_variant)]`
 help: consider boxing the large fields to reduce the total size of the enum
    |
-LL |     B(Box<[u8; 501]>),
-   |       ~~~~~~~~~~~~~~
+LL -     B([u8; 501]),
+LL +     B(Box<[u8; 501]>),
+   |
 
 error: aborting due to 1 previous error
 
diff --git a/src/tools/clippy/tests/ui-toml/large_include_file/large_include_file.stderr b/src/tools/clippy/tests/ui-toml/large_include_file/large_include_file.stderr
index 82b926cc53b..c9f0e661dbd 100644
--- a/src/tools/clippy/tests/ui-toml/large_include_file/large_include_file.stderr
+++ b/src/tools/clippy/tests/ui-toml/large_include_file/large_include_file.stderr
@@ -1,26 +1,26 @@
 error: attempted to include a large file
-  --> tests/ui-toml/large_include_file/large_include_file.rs:14:43
+  --> tests/ui-toml/large_include_file/large_include_file.rs:19:1
    |
-LL | const TOO_BIG_INCLUDE_BYTES: &[u8; 654] = include_bytes!("too_big.txt");
-   |                                           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+LL | #[doc = include_str!("too_big.txt")]
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |
    = note: the configuration allows a maximum size of 600 bytes
    = note: `-D clippy::large-include-file` implied by `-D warnings`
    = help: to override `-D warnings` add `#[allow(clippy::large_include_file)]`
 
 error: attempted to include a large file
-  --> tests/ui-toml/large_include_file/large_include_file.rs:16:35
+  --> tests/ui-toml/large_include_file/large_include_file.rs:14:43
    |
-LL | const TOO_BIG_INCLUDE_STR: &str = include_str!("too_big.txt");
-   |                                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^
+LL | const TOO_BIG_INCLUDE_BYTES: &[u8; 654] = include_bytes!("too_big.txt");
+   |                                           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |
    = note: the configuration allows a maximum size of 600 bytes
 
 error: attempted to include a large file
-  --> tests/ui-toml/large_include_file/large_include_file.rs:19:1
+  --> tests/ui-toml/large_include_file/large_include_file.rs:16:35
    |
-LL | #[doc = include_str!("too_big.txt")]
-   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+LL | const TOO_BIG_INCLUDE_STR: &str = include_str!("too_big.txt");
+   |                                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |
    = note: the configuration allows a maximum size of 600 bytes
 
diff --git a/src/tools/clippy/tests/ui-toml/renamed_function_params/renamed_function_params.default.stderr b/src/tools/clippy/tests/ui-toml/renamed_function_params/renamed_function_params.default.stderr
index 2d700f60759..de9f17520ff 100644
--- a/src/tools/clippy/tests/ui-toml/renamed_function_params/renamed_function_params.default.stderr
+++ b/src/tools/clippy/tests/ui-toml/renamed_function_params/renamed_function_params.default.stderr
@@ -33,8 +33,9 @@ LL |     fn hash_slice<H: Hasher>(date: &[Self], states: &mut H) {
    |
 help: consider using the default names
    |
-LL |     fn hash_slice<H: Hasher>(data: &[Self], state: &mut H) {
-   |                              ~~~~           ~~~~~
+LL -     fn hash_slice<H: Hasher>(date: &[Self], states: &mut H) {
+LL +     fn hash_slice<H: Hasher>(data: &[Self], state: &mut H) {
+   |
 
 error: renamed function parameter of trait impl
   --> tests/ui-toml/renamed_function_params/renamed_function_params.rs:80:18
diff --git a/src/tools/clippy/tests/ui-toml/renamed_function_params/renamed_function_params.extend.stderr b/src/tools/clippy/tests/ui-toml/renamed_function_params/renamed_function_params.extend.stderr
index e57554fa613..bdc4eeaad80 100644
--- a/src/tools/clippy/tests/ui-toml/renamed_function_params/renamed_function_params.extend.stderr
+++ b/src/tools/clippy/tests/ui-toml/renamed_function_params/renamed_function_params.extend.stderr
@@ -27,8 +27,9 @@ LL |     fn hash_slice<H: Hasher>(date: &[Self], states: &mut H) {
    |
 help: consider using the default names
    |
-LL |     fn hash_slice<H: Hasher>(data: &[Self], state: &mut H) {
-   |                              ~~~~           ~~~~~
+LL -     fn hash_slice<H: Hasher>(date: &[Self], states: &mut H) {
+LL +     fn hash_slice<H: Hasher>(data: &[Self], state: &mut H) {
+   |
 
 error: aborting due to 4 previous errors
 
diff --git a/src/tools/clippy/tests/ui-toml/unwrap_used/unwrap_used.stderr b/src/tools/clippy/tests/ui-toml/unwrap_used/unwrap_used.stderr
index b58ce9b8af3..2aff276a4a1 100644
--- a/src/tools/clippy/tests/ui-toml/unwrap_used/unwrap_used.stderr
+++ b/src/tools/clippy/tests/ui-toml/unwrap_used/unwrap_used.stderr
@@ -8,8 +8,9 @@ LL |         let _ = boxed_slice.get(1).unwrap();
    = help: to override `-D warnings` add `#[allow(clippy::get_unwrap)]`
 help: using `[]` is clearer and more concise
    |
-LL |         let _ = &boxed_slice[1];
-   |                 ~~~~~~~~~~~~~~~
+LL -         let _ = boxed_slice.get(1).unwrap();
+LL +         let _ = &boxed_slice[1];
+   |
 
 error: used `unwrap()` on an `Option` value
   --> tests/ui-toml/unwrap_used/unwrap_used.rs:38:17
@@ -30,8 +31,9 @@ LL |         let _ = some_slice.get(0).unwrap();
    |
 help: using `[]` is clearer and more concise
    |
-LL |         let _ = &some_slice[0];
-   |                 ~~~~~~~~~~~~~~
+LL -         let _ = some_slice.get(0).unwrap();
+LL +         let _ = &some_slice[0];
+   |
 
 error: used `unwrap()` on an `Option` value
   --> tests/ui-toml/unwrap_used/unwrap_used.rs:39:17
@@ -50,8 +52,9 @@ LL |         let _ = some_vec.get(0).unwrap();
    |
 help: using `[]` is clearer and more concise
    |
-LL |         let _ = &some_vec[0];
-   |                 ~~~~~~~~~~~~
+LL -         let _ = some_vec.get(0).unwrap();
+LL +         let _ = &some_vec[0];
+   |
 
 error: used `unwrap()` on an `Option` value
   --> tests/ui-toml/unwrap_used/unwrap_used.rs:40:17
@@ -70,8 +73,9 @@ LL |         let _ = some_vecdeque.get(0).unwrap();
    |
 help: using `[]` is clearer and more concise
    |
-LL |         let _ = &some_vecdeque[0];
-   |                 ~~~~~~~~~~~~~~~~~
+LL -         let _ = some_vecdeque.get(0).unwrap();
+LL +         let _ = &some_vecdeque[0];
+   |
 
 error: used `unwrap()` on an `Option` value
   --> tests/ui-toml/unwrap_used/unwrap_used.rs:41:17
@@ -90,8 +94,9 @@ LL |         let _ = some_hashmap.get(&1).unwrap();
    |
 help: using `[]` is clearer and more concise
    |
-LL |         let _ = &some_hashmap[&1];
-   |                 ~~~~~~~~~~~~~~~~~
+LL -         let _ = some_hashmap.get(&1).unwrap();
+LL +         let _ = &some_hashmap[&1];
+   |
 
 error: used `unwrap()` on an `Option` value
   --> tests/ui-toml/unwrap_used/unwrap_used.rs:42:17
@@ -110,8 +115,9 @@ LL |         let _ = some_btreemap.get(&1).unwrap();
    |
 help: using `[]` is clearer and more concise
    |
-LL |         let _ = &some_btreemap[&1];
-   |                 ~~~~~~~~~~~~~~~~~~
+LL -         let _ = some_btreemap.get(&1).unwrap();
+LL +         let _ = &some_btreemap[&1];
+   |
 
 error: used `unwrap()` on an `Option` value
   --> tests/ui-toml/unwrap_used/unwrap_used.rs:43:17
@@ -130,8 +136,9 @@ LL |         let _: u8 = *boxed_slice.get(1).unwrap();
    |
 help: using `[]` is clearer and more concise
    |
-LL |         let _: u8 = boxed_slice[1];
-   |                     ~~~~~~~~~~~~~~
+LL -         let _: u8 = *boxed_slice.get(1).unwrap();
+LL +         let _: u8 = boxed_slice[1];
+   |
 
 error: used `unwrap()` on an `Option` value
   --> tests/ui-toml/unwrap_used/unwrap_used.rs:47:22
@@ -150,8 +157,9 @@ LL |         *boxed_slice.get_mut(0).unwrap() = 1;
    |
 help: using `[]` is clearer and more concise
    |
-LL |         boxed_slice[0] = 1;
-   |         ~~~~~~~~~~~~~~
+LL -         *boxed_slice.get_mut(0).unwrap() = 1;
+LL +         boxed_slice[0] = 1;
+   |
 
 error: used `unwrap()` on an `Option` value
   --> tests/ui-toml/unwrap_used/unwrap_used.rs:52:10
@@ -170,8 +178,9 @@ LL |         *some_slice.get_mut(0).unwrap() = 1;
    |
 help: using `[]` is clearer and more concise
    |
-LL |         some_slice[0] = 1;
-   |         ~~~~~~~~~~~~~
+LL -         *some_slice.get_mut(0).unwrap() = 1;
+LL +         some_slice[0] = 1;
+   |
 
 error: used `unwrap()` on an `Option` value
   --> tests/ui-toml/unwrap_used/unwrap_used.rs:53:10
@@ -190,8 +199,9 @@ LL |         *some_vec.get_mut(0).unwrap() = 1;
    |
 help: using `[]` is clearer and more concise
    |
-LL |         some_vec[0] = 1;
-   |         ~~~~~~~~~~~
+LL -         *some_vec.get_mut(0).unwrap() = 1;
+LL +         some_vec[0] = 1;
+   |
 
 error: used `unwrap()` on an `Option` value
   --> tests/ui-toml/unwrap_used/unwrap_used.rs:54:10
@@ -210,8 +220,9 @@ LL |         *some_vecdeque.get_mut(0).unwrap() = 1;
    |
 help: using `[]` is clearer and more concise
    |
-LL |         some_vecdeque[0] = 1;
-   |         ~~~~~~~~~~~~~~~~
+LL -         *some_vecdeque.get_mut(0).unwrap() = 1;
+LL +         some_vecdeque[0] = 1;
+   |
 
 error: used `unwrap()` on an `Option` value
   --> tests/ui-toml/unwrap_used/unwrap_used.rs:55:10
@@ -230,8 +241,9 @@ LL |         let _ = some_vec.get(0..1).unwrap().to_vec();
    |
 help: using `[]` is clearer and more concise
    |
-LL |         let _ = some_vec[0..1].to_vec();
-   |                 ~~~~~~~~~~~~~~
+LL -         let _ = some_vec.get(0..1).unwrap().to_vec();
+LL +         let _ = some_vec[0..1].to_vec();
+   |
 
 error: used `unwrap()` on an `Option` value
   --> tests/ui-toml/unwrap_used/unwrap_used.rs:67:17
@@ -250,8 +262,9 @@ LL |         let _ = some_vec.get_mut(0..1).unwrap().to_vec();
    |
 help: using `[]` is clearer and more concise
    |
-LL |         let _ = some_vec[0..1].to_vec();
-   |                 ~~~~~~~~~~~~~~
+LL -         let _ = some_vec.get_mut(0..1).unwrap().to_vec();
+LL +         let _ = some_vec[0..1].to_vec();
+   |
 
 error: used `unwrap()` on an `Option` value
   --> tests/ui-toml/unwrap_used/unwrap_used.rs:68:17
@@ -270,8 +283,9 @@ LL |     let _ = boxed_slice.get(1).unwrap();
    |
 help: using `[]` is clearer and more concise
    |
-LL |     let _ = &boxed_slice[1];
-   |             ~~~~~~~~~~~~~~~
+LL -     let _ = boxed_slice.get(1).unwrap();
+LL +     let _ = &boxed_slice[1];
+   |
 
 error: called `.get().unwrap()` on a slice
   --> tests/ui-toml/unwrap_used/unwrap_used.rs:94:17
@@ -281,8 +295,9 @@ LL |         let _ = Box::new([0]).get(1).unwrap();
    |
 help: using `[]` is clearer and more concise
    |
-LL |         let _ = &Box::new([0])[1];
-   |                 ~~~~~~~~~~~~~~~~~
+LL -         let _ = Box::new([0]).get(1).unwrap();
+LL +         let _ = &Box::new([0])[1];
+   |
 
 error: aborting due to 28 previous errors
 
diff --git a/src/tools/clippy/tests/ui/assign_ops2.stderr b/src/tools/clippy/tests/ui/assign_ops2.stderr
index ddeba2b2ff8..09b101b216a 100644
--- a/src/tools/clippy/tests/ui/assign_ops2.stderr
+++ b/src/tools/clippy/tests/ui/assign_ops2.stderr
@@ -8,12 +8,14 @@ LL |     a += a + 1;
    = help: to override `-D warnings` add `#[allow(clippy::misrefactored_assign_op)]`
 help: did you mean `a = a + 1` or `a = a + a + 1`? Consider replacing it with
    |
-LL |     a += 1;
-   |     ~~~~~~
+LL -     a += a + 1;
+LL +     a += 1;
+   |
 help: or
    |
-LL |     a = a + a + 1;
-   |     ~~~~~~~~~~~~~
+LL -     a += a + 1;
+LL +     a = a + a + 1;
+   |
 
 error: variable appears on both sides of an assignment operation
   --> tests/ui/assign_ops2.rs:11:5
@@ -23,12 +25,14 @@ LL |     a += 1 + a;
    |
 help: did you mean `a = a + 1` or `a = a + 1 + a`? Consider replacing it with
    |
-LL |     a += 1;
-   |     ~~~~~~
+LL -     a += 1 + a;
+LL +     a += 1;
+   |
 help: or
    |
-LL |     a = a + 1 + a;
-   |     ~~~~~~~~~~~~~
+LL -     a += 1 + a;
+LL +     a = a + 1 + a;
+   |
 
 error: variable appears on both sides of an assignment operation
   --> tests/ui/assign_ops2.rs:13:5
@@ -38,12 +42,14 @@ LL |     a -= a - 1;
    |
 help: did you mean `a = a - 1` or `a = a - (a - 1)`? Consider replacing it with
    |
-LL |     a -= 1;
-   |     ~~~~~~
+LL -     a -= a - 1;
+LL +     a -= 1;
+   |
 help: or
    |
-LL |     a = a - (a - 1);
-   |     ~~~~~~~~~~~~~~~
+LL -     a -= a - 1;
+LL +     a = a - (a - 1);
+   |
 
 error: variable appears on both sides of an assignment operation
   --> tests/ui/assign_ops2.rs:15:5
@@ -53,12 +59,14 @@ LL |     a *= a * 99;
    |
 help: did you mean `a = a * 99` or `a = a * a * 99`? Consider replacing it with
    |
-LL |     a *= 99;
-   |     ~~~~~~~
+LL -     a *= a * 99;
+LL +     a *= 99;
+   |
 help: or
    |
-LL |     a = a * a * 99;
-   |     ~~~~~~~~~~~~~~
+LL -     a *= a * 99;
+LL +     a = a * a * 99;
+   |
 
 error: variable appears on both sides of an assignment operation
   --> tests/ui/assign_ops2.rs:17:5
@@ -68,12 +76,14 @@ LL |     a *= 42 * a;
    |
 help: did you mean `a = a * 42` or `a = a * 42 * a`? Consider replacing it with
    |
-LL |     a *= 42;
-   |     ~~~~~~~
+LL -     a *= 42 * a;
+LL +     a *= 42;
+   |
 help: or
    |
-LL |     a = a * 42 * a;
-   |     ~~~~~~~~~~~~~~
+LL -     a *= 42 * a;
+LL +     a = a * 42 * a;
+   |
 
 error: variable appears on both sides of an assignment operation
   --> tests/ui/assign_ops2.rs:19:5
@@ -83,12 +93,14 @@ LL |     a /= a / 2;
    |
 help: did you mean `a = a / 2` or `a = a / (a / 2)`? Consider replacing it with
    |
-LL |     a /= 2;
-   |     ~~~~~~
+LL -     a /= a / 2;
+LL +     a /= 2;
+   |
 help: or
    |
-LL |     a = a / (a / 2);
-   |     ~~~~~~~~~~~~~~~
+LL -     a /= a / 2;
+LL +     a = a / (a / 2);
+   |
 
 error: variable appears on both sides of an assignment operation
   --> tests/ui/assign_ops2.rs:21:5
@@ -98,12 +110,14 @@ LL |     a %= a % 5;
    |
 help: did you mean `a = a % 5` or `a = a % (a % 5)`? Consider replacing it with
    |
-LL |     a %= 5;
-   |     ~~~~~~
+LL -     a %= a % 5;
+LL +     a %= 5;
+   |
 help: or
    |
-LL |     a = a % (a % 5);
-   |     ~~~~~~~~~~~~~~~
+LL -     a %= a % 5;
+LL +     a = a % (a % 5);
+   |
 
 error: variable appears on both sides of an assignment operation
   --> tests/ui/assign_ops2.rs:23:5
@@ -113,12 +127,14 @@ LL |     a &= a & 1;
    |
 help: did you mean `a = a & 1` or `a = a & a & 1`? Consider replacing it with
    |
-LL |     a &= 1;
-   |     ~~~~~~
+LL -     a &= a & 1;
+LL +     a &= 1;
+   |
 help: or
    |
-LL |     a = a & a & 1;
-   |     ~~~~~~~~~~~~~
+LL -     a &= a & 1;
+LL +     a = a & a & 1;
+   |
 
 error: variable appears on both sides of an assignment operation
   --> tests/ui/assign_ops2.rs:25:5
@@ -128,12 +144,14 @@ LL |     a *= a * a;
    |
 help: did you mean `a = a * a` or `a = a * a * a`? Consider replacing it with
    |
-LL |     a *= a;
-   |     ~~~~~~
+LL -     a *= a * a;
+LL +     a *= a;
+   |
 help: or
    |
-LL |     a = a * a * a;
-   |     ~~~~~~~~~~~~~
+LL -     a *= a * a;
+LL +     a = a * a * a;
+   |
 
 error: manual implementation of an assign operation
   --> tests/ui/assign_ops2.rs:63:5
diff --git a/src/tools/clippy/tests/ui/async_yields_async.stderr b/src/tools/clippy/tests/ui/async_yields_async.stderr
index 474914299d0..8c023d0d61f 100644
--- a/src/tools/clippy/tests/ui/async_yields_async.stderr
+++ b/src/tools/clippy/tests/ui/async_yields_async.stderr
@@ -14,9 +14,9 @@ LL | |      };
    = help: to override `-D warnings` add `#[allow(clippy::async_yields_async)]`
 help: consider awaiting this value
    |
-LL ~         async {
-LL +             3
-LL +         }.await
+LL |         async {
+LL |             3
+LL ~         }.await
    |
 
 error: an async construct yields a type which is itself awaitable
@@ -46,9 +46,9 @@ LL | |      };
    |
 help: consider awaiting this value
    |
-LL ~         async {
-LL +             3
-LL +         }.await
+LL |         async {
+LL |             3
+LL ~         }.await
    |
 
 error: an async construct yields a type which is itself awaitable
diff --git a/src/tools/clippy/tests/ui/author/blocks.stdout b/src/tools/clippy/tests/ui/author/blocks.stdout
index 6bf48d5ba4e..54325f9776c 100644
--- a/src/tools/clippy/tests/ui/author/blocks.stdout
+++ b/src/tools/clippy/tests/ui/author/blocks.stdout
@@ -42,10 +42,10 @@ if let ExprKind::Block(block, None) = expr.kind
 }
 if let ExprKind::Closure { capture_clause: CaptureBy::Value { .. }, fn_decl: fn_decl, body: body_id, closure_kind: ClosureKind::CoroutineClosure(CoroutineDesugaring::Async), .. } = expr.kind
     && let FnRetTy::DefaultReturn(_) = fn_decl.output
-    && expr1 = &cx.tcx.hir().body(body_id).value
+    && expr1 = &cx.tcx.hir_body(body_id).value
     && let ExprKind::Closure { capture_clause: CaptureBy::Ref, fn_decl: fn_decl1, body: body_id1, closure_kind: ClosureKind::Coroutine(CoroutineKind::Desugared(CoroutineDesugaring::Async, CoroutineSource::Closure)), .. } = expr1.kind
     && let FnRetTy::DefaultReturn(_) = fn_decl1.output
-    && expr2 = &cx.tcx.hir().body(body_id1).value
+    && expr2 = &cx.tcx.hir_body(body_id1).value
     && let ExprKind::Block(block, None) = expr2.kind
     && block.stmts.is_empty()
     && let Some(trailing_expr) = block.expr
diff --git a/src/tools/clippy/tests/ui/author/macro_in_closure.stdout b/src/tools/clippy/tests/ui/author/macro_in_closure.stdout
index 66caf382d89..3186d0cbc27 100644
--- a/src/tools/clippy/tests/ui/author/macro_in_closure.stdout
+++ b/src/tools/clippy/tests/ui/author/macro_in_closure.stdout
@@ -2,7 +2,7 @@ if let StmtKind::Let(local) = stmt.kind
     && let Some(init) = local.init
     && let ExprKind::Closure { capture_clause: CaptureBy::Ref, fn_decl: fn_decl, body: body_id, closure_kind: ClosureKind::Closure, .. } = init.kind
     && let FnRetTy::DefaultReturn(_) = fn_decl.output
-    && expr = &cx.tcx.hir().body(body_id).value
+    && expr = &cx.tcx.hir_body(body_id).value
     && let ExprKind::Block(block, None) = expr.kind
     && block.stmts.len() == 1
     && let StmtKind::Semi(e) = block.stmts[0].kind
diff --git a/src/tools/clippy/tests/ui/author/repeat.stdout b/src/tools/clippy/tests/ui/author/repeat.stdout
index 1a608734ada..f2c6b3f807f 100644
--- a/src/tools/clippy/tests/ui/author/repeat.stdout
+++ b/src/tools/clippy/tests/ui/author/repeat.stdout
@@ -2,7 +2,7 @@ if let ExprKind::Repeat(value, length) = expr.kind
     && let ExprKind::Lit(ref lit) = value.kind
     && let LitKind::Int(1, LitIntType::Unsigned(UintTy::U8)) = lit.node
     && let ConstArgKind::Anon(anon_const) = length.kind
-    && expr1 = &cx.tcx.hir().body(anon_const.body).value
+    && expr1 = &cx.tcx.hir_body(anon_const.body).value
     && let ExprKind::Lit(ref lit1) = expr1.kind
     && let LitKind::Int(5, LitIntType::Unsuffixed) = lit1.node
 {
diff --git a/src/tools/clippy/tests/ui/bind_instead_of_map_multipart.stderr b/src/tools/clippy/tests/ui/bind_instead_of_map_multipart.stderr
index d271381adea..7c5882d4296 100644
--- a/src/tools/clippy/tests/ui/bind_instead_of_map_multipart.stderr
+++ b/src/tools/clippy/tests/ui/bind_instead_of_map_multipart.stderr
@@ -11,8 +11,9 @@ LL | #![deny(clippy::bind_instead_of_map)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^
 help: use `map` instead
    |
-LL |     let _ = Some("42").map(|s| if s.len() < 42 { 0 } else { s.len() });
-   |                        ~~~                       ~          ~~~~~~~
+LL -     let _ = Some("42").and_then(|s| if s.len() < 42 { Some(0) } else { Some(s.len()) });
+LL +     let _ = Some("42").map(|s| if s.len() < 42 { 0 } else { s.len() });
+   |
 
 error: using `Result.and_then(|x| Ok(y))`, which is more succinctly expressed as `map(|x| y)`
   --> tests/ui/bind_instead_of_map_multipart.rs:8:13
@@ -22,8 +23,9 @@ LL |     let _ = Ok::<_, ()>("42").and_then(|s| if s.len() < 42 { Ok(0) } else {
    |
 help: use `map` instead
    |
-LL |     let _ = Ok::<_, ()>("42").map(|s| if s.len() < 42 { 0 } else { s.len() });
-   |                               ~~~                       ~          ~~~~~~~
+LL -     let _ = Ok::<_, ()>("42").and_then(|s| if s.len() < 42 { Ok(0) } else { Ok(s.len()) });
+LL +     let _ = Ok::<_, ()>("42").map(|s| if s.len() < 42 { 0 } else { s.len() });
+   |
 
 error: using `Result.or_else(|x| Err(y))`, which is more succinctly expressed as `map_err(|x| y)`
   --> tests/ui/bind_instead_of_map_multipart.rs:11:13
@@ -33,8 +35,9 @@ LL |     let _ = Err::<(), _>("42").or_else(|s| if s.len() < 42 { Err(s.len() +
    |
 help: use `map_err` instead
    |
-LL |     let _ = Err::<(), _>("42").map_err(|s| if s.len() < 42 { s.len() + 20 } else { s.len() });
-   |                                ~~~~~~~                       ~~~~~~~~~~~~          ~~~~~~~
+LL -     let _ = Err::<(), _>("42").or_else(|s| if s.len() < 42 { Err(s.len() + 20) } else { Err(s.len()) });
+LL +     let _ = Err::<(), _>("42").map_err(|s| if s.len() < 42 { s.len() + 20 } else { s.len() });
+   |
 
 error: using `Option.and_then(|x| Some(y))`, which is more succinctly expressed as `map(|x| y)`
   --> tests/ui/bind_instead_of_map_multipart.rs:19:5
@@ -83,8 +86,9 @@ LL |     let _ = Some("").and_then(|s| if s.len() == 20 { Some(m!()) } else { So
    |
 help: use `map` instead
    |
-LL |     let _ = Some("").map(|s| if s.len() == 20 { m!() } else { Some(20) });
-   |                      ~~~                        ~~~~          ~~~~~~~~
+LL -     let _ = Some("").and_then(|s| if s.len() == 20 { Some(m!()) } else { Some(Some(20)) });
+LL +     let _ = Some("").map(|s| if s.len() == 20 { m!() } else { Some(20) });
+   |
 
 error: aborting due to 5 previous errors
 
diff --git a/src/tools/clippy/tests/ui/borrow_deref_ref_unfixable.stderr b/src/tools/clippy/tests/ui/borrow_deref_ref_unfixable.stderr
index 7d3a5c84a82..b8a0eedeb9e 100644
--- a/src/tools/clippy/tests/ui/borrow_deref_ref_unfixable.stderr
+++ b/src/tools/clippy/tests/ui/borrow_deref_ref_unfixable.stderr
@@ -8,12 +8,13 @@ LL |         let x: &str = &*s;
    = help: to override `-D warnings` add `#[allow(clippy::borrow_deref_ref)]`
 help: if you would like to reborrow, try removing `&*`
    |
-LL |         let x: &str = s;
-   |                       ~
+LL -         let x: &str = &*s;
+LL +         let x: &str = s;
+   |
 help: if you would like to deref, try using `&**`
    |
 LL |         let x: &str = &**s;
-   |                       ~~~~
+   |                         +
 
 error: aborting due to 1 previous error
 
diff --git a/src/tools/clippy/tests/ui/cast.stderr b/src/tools/clippy/tests/ui/cast.stderr
index 452482fc88e..901447c738e 100644
--- a/src/tools/clippy/tests/ui/cast.stderr
+++ b/src/tools/clippy/tests/ui/cast.stderr
@@ -81,8 +81,9 @@ LL |     1i32 as i8;
    = help: if this is intentional allow the lint with `#[allow(clippy::cast_possible_truncation)]` ...
 help: ... or use `try_from` and handle the error accordingly
    |
-LL |     i8::try_from(1i32);
-   |     ~~~~~~~~~~~~~~~~~~
+LL -     1i32 as i8;
+LL +     i8::try_from(1i32);
+   |
 
 error: casting `i32` to `u8` may truncate the value
   --> tests/ui/cast.rs:52:5
@@ -93,8 +94,9 @@ LL |     1i32 as u8;
    = help: if this is intentional allow the lint with `#[allow(clippy::cast_possible_truncation)]` ...
 help: ... or use `try_from` and handle the error accordingly
    |
-LL |     u8::try_from(1i32);
-   |     ~~~~~~~~~~~~~~~~~~
+LL -     1i32 as u8;
+LL +     u8::try_from(1i32);
+   |
 
 error: casting `f64` to `isize` may truncate the value
   --> tests/ui/cast.rs:54:5
@@ -127,8 +129,9 @@ LL |     1f32 as u32 as u16;
    = help: if this is intentional allow the lint with `#[allow(clippy::cast_possible_truncation)]` ...
 help: ... or use `try_from` and handle the error accordingly
    |
-LL |     u16::try_from(1f32 as u32);
-   |     ~~~~~~~~~~~~~~~~~~~~~~~~~~
+LL -     1f32 as u32 as u16;
+LL +     u16::try_from(1f32 as u32);
+   |
 
 error: casting `f32` to `u32` may truncate the value
   --> tests/ui/cast.rs:59:5
@@ -153,8 +156,9 @@ LL |         let _x: i8 = 1i32 as _;
    = help: if this is intentional allow the lint with `#[allow(clippy::cast_possible_truncation)]` ...
 help: ... or use `try_from` and handle the error accordingly
    |
-LL |         let _x: i8 = 1i32.try_into();
-   |                      ~~~~~~~~~~~~~~~
+LL -         let _x: i8 = 1i32 as _;
+LL +         let _x: i8 = 1i32.try_into();
+   |
 
 error: casting `f32` to `i32` may truncate the value
   --> tests/ui/cast.rs:66:9
@@ -228,8 +232,9 @@ LL |     1usize as i8;
    = help: if this is intentional allow the lint with `#[allow(clippy::cast_possible_truncation)]` ...
 help: ... or use `try_from` and handle the error accordingly
    |
-LL |     i8::try_from(1usize);
-   |     ~~~~~~~~~~~~~~~~~~~~
+LL -     1usize as i8;
+LL +     i8::try_from(1usize);
+   |
 
 error: casting `usize` to `i16` may truncate the value
   --> tests/ui/cast.rs:90:5
@@ -240,8 +245,9 @@ LL |     1usize as i16;
    = help: if this is intentional allow the lint with `#[allow(clippy::cast_possible_truncation)]` ...
 help: ... or use `try_from` and handle the error accordingly
    |
-LL |     i16::try_from(1usize);
-   |     ~~~~~~~~~~~~~~~~~~~~~
+LL -     1usize as i16;
+LL +     i16::try_from(1usize);
+   |
 
 error: casting `usize` to `i16` may wrap around the value on targets with 16-bit wide pointers
   --> tests/ui/cast.rs:90:5
@@ -261,8 +267,9 @@ LL |     1usize as i32;
    = help: if this is intentional allow the lint with `#[allow(clippy::cast_possible_truncation)]` ...
 help: ... or use `try_from` and handle the error accordingly
    |
-LL |     i32::try_from(1usize);
-   |     ~~~~~~~~~~~~~~~~~~~~~
+LL -     1usize as i32;
+LL +     i32::try_from(1usize);
+   |
 
 error: casting `usize` to `i32` may wrap around the value on targets with 32-bit wide pointers
   --> tests/ui/cast.rs:95:5
@@ -300,8 +307,9 @@ LL |     1u64 as isize;
    = help: if this is intentional allow the lint with `#[allow(clippy::cast_possible_truncation)]` ...
 help: ... or use `try_from` and handle the error accordingly
    |
-LL |     isize::try_from(1u64);
-   |     ~~~~~~~~~~~~~~~~~~~~~
+LL -     1u64 as isize;
+LL +     isize::try_from(1u64);
+   |
 
 error: casting `u64` to `isize` may wrap around the value on targets with 64-bit wide pointers
   --> tests/ui/cast.rs:111:5
@@ -360,8 +368,9 @@ LL |     (-99999999999i64).min(1) as i8;
    = help: if this is intentional allow the lint with `#[allow(clippy::cast_possible_truncation)]` ...
 help: ... or use `try_from` and handle the error accordingly
    |
-LL |     i8::try_from((-99999999999i64).min(1));
-   |     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+LL -     (-99999999999i64).min(1) as i8;
+LL +     i8::try_from((-99999999999i64).min(1));
+   |
 
 error: casting `u64` to `u8` may truncate the value
   --> tests/ui/cast.rs:222:5
@@ -372,8 +381,9 @@ LL |     999999u64.clamp(0, 256) as u8;
    = help: if this is intentional allow the lint with `#[allow(clippy::cast_possible_truncation)]` ...
 help: ... or use `try_from` and handle the error accordingly
    |
-LL |     u8::try_from(999999u64.clamp(0, 256));
-   |     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+LL -     999999u64.clamp(0, 256) as u8;
+LL +     u8::try_from(999999u64.clamp(0, 256));
+   |
 
 error: casting `main::E2` to `u8` may truncate the value
   --> tests/ui/cast.rs:245:21
@@ -384,8 +394,9 @@ LL |             let _ = self as u8;
    = help: if this is intentional allow the lint with `#[allow(clippy::cast_possible_truncation)]` ...
 help: ... or use `try_from` and handle the error accordingly
    |
-LL |             let _ = u8::try_from(self);
-   |                     ~~~~~~~~~~~~~~~~~~
+LL -             let _ = self as u8;
+LL +             let _ = u8::try_from(self);
+   |
 
 error: casting `main::E2::B` to `u8` will truncate the value
   --> tests/ui/cast.rs:247:21
@@ -405,8 +416,9 @@ LL |             let _ = self as i8;
    = help: if this is intentional allow the lint with `#[allow(clippy::cast_possible_truncation)]` ...
 help: ... or use `try_from` and handle the error accordingly
    |
-LL |             let _ = i8::try_from(self);
-   |                     ~~~~~~~~~~~~~~~~~~
+LL -             let _ = self as i8;
+LL +             let _ = i8::try_from(self);
+   |
 
 error: casting `main::E5::A` to `i8` will truncate the value
   --> tests/ui/cast.rs:291:21
@@ -423,8 +435,9 @@ LL |             let _ = self as i16;
    = help: if this is intentional allow the lint with `#[allow(clippy::cast_possible_truncation)]` ...
 help: ... or use `try_from` and handle the error accordingly
    |
-LL |             let _ = i16::try_from(self);
-   |                     ~~~~~~~~~~~~~~~~~~~
+LL -             let _ = self as i16;
+LL +             let _ = i16::try_from(self);
+   |
 
 error: casting `main::E7` to `usize` may truncate the value on targets with 32-bit wide pointers
   --> tests/ui/cast.rs:327:21
@@ -435,8 +448,9 @@ LL |             let _ = self as usize;
    = help: if this is intentional allow the lint with `#[allow(clippy::cast_possible_truncation)]` ...
 help: ... or use `try_from` and handle the error accordingly
    |
-LL |             let _ = usize::try_from(self);
-   |                     ~~~~~~~~~~~~~~~~~~~~~
+LL -             let _ = self as usize;
+LL +             let _ = usize::try_from(self);
+   |
 
 error: casting `main::E10` to `u16` may truncate the value
   --> tests/ui/cast.rs:374:21
@@ -447,8 +461,9 @@ LL |             let _ = self as u16;
    = help: if this is intentional allow the lint with `#[allow(clippy::cast_possible_truncation)]` ...
 help: ... or use `try_from` and handle the error accordingly
    |
-LL |             let _ = u16::try_from(self);
-   |                     ~~~~~~~~~~~~~~~~~~~
+LL -             let _ = self as u16;
+LL +             let _ = u16::try_from(self);
+   |
 
 error: casting `u32` to `u8` may truncate the value
   --> tests/ui/cast.rs:385:13
@@ -459,8 +474,9 @@ LL |     let c = (q >> 16) as u8;
    = help: if this is intentional allow the lint with `#[allow(clippy::cast_possible_truncation)]` ...
 help: ... or use `try_from` and handle the error accordingly
    |
-LL |     let c = u8::try_from(q >> 16);
-   |             ~~~~~~~~~~~~~~~~~~~~~
+LL -     let c = (q >> 16) as u8;
+LL +     let c = u8::try_from(q >> 16);
+   |
 
 error: casting `u32` to `u8` may truncate the value
   --> tests/ui/cast.rs:389:13
@@ -471,8 +487,9 @@ LL |     let c = (q / 1000) as u8;
    = help: if this is intentional allow the lint with `#[allow(clippy::cast_possible_truncation)]` ...
 help: ... or use `try_from` and handle the error accordingly
    |
-LL |     let c = u8::try_from(q / 1000);
-   |             ~~~~~~~~~~~~~~~~~~~~~~
+LL -     let c = (q / 1000) as u8;
+LL +     let c = u8::try_from(q / 1000);
+   |
 
 error: casting `i32` to `u32` may lose the sign of the value
   --> tests/ui/cast.rs:401:9
@@ -674,8 +691,9 @@ LL |     m!();
    = note: this error originates in the macro `m` (in Nightly builds, run with -Z macro-backtrace for more info)
 help: ... or use `try_from` and handle the error accordingly
    |
-LL |             let _ = u8::try_from(u32::MAX); // cast_possible_truncation
-   |                     ~~~~~~~~~~~~~~~~~~~~~~
+LL -             let _ = u32::MAX as u8; // cast_possible_truncation
+LL +             let _ = u8::try_from(u32::MAX); // cast_possible_truncation
+   |
 
 error: casting `f64` to `f32` may truncate the value
   --> tests/ui/cast.rs:474:21
@@ -698,7 +716,8 @@ LL |     bar.unwrap().unwrap() as usize
    = help: if this is intentional allow the lint with `#[allow(clippy::cast_possible_truncation)]` ...
 help: ... or use `try_from` and handle the error accordingly
    |
-LL |     usize::try_from(bar.unwrap().unwrap())
+LL -     bar.unwrap().unwrap() as usize
+LL +     usize::try_from(bar.unwrap().unwrap())
    |
 
 error: casting `i64` to `usize` may lose the sign of the value
@@ -716,8 +735,9 @@ LL |     (256 & 999999u64) as u8;
    = help: if this is intentional allow the lint with `#[allow(clippy::cast_possible_truncation)]` ...
 help: ... or use `try_from` and handle the error accordingly
    |
-LL |     u8::try_from(256 & 999999u64);
-   |     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+LL -     (256 & 999999u64) as u8;
+LL +     u8::try_from(256 & 999999u64);
+   |
 
 error: casting `u64` to `u8` may truncate the value
   --> tests/ui/cast.rs:500:5
@@ -728,8 +748,9 @@ LL |     (255 % 999999u64) as u8;
    = help: if this is intentional allow the lint with `#[allow(clippy::cast_possible_truncation)]` ...
 help: ... or use `try_from` and handle the error accordingly
    |
-LL |     u8::try_from(255 % 999999u64);
-   |     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+LL -     (255 % 999999u64) as u8;
+LL +     u8::try_from(255 % 999999u64);
+   |
 
 error: aborting due to 92 previous errors
 
diff --git a/src/tools/clippy/tests/ui/cast_lossless_bool.stderr b/src/tools/clippy/tests/ui/cast_lossless_bool.stderr
index 82d6b2e4b8e..68992271762 100644
--- a/src/tools/clippy/tests/ui/cast_lossless_bool.stderr
+++ b/src/tools/clippy/tests/ui/cast_lossless_bool.stderr
@@ -9,8 +9,9 @@ LL |     let _ = true as u8;
    = help: to override `-D warnings` add `#[allow(clippy::cast_lossless)]`
 help: use `u8::from` instead
    |
-LL |     let _ = u8::from(true);
-   |             ~~~~~~~~~~~~~~
+LL -     let _ = true as u8;
+LL +     let _ = u8::from(true);
+   |
 
 error: casts from `bool` to `u16` can be expressed infallibly using `From`
   --> tests/ui/cast_lossless_bool.rs:9:13
@@ -21,8 +22,9 @@ LL |     let _ = true as u16;
    = help: an `as` cast can become silently lossy if the types change in the future
 help: use `u16::from` instead
    |
-LL |     let _ = u16::from(true);
-   |             ~~~~~~~~~~~~~~~
+LL -     let _ = true as u16;
+LL +     let _ = u16::from(true);
+   |
 
 error: casts from `bool` to `u32` can be expressed infallibly using `From`
   --> tests/ui/cast_lossless_bool.rs:10:13
@@ -33,8 +35,9 @@ LL |     let _ = true as u32;
    = help: an `as` cast can become silently lossy if the types change in the future
 help: use `u32::from` instead
    |
-LL |     let _ = u32::from(true);
-   |             ~~~~~~~~~~~~~~~
+LL -     let _ = true as u32;
+LL +     let _ = u32::from(true);
+   |
 
 error: casts from `bool` to `u64` can be expressed infallibly using `From`
   --> tests/ui/cast_lossless_bool.rs:11:13
@@ -45,8 +48,9 @@ LL |     let _ = true as u64;
    = help: an `as` cast can become silently lossy if the types change in the future
 help: use `u64::from` instead
    |
-LL |     let _ = u64::from(true);
-   |             ~~~~~~~~~~~~~~~
+LL -     let _ = true as u64;
+LL +     let _ = u64::from(true);
+   |
 
 error: casts from `bool` to `u128` can be expressed infallibly using `From`
   --> tests/ui/cast_lossless_bool.rs:12:13
@@ -57,8 +61,9 @@ LL |     let _ = true as u128;
    = help: an `as` cast can become silently lossy if the types change in the future
 help: use `u128::from` instead
    |
-LL |     let _ = u128::from(true);
-   |             ~~~~~~~~~~~~~~~~
+LL -     let _ = true as u128;
+LL +     let _ = u128::from(true);
+   |
 
 error: casts from `bool` to `usize` can be expressed infallibly using `From`
   --> tests/ui/cast_lossless_bool.rs:13:13
@@ -69,8 +74,9 @@ LL |     let _ = true as usize;
    = help: an `as` cast can become silently lossy if the types change in the future
 help: use `usize::from` instead
    |
-LL |     let _ = usize::from(true);
-   |             ~~~~~~~~~~~~~~~~~
+LL -     let _ = true as usize;
+LL +     let _ = usize::from(true);
+   |
 
 error: casts from `bool` to `i8` can be expressed infallibly using `From`
   --> tests/ui/cast_lossless_bool.rs:15:13
@@ -81,8 +87,9 @@ LL |     let _ = true as i8;
    = help: an `as` cast can become silently lossy if the types change in the future
 help: use `i8::from` instead
    |
-LL |     let _ = i8::from(true);
-   |             ~~~~~~~~~~~~~~
+LL -     let _ = true as i8;
+LL +     let _ = i8::from(true);
+   |
 
 error: casts from `bool` to `i16` can be expressed infallibly using `From`
   --> tests/ui/cast_lossless_bool.rs:16:13
@@ -93,8 +100,9 @@ LL |     let _ = true as i16;
    = help: an `as` cast can become silently lossy if the types change in the future
 help: use `i16::from` instead
    |
-LL |     let _ = i16::from(true);
-   |             ~~~~~~~~~~~~~~~
+LL -     let _ = true as i16;
+LL +     let _ = i16::from(true);
+   |
 
 error: casts from `bool` to `i32` can be expressed infallibly using `From`
   --> tests/ui/cast_lossless_bool.rs:17:13
@@ -105,8 +113,9 @@ LL |     let _ = true as i32;
    = help: an `as` cast can become silently lossy if the types change in the future
 help: use `i32::from` instead
    |
-LL |     let _ = i32::from(true);
-   |             ~~~~~~~~~~~~~~~
+LL -     let _ = true as i32;
+LL +     let _ = i32::from(true);
+   |
 
 error: casts from `bool` to `i64` can be expressed infallibly using `From`
   --> tests/ui/cast_lossless_bool.rs:18:13
@@ -117,8 +126,9 @@ LL |     let _ = true as i64;
    = help: an `as` cast can become silently lossy if the types change in the future
 help: use `i64::from` instead
    |
-LL |     let _ = i64::from(true);
-   |             ~~~~~~~~~~~~~~~
+LL -     let _ = true as i64;
+LL +     let _ = i64::from(true);
+   |
 
 error: casts from `bool` to `i128` can be expressed infallibly using `From`
   --> tests/ui/cast_lossless_bool.rs:19:13
@@ -129,8 +139,9 @@ LL |     let _ = true as i128;
    = help: an `as` cast can become silently lossy if the types change in the future
 help: use `i128::from` instead
    |
-LL |     let _ = i128::from(true);
-   |             ~~~~~~~~~~~~~~~~
+LL -     let _ = true as i128;
+LL +     let _ = i128::from(true);
+   |
 
 error: casts from `bool` to `isize` can be expressed infallibly using `From`
   --> tests/ui/cast_lossless_bool.rs:20:13
@@ -141,8 +152,9 @@ LL |     let _ = true as isize;
    = help: an `as` cast can become silently lossy if the types change in the future
 help: use `isize::from` instead
    |
-LL |     let _ = isize::from(true);
-   |             ~~~~~~~~~~~~~~~~~
+LL -     let _ = true as isize;
+LL +     let _ = isize::from(true);
+   |
 
 error: casts from `bool` to `u16` can be expressed infallibly using `From`
   --> tests/ui/cast_lossless_bool.rs:23:13
@@ -153,8 +165,9 @@ LL |     let _ = (true | false) as u16;
    = help: an `as` cast can become silently lossy if the types change in the future
 help: use `u16::from` instead
    |
-LL |     let _ = u16::from(true | false);
-   |             ~~~~~~~~~~~~~~~~~~~~~~~
+LL -     let _ = (true | false) as u16;
+LL +     let _ = u16::from(true | false);
+   |
 
 error: casts from `bool` to `u8` can be expressed infallibly using `From`
   --> tests/ui/cast_lossless_bool.rs:25:13
@@ -165,8 +178,9 @@ LL |     let _ = true as U8;
    = help: an `as` cast can become silently lossy if the types change in the future
 help: use `U8::from` instead
    |
-LL |     let _ = U8::from(true);
-   |             ~~~~~~~~~~~~~~
+LL -     let _ = true as U8;
+LL +     let _ = U8::from(true);
+   |
 
 error: casts from `bool` to `u8` can be expressed infallibly using `From`
   --> tests/ui/cast_lossless_bool.rs:53:13
@@ -177,8 +191,9 @@ LL |     let _ = true as u8;
    = help: an `as` cast can become silently lossy if the types change in the future
 help: use `u8::from` instead
    |
-LL |     let _ = u8::from(true);
-   |             ~~~~~~~~~~~~~~
+LL -     let _ = true as u8;
+LL +     let _ = u8::from(true);
+   |
 
 error: aborting due to 15 previous errors
 
diff --git a/src/tools/clippy/tests/ui/cast_lossless_float.stderr b/src/tools/clippy/tests/ui/cast_lossless_float.stderr
index b36f8bcecf5..3f405e3f402 100644
--- a/src/tools/clippy/tests/ui/cast_lossless_float.stderr
+++ b/src/tools/clippy/tests/ui/cast_lossless_float.stderr
@@ -9,8 +9,9 @@ LL |     let _ = x0 as f32;
    = help: to override `-D warnings` add `#[allow(clippy::cast_lossless)]`
 help: use `f32::from` instead
    |
-LL |     let _ = f32::from(x0);
-   |             ~~~~~~~~~~~~~
+LL -     let _ = x0 as f32;
+LL +     let _ = f32::from(x0);
+   |
 
 error: casts from `i8` to `f64` can be expressed infallibly using `From`
   --> tests/ui/cast_lossless_float.rs:13:13
@@ -21,8 +22,9 @@ LL |     let _ = x0 as f64;
    = help: an `as` cast can become silently lossy if the types change in the future
 help: use `f64::from` instead
    |
-LL |     let _ = f64::from(x0);
-   |             ~~~~~~~~~~~~~
+LL -     let _ = x0 as f64;
+LL +     let _ = f64::from(x0);
+   |
 
 error: casts from `i8` to `f32` can be expressed infallibly using `From`
   --> tests/ui/cast_lossless_float.rs:14:13
@@ -33,8 +35,9 @@ LL |     let _ = x0 as F32;
    = help: an `as` cast can become silently lossy if the types change in the future
 help: use `F32::from` instead
    |
-LL |     let _ = F32::from(x0);
-   |             ~~~~~~~~~~~~~
+LL -     let _ = x0 as F32;
+LL +     let _ = F32::from(x0);
+   |
 
 error: casts from `i8` to `f64` can be expressed infallibly using `From`
   --> tests/ui/cast_lossless_float.rs:15:13
@@ -45,8 +48,9 @@ LL |     let _ = x0 as F64;
    = help: an `as` cast can become silently lossy if the types change in the future
 help: use `F64::from` instead
    |
-LL |     let _ = F64::from(x0);
-   |             ~~~~~~~~~~~~~
+LL -     let _ = x0 as F64;
+LL +     let _ = F64::from(x0);
+   |
 
 error: casts from `u8` to `f32` can be expressed infallibly using `From`
   --> tests/ui/cast_lossless_float.rs:17:13
@@ -57,8 +61,9 @@ LL |     let _ = x1 as f32;
    = help: an `as` cast can become silently lossy if the types change in the future
 help: use `f32::from` instead
    |
-LL |     let _ = f32::from(x1);
-   |             ~~~~~~~~~~~~~
+LL -     let _ = x1 as f32;
+LL +     let _ = f32::from(x1);
+   |
 
 error: casts from `u8` to `f64` can be expressed infallibly using `From`
   --> tests/ui/cast_lossless_float.rs:18:13
@@ -69,8 +74,9 @@ LL |     let _ = x1 as f64;
    = help: an `as` cast can become silently lossy if the types change in the future
 help: use `f64::from` instead
    |
-LL |     let _ = f64::from(x1);
-   |             ~~~~~~~~~~~~~
+LL -     let _ = x1 as f64;
+LL +     let _ = f64::from(x1);
+   |
 
 error: casts from `i16` to `f32` can be expressed infallibly using `From`
   --> tests/ui/cast_lossless_float.rs:20:13
@@ -81,8 +87,9 @@ LL |     let _ = x2 as f32;
    = help: an `as` cast can become silently lossy if the types change in the future
 help: use `f32::from` instead
    |
-LL |     let _ = f32::from(x2);
-   |             ~~~~~~~~~~~~~
+LL -     let _ = x2 as f32;
+LL +     let _ = f32::from(x2);
+   |
 
 error: casts from `i16` to `f64` can be expressed infallibly using `From`
   --> tests/ui/cast_lossless_float.rs:21:13
@@ -93,8 +100,9 @@ LL |     let _ = x2 as f64;
    = help: an `as` cast can become silently lossy if the types change in the future
 help: use `f64::from` instead
    |
-LL |     let _ = f64::from(x2);
-   |             ~~~~~~~~~~~~~
+LL -     let _ = x2 as f64;
+LL +     let _ = f64::from(x2);
+   |
 
 error: casts from `u16` to `f32` can be expressed infallibly using `From`
   --> tests/ui/cast_lossless_float.rs:23:13
@@ -105,8 +113,9 @@ LL |     let _ = x3 as f32;
    = help: an `as` cast can become silently lossy if the types change in the future
 help: use `f32::from` instead
    |
-LL |     let _ = f32::from(x3);
-   |             ~~~~~~~~~~~~~
+LL -     let _ = x3 as f32;
+LL +     let _ = f32::from(x3);
+   |
 
 error: casts from `u16` to `f64` can be expressed infallibly using `From`
   --> tests/ui/cast_lossless_float.rs:24:13
@@ -117,8 +126,9 @@ LL |     let _ = x3 as f64;
    = help: an `as` cast can become silently lossy if the types change in the future
 help: use `f64::from` instead
    |
-LL |     let _ = f64::from(x3);
-   |             ~~~~~~~~~~~~~
+LL -     let _ = x3 as f64;
+LL +     let _ = f64::from(x3);
+   |
 
 error: casts from `i32` to `f64` can be expressed infallibly using `From`
   --> tests/ui/cast_lossless_float.rs:26:13
@@ -129,8 +139,9 @@ LL |     let _ = x4 as f64;
    = help: an `as` cast can become silently lossy if the types change in the future
 help: use `f64::from` instead
    |
-LL |     let _ = f64::from(x4);
-   |             ~~~~~~~~~~~~~
+LL -     let _ = x4 as f64;
+LL +     let _ = f64::from(x4);
+   |
 
 error: casts from `u32` to `f64` can be expressed infallibly using `From`
   --> tests/ui/cast_lossless_float.rs:28:13
@@ -141,8 +152,9 @@ LL |     let _ = x5 as f64;
    = help: an `as` cast can become silently lossy if the types change in the future
 help: use `f64::from` instead
    |
-LL |     let _ = f64::from(x5);
-   |             ~~~~~~~~~~~~~
+LL -     let _ = x5 as f64;
+LL +     let _ = f64::from(x5);
+   |
 
 error: casts from `f32` to `f64` can be expressed infallibly using `From`
   --> tests/ui/cast_lossless_float.rs:31:13
@@ -153,8 +165,9 @@ LL |     let _ = 1.0f32 as f64;
    = help: an `as` cast can become silently lossy if the types change in the future
 help: use `f64::from` instead
    |
-LL |     let _ = f64::from(1.0f32);
-   |             ~~~~~~~~~~~~~~~~~
+LL -     let _ = 1.0f32 as f64;
+LL +     let _ = f64::from(1.0f32);
+   |
 
 error: aborting due to 13 previous errors
 
diff --git a/src/tools/clippy/tests/ui/cast_lossless_integer.stderr b/src/tools/clippy/tests/ui/cast_lossless_integer.stderr
index c93ecb8fb56..d2580913bb5 100644
--- a/src/tools/clippy/tests/ui/cast_lossless_integer.stderr
+++ b/src/tools/clippy/tests/ui/cast_lossless_integer.stderr
@@ -9,8 +9,9 @@ LL |     0u8 as u16;
    = help: to override `-D warnings` add `#[allow(clippy::cast_lossless)]`
 help: use `u16::from` instead
    |
-LL |     u16::from(0u8);
-   |     ~~~~~~~~~~~~~~
+LL -     0u8 as u16;
+LL +     u16::from(0u8);
+   |
 
 error: casts from `u8` to `i16` can be expressed infallibly using `From`
   --> tests/ui/cast_lossless_integer.rs:10:5
@@ -21,8 +22,9 @@ LL |     0u8 as i16;
    = help: an `as` cast can become silently lossy if the types change in the future
 help: use `i16::from` instead
    |
-LL |     i16::from(0u8);
-   |     ~~~~~~~~~~~~~~
+LL -     0u8 as i16;
+LL +     i16::from(0u8);
+   |
 
 error: casts from `u8` to `u32` can be expressed infallibly using `From`
   --> tests/ui/cast_lossless_integer.rs:12:5
@@ -33,8 +35,9 @@ LL |     0u8 as u32;
    = help: an `as` cast can become silently lossy if the types change in the future
 help: use `u32::from` instead
    |
-LL |     u32::from(0u8);
-   |     ~~~~~~~~~~~~~~
+LL -     0u8 as u32;
+LL +     u32::from(0u8);
+   |
 
 error: casts from `u8` to `i32` can be expressed infallibly using `From`
   --> tests/ui/cast_lossless_integer.rs:14:5
@@ -45,8 +48,9 @@ LL |     0u8 as i32;
    = help: an `as` cast can become silently lossy if the types change in the future
 help: use `i32::from` instead
    |
-LL |     i32::from(0u8);
-   |     ~~~~~~~~~~~~~~
+LL -     0u8 as i32;
+LL +     i32::from(0u8);
+   |
 
 error: casts from `u8` to `u64` can be expressed infallibly using `From`
   --> tests/ui/cast_lossless_integer.rs:16:5
@@ -57,8 +61,9 @@ LL |     0u8 as u64;
    = help: an `as` cast can become silently lossy if the types change in the future
 help: use `u64::from` instead
    |
-LL |     u64::from(0u8);
-   |     ~~~~~~~~~~~~~~
+LL -     0u8 as u64;
+LL +     u64::from(0u8);
+   |
 
 error: casts from `u8` to `i64` can be expressed infallibly using `From`
   --> tests/ui/cast_lossless_integer.rs:18:5
@@ -69,8 +74,9 @@ LL |     0u8 as i64;
    = help: an `as` cast can become silently lossy if the types change in the future
 help: use `i64::from` instead
    |
-LL |     i64::from(0u8);
-   |     ~~~~~~~~~~~~~~
+LL -     0u8 as i64;
+LL +     i64::from(0u8);
+   |
 
 error: casts from `u8` to `u128` can be expressed infallibly using `From`
   --> tests/ui/cast_lossless_integer.rs:20:5
@@ -81,8 +87,9 @@ LL |     0u8 as u128;
    = help: an `as` cast can become silently lossy if the types change in the future
 help: use `u128::from` instead
    |
-LL |     u128::from(0u8);
-   |     ~~~~~~~~~~~~~~~
+LL -     0u8 as u128;
+LL +     u128::from(0u8);
+   |
 
 error: casts from `u8` to `i128` can be expressed infallibly using `From`
   --> tests/ui/cast_lossless_integer.rs:22:5
@@ -93,8 +100,9 @@ LL |     0u8 as i128;
    = help: an `as` cast can become silently lossy if the types change in the future
 help: use `i128::from` instead
    |
-LL |     i128::from(0u8);
-   |     ~~~~~~~~~~~~~~~
+LL -     0u8 as i128;
+LL +     i128::from(0u8);
+   |
 
 error: casts from `u16` to `u32` can be expressed infallibly using `From`
   --> tests/ui/cast_lossless_integer.rs:25:5
@@ -105,8 +113,9 @@ LL |     0u16 as u32;
    = help: an `as` cast can become silently lossy if the types change in the future
 help: use `u32::from` instead
    |
-LL |     u32::from(0u16);
-   |     ~~~~~~~~~~~~~~~
+LL -     0u16 as u32;
+LL +     u32::from(0u16);
+   |
 
 error: casts from `u16` to `i32` can be expressed infallibly using `From`
   --> tests/ui/cast_lossless_integer.rs:27:5
@@ -117,8 +126,9 @@ LL |     0u16 as i32;
    = help: an `as` cast can become silently lossy if the types change in the future
 help: use `i32::from` instead
    |
-LL |     i32::from(0u16);
-   |     ~~~~~~~~~~~~~~~
+LL -     0u16 as i32;
+LL +     i32::from(0u16);
+   |
 
 error: casts from `u16` to `u64` can be expressed infallibly using `From`
   --> tests/ui/cast_lossless_integer.rs:29:5
@@ -129,8 +139,9 @@ LL |     0u16 as u64;
    = help: an `as` cast can become silently lossy if the types change in the future
 help: use `u64::from` instead
    |
-LL |     u64::from(0u16);
-   |     ~~~~~~~~~~~~~~~
+LL -     0u16 as u64;
+LL +     u64::from(0u16);
+   |
 
 error: casts from `u16` to `i64` can be expressed infallibly using `From`
   --> tests/ui/cast_lossless_integer.rs:31:5
@@ -141,8 +152,9 @@ LL |     0u16 as i64;
    = help: an `as` cast can become silently lossy if the types change in the future
 help: use `i64::from` instead
    |
-LL |     i64::from(0u16);
-   |     ~~~~~~~~~~~~~~~
+LL -     0u16 as i64;
+LL +     i64::from(0u16);
+   |
 
 error: casts from `u16` to `u128` can be expressed infallibly using `From`
   --> tests/ui/cast_lossless_integer.rs:33:5
@@ -153,8 +165,9 @@ LL |     0u16 as u128;
    = help: an `as` cast can become silently lossy if the types change in the future
 help: use `u128::from` instead
    |
-LL |     u128::from(0u16);
-   |     ~~~~~~~~~~~~~~~~
+LL -     0u16 as u128;
+LL +     u128::from(0u16);
+   |
 
 error: casts from `u16` to `i128` can be expressed infallibly using `From`
   --> tests/ui/cast_lossless_integer.rs:35:5
@@ -165,8 +178,9 @@ LL |     0u16 as i128;
    = help: an `as` cast can become silently lossy if the types change in the future
 help: use `i128::from` instead
    |
-LL |     i128::from(0u16);
-   |     ~~~~~~~~~~~~~~~~
+LL -     0u16 as i128;
+LL +     i128::from(0u16);
+   |
 
 error: casts from `u32` to `u64` can be expressed infallibly using `From`
   --> tests/ui/cast_lossless_integer.rs:38:5
@@ -177,8 +191,9 @@ LL |     0u32 as u64;
    = help: an `as` cast can become silently lossy if the types change in the future
 help: use `u64::from` instead
    |
-LL |     u64::from(0u32);
-   |     ~~~~~~~~~~~~~~~
+LL -     0u32 as u64;
+LL +     u64::from(0u32);
+   |
 
 error: casts from `u32` to `i64` can be expressed infallibly using `From`
   --> tests/ui/cast_lossless_integer.rs:40:5
@@ -189,8 +204,9 @@ LL |     0u32 as i64;
    = help: an `as` cast can become silently lossy if the types change in the future
 help: use `i64::from` instead
    |
-LL |     i64::from(0u32);
-   |     ~~~~~~~~~~~~~~~
+LL -     0u32 as i64;
+LL +     i64::from(0u32);
+   |
 
 error: casts from `u32` to `u128` can be expressed infallibly using `From`
   --> tests/ui/cast_lossless_integer.rs:42:5
@@ -201,8 +217,9 @@ LL |     0u32 as u128;
    = help: an `as` cast can become silently lossy if the types change in the future
 help: use `u128::from` instead
    |
-LL |     u128::from(0u32);
-   |     ~~~~~~~~~~~~~~~~
+LL -     0u32 as u128;
+LL +     u128::from(0u32);
+   |
 
 error: casts from `u32` to `i128` can be expressed infallibly using `From`
   --> tests/ui/cast_lossless_integer.rs:44:5
@@ -213,8 +230,9 @@ LL |     0u32 as i128;
    = help: an `as` cast can become silently lossy if the types change in the future
 help: use `i128::from` instead
    |
-LL |     i128::from(0u32);
-   |     ~~~~~~~~~~~~~~~~
+LL -     0u32 as i128;
+LL +     i128::from(0u32);
+   |
 
 error: casts from `u64` to `u128` can be expressed infallibly using `From`
   --> tests/ui/cast_lossless_integer.rs:47:5
@@ -225,8 +243,9 @@ LL |     0u64 as u128;
    = help: an `as` cast can become silently lossy if the types change in the future
 help: use `u128::from` instead
    |
-LL |     u128::from(0u64);
-   |     ~~~~~~~~~~~~~~~~
+LL -     0u64 as u128;
+LL +     u128::from(0u64);
+   |
 
 error: casts from `u64` to `i128` can be expressed infallibly using `From`
   --> tests/ui/cast_lossless_integer.rs:49:5
@@ -237,8 +256,9 @@ LL |     0u64 as i128;
    = help: an `as` cast can become silently lossy if the types change in the future
 help: use `i128::from` instead
    |
-LL |     i128::from(0u64);
-   |     ~~~~~~~~~~~~~~~~
+LL -     0u64 as i128;
+LL +     i128::from(0u64);
+   |
 
 error: casts from `i8` to `i16` can be expressed infallibly using `From`
   --> tests/ui/cast_lossless_integer.rs:52:5
@@ -249,8 +269,9 @@ LL |     0i8 as i16;
    = help: an `as` cast can become silently lossy if the types change in the future
 help: use `i16::from` instead
    |
-LL |     i16::from(0i8);
-   |     ~~~~~~~~~~~~~~
+LL -     0i8 as i16;
+LL +     i16::from(0i8);
+   |
 
 error: casts from `i8` to `i32` can be expressed infallibly using `From`
   --> tests/ui/cast_lossless_integer.rs:54:5
@@ -261,8 +282,9 @@ LL |     0i8 as i32;
    = help: an `as` cast can become silently lossy if the types change in the future
 help: use `i32::from` instead
    |
-LL |     i32::from(0i8);
-   |     ~~~~~~~~~~~~~~
+LL -     0i8 as i32;
+LL +     i32::from(0i8);
+   |
 
 error: casts from `i8` to `i64` can be expressed infallibly using `From`
   --> tests/ui/cast_lossless_integer.rs:56:5
@@ -273,8 +295,9 @@ LL |     0i8 as i64;
    = help: an `as` cast can become silently lossy if the types change in the future
 help: use `i64::from` instead
    |
-LL |     i64::from(0i8);
-   |     ~~~~~~~~~~~~~~
+LL -     0i8 as i64;
+LL +     i64::from(0i8);
+   |
 
 error: casts from `i8` to `i128` can be expressed infallibly using `From`
   --> tests/ui/cast_lossless_integer.rs:58:5
@@ -285,8 +308,9 @@ LL |     0i8 as i128;
    = help: an `as` cast can become silently lossy if the types change in the future
 help: use `i128::from` instead
    |
-LL |     i128::from(0i8);
-   |     ~~~~~~~~~~~~~~~
+LL -     0i8 as i128;
+LL +     i128::from(0i8);
+   |
 
 error: casts from `i16` to `i32` can be expressed infallibly using `From`
   --> tests/ui/cast_lossless_integer.rs:61:5
@@ -297,8 +321,9 @@ LL |     0i16 as i32;
    = help: an `as` cast can become silently lossy if the types change in the future
 help: use `i32::from` instead
    |
-LL |     i32::from(0i16);
-   |     ~~~~~~~~~~~~~~~
+LL -     0i16 as i32;
+LL +     i32::from(0i16);
+   |
 
 error: casts from `i16` to `i64` can be expressed infallibly using `From`
   --> tests/ui/cast_lossless_integer.rs:63:5
@@ -309,8 +334,9 @@ LL |     0i16 as i64;
    = help: an `as` cast can become silently lossy if the types change in the future
 help: use `i64::from` instead
    |
-LL |     i64::from(0i16);
-   |     ~~~~~~~~~~~~~~~
+LL -     0i16 as i64;
+LL +     i64::from(0i16);
+   |
 
 error: casts from `i16` to `i128` can be expressed infallibly using `From`
   --> tests/ui/cast_lossless_integer.rs:65:5
@@ -321,8 +347,9 @@ LL |     0i16 as i128;
    = help: an `as` cast can become silently lossy if the types change in the future
 help: use `i128::from` instead
    |
-LL |     i128::from(0i16);
-   |     ~~~~~~~~~~~~~~~~
+LL -     0i16 as i128;
+LL +     i128::from(0i16);
+   |
 
 error: casts from `i32` to `i64` can be expressed infallibly using `From`
   --> tests/ui/cast_lossless_integer.rs:68:5
@@ -333,8 +360,9 @@ LL |     0i32 as i64;
    = help: an `as` cast can become silently lossy if the types change in the future
 help: use `i64::from` instead
    |
-LL |     i64::from(0i32);
-   |     ~~~~~~~~~~~~~~~
+LL -     0i32 as i64;
+LL +     i64::from(0i32);
+   |
 
 error: casts from `i32` to `i128` can be expressed infallibly using `From`
   --> tests/ui/cast_lossless_integer.rs:70:5
@@ -345,8 +373,9 @@ LL |     0i32 as i128;
    = help: an `as` cast can become silently lossy if the types change in the future
 help: use `i128::from` instead
    |
-LL |     i128::from(0i32);
-   |     ~~~~~~~~~~~~~~~~
+LL -     0i32 as i128;
+LL +     i128::from(0i32);
+   |
 
 error: casts from `i64` to `i128` can be expressed infallibly using `From`
   --> tests/ui/cast_lossless_integer.rs:73:5
@@ -357,8 +386,9 @@ LL |     0i64 as i128;
    = help: an `as` cast can become silently lossy if the types change in the future
 help: use `i128::from` instead
    |
-LL |     i128::from(0i64);
-   |     ~~~~~~~~~~~~~~~~
+LL -     0i64 as i128;
+LL +     i128::from(0i64);
+   |
 
 error: casts from `u8` to `u16` can be expressed infallibly using `From`
   --> tests/ui/cast_lossless_integer.rs:77:13
@@ -369,8 +399,9 @@ LL |     let _ = (1u8 + 1u8) as u16;
    = help: an `as` cast can become silently lossy if the types change in the future
 help: use `u16::from` instead
    |
-LL |     let _ = u16::from(1u8 + 1u8);
-   |             ~~~~~~~~~~~~~~~~~~~~
+LL -     let _ = (1u8 + 1u8) as u16;
+LL +     let _ = u16::from(1u8 + 1u8);
+   |
 
 error: casts from `i8` to `i64` can be expressed infallibly using `From`
   --> tests/ui/cast_lossless_integer.rs:80:13
@@ -381,8 +412,9 @@ LL |     let _ = 1i8 as I64Alias;
    = help: an `as` cast can become silently lossy if the types change in the future
 help: use `I64Alias::from` instead
    |
-LL |     let _ = I64Alias::from(1i8);
-   |             ~~~~~~~~~~~~~~~~~~~
+LL -     let _ = 1i8 as I64Alias;
+LL +     let _ = I64Alias::from(1i8);
+   |
 
 error: casts from `u8` to `u16` can be expressed infallibly using `From`
   --> tests/ui/cast_lossless_integer.rs:83:18
@@ -393,8 +425,9 @@ LL |     let _: u16 = 0u8 as _;
    = help: an `as` cast can become silently lossy if the types change in the future
 help: use `Into::into` instead
    |
-LL |     let _: u16 = 0u8.into();
-   |                  ~~~~~~~~~~
+LL -     let _: u16 = 0u8 as _;
+LL +     let _: u16 = 0u8.into();
+   |
 
 error: casts from `i8` to `i16` can be expressed infallibly using `From`
   --> tests/ui/cast_lossless_integer.rs:85:18
@@ -405,8 +438,9 @@ LL |     let _: i16 = -1i8 as _;
    = help: an `as` cast can become silently lossy if the types change in the future
 help: use `Into::into` instead
    |
-LL |     let _: i16 = (-1i8).into();
-   |                  ~~~~~~~~~~~~~
+LL -     let _: i16 = -1i8 as _;
+LL +     let _: i16 = (-1i8).into();
+   |
 
 error: casts from `u8` to `u16` can be expressed infallibly using `From`
   --> tests/ui/cast_lossless_integer.rs:87:18
@@ -417,8 +451,9 @@ LL |     let _: u16 = (1u8 + 2) as _;
    = help: an `as` cast can become silently lossy if the types change in the future
 help: use `Into::into` instead
    |
-LL |     let _: u16 = (1u8 + 2).into();
-   |                  ~~~~~~~~~~~~~~~~
+LL -     let _: u16 = (1u8 + 2) as _;
+LL +     let _: u16 = (1u8 + 2).into();
+   |
 
 error: casts from `u16` to `u32` can be expressed infallibly using `From`
   --> tests/ui/cast_lossless_integer.rs:89:18
@@ -429,8 +464,9 @@ LL |     let _: u32 = 1i8 as u16 as _;
    = help: an `as` cast can become silently lossy if the types change in the future
 help: use `Into::into` instead
    |
-LL |     let _: u32 = (1i8 as u16).into();
-   |                  ~~~~~~~~~~~~~~~~~~~
+LL -     let _: u32 = 1i8 as u16 as _;
+LL +     let _: u32 = (1i8 as u16).into();
+   |
 
 error: casts from `i8` to `i32` can be expressed infallibly using `From`
   --> tests/ui/cast_lossless_integer.rs:124:13
@@ -441,8 +477,9 @@ LL |     let _ = sign_cast!(x, u8, i8) as i32;
    = help: an `as` cast can become silently lossy if the types change in the future
 help: use `i32::from` instead
    |
-LL |     let _ = i32::from(sign_cast!(x, u8, i8));
-   |             ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+LL -     let _ = sign_cast!(x, u8, i8) as i32;
+LL +     let _ = i32::from(sign_cast!(x, u8, i8));
+   |
 
 error: casts from `i8` to `i32` can be expressed infallibly using `From`
   --> tests/ui/cast_lossless_integer.rs:126:13
@@ -453,8 +490,9 @@ LL |     let _ = (sign_cast!(x, u8, i8) + 1) as i32;
    = help: an `as` cast can become silently lossy if the types change in the future
 help: use `i32::from` instead
    |
-LL |     let _ = i32::from(sign_cast!(x, u8, i8) + 1);
-   |             ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+LL -     let _ = (sign_cast!(x, u8, i8) + 1) as i32;
+LL +     let _ = i32::from(sign_cast!(x, u8, i8) + 1);
+   |
 
 error: casts from `u8` to `u32` can be expressed infallibly using `From`
   --> tests/ui/cast_lossless_integer.rs:133:13
@@ -469,7 +507,8 @@ LL |     let _ = in_macro!();
    = note: this error originates in the macro `in_macro` (in Nightly builds, run with -Z macro-backtrace for more info)
 help: use `u32::from` instead
    |
-LL |             u32::from(1u8)
+LL -             1u8 as u32
+LL +             u32::from(1u8)
    |
 
 error: casts from `u8` to `u32` can be expressed infallibly using `From`
@@ -481,8 +520,9 @@ LL |     let _ = 0u8 as ty!();
    = help: an `as` cast can become silently lossy if the types change in the future
 help: use `<ty!()>::from` instead
    |
-LL |     let _ = <ty!()>::from(0u8);
-   |             ~~~~~~~~~~~~~~~~~~
+LL -     let _ = 0u8 as ty!();
+LL +     let _ = <ty!()>::from(0u8);
+   |
 
 error: aborting due to 40 previous errors
 
diff --git a/src/tools/clippy/tests/ui/cast_size.64bit.stderr b/src/tools/clippy/tests/ui/cast_size.64bit.stderr
index bc37107d80e..6b9919f8a10 100644
--- a/src/tools/clippy/tests/ui/cast_size.64bit.stderr
+++ b/src/tools/clippy/tests/ui/cast_size.64bit.stderr
@@ -9,8 +9,9 @@ LL |     1isize as i8;
    = help: to override `-D warnings` add `#[allow(clippy::cast_possible_truncation)]`
 help: ... or use `try_from` and handle the error accordingly
    |
-LL |     i8::try_from(1isize);
-   |     ~~~~~~~~~~~~~~~~~~~~
+LL -     1isize as i8;
+LL +     i8::try_from(1isize);
+   |
 
 error: casting `isize` to `f32` causes a loss of precision (`isize` is 32 or 64 bits wide, but `f32`'s mantissa is only 23 bits wide)
   --> tests/ui/cast_size.rs:21:5
@@ -48,8 +49,9 @@ LL |     1isize as i32;
    = help: if this is intentional allow the lint with `#[allow(clippy::cast_possible_truncation)]` ...
 help: ... or use `try_from` and handle the error accordingly
    |
-LL |     i32::try_from(1isize);
-   |     ~~~~~~~~~~~~~~~~~~~~~
+LL -     1isize as i32;
+LL +     i32::try_from(1isize);
+   |
 
 error: casting `isize` to `u32` may truncate the value on targets with 64-bit wide pointers
   --> tests/ui/cast_size.rs:29:5
@@ -60,8 +62,9 @@ LL |     1isize as u32;
    = help: if this is intentional allow the lint with `#[allow(clippy::cast_possible_truncation)]` ...
 help: ... or use `try_from` and handle the error accordingly
    |
-LL |     u32::try_from(1isize);
-   |     ~~~~~~~~~~~~~~~~~~~~~
+LL -     1isize as u32;
+LL +     u32::try_from(1isize);
+   |
 
 error: casting `usize` to `u32` may truncate the value on targets with 64-bit wide pointers
   --> tests/ui/cast_size.rs:30:5
@@ -72,8 +75,9 @@ LL |     1usize as u32;
    = help: if this is intentional allow the lint with `#[allow(clippy::cast_possible_truncation)]` ...
 help: ... or use `try_from` and handle the error accordingly
    |
-LL |     u32::try_from(1usize);
-   |     ~~~~~~~~~~~~~~~~~~~~~
+LL -     1usize as u32;
+LL +     u32::try_from(1usize);
+   |
 
 error: casting `usize` to `i32` may truncate the value on targets with 64-bit wide pointers
   --> tests/ui/cast_size.rs:31:5
@@ -84,8 +88,9 @@ LL |     1usize as i32;
    = help: if this is intentional allow the lint with `#[allow(clippy::cast_possible_truncation)]` ...
 help: ... or use `try_from` and handle the error accordingly
    |
-LL |     i32::try_from(1usize);
-   |     ~~~~~~~~~~~~~~~~~~~~~
+LL -     1usize as i32;
+LL +     i32::try_from(1usize);
+   |
 
 error: casting `usize` to `i32` may wrap around the value on targets with 32-bit wide pointers
   --> tests/ui/cast_size.rs:31:5
@@ -105,8 +110,9 @@ LL |     1i64 as isize;
    = help: if this is intentional allow the lint with `#[allow(clippy::cast_possible_truncation)]` ...
 help: ... or use `try_from` and handle the error accordingly
    |
-LL |     isize::try_from(1i64);
-   |     ~~~~~~~~~~~~~~~~~~~~~
+LL -     1i64 as isize;
+LL +     isize::try_from(1i64);
+   |
 
 error: casting `i64` to `usize` may truncate the value on targets with 32-bit wide pointers
   --> tests/ui/cast_size.rs:33:5
@@ -117,8 +123,9 @@ LL |     1i64 as usize;
    = help: if this is intentional allow the lint with `#[allow(clippy::cast_possible_truncation)]` ...
 help: ... or use `try_from` and handle the error accordingly
    |
-LL |     usize::try_from(1i64);
-   |     ~~~~~~~~~~~~~~~~~~~~~
+LL -     1i64 as usize;
+LL +     usize::try_from(1i64);
+   |
 
 error: casting `u64` to `isize` may truncate the value on targets with 32-bit wide pointers
   --> tests/ui/cast_size.rs:34:5
@@ -129,8 +136,9 @@ LL |     1u64 as isize;
    = help: if this is intentional allow the lint with `#[allow(clippy::cast_possible_truncation)]` ...
 help: ... or use `try_from` and handle the error accordingly
    |
-LL |     isize::try_from(1u64);
-   |     ~~~~~~~~~~~~~~~~~~~~~
+LL -     1u64 as isize;
+LL +     isize::try_from(1u64);
+   |
 
 error: casting `u64` to `isize` may wrap around the value on targets with 64-bit wide pointers
   --> tests/ui/cast_size.rs:34:5
@@ -147,8 +155,9 @@ LL |     1u64 as usize;
    = help: if this is intentional allow the lint with `#[allow(clippy::cast_possible_truncation)]` ...
 help: ... or use `try_from` and handle the error accordingly
    |
-LL |     usize::try_from(1u64);
-   |     ~~~~~~~~~~~~~~~~~~~~~
+LL -     1u64 as usize;
+LL +     usize::try_from(1u64);
+   |
 
 error: casting `u32` to `isize` may wrap around the value on targets with 32-bit wide pointers
   --> tests/ui/cast_size.rs:36:5
diff --git a/src/tools/clippy/tests/ui/create_dir.stderr b/src/tools/clippy/tests/ui/create_dir.stderr
index ab51705bb55..9bb98a2606d 100644
--- a/src/tools/clippy/tests/ui/create_dir.stderr
+++ b/src/tools/clippy/tests/ui/create_dir.stderr
@@ -8,8 +8,9 @@ LL |     std::fs::create_dir("foo");
    = help: to override `-D warnings` add `#[allow(clippy::create_dir)]`
 help: consider calling `std::fs::create_dir_all` instead
    |
-LL |     create_dir_all("foo");
-   |     ~~~~~~~~~~~~~~~~~~~~~
+LL -     std::fs::create_dir("foo");
+LL +     create_dir_all("foo");
+   |
 
 error: calling `std::fs::create_dir` where there may be a better way
   --> tests/ui/create_dir.rs:11:5
@@ -19,8 +20,9 @@ LL |     std::fs::create_dir("bar").unwrap();
    |
 help: consider calling `std::fs::create_dir_all` instead
    |
-LL |     create_dir_all("bar").unwrap();
-   |     ~~~~~~~~~~~~~~~~~~~~~
+LL -     std::fs::create_dir("bar").unwrap();
+LL +     create_dir_all("bar").unwrap();
+   |
 
 error: aborting due to 2 previous errors
 
diff --git a/src/tools/clippy/tests/ui/dbg_macro/dbg_macro.stderr b/src/tools/clippy/tests/ui/dbg_macro/dbg_macro.stderr
index b3d74b9ff61..f218614fdd6 100644
--- a/src/tools/clippy/tests/ui/dbg_macro/dbg_macro.stderr
+++ b/src/tools/clippy/tests/ui/dbg_macro/dbg_macro.stderr
@@ -8,8 +8,9 @@ LL |     if let Some(n) = dbg!(n.checked_sub(4)) { n } else { n }
    = help: to override `-D warnings` add `#[allow(clippy::dbg_macro)]`
 help: remove the invocation before committing it to a version control system
    |
-LL |     if let Some(n) = n.checked_sub(4) { n } else { n }
-   |                      ~~~~~~~~~~~~~~~~
+LL -     if let Some(n) = dbg!(n.checked_sub(4)) { n } else { n }
+LL +     if let Some(n) = n.checked_sub(4) { n } else { n }
+   |
 
 error: the `dbg!` macro is intended as a debugging tool
   --> tests/ui/dbg_macro/dbg_macro.rs:11:8
@@ -19,8 +20,9 @@ LL |     if dbg!(n <= 1) {
    |
 help: remove the invocation before committing it to a version control system
    |
-LL |     if n <= 1 {
-   |        ~~~~~~
+LL -     if dbg!(n <= 1) {
+LL +     if n <= 1 {
+   |
 
 error: the `dbg!` macro is intended as a debugging tool
   --> tests/ui/dbg_macro/dbg_macro.rs:13:9
@@ -30,7 +32,8 @@ LL |         dbg!(1)
    |
 help: remove the invocation before committing it to a version control system
    |
-LL |         1
+LL -         dbg!(1)
+LL +         1
    |
 
 error: the `dbg!` macro is intended as a debugging tool
@@ -41,7 +44,8 @@ LL |         dbg!(n * factorial(n - 1))
    |
 help: remove the invocation before committing it to a version control system
    |
-LL |         n * factorial(n - 1)
+LL -         dbg!(n * factorial(n - 1))
+LL +         n * factorial(n - 1)
    |
 
 error: the `dbg!` macro is intended as a debugging tool
@@ -52,8 +56,9 @@ LL |     dbg!(42);
    |
 help: remove the invocation before committing it to a version control system
    |
-LL |     42;
-   |     ~~
+LL -     dbg!(42);
+LL +     42;
+   |
 
 error: the `dbg!` macro is intended as a debugging tool
   --> tests/ui/dbg_macro/dbg_macro.rs:24:14
@@ -63,8 +68,9 @@ LL |     foo(3) + dbg!(factorial(4));
    |
 help: remove the invocation before committing it to a version control system
    |
-LL |     foo(3) + factorial(4);
-   |              ~~~~~~~~~~~~
+LL -     foo(3) + dbg!(factorial(4));
+LL +     foo(3) + factorial(4);
+   |
 
 error: the `dbg!` macro is intended as a debugging tool
   --> tests/ui/dbg_macro/dbg_macro.rs:26:5
@@ -74,8 +80,9 @@ LL |     dbg!(1, 2, 3, 4, 5);
    |
 help: remove the invocation before committing it to a version control system
    |
-LL |     (1, 2, 3, 4, 5);
-   |     ~~~~~~~~~~~~~~~
+LL -     dbg!(1, 2, 3, 4, 5);
+LL +     (1, 2, 3, 4, 5);
+   |
 
 error: the `dbg!` macro is intended as a debugging tool
   --> tests/ui/dbg_macro/dbg_macro.rs:48:5
@@ -96,8 +103,9 @@ LL |     let _ = dbg!();
    |
 help: remove the invocation before committing it to a version control system
    |
-LL |     let _ = ();
-   |             ~~
+LL -     let _ = dbg!();
+LL +     let _ = ();
+   |
 
 error: the `dbg!` macro is intended as a debugging tool
   --> tests/ui/dbg_macro/dbg_macro.rs:53:9
@@ -107,8 +115,9 @@ LL |     bar(dbg!());
    |
 help: remove the invocation before committing it to a version control system
    |
-LL |     bar(());
-   |         ~~
+LL -     bar(dbg!());
+LL +     bar(());
+   |
 
 error: the `dbg!` macro is intended as a debugging tool
   --> tests/ui/dbg_macro/dbg_macro.rs:55:10
@@ -118,8 +127,9 @@ LL |     foo!(dbg!());
    |
 help: remove the invocation before committing it to a version control system
    |
-LL |     foo!(());
-   |          ~~
+LL -     foo!(dbg!());
+LL +     foo!(());
+   |
 
 error: the `dbg!` macro is intended as a debugging tool
   --> tests/ui/dbg_macro/dbg_macro.rs:57:16
@@ -129,8 +139,9 @@ LL |     foo2!(foo!(dbg!()));
    |
 help: remove the invocation before committing it to a version control system
    |
-LL |     foo2!(foo!(()));
-   |                ~~
+LL -     foo2!(foo!(dbg!()));
+LL +     foo2!(foo!(()));
+   |
 
 error: the `dbg!` macro is intended as a debugging tool
   --> tests/ui/dbg_macro/dbg_macro.rs:43:13
@@ -155,8 +166,9 @@ LL |         dbg!(2);
    |
 help: remove the invocation before committing it to a version control system
    |
-LL |         2;
-   |         ~
+LL -         dbg!(2);
+LL +         2;
+   |
 
 error: the `dbg!` macro is intended as a debugging tool
   --> tests/ui/dbg_macro/dbg_macro.rs:86:5
@@ -166,8 +178,9 @@ LL |     dbg!(1);
    |
 help: remove the invocation before committing it to a version control system
    |
-LL |     1;
-   |     ~
+LL -     dbg!(1);
+LL +     1;
+   |
 
 error: the `dbg!` macro is intended as a debugging tool
   --> tests/ui/dbg_macro/dbg_macro.rs:92:5
@@ -177,8 +190,9 @@ LL |     dbg!(1);
    |
 help: remove the invocation before committing it to a version control system
    |
-LL |     1;
-   |     ~
+LL -     dbg!(1);
+LL +     1;
+   |
 
 error: the `dbg!` macro is intended as a debugging tool
   --> tests/ui/dbg_macro/dbg_macro.rs:99:9
@@ -188,8 +202,9 @@ LL |         dbg!(1);
    |
 help: remove the invocation before committing it to a version control system
    |
-LL |         1;
-   |         ~
+LL -         dbg!(1);
+LL +         1;
+   |
 
 error: the `dbg!` macro is intended as a debugging tool
   --> tests/ui/dbg_macro/dbg_macro.rs:106:31
@@ -199,8 +214,9 @@ LL |         println!("dbg: {:?}", dbg!(s));
    |
 help: remove the invocation before committing it to a version control system
    |
-LL |         println!("dbg: {:?}", s);
-   |                               ~
+LL -         println!("dbg: {:?}", dbg!(s));
+LL +         println!("dbg: {:?}", s);
+   |
 
 error: the `dbg!` macro is intended as a debugging tool
   --> tests/ui/dbg_macro/dbg_macro.rs:108:22
@@ -210,8 +226,9 @@ LL |         print!("{}", dbg!(s));
    |
 help: remove the invocation before committing it to a version control system
    |
-LL |         print!("{}", s);
-   |                      ~
+LL -         print!("{}", dbg!(s));
+LL +         print!("{}", s);
+   |
 
 error: aborting due to 19 previous errors
 
diff --git a/src/tools/clippy/tests/ui/dbg_macro/dbg_macro_unfixable.stderr b/src/tools/clippy/tests/ui/dbg_macro/dbg_macro_unfixable.stderr
index b8e91906b93..e8d5f9f2f46 100644
--- a/src/tools/clippy/tests/ui/dbg_macro/dbg_macro_unfixable.stderr
+++ b/src/tools/clippy/tests/ui/dbg_macro/dbg_macro_unfixable.stderr
@@ -19,8 +19,9 @@ LL |     dbg!(dbg!(dbg!(42)));
    |
 help: remove the invocation before committing it to a version control system
    |
-LL |     dbg!(dbg!(42));
-   |     ~~~~~~~~~~~~~~
+LL -     dbg!(dbg!(dbg!(42)));
+LL +     dbg!(dbg!(42));
+   |
 
 error: the `dbg!` macro is intended as a debugging tool
   --> tests/ui/dbg_macro/dbg_macro_unfixable.rs:8:10
@@ -30,8 +31,9 @@ LL |     dbg!(dbg!(dbg!(42)));
    |
 help: remove the invocation before committing it to a version control system
    |
-LL |     dbg!(dbg!(42));
-   |          ~~~~~~~~
+LL -     dbg!(dbg!(dbg!(42)));
+LL +     dbg!(dbg!(42));
+   |
 
 error: the `dbg!` macro is intended as a debugging tool
   --> tests/ui/dbg_macro/dbg_macro_unfixable.rs:8:15
@@ -41,8 +43,9 @@ LL |     dbg!(dbg!(dbg!(42)));
    |
 help: remove the invocation before committing it to a version control system
    |
-LL |     dbg!(dbg!(42));
-   |               ~~
+LL -     dbg!(dbg!(dbg!(42)));
+LL +     dbg!(dbg!(42));
+   |
 
 error: the `dbg!` macro is intended as a debugging tool
   --> tests/ui/dbg_macro/dbg_macro_unfixable.rs:10:5
@@ -52,8 +55,9 @@ LL |     dbg!(1, 2, dbg!(3, 4));
    |
 help: remove the invocation before committing it to a version control system
    |
-LL |     (1, 2, dbg!(3, 4));
-   |     ~~~~~~~~~~~~~~~~~~
+LL -     dbg!(1, 2, dbg!(3, 4));
+LL +     (1, 2, dbg!(3, 4));
+   |
 
 error: the `dbg!` macro is intended as a debugging tool
   --> tests/ui/dbg_macro/dbg_macro_unfixable.rs:10:16
@@ -63,8 +67,9 @@ LL |     dbg!(1, 2, dbg!(3, 4));
    |
 help: remove the invocation before committing it to a version control system
    |
-LL |     dbg!(1, 2, (3, 4));
-   |                ~~~~~~
+LL -     dbg!(1, 2, dbg!(3, 4));
+LL +     dbg!(1, 2, (3, 4));
+   |
 
 error: aborting due to 6 previous errors
 
diff --git a/src/tools/clippy/tests/ui/doc/doc-fixable.stderr b/src/tools/clippy/tests/ui/doc/doc-fixable.stderr
index 27a04e4b558..54d73581485 100644
--- a/src/tools/clippy/tests/ui/doc/doc-fixable.stderr
+++ b/src/tools/clippy/tests/ui/doc/doc-fixable.stderr
@@ -8,8 +8,9 @@ LL | /// The foo_bar function does _nothing_. See also foo::bar. (note the dot t
    = help: to override `-D warnings` add `#[allow(clippy::doc_markdown)]`
 help: try
    |
-LL | /// The `foo_bar` function does _nothing_. See also foo::bar. (note the dot there)
-   |         ~~~~~~~~~
+LL - /// The foo_bar function does _nothing_. See also foo::bar. (note the dot there)
+LL + /// The `foo_bar` function does _nothing_. See also foo::bar. (note the dot there)
+   |
 
 error: item in documentation is missing backticks
   --> tests/ui/doc/doc-fixable.rs:9:51
@@ -19,8 +20,9 @@ LL | /// The foo_bar function does _nothing_. See also foo::bar. (note the dot t
    |
 help: try
    |
-LL | /// The foo_bar function does _nothing_. See also `foo::bar`. (note the dot there)
-   |                                                   ~~~~~~~~~~
+LL - /// The foo_bar function does _nothing_. See also foo::bar. (note the dot there)
+LL + /// The foo_bar function does _nothing_. See also `foo::bar`. (note the dot there)
+   |
 
 error: item in documentation is missing backticks
   --> tests/ui/doc/doc-fixable.rs:10:83
@@ -30,8 +32,9 @@ LL | /// Markdown is _weird_. I mean _really weird_. This \_ is ok. So is `_`. B
    |
 help: try
    |
-LL | /// Markdown is _weird_. I mean _really weird_. This \_ is ok. So is `_`. But not `Foo::some_fun`
-   |                                                                                   ~~~~~~~~~~~~~~~
+LL - /// Markdown is _weird_. I mean _really weird_. This \_ is ok. So is `_`. But not Foo::some_fun
+LL + /// Markdown is _weird_. I mean _really weird_. This \_ is ok. So is `_`. But not `Foo::some_fun`
+   |
 
 error: item in documentation is missing backticks
   --> tests/ui/doc/doc-fixable.rs:12:13
@@ -41,8 +44,9 @@ LL | /// Here be ::a::global:path, and _::another::global::path_.  :: is not a p
    |
 help: try
    |
-LL | /// Here be `::a::global:path`, and _::another::global::path_.  :: is not a path though.
-   |             ~~~~~~~~~~~~~~~~~~
+LL - /// Here be ::a::global:path, and _::another::global::path_.  :: is not a path though.
+LL + /// Here be `::a::global:path`, and _::another::global::path_.  :: is not a path though.
+   |
 
 error: item in documentation is missing backticks
   --> tests/ui/doc/doc-fixable.rs:12:36
@@ -52,8 +56,9 @@ LL | /// Here be ::a::global:path, and _::another::global::path_.  :: is not a p
    |
 help: try
    |
-LL | /// Here be ::a::global:path, and _`::another::global::path`_.  :: is not a path though.
-   |                                    ~~~~~~~~~~~~~~~~~~~~~~~~~
+LL - /// Here be ::a::global:path, and _::another::global::path_.  :: is not a path though.
+LL + /// Here be ::a::global:path, and _`::another::global::path`_.  :: is not a path though.
+   |
 
 error: item in documentation is missing backticks
   --> tests/ui/doc/doc-fixable.rs:13:25
@@ -63,8 +68,9 @@ LL | /// Import an item from ::awesome::global::blob:: (Intended postfix)
    |
 help: try
    |
-LL | /// Import an item from `::awesome::global::blob::` (Intended postfix)
-   |                         ~~~~~~~~~~~~~~~~~~~~~~~~~~~
+LL - /// Import an item from ::awesome::global::blob:: (Intended postfix)
+LL + /// Import an item from `::awesome::global::blob::` (Intended postfix)
+   |
 
 error: item in documentation is missing backticks
   --> tests/ui/doc/doc-fixable.rs:14:31
@@ -74,8 +80,9 @@ LL | /// These are the options for ::Cat: (Intended trailing single colon, shoul
    |
 help: try
    |
-LL | /// These are the options for `::Cat`: (Intended trailing single colon, shouldn't be linted)
-   |                               ~~~~~~~
+LL - /// These are the options for ::Cat: (Intended trailing single colon, shouldn't be linted)
+LL + /// These are the options for `::Cat`: (Intended trailing single colon, shouldn't be linted)
+   |
 
 error: item in documentation is missing backticks
   --> tests/ui/doc/doc-fixable.rs:15:22
@@ -85,8 +92,9 @@ LL | /// That's not code ~NotInCodeBlock~.
    |
 help: try
    |
-LL | /// That's not code ~`NotInCodeBlock`~.
-   |                      ~~~~~~~~~~~~~~~~
+LL - /// That's not code ~NotInCodeBlock~.
+LL + /// That's not code ~`NotInCodeBlock`~.
+   |
 
 error: item in documentation is missing backticks
   --> tests/ui/doc/doc-fixable.rs:16:5
@@ -96,8 +104,9 @@ LL | /// be_sure_we_got_to_the_end_of_it
    |
 help: try
    |
-LL | /// `be_sure_we_got_to_the_end_of_it`
-   |     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+LL - /// be_sure_we_got_to_the_end_of_it
+LL + /// `be_sure_we_got_to_the_end_of_it`
+   |
 
 error: item in documentation is missing backticks
   --> tests/ui/doc/doc-fixable.rs:30:5
@@ -107,8 +116,9 @@ LL | /// be_sure_we_got_to_the_end_of_it
    |
 help: try
    |
-LL | /// `be_sure_we_got_to_the_end_of_it`
-   |     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+LL - /// be_sure_we_got_to_the_end_of_it
+LL + /// `be_sure_we_got_to_the_end_of_it`
+   |
 
 error: item in documentation is missing backticks
   --> tests/ui/doc/doc-fixable.rs:37:5
@@ -118,8 +128,9 @@ LL | /// be_sure_we_got_to_the_end_of_it
    |
 help: try
    |
-LL | /// `be_sure_we_got_to_the_end_of_it`
-   |     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+LL - /// be_sure_we_got_to_the_end_of_it
+LL + /// `be_sure_we_got_to_the_end_of_it`
+   |
 
 error: item in documentation is missing backticks
   --> tests/ui/doc/doc-fixable.rs:51:5
@@ -129,8 +140,9 @@ LL | /// be_sure_we_got_to_the_end_of_it
    |
 help: try
    |
-LL | /// `be_sure_we_got_to_the_end_of_it`
-   |     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+LL - /// be_sure_we_got_to_the_end_of_it
+LL + /// `be_sure_we_got_to_the_end_of_it`
+   |
 
 error: item in documentation is missing backticks
   --> tests/ui/doc/doc-fixable.rs:79:5
@@ -140,8 +152,9 @@ LL | /// be_sure_we_got_to_the_end_of_it
    |
 help: try
    |
-LL | /// `be_sure_we_got_to_the_end_of_it`
-   |     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+LL - /// be_sure_we_got_to_the_end_of_it
+LL + /// `be_sure_we_got_to_the_end_of_it`
+   |
 
 error: item in documentation is missing backticks
   --> tests/ui/doc/doc-fixable.rs:96:5
@@ -151,8 +164,9 @@ LL | /// be_sure_we_got_to_the_end_of_it
    |
 help: try
    |
-LL | /// `be_sure_we_got_to_the_end_of_it`
-   |     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+LL - /// be_sure_we_got_to_the_end_of_it
+LL + /// `be_sure_we_got_to_the_end_of_it`
+   |
 
 error: item in documentation is missing backticks
   --> tests/ui/doc/doc-fixable.rs:104:8
@@ -162,8 +176,9 @@ LL | /// ## CamelCaseThing
    |
 help: try
    |
-LL | /// ## `CamelCaseThing`
-   |        ~~~~~~~~~~~~~~~~
+LL - /// ## CamelCaseThing
+LL + /// ## `CamelCaseThing`
+   |
 
 error: item in documentation is missing backticks
   --> tests/ui/doc/doc-fixable.rs:107:7
@@ -173,8 +188,9 @@ LL | /// # CamelCaseThing
    |
 help: try
    |
-LL | /// # `CamelCaseThing`
-   |       ~~~~~~~~~~~~~~~~
+LL - /// # CamelCaseThing
+LL + /// # `CamelCaseThing`
+   |
 
 error: item in documentation is missing backticks
   --> tests/ui/doc/doc-fixable.rs:109:22
@@ -184,8 +200,9 @@ LL | /// Not a title #897 CamelCaseThing
    |
 help: try
    |
-LL | /// Not a title #897 `CamelCaseThing`
-   |                      ~~~~~~~~~~~~~~~~
+LL - /// Not a title #897 CamelCaseThing
+LL + /// Not a title #897 `CamelCaseThing`
+   |
 
 error: item in documentation is missing backticks
   --> tests/ui/doc/doc-fixable.rs:110:5
@@ -195,8 +212,9 @@ LL | /// be_sure_we_got_to_the_end_of_it
    |
 help: try
    |
-LL | /// `be_sure_we_got_to_the_end_of_it`
-   |     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+LL - /// be_sure_we_got_to_the_end_of_it
+LL + /// `be_sure_we_got_to_the_end_of_it`
+   |
 
 error: item in documentation is missing backticks
   --> tests/ui/doc/doc-fixable.rs:117:5
@@ -206,8 +224,9 @@ LL | /// be_sure_we_got_to_the_end_of_it
    |
 help: try
    |
-LL | /// `be_sure_we_got_to_the_end_of_it`
-   |     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+LL - /// be_sure_we_got_to_the_end_of_it
+LL + /// `be_sure_we_got_to_the_end_of_it`
+   |
 
 error: item in documentation is missing backticks
   --> tests/ui/doc/doc-fixable.rs:130:5
@@ -217,8 +236,9 @@ LL | /// be_sure_we_got_to_the_end_of_it
    |
 help: try
    |
-LL | /// `be_sure_we_got_to_the_end_of_it`
-   |     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+LL - /// be_sure_we_got_to_the_end_of_it
+LL + /// `be_sure_we_got_to_the_end_of_it`
+   |
 
 error: item in documentation is missing backticks
   --> tests/ui/doc/doc-fixable.rs:141:43
@@ -228,8 +248,9 @@ LL | /** E.g., serialization of an empty list: FooBar
    |
 help: try
    |
-LL | /** E.g., serialization of an empty list: `FooBar`
-   |                                           ~~~~~~~~
+LL - /** E.g., serialization of an empty list: FooBar
+LL + /** E.g., serialization of an empty list: `FooBar`
+   |
 
 error: item in documentation is missing backticks
   --> tests/ui/doc/doc-fixable.rs:146:5
@@ -239,8 +260,9 @@ LL | And BarQuz too.
    |
 help: try
    |
-LL | And `BarQuz` too.
-   |     ~~~~~~~~
+LL - And BarQuz too.
+LL + And `BarQuz` too.
+   |
 
 error: item in documentation is missing backticks
   --> tests/ui/doc/doc-fixable.rs:147:1
@@ -250,7 +272,8 @@ LL | be_sure_we_got_to_the_end_of_it
    |
 help: try
    |
-LL | `be_sure_we_got_to_the_end_of_it`
+LL - be_sure_we_got_to_the_end_of_it
+LL + `be_sure_we_got_to_the_end_of_it`
    |
 
 error: item in documentation is missing backticks
@@ -261,8 +284,9 @@ LL | /** E.g., serialization of an empty list: FooBar
    |
 help: try
    |
-LL | /** E.g., serialization of an empty list: `FooBar`
-   |                                           ~~~~~~~~
+LL - /** E.g., serialization of an empty list: FooBar
+LL + /** E.g., serialization of an empty list: `FooBar`
+   |
 
 error: item in documentation is missing backticks
   --> tests/ui/doc/doc-fixable.rs:157:5
@@ -272,8 +296,9 @@ LL | And BarQuz too.
    |
 help: try
    |
-LL | And `BarQuz` too.
-   |     ~~~~~~~~
+LL - And BarQuz too.
+LL + And `BarQuz` too.
+   |
 
 error: item in documentation is missing backticks
   --> tests/ui/doc/doc-fixable.rs:158:1
@@ -283,7 +308,8 @@ LL | be_sure_we_got_to_the_end_of_it
    |
 help: try
    |
-LL | `be_sure_we_got_to_the_end_of_it`
+LL - be_sure_we_got_to_the_end_of_it
+LL + `be_sure_we_got_to_the_end_of_it`
    |
 
 error: item in documentation is missing backticks
@@ -294,8 +320,9 @@ LL | /// be_sure_we_got_to_the_end_of_it
    |
 help: try
    |
-LL | /// `be_sure_we_got_to_the_end_of_it`
-   |     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+LL - /// be_sure_we_got_to_the_end_of_it
+LL + /// `be_sure_we_got_to_the_end_of_it`
+   |
 
 error: item in documentation is missing backticks
   --> tests/ui/doc/doc-fixable.rs:188:22
@@ -305,8 +332,9 @@ LL | /// An iterator over mycrate::Collection's values.
    |
 help: try
    |
-LL | /// An iterator over `mycrate::Collection`'s values.
-   |                      ~~~~~~~~~~~~~~~~~~~~~
+LL - /// An iterator over mycrate::Collection's values.
+LL + /// An iterator over `mycrate::Collection`'s values.
+   |
 
 error: item in documentation is missing backticks
   --> tests/ui/doc/doc-fixable.rs:212:34
@@ -316,8 +344,9 @@ LL | /// Foo \[bar\] \[baz\] \[qux\]. DocMarkdownLint
    |
 help: try
    |
-LL | /// Foo \[bar\] \[baz\] \[qux\]. `DocMarkdownLint`
-   |                                  ~~~~~~~~~~~~~~~~~
+LL - /// Foo \[bar\] \[baz\] \[qux\]. DocMarkdownLint
+LL + /// Foo \[bar\] \[baz\] \[qux\]. `DocMarkdownLint`
+   |
 
 error: item in documentation is missing backticks
   --> tests/ui/doc/doc-fixable.rs:235:22
@@ -327,8 +356,9 @@ LL | /// There is no try (do() or do_not()).
    |
 help: try
    |
-LL | /// There is no try (`do()` or do_not()).
-   |                      ~~~~~~
+LL - /// There is no try (do() or do_not()).
+LL + /// There is no try (`do()` or do_not()).
+   |
 
 error: item in documentation is missing backticks
   --> tests/ui/doc/doc-fixable.rs:235:30
@@ -338,8 +368,9 @@ LL | /// There is no try (do() or do_not()).
    |
 help: try
    |
-LL | /// There is no try (do() or `do_not()`).
-   |                              ~~~~~~~~~~
+LL - /// There is no try (do() or do_not()).
+LL + /// There is no try (do() or `do_not()`).
+   |
 
 error: item in documentation is missing backticks
   --> tests/ui/doc/doc-fixable.rs:238:5
@@ -349,8 +380,9 @@ LL | /// ABes
    |
 help: try
    |
-LL | /// `ABes`
-   |     ~~~~~~
+LL - /// ABes
+LL + /// `ABes`
+   |
 
 error: item in documentation is missing backticks
   --> tests/ui/doc/doc-fixable.rs:244:9
@@ -360,8 +392,9 @@ LL |     /// foo()
    |
 help: try
    |
-LL |     /// `foo()`
-   |         ~~~~~~~
+LL -     /// foo()
+LL +     /// `foo()`
+   |
 
 error: you should put bare URLs between `<`/`>` or make a proper Markdown link
   --> tests/ui/doc/doc-fixable.rs:248:5
diff --git a/src/tools/clippy/tests/ui/doc/doc_markdown-issue_13097.stderr b/src/tools/clippy/tests/ui/doc/doc_markdown-issue_13097.stderr
index ae68a767ec9..65b8f2ed80b 100644
--- a/src/tools/clippy/tests/ui/doc/doc_markdown-issue_13097.stderr
+++ b/src/tools/clippy/tests/ui/doc/doc_markdown-issue_13097.stderr
@@ -11,8 +11,9 @@ LL | #![deny(clippy::doc_markdown)]
    |         ^^^^^^^^^^^^^^^^^^^^
 help: try
    |
-LL |     /// `HumaNified`
-   |         ~~~~~~~~~~~~
+LL -     /// HumaNified
+LL +     /// `HumaNified`
+   |
 
 error: aborting due to 1 previous error
 
diff --git a/src/tools/clippy/tests/ui/doc/issue_10262.stderr b/src/tools/clippy/tests/ui/doc/issue_10262.stderr
index f43d9551e94..f9ecb3de219 100644
--- a/src/tools/clippy/tests/ui/doc/issue_10262.stderr
+++ b/src/tools/clippy/tests/ui/doc/issue_10262.stderr
@@ -8,8 +8,9 @@ LL | /// AviSynth documentation:
    = help: to override `-D warnings` add `#[allow(clippy::doc_markdown)]`
 help: try
    |
-LL | /// `AviSynth` documentation:
-   |     ~~~~~~~~~~
+LL - /// AviSynth documentation:
+LL + /// `AviSynth` documentation:
+   |
 
 error: aborting due to 1 previous error
 
diff --git a/src/tools/clippy/tests/ui/doc/issue_12795.stderr b/src/tools/clippy/tests/ui/doc/issue_12795.stderr
index 5700145ec8f..047de915ed4 100644
--- a/src/tools/clippy/tests/ui/doc/issue_12795.stderr
+++ b/src/tools/clippy/tests/ui/doc/issue_12795.stderr
@@ -8,8 +8,9 @@ LL | //! A comment with a_b(x) and a_c in it and (a_b((c)) ) too and (maybe a_b(
    = help: to override `-D warnings` add `#[allow(clippy::doc_markdown)]`
 help: try
    |
-LL | //! A comment with `a_b(x)` and a_c in it and (a_b((c)) ) too and (maybe a_b((c)))
-   |                    ~~~~~~~~
+LL - //! A comment with a_b(x) and a_c in it and (a_b((c)) ) too and (maybe a_b((c)))
+LL + //! A comment with `a_b(x)` and a_c in it and (a_b((c)) ) too and (maybe a_b((c)))
+   |
 
 error: item in documentation is missing backticks
   --> tests/ui/doc/issue_12795.rs:3:31
@@ -19,8 +20,9 @@ LL | //! A comment with a_b(x) and a_c in it and (a_b((c)) ) too and (maybe a_b(
    |
 help: try
    |
-LL | //! A comment with a_b(x) and `a_c` in it and (a_b((c)) ) too and (maybe a_b((c)))
-   |                               ~~~~~
+LL - //! A comment with a_b(x) and a_c in it and (a_b((c)) ) too and (maybe a_b((c)))
+LL + //! A comment with a_b(x) and `a_c` in it and (a_b((c)) ) too and (maybe a_b((c)))
+   |
 
 error: item in documentation is missing backticks
   --> tests/ui/doc/issue_12795.rs:3:46
@@ -30,8 +32,9 @@ LL | //! A comment with a_b(x) and a_c in it and (a_b((c)) ) too and (maybe a_b(
    |
 help: try
    |
-LL | //! A comment with a_b(x) and a_c in it and (`a_b((c))` ) too and (maybe a_b((c)))
-   |                                              ~~~~~~~~~~
+LL - //! A comment with a_b(x) and a_c in it and (a_b((c)) ) too and (maybe a_b((c)))
+LL + //! A comment with a_b(x) and a_c in it and (`a_b((c))` ) too and (maybe a_b((c)))
+   |
 
 error: item in documentation is missing backticks
   --> tests/ui/doc/issue_12795.rs:3:72
@@ -41,8 +44,9 @@ LL | //! A comment with a_b(x) and a_c in it and (a_b((c)) ) too and (maybe a_b(
    |
 help: try
    |
-LL | //! A comment with a_b(x) and a_c in it and (a_b((c)) ) too and (maybe `a_b((c))`)
-   |                                                                        ~~~~~~~~~~
+LL - //! A comment with a_b(x) and a_c in it and (a_b((c)) ) too and (maybe a_b((c)))
+LL + //! A comment with a_b(x) and a_c in it and (a_b((c)) ) too and (maybe `a_b((c))`)
+   |
 
 error: aborting due to 4 previous errors
 
diff --git a/src/tools/clippy/tests/ui/doc/issue_9473.stderr b/src/tools/clippy/tests/ui/doc/issue_9473.stderr
index 35aa2884cc1..744c8dc8c83 100644
--- a/src/tools/clippy/tests/ui/doc/issue_9473.stderr
+++ b/src/tools/clippy/tests/ui/doc/issue_9473.stderr
@@ -8,8 +8,9 @@ LL | /// Blah blah blah <code>[FooBar]&lt;[FooBar]&gt;</code>[FooBar].
    = help: to override `-D warnings` add `#[allow(clippy::doc_markdown)]`
 help: try
    |
-LL | /// Blah blah blah <code>[FooBar]&lt;[FooBar]&gt;</code>[`FooBar`].
-   |                                                          ~~~~~~~~
+LL - /// Blah blah blah <code>[FooBar]&lt;[FooBar]&gt;</code>[FooBar].
+LL + /// Blah blah blah <code>[FooBar]&lt;[FooBar]&gt;</code>[`FooBar`].
+   |
 
 error: aborting due to 1 previous error
 
diff --git a/src/tools/clippy/tests/ui/doc/unbalanced_ticks.stderr b/src/tools/clippy/tests/ui/doc/unbalanced_ticks.stderr
index c9fd25eb1a1..4114a823822 100644
--- a/src/tools/clippy/tests/ui/doc/unbalanced_ticks.stderr
+++ b/src/tools/clippy/tests/ui/doc/unbalanced_ticks.stderr
@@ -29,8 +29,9 @@ LL | /// This paragraph is fine and should_be linted normally.
    |
 help: try
    |
-LL | /// This paragraph is fine and `should_be` linted normally.
-   |                                ~~~~~~~~~~~
+LL - /// This paragraph is fine and should_be linted normally.
+LL + /// This paragraph is fine and `should_be` linted normally.
+   |
 
 error: backticks are unbalanced
   --> tests/ui/doc/unbalanced_ticks.rs:20:5
@@ -48,8 +49,9 @@ LL | /// ## not_fine
    |
 help: try
    |
-LL | /// ## `not_fine`
-   |        ~~~~~~~~~~
+LL - /// ## not_fine
+LL + /// ## `not_fine`
+   |
 
 error: backticks are unbalanced
   --> tests/ui/doc/unbalanced_ticks.rs:37:5
@@ -75,8 +77,9 @@ LL | /// - This item needs backticks_here
    |
 help: try
    |
-LL | /// - This item needs `backticks_here`
-   |                       ~~~~~~~~~~~~~~~~
+LL - /// - This item needs backticks_here
+LL + /// - This item needs `backticks_here`
+   |
 
 error: backticks are unbalanced
   --> tests/ui/doc/unbalanced_ticks.rs:53:5
diff --git a/src/tools/clippy/tests/ui/eager_transmute.stderr b/src/tools/clippy/tests/ui/eager_transmute.stderr
index 5cf7bd49a92..68690c3730d 100644
--- a/src/tools/clippy/tests/ui/eager_transmute.stderr
+++ b/src/tools/clippy/tests/ui/eager_transmute.stderr
@@ -8,8 +8,9 @@ LL |     (op < 4).then_some(unsafe { std::mem::transmute(op) })
    = help: to override `-D warnings` add `#[allow(clippy::eager_transmute)]`
 help: consider using `bool::then` to only transmute if the condition holds
    |
-LL |     (op < 4).then(|| unsafe { std::mem::transmute(op) })
-   |              ~~~~ ++
+LL -     (op < 4).then_some(unsafe { std::mem::transmute(op) })
+LL +     (op < 4).then(|| unsafe { std::mem::transmute(op) })
+   |
 
 error: this transmute is always evaluated eagerly, even if the condition is false
   --> tests/ui/eager_transmute.rs:27:33
@@ -19,8 +20,9 @@ LL |     (op < 4).then_some(unsafe { std::mem::transmute::<_, Opcode>(op) });
    |
 help: consider using `bool::then` to only transmute if the condition holds
    |
-LL |     (op < 4).then(|| unsafe { std::mem::transmute::<_, Opcode>(op) });
-   |              ~~~~ ++
+LL -     (op < 4).then_some(unsafe { std::mem::transmute::<_, Opcode>(op) });
+LL +     (op < 4).then(|| unsafe { std::mem::transmute::<_, Opcode>(op) });
+   |
 
 error: this transmute is always evaluated eagerly, even if the condition is false
   --> tests/ui/eager_transmute.rs:28:33
@@ -30,8 +32,9 @@ LL |     (op > 4).then_some(unsafe { std::mem::transmute::<_, Opcode>(op) });
    |
 help: consider using `bool::then` to only transmute if the condition holds
    |
-LL |     (op > 4).then(|| unsafe { std::mem::transmute::<_, Opcode>(op) });
-   |              ~~~~ ++
+LL -     (op > 4).then_some(unsafe { std::mem::transmute::<_, Opcode>(op) });
+LL +     (op > 4).then(|| unsafe { std::mem::transmute::<_, Opcode>(op) });
+   |
 
 error: this transmute is always evaluated eagerly, even if the condition is false
   --> tests/ui/eager_transmute.rs:29:34
@@ -41,8 +44,9 @@ LL |     (op == 0).then_some(unsafe { std::mem::transmute::<_, Opcode>(op) });
    |
 help: consider using `bool::then` to only transmute if the condition holds
    |
-LL |     (op == 0).then(|| unsafe { std::mem::transmute::<_, Opcode>(op) });
-   |               ~~~~ ++
+LL -     (op == 0).then_some(unsafe { std::mem::transmute::<_, Opcode>(op) });
+LL +     (op == 0).then(|| unsafe { std::mem::transmute::<_, Opcode>(op) });
+   |
 
 error: this transmute is always evaluated eagerly, even if the condition is false
   --> tests/ui/eager_transmute.rs:31:68
@@ -52,8 +56,9 @@ LL |     let _: Option<Opcode> = (op > 0 && op < 10).then_some(unsafe { std::mem
    |
 help: consider using `bool::then` to only transmute if the condition holds
    |
-LL |     let _: Option<Opcode> = (op > 0 && op < 10).then(|| unsafe { std::mem::transmute(op) });
-   |                                                 ~~~~ ++
+LL -     let _: Option<Opcode> = (op > 0 && op < 10).then_some(unsafe { std::mem::transmute(op) });
+LL +     let _: Option<Opcode> = (op > 0 && op < 10).then(|| unsafe { std::mem::transmute(op) });
+   |
 
 error: this transmute is always evaluated eagerly, even if the condition is false
   --> tests/ui/eager_transmute.rs:32:86
@@ -63,8 +68,9 @@ LL |     let _: Option<Opcode> = (op > 0 && op < 10 && unrelated == 0).then_some
    |
 help: consider using `bool::then` to only transmute if the condition holds
    |
-LL |     let _: Option<Opcode> = (op > 0 && op < 10 && unrelated == 0).then(|| unsafe { std::mem::transmute(op) });
-   |                                                                   ~~~~ ++
+LL -     let _: Option<Opcode> = (op > 0 && op < 10 && unrelated == 0).then_some(unsafe { std::mem::transmute(op) });
+LL +     let _: Option<Opcode> = (op > 0 && op < 10 && unrelated == 0).then(|| unsafe { std::mem::transmute(op) });
+   |
 
 error: this transmute is always evaluated eagerly, even if the condition is false
   --> tests/ui/eager_transmute.rs:35:84
@@ -74,8 +80,9 @@ LL |     let _: Option<Opcode> = (op2.foo[0] > 0 && op2.foo[0] < 10).then_some(u
    |
 help: consider using `bool::then` to only transmute if the condition holds
    |
-LL |     let _: Option<Opcode> = (op2.foo[0] > 0 && op2.foo[0] < 10).then(|| unsafe { std::mem::transmute(op2.foo[0]) });
-   |                                                                 ~~~~ ++
+LL -     let _: Option<Opcode> = (op2.foo[0] > 0 && op2.foo[0] < 10).then_some(unsafe { std::mem::transmute(op2.foo[0]) });
+LL +     let _: Option<Opcode> = (op2.foo[0] > 0 && op2.foo[0] < 10).then(|| unsafe { std::mem::transmute(op2.foo[0]) });
+   |
 
 error: this transmute is always evaluated eagerly, even if the condition is false
   --> tests/ui/eager_transmute.rs:47:70
@@ -85,8 +92,9 @@ LL |     let _: Option<Opcode> = (1..=3).contains(&op).then_some(unsafe { std::m
    |
 help: consider using `bool::then` to only transmute if the condition holds
    |
-LL |     let _: Option<Opcode> = (1..=3).contains(&op).then(|| unsafe { std::mem::transmute(op) });
-   |                                                   ~~~~ ++
+LL -     let _: Option<Opcode> = (1..=3).contains(&op).then_some(unsafe { std::mem::transmute(op) });
+LL +     let _: Option<Opcode> = (1..=3).contains(&op).then(|| unsafe { std::mem::transmute(op) });
+   |
 
 error: this transmute is always evaluated eagerly, even if the condition is false
   --> tests/ui/eager_transmute.rs:48:83
@@ -96,8 +104,9 @@ LL |     let _: Option<Opcode> = ((1..=3).contains(&op) || op == 4).then_some(un
    |
 help: consider using `bool::then` to only transmute if the condition holds
    |
-LL |     let _: Option<Opcode> = ((1..=3).contains(&op) || op == 4).then(|| unsafe { std::mem::transmute(op) });
-   |                                                                ~~~~ ++
+LL -     let _: Option<Opcode> = ((1..=3).contains(&op) || op == 4).then_some(unsafe { std::mem::transmute(op) });
+LL +     let _: Option<Opcode> = ((1..=3).contains(&op) || op == 4).then(|| unsafe { std::mem::transmute(op) });
+   |
 
 error: this transmute is always evaluated eagerly, even if the condition is false
   --> tests/ui/eager_transmute.rs:49:69
@@ -107,8 +116,9 @@ LL |     let _: Option<Opcode> = (1..3).contains(&op).then_some(unsafe { std::me
    |
 help: consider using `bool::then` to only transmute if the condition holds
    |
-LL |     let _: Option<Opcode> = (1..3).contains(&op).then(|| unsafe { std::mem::transmute(op) });
-   |                                                  ~~~~ ++
+LL -     let _: Option<Opcode> = (1..3).contains(&op).then_some(unsafe { std::mem::transmute(op) });
+LL +     let _: Option<Opcode> = (1..3).contains(&op).then(|| unsafe { std::mem::transmute(op) });
+   |
 
 error: this transmute is always evaluated eagerly, even if the condition is false
   --> tests/ui/eager_transmute.rs:50:68
@@ -118,8 +128,9 @@ LL |     let _: Option<Opcode> = (1..).contains(&op).then_some(unsafe { std::mem
    |
 help: consider using `bool::then` to only transmute if the condition holds
    |
-LL |     let _: Option<Opcode> = (1..).contains(&op).then(|| unsafe { std::mem::transmute(op) });
-   |                                                 ~~~~ ++
+LL -     let _: Option<Opcode> = (1..).contains(&op).then_some(unsafe { std::mem::transmute(op) });
+LL +     let _: Option<Opcode> = (1..).contains(&op).then(|| unsafe { std::mem::transmute(op) });
+   |
 
 error: this transmute is always evaluated eagerly, even if the condition is false
   --> tests/ui/eager_transmute.rs:51:68
@@ -129,8 +140,9 @@ LL |     let _: Option<Opcode> = (..3).contains(&op).then_some(unsafe { std::mem
    |
 help: consider using `bool::then` to only transmute if the condition holds
    |
-LL |     let _: Option<Opcode> = (..3).contains(&op).then(|| unsafe { std::mem::transmute(op) });
-   |                                                 ~~~~ ++
+LL -     let _: Option<Opcode> = (..3).contains(&op).then_some(unsafe { std::mem::transmute(op) });
+LL +     let _: Option<Opcode> = (..3).contains(&op).then(|| unsafe { std::mem::transmute(op) });
+   |
 
 error: this transmute is always evaluated eagerly, even if the condition is false
   --> tests/ui/eager_transmute.rs:52:69
@@ -140,8 +152,9 @@ LL |     let _: Option<Opcode> = (..=3).contains(&op).then_some(unsafe { std::me
    |
 help: consider using `bool::then` to only transmute if the condition holds
    |
-LL |     let _: Option<Opcode> = (..=3).contains(&op).then(|| unsafe { std::mem::transmute(op) });
-   |                                                  ~~~~ ++
+LL -     let _: Option<Opcode> = (..=3).contains(&op).then_some(unsafe { std::mem::transmute(op) });
+LL +     let _: Option<Opcode> = (..=3).contains(&op).then(|| unsafe { std::mem::transmute(op) });
+   |
 
 error: this transmute is always evaluated eagerly, even if the condition is false
   --> tests/ui/eager_transmute.rs:61:24
@@ -151,8 +164,9 @@ LL |     (op < 4).then_some(std::mem::transmute::<_, Opcode>(op));
    |
 help: consider using `bool::then` to only transmute if the condition holds
    |
-LL |     (op < 4).then(|| std::mem::transmute::<_, Opcode>(op));
-   |              ~~~~ ++
+LL -     (op < 4).then_some(std::mem::transmute::<_, Opcode>(op));
+LL +     (op < 4).then(|| std::mem::transmute::<_, Opcode>(op));
+   |
 
 error: this transmute is always evaluated eagerly, even if the condition is false
   --> tests/ui/eager_transmute.rs:90:62
@@ -162,8 +176,9 @@ LL |     let _: Option<NonZero<u8>> = (v1 > 0).then_some(unsafe { std::mem::tran
    |
 help: consider using `bool::then` to only transmute if the condition holds
    |
-LL |     let _: Option<NonZero<u8>> = (v1 > 0).then(|| unsafe { std::mem::transmute(v1) });
-   |                                           ~~~~ ++
+LL -     let _: Option<NonZero<u8>> = (v1 > 0).then_some(unsafe { std::mem::transmute(v1) });
+LL +     let _: Option<NonZero<u8>> = (v1 > 0).then(|| unsafe { std::mem::transmute(v1) });
+   |
 
 error: this transmute is always evaluated eagerly, even if the condition is false
   --> tests/ui/eager_transmute.rs:96:86
@@ -173,8 +188,9 @@ LL |     let _: Option<NonMaxU8> = (v2 < NonZero::new(255u8).unwrap()).then_some
    |
 help: consider using `bool::then` to only transmute if the condition holds
    |
-LL |     let _: Option<NonMaxU8> = (v2 < NonZero::new(255u8).unwrap()).then(|| unsafe { std::mem::transmute(v2) });
-   |                                                                   ~~~~ ++
+LL -     let _: Option<NonMaxU8> = (v2 < NonZero::new(255u8).unwrap()).then_some(unsafe { std::mem::transmute(v2) });
+LL +     let _: Option<NonMaxU8> = (v2 < NonZero::new(255u8).unwrap()).then(|| unsafe { std::mem::transmute(v2) });
+   |
 
 error: this transmute is always evaluated eagerly, even if the condition is false
   --> tests/ui/eager_transmute.rs:102:93
@@ -184,8 +200,9 @@ LL |     let _: Option<NonZeroNonMaxU8> = (v2 < NonZero::new(255u8).unwrap()).th
    |
 help: consider using `bool::then` to only transmute if the condition holds
    |
-LL |     let _: Option<NonZeroNonMaxU8> = (v2 < NonZero::new(255u8).unwrap()).then(|| unsafe { std::mem::transmute(v2) });
-   |                                                                          ~~~~ ++
+LL -     let _: Option<NonZeroNonMaxU8> = (v2 < NonZero::new(255u8).unwrap()).then_some(unsafe { std::mem::transmute(v2) });
+LL +     let _: Option<NonZeroNonMaxU8> = (v2 < NonZero::new(255u8).unwrap()).then(|| unsafe { std::mem::transmute(v2) });
+   |
 
 error: aborting due to 17 previous errors
 
diff --git a/src/tools/clippy/tests/ui/empty_line_after/doc_comments.stderr b/src/tools/clippy/tests/ui/empty_line_after/doc_comments.stderr
index 7b197ae67e0..ca05a1b03eb 100644
--- a/src/tools/clippy/tests/ui/empty_line_after/doc_comments.stderr
+++ b/src/tools/clippy/tests/ui/empty_line_after/doc_comments.stderr
@@ -131,8 +131,9 @@ LL |       fn first_in_module() {}
    = help: if the empty line is unintentional, remove it
 help: if the comment should document the parent module use an inner doc comment
    |
-LL |     /*!
-   |       ~
+LL -     /**
+LL +     /*!
+   |
 
 error: empty line after doc comment
   --> tests/ui/empty_line_after/doc_comments.rs:85:5
diff --git a/src/tools/clippy/tests/ui/excessive_precision.stderr b/src/tools/clippy/tests/ui/excessive_precision.stderr
index 81e4fb6765d..b1f12eed420 100644
--- a/src/tools/clippy/tests/ui/excessive_precision.stderr
+++ b/src/tools/clippy/tests/ui/excessive_precision.stderr
@@ -8,8 +8,9 @@ LL |     const BAD32_1: f32 = 0.123_456_789_f32;
    = help: to override `-D warnings` add `#[allow(clippy::excessive_precision)]`
 help: consider changing the type or truncating it to
    |
-LL |     const BAD32_1: f32 = 0.123_456_79_f32;
-   |                          ~~~~~~~~~~~~~~~~
+LL -     const BAD32_1: f32 = 0.123_456_789_f32;
+LL +     const BAD32_1: f32 = 0.123_456_79_f32;
+   |
 
 error: float has excessive precision
   --> tests/ui/excessive_precision.rs:21:26
@@ -19,8 +20,9 @@ LL |     const BAD32_2: f32 = 0.123_456_789;
    |
 help: consider changing the type or truncating it to
    |
-LL |     const BAD32_2: f32 = 0.123_456_79;
-   |                          ~~~~~~~~~~~~
+LL -     const BAD32_2: f32 = 0.123_456_789;
+LL +     const BAD32_2: f32 = 0.123_456_79;
+   |
 
 error: float has excessive precision
   --> tests/ui/excessive_precision.rs:22:26
@@ -30,8 +32,9 @@ LL |     const BAD32_3: f32 = 0.100_000_000_000_1;
    |
 help: consider changing the type or truncating it to
    |
-LL |     const BAD32_3: f32 = 0.1;
-   |                          ~~~
+LL -     const BAD32_3: f32 = 0.100_000_000_000_1;
+LL +     const BAD32_3: f32 = 0.1;
+   |
 
 error: float has excessive precision
   --> tests/ui/excessive_precision.rs:23:29
@@ -41,8 +44,9 @@ LL |     const BAD32_EDGE: f32 = 1.000_000_9;
    |
 help: consider changing the type or truncating it to
    |
-LL |     const BAD32_EDGE: f32 = 1.000_001;
-   |                             ~~~~~~~~~
+LL -     const BAD32_EDGE: f32 = 1.000_000_9;
+LL +     const BAD32_EDGE: f32 = 1.000_001;
+   |
 
 error: float has excessive precision
   --> tests/ui/excessive_precision.rs:27:26
@@ -52,8 +56,9 @@ LL |     const BAD64_3: f64 = 0.100_000_000_000_000_000_1;
    |
 help: consider changing the type or truncating it to
    |
-LL |     const BAD64_3: f64 = 0.1;
-   |                          ~~~
+LL -     const BAD64_3: f64 = 0.100_000_000_000_000_000_1;
+LL +     const BAD64_3: f64 = 0.1;
+   |
 
 error: float has excessive precision
   --> tests/ui/excessive_precision.rs:30:22
@@ -63,8 +68,9 @@ LL |     println!("{:?}", 8.888_888_888_888_888_888_888);
    |
 help: consider changing the type or truncating it to
    |
-LL |     println!("{:?}", 8.888_888_888_888_89);
-   |                      ~~~~~~~~~~~~~~~~~~~~
+LL -     println!("{:?}", 8.888_888_888_888_888_888_888);
+LL +     println!("{:?}", 8.888_888_888_888_89);
+   |
 
 error: float has excessive precision
   --> tests/ui/excessive_precision.rs:41:22
@@ -74,8 +80,9 @@ LL |     let bad32: f32 = 1.123_456_789;
    |
 help: consider changing the type or truncating it to
    |
-LL |     let bad32: f32 = 1.123_456_8;
-   |                      ~~~~~~~~~~~
+LL -     let bad32: f32 = 1.123_456_789;
+LL +     let bad32: f32 = 1.123_456_8;
+   |
 
 error: float has excessive precision
   --> tests/ui/excessive_precision.rs:42:26
@@ -85,8 +92,9 @@ LL |     let bad32_suf: f32 = 1.123_456_789_f32;
    |
 help: consider changing the type or truncating it to
    |
-LL |     let bad32_suf: f32 = 1.123_456_8_f32;
-   |                          ~~~~~~~~~~~~~~~
+LL -     let bad32_suf: f32 = 1.123_456_789_f32;
+LL +     let bad32_suf: f32 = 1.123_456_8_f32;
+   |
 
 error: float has excessive precision
   --> tests/ui/excessive_precision.rs:43:21
@@ -96,8 +104,9 @@ LL |     let bad32_inf = 1.123_456_789_f32;
    |
 help: consider changing the type or truncating it to
    |
-LL |     let bad32_inf = 1.123_456_8_f32;
-   |                     ~~~~~~~~~~~~~~~
+LL -     let bad32_inf = 1.123_456_789_f32;
+LL +     let bad32_inf = 1.123_456_8_f32;
+   |
 
 error: float has excessive precision
   --> tests/ui/excessive_precision.rs:53:36
@@ -107,8 +116,9 @@ LL |     let bad_vec32: Vec<f32> = vec![0.123_456_789];
    |
 help: consider changing the type or truncating it to
    |
-LL |     let bad_vec32: Vec<f32> = vec![0.123_456_79];
-   |                                    ~~~~~~~~~~~~
+LL -     let bad_vec32: Vec<f32> = vec![0.123_456_789];
+LL +     let bad_vec32: Vec<f32> = vec![0.123_456_79];
+   |
 
 error: float has excessive precision
   --> tests/ui/excessive_precision.rs:54:36
@@ -118,8 +128,9 @@ LL |     let bad_vec64: Vec<f64> = vec![0.123_456_789_123_456_789];
    |
 help: consider changing the type or truncating it to
    |
-LL |     let bad_vec64: Vec<f64> = vec![0.123_456_789_123_456_78];
-   |                                    ~~~~~~~~~~~~~~~~~~~~~~~~
+LL -     let bad_vec64: Vec<f64> = vec![0.123_456_789_123_456_789];
+LL +     let bad_vec64: Vec<f64> = vec![0.123_456_789_123_456_78];
+   |
 
 error: float has excessive precision
   --> tests/ui/excessive_precision.rs:58:24
@@ -129,8 +140,9 @@ LL |     let bad_e32: f32 = 1.123_456_788_888e-10;
    |
 help: consider changing the type or truncating it to
    |
-LL |     let bad_e32: f32 = 1.123_456_8e-10;
-   |                        ~~~~~~~~~~~~~~~
+LL -     let bad_e32: f32 = 1.123_456_788_888e-10;
+LL +     let bad_e32: f32 = 1.123_456_8e-10;
+   |
 
 error: float has excessive precision
   --> tests/ui/excessive_precision.rs:61:27
@@ -140,8 +152,9 @@ LL |     let bad_bige32: f32 = 1.123_456_788_888E-10;
    |
 help: consider changing the type or truncating it to
    |
-LL |     let bad_bige32: f32 = 1.123_456_8E-10;
-   |                           ~~~~~~~~~~~~~~~
+LL -     let bad_bige32: f32 = 1.123_456_788_888E-10;
+LL +     let bad_bige32: f32 = 1.123_456_8E-10;
+   |
 
 error: float has excessive precision
   --> tests/ui/excessive_precision.rs:70:13
@@ -151,8 +164,9 @@ LL |     let _ = 2.225_073_858_507_201_1e-308_f64;
    |
 help: consider changing the type or truncating it to
    |
-LL |     let _ = 2.225_073_858_507_201e-308_f64;
-   |             ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+LL -     let _ = 2.225_073_858_507_201_1e-308_f64;
+LL +     let _ = 2.225_073_858_507_201e-308_f64;
+   |
 
 error: float has excessive precision
   --> tests/ui/excessive_precision.rs:73:13
@@ -162,8 +176,9 @@ LL |     let _ = 1.000_000_000_000_001e-324_f64;
    |
 help: consider changing the type or truncating it to
    |
-LL |     let _ = 0_f64;
-   |             ~~~~~
+LL -     let _ = 1.000_000_000_000_001e-324_f64;
+LL +     let _ = 0_f64;
+   |
 
 error: float has excessive precision
   --> tests/ui/excessive_precision.rs:83:20
@@ -173,8 +188,9 @@ LL |     const _: f64 = 3.0000000000000000e+00;
    |
 help: consider changing the type or truncating it to
    |
-LL |     const _: f64 = 3.0;
-   |                    ~~~
+LL -     const _: f64 = 3.0000000000000000e+00;
+LL +     const _: f64 = 3.0;
+   |
 
 error: aborting due to 16 previous errors
 
diff --git a/src/tools/clippy/tests/ui/fn_to_numeric_cast_any.stderr b/src/tools/clippy/tests/ui/fn_to_numeric_cast_any.stderr
index a05b7138bc9..0238e3a9136 100644
--- a/src/tools/clippy/tests/ui/fn_to_numeric_cast_any.stderr
+++ b/src/tools/clippy/tests/ui/fn_to_numeric_cast_any.stderr
@@ -9,7 +9,7 @@ LL |     let _ = foo as i8;
 help: did you mean to invoke the function?
    |
 LL |     let _ = foo() as i8;
-   |             ~~~~~~~~~~~
+   |                ++
 
 error: casting function pointer `foo` to `i16`
   --> tests/ui/fn_to_numeric_cast_any.rs:26:13
@@ -20,7 +20,7 @@ LL |     let _ = foo as i16;
 help: did you mean to invoke the function?
    |
 LL |     let _ = foo() as i16;
-   |             ~~~~~~~~~~~~
+   |                ++
 
 error: casting function pointer `foo` to `i32`
   --> tests/ui/fn_to_numeric_cast_any.rs:28:13
@@ -31,7 +31,7 @@ LL |     let _ = foo as i32;
 help: did you mean to invoke the function?
    |
 LL |     let _ = foo() as i32;
-   |             ~~~~~~~~~~~~
+   |                ++
 
 error: casting function pointer `foo` to `i64`
   --> tests/ui/fn_to_numeric_cast_any.rs:30:13
@@ -42,7 +42,7 @@ LL |     let _ = foo as i64;
 help: did you mean to invoke the function?
    |
 LL |     let _ = foo() as i64;
-   |             ~~~~~~~~~~~~
+   |                ++
 
 error: casting function pointer `foo` to `i128`
   --> tests/ui/fn_to_numeric_cast_any.rs:32:13
@@ -53,7 +53,7 @@ LL |     let _ = foo as i128;
 help: did you mean to invoke the function?
    |
 LL |     let _ = foo() as i128;
-   |             ~~~~~~~~~~~~~
+   |                ++
 
 error: casting function pointer `foo` to `isize`
   --> tests/ui/fn_to_numeric_cast_any.rs:34:13
@@ -64,7 +64,7 @@ LL |     let _ = foo as isize;
 help: did you mean to invoke the function?
    |
 LL |     let _ = foo() as isize;
-   |             ~~~~~~~~~~~~~~
+   |                ++
 
 error: casting function pointer `foo` to `u8`
   --> tests/ui/fn_to_numeric_cast_any.rs:37:13
@@ -75,7 +75,7 @@ LL |     let _ = foo as u8;
 help: did you mean to invoke the function?
    |
 LL |     let _ = foo() as u8;
-   |             ~~~~~~~~~~~
+   |                ++
 
 error: casting function pointer `foo` to `u16`
   --> tests/ui/fn_to_numeric_cast_any.rs:39:13
@@ -86,7 +86,7 @@ LL |     let _ = foo as u16;
 help: did you mean to invoke the function?
    |
 LL |     let _ = foo() as u16;
-   |             ~~~~~~~~~~~~
+   |                ++
 
 error: casting function pointer `foo` to `u32`
   --> tests/ui/fn_to_numeric_cast_any.rs:41:13
@@ -97,7 +97,7 @@ LL |     let _ = foo as u32;
 help: did you mean to invoke the function?
    |
 LL |     let _ = foo() as u32;
-   |             ~~~~~~~~~~~~
+   |                ++
 
 error: casting function pointer `foo` to `u64`
   --> tests/ui/fn_to_numeric_cast_any.rs:43:13
@@ -108,7 +108,7 @@ LL |     let _ = foo as u64;
 help: did you mean to invoke the function?
    |
 LL |     let _ = foo() as u64;
-   |             ~~~~~~~~~~~~
+   |                ++
 
 error: casting function pointer `foo` to `u128`
   --> tests/ui/fn_to_numeric_cast_any.rs:45:13
@@ -119,7 +119,7 @@ LL |     let _ = foo as u128;
 help: did you mean to invoke the function?
    |
 LL |     let _ = foo() as u128;
-   |             ~~~~~~~~~~~~~
+   |                ++
 
 error: casting function pointer `foo` to `usize`
   --> tests/ui/fn_to_numeric_cast_any.rs:47:13
@@ -130,7 +130,7 @@ LL |     let _ = foo as usize;
 help: did you mean to invoke the function?
    |
 LL |     let _ = foo() as usize;
-   |             ~~~~~~~~~~~~~~
+   |                ++
 
 error: casting function pointer `Struct::static_method` to `usize`
   --> tests/ui/fn_to_numeric_cast_any.rs:52:13
@@ -141,7 +141,7 @@ LL |     let _ = Struct::static_method as usize;
 help: did you mean to invoke the function?
    |
 LL |     let _ = Struct::static_method() as usize;
-   |             ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+   |                                  ++
 
 error: casting function pointer `f` to `usize`
   --> tests/ui/fn_to_numeric_cast_any.rs:57:5
@@ -152,7 +152,7 @@ LL |     f as usize
 help: did you mean to invoke the function?
    |
 LL |     f() as usize
-   |
+   |      ++
 
 error: casting function pointer `T::static_method` to `usize`
   --> tests/ui/fn_to_numeric_cast_any.rs:62:5
@@ -163,7 +163,7 @@ LL |     T::static_method as usize
 help: did you mean to invoke the function?
    |
 LL |     T::static_method() as usize
-   |
+   |                     ++
 
 error: casting function pointer `(clos as fn(u32) -> u32)` to `usize`
   --> tests/ui/fn_to_numeric_cast_any.rs:69:13
@@ -174,7 +174,7 @@ LL |     let _ = (clos as fn(u32) -> u32) as usize;
 help: did you mean to invoke the function?
    |
 LL |     let _ = (clos as fn(u32) -> u32)() as usize;
-   |             ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+   |                                     ++
 
 error: casting function pointer `foo` to `*const ()`
   --> tests/ui/fn_to_numeric_cast_any.rs:74:13
@@ -185,7 +185,7 @@ LL |     let _ = foo as *const ();
 help: did you mean to invoke the function?
    |
 LL |     let _ = foo() as *const ();
-   |             ~~~~~~~~~~~~~~~~~~
+   |                ++
 
 error: aborting due to 17 previous errors
 
diff --git a/src/tools/clippy/tests/ui/for_kv_map.stderr b/src/tools/clippy/tests/ui/for_kv_map.stderr
index adcc3ab8fdb..11b61c8cbc6 100644
--- a/src/tools/clippy/tests/ui/for_kv_map.stderr
+++ b/src/tools/clippy/tests/ui/for_kv_map.stderr
@@ -8,8 +8,9 @@ LL |     for (_, v) in &m {
    = help: to override `-D warnings` add `#[allow(clippy::for_kv_map)]`
 help: use the corresponding method
    |
-LL |     for v in m.values() {
-   |         ~    ~~~~~~~~~~
+LL -     for (_, v) in &m {
+LL +     for v in m.values() {
+   |
 
 error: you seem to want to iterate on a map's values
   --> tests/ui/for_kv_map.rs:16:19
@@ -19,8 +20,9 @@ LL |     for (_, v) in &*m {
    |
 help: use the corresponding method
    |
-LL |     for v in (*m).values() {
-   |         ~    ~~~~~~~~~~~~~
+LL -     for (_, v) in &*m {
+LL +     for v in (*m).values() {
+   |
 
 error: you seem to want to iterate on a map's values
   --> tests/ui/for_kv_map.rs:25:19
@@ -30,8 +32,9 @@ LL |     for (_, v) in &mut m {
    |
 help: use the corresponding method
    |
-LL |     for v in m.values_mut() {
-   |         ~    ~~~~~~~~~~~~~~
+LL -     for (_, v) in &mut m {
+LL +     for v in m.values_mut() {
+   |
 
 error: you seem to want to iterate on a map's values
   --> tests/ui/for_kv_map.rs:31:19
@@ -41,8 +44,9 @@ LL |     for (_, v) in &mut *m {
    |
 help: use the corresponding method
    |
-LL |     for v in (*m).values_mut() {
-   |         ~    ~~~~~~~~~~~~~~~~~
+LL -     for (_, v) in &mut *m {
+LL +     for v in (*m).values_mut() {
+   |
 
 error: you seem to want to iterate on a map's keys
   --> tests/ui/for_kv_map.rs:38:24
@@ -52,8 +56,9 @@ LL |     for (k, _value) in rm {
    |
 help: use the corresponding method
    |
-LL |     for k in rm.keys() {
-   |         ~    ~~~~~~~~~
+LL -     for (k, _value) in rm {
+LL +     for k in rm.keys() {
+   |
 
 error: you seem to want to iterate on a map's keys
   --> tests/ui/for_kv_map.rs:45:32
@@ -63,8 +68,9 @@ LL |     'label: for (k, _value) in rm {
    |
 help: use the corresponding method
    |
-LL |     'label: for k in rm.keys() {
-   |                 ~    ~~~~~~~~~
+LL -     'label: for (k, _value) in rm {
+LL +     'label: for k in rm.keys() {
+   |
 
 error: aborting due to 6 previous errors
 
diff --git a/src/tools/clippy/tests/ui/four_forward_slashes.stderr b/src/tools/clippy/tests/ui/four_forward_slashes.stderr
index 3606a2227a0..d5bf71fadc7 100644
--- a/src/tools/clippy/tests/ui/four_forward_slashes.stderr
+++ b/src/tools/clippy/tests/ui/four_forward_slashes.stderr
@@ -9,6 +9,8 @@ LL | | fn a() {}
    = help: to override `-D warnings` add `#[allow(clippy::four_forward_slashes)]`
 help: make this a doc comment by removing one `/`
    |
+LL - //// whoops
+LL - fn a() {}
 LL + /// whoops
    |
 
@@ -22,6 +24,8 @@ LL | | fn b() {}
    |
 help: make this a doc comment by removing one `/`
    |
+LL - //// whoops
+LL - #[allow(dead_code)]
 LL + /// whoops
    |
 
@@ -50,6 +54,8 @@ LL | | fn g() {}
    |
 help: make this a doc comment by removing one `/`
    |
+LL - //// between attributes
+LL - #[allow(dead_code)]
 LL + /// between attributes
    |
 
@@ -62,6 +68,8 @@ LL | | fn h() {}
    |
 help: make this a doc comment by removing one `/`
    |
+LL -     //// not very start of contents
+LL - fn h() {}
 LL + /// not very start of contents
    |
 
diff --git a/src/tools/clippy/tests/ui/four_forward_slashes_first_line.stderr b/src/tools/clippy/tests/ui/four_forward_slashes_first_line.stderr
index 81732346412..83bfb60eb16 100644
--- a/src/tools/clippy/tests/ui/four_forward_slashes_first_line.stderr
+++ b/src/tools/clippy/tests/ui/four_forward_slashes_first_line.stderr
@@ -9,6 +9,8 @@ LL | | fn a() {}
    = help: to override `-D warnings` add `#[allow(clippy::four_forward_slashes)]`
 help: make this a doc comment by removing one `/`
    |
+LL - //// borked doc comment on the first line. doesn't combust!
+LL - fn a() {}
 LL + /// borked doc comment on the first line. doesn't combust!
    |
 
diff --git a/src/tools/clippy/tests/ui/get_unwrap.stderr b/src/tools/clippy/tests/ui/get_unwrap.stderr
index 8eacb249c60..fc6c0b9299c 100644
--- a/src/tools/clippy/tests/ui/get_unwrap.stderr
+++ b/src/tools/clippy/tests/ui/get_unwrap.stderr
@@ -11,8 +11,9 @@ LL | #![deny(clippy::get_unwrap)]
    |         ^^^^^^^^^^^^^^^^^^
 help: using `[]` is clearer and more concise
    |
-LL |         let _ = &boxed_slice[1];
-   |                 ~~~~~~~~~~~~~~~
+LL -         let _ = boxed_slice.get(1).unwrap();
+LL +         let _ = &boxed_slice[1];
+   |
 
 error: used `unwrap()` on an `Option` value
   --> tests/ui/get_unwrap.rs:37:17
@@ -33,8 +34,9 @@ LL |         let _ = some_slice.get(0).unwrap();
    |
 help: using `[]` is clearer and more concise
    |
-LL |         let _ = &some_slice[0];
-   |                 ~~~~~~~~~~~~~~
+LL -         let _ = some_slice.get(0).unwrap();
+LL +         let _ = &some_slice[0];
+   |
 
 error: used `unwrap()` on an `Option` value
   --> tests/ui/get_unwrap.rs:38:17
@@ -53,8 +55,9 @@ LL |         let _ = some_vec.get(0).unwrap();
    |
 help: using `[]` is clearer and more concise
    |
-LL |         let _ = &some_vec[0];
-   |                 ~~~~~~~~~~~~
+LL -         let _ = some_vec.get(0).unwrap();
+LL +         let _ = &some_vec[0];
+   |
 
 error: used `unwrap()` on an `Option` value
   --> tests/ui/get_unwrap.rs:39:17
@@ -73,8 +76,9 @@ LL |         let _ = some_vecdeque.get(0).unwrap();
    |
 help: using `[]` is clearer and more concise
    |
-LL |         let _ = &some_vecdeque[0];
-   |                 ~~~~~~~~~~~~~~~~~
+LL -         let _ = some_vecdeque.get(0).unwrap();
+LL +         let _ = &some_vecdeque[0];
+   |
 
 error: used `unwrap()` on an `Option` value
   --> tests/ui/get_unwrap.rs:40:17
@@ -93,8 +97,9 @@ LL |         let _ = some_hashmap.get(&1).unwrap();
    |
 help: using `[]` is clearer and more concise
    |
-LL |         let _ = &some_hashmap[&1];
-   |                 ~~~~~~~~~~~~~~~~~
+LL -         let _ = some_hashmap.get(&1).unwrap();
+LL +         let _ = &some_hashmap[&1];
+   |
 
 error: used `unwrap()` on an `Option` value
   --> tests/ui/get_unwrap.rs:41:17
@@ -113,8 +118,9 @@ LL |         let _ = some_btreemap.get(&1).unwrap();
    |
 help: using `[]` is clearer and more concise
    |
-LL |         let _ = &some_btreemap[&1];
-   |                 ~~~~~~~~~~~~~~~~~~
+LL -         let _ = some_btreemap.get(&1).unwrap();
+LL +         let _ = &some_btreemap[&1];
+   |
 
 error: used `unwrap()` on an `Option` value
   --> tests/ui/get_unwrap.rs:42:17
@@ -133,8 +139,9 @@ LL |         let _: u8 = *boxed_slice.get(1).unwrap();
    |
 help: using `[]` is clearer and more concise
    |
-LL |         let _: u8 = boxed_slice[1];
-   |                     ~~~~~~~~~~~~~~
+LL -         let _: u8 = *boxed_slice.get(1).unwrap();
+LL +         let _: u8 = boxed_slice[1];
+   |
 
 error: used `unwrap()` on an `Option` value
   --> tests/ui/get_unwrap.rs:46:22
@@ -153,8 +160,9 @@ LL |         *boxed_slice.get_mut(0).unwrap() = 1;
    |
 help: using `[]` is clearer and more concise
    |
-LL |         boxed_slice[0] = 1;
-   |         ~~~~~~~~~~~~~~
+LL -         *boxed_slice.get_mut(0).unwrap() = 1;
+LL +         boxed_slice[0] = 1;
+   |
 
 error: used `unwrap()` on an `Option` value
   --> tests/ui/get_unwrap.rs:51:10
@@ -173,8 +181,9 @@ LL |         *some_slice.get_mut(0).unwrap() = 1;
    |
 help: using `[]` is clearer and more concise
    |
-LL |         some_slice[0] = 1;
-   |         ~~~~~~~~~~~~~
+LL -         *some_slice.get_mut(0).unwrap() = 1;
+LL +         some_slice[0] = 1;
+   |
 
 error: used `unwrap()` on an `Option` value
   --> tests/ui/get_unwrap.rs:52:10
@@ -193,8 +202,9 @@ LL |         *some_vec.get_mut(0).unwrap() = 1;
    |
 help: using `[]` is clearer and more concise
    |
-LL |         some_vec[0] = 1;
-   |         ~~~~~~~~~~~
+LL -         *some_vec.get_mut(0).unwrap() = 1;
+LL +         some_vec[0] = 1;
+   |
 
 error: used `unwrap()` on an `Option` value
   --> tests/ui/get_unwrap.rs:53:10
@@ -213,8 +223,9 @@ LL |         *some_vecdeque.get_mut(0).unwrap() = 1;
    |
 help: using `[]` is clearer and more concise
    |
-LL |         some_vecdeque[0] = 1;
-   |         ~~~~~~~~~~~~~~~~
+LL -         *some_vecdeque.get_mut(0).unwrap() = 1;
+LL +         some_vecdeque[0] = 1;
+   |
 
 error: used `unwrap()` on an `Option` value
   --> tests/ui/get_unwrap.rs:54:10
@@ -233,8 +244,9 @@ LL |         let _ = some_vec.get(0..1).unwrap().to_vec();
    |
 help: using `[]` is clearer and more concise
    |
-LL |         let _ = some_vec[0..1].to_vec();
-   |                 ~~~~~~~~~~~~~~
+LL -         let _ = some_vec.get(0..1).unwrap().to_vec();
+LL +         let _ = some_vec[0..1].to_vec();
+   |
 
 error: used `unwrap()` on an `Option` value
   --> tests/ui/get_unwrap.rs:66:17
@@ -253,8 +265,9 @@ LL |         let _ = some_vec.get_mut(0..1).unwrap().to_vec();
    |
 help: using `[]` is clearer and more concise
    |
-LL |         let _ = some_vec[0..1].to_vec();
-   |                 ~~~~~~~~~~~~~~
+LL -         let _ = some_vec.get_mut(0..1).unwrap().to_vec();
+LL +         let _ = some_vec[0..1].to_vec();
+   |
 
 error: used `unwrap()` on an `Option` value
   --> tests/ui/get_unwrap.rs:67:17
@@ -273,8 +286,9 @@ LL |         let _x: &i32 = f.get(1 + 2).unwrap();
    |
 help: using `[]` is clearer and more concise
    |
-LL |         let _x: &i32 = &f[1 + 2];
-   |                        ~~~~~~~~~
+LL -         let _x: &i32 = f.get(1 + 2).unwrap();
+LL +         let _x: &i32 = &f[1 + 2];
+   |
 
 error: called `.get().unwrap()` on a slice
   --> tests/ui/get_unwrap.rs:81:18
@@ -284,8 +298,9 @@ LL |         let _x = f.get(1 + 2).unwrap().to_string();
    |
 help: using `[]` is clearer and more concise
    |
-LL |         let _x = f[1 + 2].to_string();
-   |                  ~~~~~~~~
+LL -         let _x = f.get(1 + 2).unwrap().to_string();
+LL +         let _x = f[1 + 2].to_string();
+   |
 
 error: called `.get().unwrap()` on a slice
   --> tests/ui/get_unwrap.rs:84:18
@@ -295,8 +310,9 @@ LL |         let _x = f.get(1 + 2).unwrap().abs();
    |
 help: using `[]` is clearer and more concise
    |
-LL |         let _x = f[1 + 2].abs();
-   |                  ~~~~~~~~
+LL -         let _x = f.get(1 + 2).unwrap().abs();
+LL +         let _x = f[1 + 2].abs();
+   |
 
 error: called `.get_mut().unwrap()` on a slice
   --> tests/ui/get_unwrap.rs:101:33
@@ -306,8 +322,9 @@ LL |                         let b = rest.get_mut(linidx(j, k) - linidx(i, k) -
    |
 help: using `[]` is clearer and more concise
    |
-LL |                         let b = &mut rest[linidx(j, k) - linidx(i, k) - 1];
-   |                                 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+LL -                         let b = rest.get_mut(linidx(j, k) - linidx(i, k) - 1).unwrap();
+LL +                         let b = &mut rest[linidx(j, k) - linidx(i, k) - 1];
+   |
 
 error: aborting due to 30 previous errors
 
diff --git a/src/tools/clippy/tests/ui/implicit_hasher.stderr b/src/tools/clippy/tests/ui/implicit_hasher.stderr
index 442f4789aac..01d08a1bd9b 100644
--- a/src/tools/clippy/tests/ui/implicit_hasher.stderr
+++ b/src/tools/clippy/tests/ui/implicit_hasher.stderr
@@ -79,7 +79,7 @@ LL | pub fn map(map: &mut HashMap<i32, i32>) {}
 help: add a type parameter for `BuildHasher`
    |
 LL | pub fn map<S: ::std::hash::BuildHasher>(map: &mut HashMap<i32, i32, S>) {}
-   |           +++++++++++++++++++++++++++++           ~~~~~~~~~~~~~~~~~~~~
+   |           +++++++++++++++++++++++++++++                           +++
 
 error: parameter of type `HashSet` should be generalized over different hashers
   --> tests/ui/implicit_hasher.rs:70:22
@@ -90,7 +90,7 @@ LL | pub fn set(set: &mut HashSet<i32>) {}
 help: add a type parameter for `BuildHasher`
    |
 LL | pub fn set<S: ::std::hash::BuildHasher>(set: &mut HashSet<i32, S>) {}
-   |           +++++++++++++++++++++++++++++           ~~~~~~~~~~~~~~~
+   |           +++++++++++++++++++++++++++++                      +++
 
 error: impl for `HashMap` should be generalized over different hashers
   --> tests/ui/implicit_hasher.rs:76:43
@@ -115,7 +115,7 @@ LL | pub async fn election_vote(_data: HashMap<i32, i32>) {}
 help: add a type parameter for `BuildHasher`
    |
 LL | pub async fn election_vote<S: ::std::hash::BuildHasher>(_data: HashMap<i32, i32, S>) {}
-   |                           +++++++++++++++++++++++++++++        ~~~~~~~~~~~~~~~~~~~~
+   |                           +++++++++++++++++++++++++++++                        +++
 
 error: aborting due to 9 previous errors
 
diff --git a/src/tools/clippy/tests/ui/implicit_return.stderr b/src/tools/clippy/tests/ui/implicit_return.stderr
index 3b06f26f5a0..936a779fa74 100644
--- a/src/tools/clippy/tests/ui/implicit_return.stderr
+++ b/src/tools/clippy/tests/ui/implicit_return.stderr
@@ -9,7 +9,7 @@ LL |     true
 help: add `return` as shown
    |
 LL |     return true
-   |
+   |     ++++++
 
 error: missing `return` statement
   --> tests/ui/implicit_return.rs:19:15
@@ -20,7 +20,7 @@ LL |     if true { true } else { false }
 help: add `return` as shown
    |
 LL |     if true { return true } else { false }
-   |               ~~~~~~~~~~~
+   |               ++++++
 
 error: missing `return` statement
   --> tests/ui/implicit_return.rs:19:29
@@ -31,7 +31,7 @@ LL |     if true { true } else { false }
 help: add `return` as shown
    |
 LL |     if true { true } else { return false }
-   |                             ~~~~~~~~~~~~
+   |                             ++++++
 
 error: missing `return` statement
   --> tests/ui/implicit_return.rs:25:17
@@ -42,7 +42,7 @@ LL |         true => false,
 help: add `return` as shown
    |
 LL |         true => return false,
-   |                 ~~~~~~~~~~~~
+   |                 ++++++
 
 error: missing `return` statement
   --> tests/ui/implicit_return.rs:26:20
@@ -53,7 +53,7 @@ LL |         false => { true },
 help: add `return` as shown
    |
 LL |         false => { return true },
-   |                    ~~~~~~~~~~~
+   |                    ++++++
 
 error: missing `return` statement
   --> tests/ui/implicit_return.rs:39:9
@@ -63,8 +63,9 @@ LL |         break true;
    |
 help: change `break` to `return` as shown
    |
-LL |         return true;
-   |         ~~~~~~~~~~~
+LL -         break true;
+LL +         return true;
+   |
 
 error: missing `return` statement
   --> tests/ui/implicit_return.rs:46:13
@@ -74,8 +75,9 @@ LL |             break true;
    |
 help: change `break` to `return` as shown
    |
-LL |             return true;
-   |             ~~~~~~~~~~~
+LL -             break true;
+LL +             return true;
+   |
 
 error: missing `return` statement
   --> tests/ui/implicit_return.rs:54:13
@@ -85,8 +87,9 @@ LL |             break true;
    |
 help: change `break` to `return` as shown
    |
-LL |             return true;
-   |             ~~~~~~~~~~~
+LL -             break true;
+LL +             return true;
+   |
 
 error: missing `return` statement
   --> tests/ui/implicit_return.rs:72:18
@@ -97,7 +100,7 @@ LL |     let _ = || { true };
 help: add `return` as shown
    |
 LL |     let _ = || { return true };
-   |                  ~~~~~~~~~~~
+   |                  ++++++
 
 error: missing `return` statement
   --> tests/ui/implicit_return.rs:73:16
@@ -108,7 +111,7 @@ LL |     let _ = || true;
 help: add `return` as shown
    |
 LL |     let _ = || return true;
-   |                ~~~~~~~~~~~
+   |                ++++++
 
 error: missing `return` statement
   --> tests/ui/implicit_return.rs:81:5
@@ -119,7 +122,7 @@ LL |     format!("test {}", "test")
 help: add `return` as shown
    |
 LL |     return format!("test {}", "test")
-   |
+   |     ++++++
 
 error: missing `return` statement
   --> tests/ui/implicit_return.rs:90:5
@@ -130,7 +133,7 @@ LL |     m!(true, false)
 help: add `return` as shown
    |
 LL |     return m!(true, false)
-   |
+   |     ++++++
 
 error: missing `return` statement
   --> tests/ui/implicit_return.rs:96:13
@@ -140,8 +143,9 @@ LL |             break true;
    |
 help: change `break` to `return` as shown
    |
-LL |             return true;
-   |             ~~~~~~~~~~~
+LL -             break true;
+LL +             return true;
+   |
 
 error: missing `return` statement
   --> tests/ui/implicit_return.rs:101:17
@@ -151,8 +155,9 @@ LL |                 break 'outer false;
    |
 help: change `break` to `return` as shown
    |
-LL |                 return false;
-   |                 ~~~~~~~~~~~~
+LL -                 break 'outer false;
+LL +                 return false;
+   |
 
 error: missing `return` statement
   --> tests/ui/implicit_return.rs:116:5
@@ -164,10 +169,8 @@ LL | |     }
    |
 help: add `return` as shown
    |
-LL ~     return loop {
-LL +         m!(true);
-LL +     }
-   |
+LL |     return loop {
+   |     ++++++
 
 error: missing `return` statement
   --> tests/ui/implicit_return.rs:130:5
@@ -178,7 +181,7 @@ LL |     true
 help: add `return` as shown
    |
 LL |     return true
-   |
+   |     ++++++
 
 error: aborting due to 16 previous errors
 
diff --git a/src/tools/clippy/tests/ui/iter_nth.stderr b/src/tools/clippy/tests/ui/iter_nth.stderr
index 178463f5347..1167a604ecd 100644
--- a/src/tools/clippy/tests/ui/iter_nth.stderr
+++ b/src/tools/clippy/tests/ui/iter_nth.stderr
@@ -8,8 +8,9 @@ LL |         let bad_vec = some_vec.iter().nth(3);
    = help: to override `-D warnings` add `#[allow(clippy::iter_nth)]`
 help: `get` is equivalent but more concise
    |
-LL |         let bad_vec = some_vec.get(3);
-   |                                ~~~
+LL -         let bad_vec = some_vec.iter().nth(3);
+LL +         let bad_vec = some_vec.get(3);
+   |
 
 error: called `.iter().nth()` on a slice
   --> tests/ui/iter_nth.rs:35:26
@@ -19,8 +20,9 @@ LL |         let bad_slice = &some_vec[..].iter().nth(3);
    |
 help: `get` is equivalent but more concise
    |
-LL |         let bad_slice = &some_vec[..].get(3);
-   |                                       ~~~
+LL -         let bad_slice = &some_vec[..].iter().nth(3);
+LL +         let bad_slice = &some_vec[..].get(3);
+   |
 
 error: called `.iter().nth()` on a slice
   --> tests/ui/iter_nth.rs:36:31
@@ -30,8 +32,9 @@ LL |         let bad_boxed_slice = boxed_slice.iter().nth(3);
    |
 help: `get` is equivalent but more concise
    |
-LL |         let bad_boxed_slice = boxed_slice.get(3);
-   |                                           ~~~
+LL -         let bad_boxed_slice = boxed_slice.iter().nth(3);
+LL +         let bad_boxed_slice = boxed_slice.get(3);
+   |
 
 error: called `.iter().nth()` on a `VecDeque`
   --> tests/ui/iter_nth.rs:37:29
@@ -41,8 +44,9 @@ LL |         let bad_vec_deque = some_vec_deque.iter().nth(3);
    |
 help: `get` is equivalent but more concise
    |
-LL |         let bad_vec_deque = some_vec_deque.get(3);
-   |                                            ~~~
+LL -         let bad_vec_deque = some_vec_deque.iter().nth(3);
+LL +         let bad_vec_deque = some_vec_deque.get(3);
+   |
 
 error: called `.iter_mut().nth()` on a `Vec`
   --> tests/ui/iter_nth.rs:42:23
@@ -52,8 +56,9 @@ LL |         let bad_vec = some_vec.iter_mut().nth(3);
    |
 help: `get_mut` is equivalent but more concise
    |
-LL |         let bad_vec = some_vec.get_mut(3);
-   |                                ~~~~~~~
+LL -         let bad_vec = some_vec.iter_mut().nth(3);
+LL +         let bad_vec = some_vec.get_mut(3);
+   |
 
 error: called `.iter_mut().nth()` on a slice
   --> tests/ui/iter_nth.rs:45:26
@@ -63,8 +68,9 @@ LL |         let bad_slice = &some_vec[..].iter_mut().nth(3);
    |
 help: `get_mut` is equivalent but more concise
    |
-LL |         let bad_slice = &some_vec[..].get_mut(3);
-   |                                       ~~~~~~~
+LL -         let bad_slice = &some_vec[..].iter_mut().nth(3);
+LL +         let bad_slice = &some_vec[..].get_mut(3);
+   |
 
 error: called `.iter_mut().nth()` on a `VecDeque`
   --> tests/ui/iter_nth.rs:48:29
@@ -74,8 +80,9 @@ LL |         let bad_vec_deque = some_vec_deque.iter_mut().nth(3);
    |
 help: `get_mut` is equivalent but more concise
    |
-LL |         let bad_vec_deque = some_vec_deque.get_mut(3);
-   |                                            ~~~~~~~
+LL -         let bad_vec_deque = some_vec_deque.iter_mut().nth(3);
+LL +         let bad_vec_deque = some_vec_deque.get_mut(3);
+   |
 
 error: called `.iter().nth()` on a `Vec`
   --> tests/ui/iter_nth.rs:52:5
@@ -85,8 +92,9 @@ LL |     vec_ref.iter().nth(3);
    |
 help: `get` is equivalent but more concise
    |
-LL |     vec_ref.get(3);
-   |             ~~~
+LL -     vec_ref.iter().nth(3);
+LL +     vec_ref.get(3);
+   |
 
 error: aborting due to 8 previous errors
 
diff --git a/src/tools/clippy/tests/ui/join_absolute_paths.stderr b/src/tools/clippy/tests/ui/join_absolute_paths.stderr
index e7fd5508823..300946bf3b5 100644
--- a/src/tools/clippy/tests/ui/join_absolute_paths.stderr
+++ b/src/tools/clippy/tests/ui/join_absolute_paths.stderr
@@ -9,12 +9,14 @@ LL |     path.join("/sh");
    = help: to override `-D warnings` add `#[allow(clippy::join_absolute_paths)]`
 help: if this is unintentional, try removing the starting separator
    |
-LL |     path.join("sh");
-   |               ~~~~
+LL -     path.join("/sh");
+LL +     path.join("sh");
+   |
 help: if this is intentional, consider using `Path::new`
    |
-LL |     PathBuf::from("/sh");
-   |     ~~~~~~~~~~~~~~~~~~~~
+LL -     path.join("/sh");
+LL +     PathBuf::from("/sh");
+   |
 
 error: argument to `Path::join` starts with a path separator
   --> tests/ui/join_absolute_paths.rs:14:15
@@ -25,12 +27,14 @@ LL |     path.join("\\user");
    = note: joining a path starting with separator will replace the path instead
 help: if this is unintentional, try removing the starting separator
    |
-LL |     path.join("\user");
-   |               ~~~~~~~
+LL -     path.join("\\user");
+LL +     path.join("\user");
+   |
 help: if this is intentional, consider using `Path::new`
    |
-LL |     PathBuf::from("\\user");
-   |     ~~~~~~~~~~~~~~~~~~~~~~~
+LL -     path.join("\\user");
+LL +     PathBuf::from("\\user");
+   |
 
 error: argument to `Path::join` starts with a path separator
   --> tests/ui/join_absolute_paths.rs:18:15
@@ -41,12 +45,14 @@ LL |     path.join("/sh");
    = note: joining a path starting with separator will replace the path instead
 help: if this is unintentional, try removing the starting separator
    |
-LL |     path.join("sh");
-   |               ~~~~
+LL -     path.join("/sh");
+LL +     path.join("sh");
+   |
 help: if this is intentional, consider using `Path::new`
    |
-LL |     PathBuf::from("/sh");
-   |     ~~~~~~~~~~~~~~~~~~~~
+LL -     path.join("/sh");
+LL +     PathBuf::from("/sh");
+   |
 
 error: argument to `Path::join` starts with a path separator
   --> tests/ui/join_absolute_paths.rs:22:15
@@ -57,12 +63,14 @@ LL |     path.join(r#"/sh"#);
    = note: joining a path starting with separator will replace the path instead
 help: if this is unintentional, try removing the starting separator
    |
-LL |     path.join(r#"sh"#);
-   |               ~~~~~~~
+LL -     path.join(r#"/sh"#);
+LL +     path.join(r#"sh"#);
+   |
 help: if this is intentional, consider using `Path::new`
    |
-LL |     PathBuf::from(r#"/sh"#);
-   |     ~~~~~~~~~~~~~~~~~~~~~~~
+LL -     path.join(r#"/sh"#);
+LL +     PathBuf::from(r#"/sh"#);
+   |
 
 error: aborting due to 4 previous errors
 
diff --git a/src/tools/clippy/tests/ui/large_enum_variant.64bit.stderr b/src/tools/clippy/tests/ui/large_enum_variant.64bit.stderr
index 805cb406f83..60653b4abfb 100644
--- a/src/tools/clippy/tests/ui/large_enum_variant.64bit.stderr
+++ b/src/tools/clippy/tests/ui/large_enum_variant.64bit.stderr
@@ -13,8 +13,9 @@ LL | | }
    = help: to override `-D warnings` add `#[allow(clippy::large_enum_variant)]`
 help: consider boxing the large fields to reduce the total size of the enum
    |
-LL |     B(Box<[i32; 8000]>),
-   |       ~~~~~~~~~~~~~~~~
+LL -     B([i32; 8000]),
+LL +     B(Box<[i32; 8000]>),
+   |
 
 error: large size difference between variants
   --> tests/ui/large_enum_variant.rs:35:1
@@ -29,8 +30,9 @@ LL | | }
    |
 help: consider boxing the large fields to reduce the total size of the enum
    |
-LL |     ContainingLargeEnum(Box<LargeEnum>),
-   |                         ~~~~~~~~~~~~~~
+LL -     ContainingLargeEnum(LargeEnum),
+LL +     ContainingLargeEnum(Box<LargeEnum>),
+   |
 
 error: large size difference between variants
   --> tests/ui/large_enum_variant.rs:40:1
@@ -46,8 +48,9 @@ LL | | }
    |
 help: consider boxing the large fields to reduce the total size of the enum
    |
-LL |     ContainingMoreThanOneField(i32, Box<[i32; 8000]>, Box<[i32; 9500]>),
-   |                                     ~~~~~~~~~~~~~~~~  ~~~~~~~~~~~~~~~~
+LL -     ContainingMoreThanOneField(i32, [i32; 8000], [i32; 9500]),
+LL +     ContainingMoreThanOneField(i32, Box<[i32; 8000]>, Box<[i32; 9500]>),
+   |
 
 error: large size difference between variants
   --> tests/ui/large_enum_variant.rs:46:1
@@ -62,8 +65,9 @@ LL | | }
    |
 help: consider boxing the large fields to reduce the total size of the enum
    |
-LL |     StructLikeLarge { x: Box<[i32; 8000]>, y: i32 },
-   |                          ~~~~~~~~~~~~~~~~
+LL -     StructLikeLarge { x: [i32; 8000], y: i32 },
+LL +     StructLikeLarge { x: Box<[i32; 8000]>, y: i32 },
+   |
 
 error: large size difference between variants
   --> tests/ui/large_enum_variant.rs:51:1
@@ -78,8 +82,9 @@ LL | | }
    |
 help: consider boxing the large fields to reduce the total size of the enum
    |
-LL |     StructLikeLarge2 { x: Box<[i32; 8000]> },
-   |                           ~~~~~~~~~~~~~~~~
+LL -     StructLikeLarge2 { x: [i32; 8000] },
+LL +     StructLikeLarge2 { x: Box<[i32; 8000]> },
+   |
 
 error: large size difference between variants
   --> tests/ui/large_enum_variant.rs:67:1
@@ -95,8 +100,9 @@ LL | | }
    |
 help: consider boxing the large fields to reduce the total size of the enum
    |
-LL |     B(Box<[u8; 1255]>),
-   |       ~~~~~~~~~~~~~~~
+LL -     B([u8; 1255]),
+LL +     B(Box<[u8; 1255]>),
+   |
 
 error: large size difference between variants
   --> tests/ui/large_enum_variant.rs:73:1
@@ -111,8 +117,9 @@ LL | | }
    |
 help: consider boxing the large fields to reduce the total size of the enum
    |
-LL |     ContainingMoreThanOneField(Box<[i32; 8000]>, [i32; 2], Box<[i32; 9500]>, [i32; 30]),
-   |                                ~~~~~~~~~~~~~~~~            ~~~~~~~~~~~~~~~~
+LL -     ContainingMoreThanOneField([i32; 8000], [i32; 2], [i32; 9500], [i32; 30]),
+LL +     ContainingMoreThanOneField(Box<[i32; 8000]>, [i32; 2], Box<[i32; 9500]>, [i32; 30]),
+   |
 
 error: large size difference between variants
   --> tests/ui/large_enum_variant.rs:78:1
@@ -127,8 +134,9 @@ LL | | }
    |
 help: consider boxing the large fields to reduce the total size of the enum
    |
-LL |     B(Box<Struct2>),
-   |       ~~~~~~~~~~~~
+LL -     B(Struct2),
+LL +     B(Box<Struct2>),
+   |
 
 error: large size difference between variants
   --> tests/ui/large_enum_variant.rs:83:1
@@ -143,8 +151,9 @@ LL | | }
    |
 help: consider boxing the large fields to reduce the total size of the enum
    |
-LL |     B(Box<Struct2>),
-   |       ~~~~~~~~~~~~
+LL -     B(Struct2),
+LL +     B(Box<Struct2>),
+   |
 
 error: large size difference between variants
   --> tests/ui/large_enum_variant.rs:88:1
@@ -159,8 +168,9 @@ LL | | }
    |
 help: consider boxing the large fields to reduce the total size of the enum
    |
-LL |     B(Box<Struct2>),
-   |       ~~~~~~~~~~~~
+LL -     B(Struct2),
+LL +     B(Box<Struct2>),
+   |
 
 error: large size difference between variants
   --> tests/ui/large_enum_variant.rs:103:1
@@ -241,8 +251,9 @@ LL | | }
    |
 help: consider boxing the large fields to reduce the total size of the enum
    |
-LL |     Large(Box<(T, [u8; 512])>),
-   |           ~~~~~~~~~~~~~~~~~~~
+LL -     Large((T, [u8; 512])),
+LL +     Large(Box<(T, [u8; 512])>),
+   |
 
 error: large size difference between variants
   --> tests/ui/large_enum_variant.rs:143:1
@@ -257,8 +268,9 @@ LL | | }
    |
 help: consider boxing the large fields to reduce the total size of the enum
    |
-LL |     Large(Box<[Foo<u64>; 64]>),
-   |           ~~~~~~~~~~~~~~~~~~~
+LL -     Large([Foo<u64>; 64]),
+LL +     Large(Box<[Foo<u64>; 64]>),
+   |
 
 error: large size difference between variants
   --> tests/ui/large_enum_variant.rs:153:1
@@ -273,8 +285,9 @@ LL | | }
    |
 help: consider boxing the large fields to reduce the total size of the enum
    |
-LL |     Error(Box<PossiblyLargeEnumWithConst<256>>),
-   |           ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+LL -     Error(PossiblyLargeEnumWithConst<256>),
+LL +     Error(Box<PossiblyLargeEnumWithConst<256>>),
+   |
 
 error: aborting due to 16 previous errors
 
diff --git a/src/tools/clippy/tests/ui/legacy_numeric_constants.stderr b/src/tools/clippy/tests/ui/legacy_numeric_constants.stderr
index 267b9ac8e4d..91dfe79d55b 100644
--- a/src/tools/clippy/tests/ui/legacy_numeric_constants.stderr
+++ b/src/tools/clippy/tests/ui/legacy_numeric_constants.stderr
@@ -8,8 +8,9 @@ LL |     std::f32::EPSILON;
    = help: to override `-D warnings` add `#[allow(clippy::legacy_numeric_constants)]`
 help: use the associated constant instead
    |
-LL |     f32::EPSILON;
-   |     ~~~~~~~~~~~~
+LL -     std::f32::EPSILON;
+LL +     f32::EPSILON;
+   |
 
 error: usage of a legacy numeric constant
   --> tests/ui/legacy_numeric_constants.rs:34:5
@@ -19,8 +20,9 @@ LL |     std::u8::MIN;
    |
 help: use the associated constant instead
    |
-LL |     u8::MIN;
-   |     ~~~~~~~
+LL -     std::u8::MIN;
+LL +     u8::MIN;
+   |
 
 error: usage of a legacy numeric constant
   --> tests/ui/legacy_numeric_constants.rs:37:5
@@ -30,8 +32,9 @@ LL |     std::usize::MIN;
    |
 help: use the associated constant instead
    |
-LL |     usize::MIN;
-   |     ~~~~~~~~~~
+LL -     std::usize::MIN;
+LL +     usize::MIN;
+   |
 
 error: usage of a legacy numeric constant
   --> tests/ui/legacy_numeric_constants.rs:40:5
@@ -41,8 +44,9 @@ LL |     std::u32::MAX;
    |
 help: use the associated constant instead
    |
-LL |     u32::MAX;
-   |     ~~~~~~~~
+LL -     std::u32::MAX;
+LL +     u32::MAX;
+   |
 
 error: usage of a legacy numeric constant
   --> tests/ui/legacy_numeric_constants.rs:43:5
@@ -52,8 +56,9 @@ LL |     core::u32::MAX;
    |
 help: use the associated constant instead
    |
-LL |     u32::MAX;
-   |     ~~~~~~~~
+LL -     core::u32::MAX;
+LL +     u32::MAX;
+   |
 
 error: usage of a legacy numeric constant
   --> tests/ui/legacy_numeric_constants.rs:46:5
@@ -64,7 +69,7 @@ LL |     MAX;
 help: use the associated constant instead
    |
 LL |     u32::MAX;
-   |     ~~~~~~~~
+   |     +++++
 
 error: usage of a legacy numeric method
   --> tests/ui/legacy_numeric_constants.rs:49:10
@@ -74,8 +79,9 @@ LL |     i32::max_value();
    |
 help: use the associated constant instead
    |
-LL |     i32::MAX;
-   |          ~~~
+LL -     i32::max_value();
+LL +     i32::MAX;
+   |
 
 error: usage of a legacy numeric method
   --> tests/ui/legacy_numeric_constants.rs:52:9
@@ -85,8 +91,9 @@ LL |     u8::max_value();
    |
 help: use the associated constant instead
    |
-LL |     u8::MAX;
-   |         ~~~
+LL -     u8::max_value();
+LL +     u8::MAX;
+   |
 
 error: usage of a legacy numeric method
   --> tests/ui/legacy_numeric_constants.rs:55:9
@@ -96,8 +103,9 @@ LL |     u8::min_value();
    |
 help: use the associated constant instead
    |
-LL |     u8::MIN;
-   |         ~~~
+LL -     u8::min_value();
+LL +     u8::MIN;
+   |
 
 error: usage of a legacy numeric constant
   --> tests/ui/legacy_numeric_constants.rs:58:5
@@ -107,8 +115,9 @@ LL |     ::std::u8::MIN;
    |
 help: use the associated constant instead
    |
-LL |     u8::MIN;
-   |     ~~~~~~~
+LL -     ::std::u8::MIN;
+LL +     u8::MIN;
+   |
 
 error: usage of a legacy numeric method
   --> tests/ui/legacy_numeric_constants.rs:61:27
@@ -118,8 +127,9 @@ LL |     ::std::primitive::u8::min_value();
    |
 help: use the associated constant instead
    |
-LL |     ::std::primitive::u8::MIN;
-   |                           ~~~
+LL -     ::std::primitive::u8::min_value();
+LL +     ::std::primitive::u8::MIN;
+   |
 
 error: usage of a legacy numeric method
   --> tests/ui/legacy_numeric_constants.rs:64:26
@@ -129,8 +139,9 @@ LL |     std::primitive::i32::max_value();
    |
 help: use the associated constant instead
    |
-LL |     std::primitive::i32::MAX;
-   |                          ~~~
+LL -     std::primitive::i32::max_value();
+LL +     std::primitive::i32::MAX;
+   |
 
 error: usage of a legacy numeric constant
   --> tests/ui/legacy_numeric_constants.rs:67:5
@@ -140,8 +151,9 @@ LL |     self::a::u128::MAX;
    |
 help: use the associated constant instead
    |
-LL |     u128::MAX;
-   |     ~~~~~~~~~
+LL -     self::a::u128::MAX;
+LL +     u128::MAX;
+   |
 
 error: usage of a legacy numeric constant
   --> tests/ui/legacy_numeric_constants.rs:17:25
@@ -155,8 +167,9 @@ LL |     b!();
    = note: this error originates in the macro `b` (in Nightly builds, run with -Z macro-backtrace for more info)
 help: use the associated constant instead
    |
-LL |                 let x = u64::MAX;
-   |                         ~~~~~~~~
+LL -                 let x = std::u64::MAX;
+LL +                 let x = u64::MAX;
+   |
 
 error: usage of a legacy numeric constant
   --> tests/ui/legacy_numeric_constants.rs:81:14
@@ -166,8 +179,9 @@ LL |     [(0, "", std::i128::MAX)];
    |
 help: use the associated constant instead
    |
-LL |     [(0, "", i128::MAX)];
-   |              ~~~~~~~~~
+LL -     [(0, "", std::i128::MAX)];
+LL +     [(0, "", i128::MAX)];
+   |
 
 error: usage of a legacy numeric constant
   --> tests/ui/legacy_numeric_constants.rs:115:5
@@ -177,8 +191,9 @@ LL |     std::u32::MAX;
    |
 help: use the associated constant instead
    |
-LL |     u32::MAX;
-   |     ~~~~~~~~
+LL -     std::u32::MAX;
+LL +     u32::MAX;
+   |
 
 error: aborting due to 16 previous errors
 
diff --git a/src/tools/clippy/tests/ui/literals.stderr b/src/tools/clippy/tests/ui/literals.stderr
index 564e0bc4f74..576b38a47d2 100644
--- a/src/tools/clippy/tests/ui/literals.stderr
+++ b/src/tools/clippy/tests/ui/literals.stderr
@@ -71,12 +71,14 @@ LL |     let fail_multi_zero = 000_123usize;
    = help: to override `-D warnings` add `#[allow(clippy::zero_prefixed_literal)]`
 help: if you mean to use a decimal constant, remove the `0` to avoid confusion
    |
-LL |     let fail_multi_zero = 123usize;
-   |                           ~~~~~~~~
+LL -     let fail_multi_zero = 000_123usize;
+LL +     let fail_multi_zero = 123usize;
+   |
 help: if you mean to use an octal constant, use `0o`
    |
-LL |     let fail_multi_zero = 0o123usize;
-   |                           ~~~~~~~~~~
+LL -     let fail_multi_zero = 000_123usize;
+LL +     let fail_multi_zero = 0o123usize;
+   |
 
 error: integer type suffix should not be separated by an underscore
   --> tests/ui/literals.rs:36:16
@@ -92,12 +94,13 @@ LL |     let fail8 = 0123;
    |
 help: if you mean to use a decimal constant, remove the `0` to avoid confusion
    |
-LL |     let fail8 = 123;
-   |                 ~~~
+LL -     let fail8 = 0123;
+LL +     let fail8 = 123;
+   |
 help: if you mean to use an octal constant, use `0o`
    |
 LL |     let fail8 = 0o123;
-   |                 ~~~~~
+   |                  +
 
 error: integer type suffix should not be separated by an underscore
   --> tests/ui/literals.rs:48:16
@@ -143,8 +146,9 @@ LL |     let _ = 08;
    |
 help: if you mean to use a decimal constant, remove the `0` to avoid confusion
    |
-LL |     let _ = 8;
-   |             ~
+LL -     let _ = 08;
+LL +     let _ = 8;
+   |
 
 error: this is a decimal constant
   --> tests/ui/literals.rs:72:13
@@ -154,8 +158,9 @@ LL |     let _ = 09;
    |
 help: if you mean to use a decimal constant, remove the `0` to avoid confusion
    |
-LL |     let _ = 9;
-   |             ~
+LL -     let _ = 09;
+LL +     let _ = 9;
+   |
 
 error: this is a decimal constant
   --> tests/ui/literals.rs:74:13
@@ -165,8 +170,9 @@ LL |     let _ = 089;
    |
 help: if you mean to use a decimal constant, remove the `0` to avoid confusion
    |
-LL |     let _ = 89;
-   |             ~~
+LL -     let _ = 089;
+LL +     let _ = 89;
+   |
 
 error: aborting due to 20 previous errors
 
diff --git a/src/tools/clippy/tests/ui/lossy_float_literal.stderr b/src/tools/clippy/tests/ui/lossy_float_literal.stderr
index 3026854e317..118351a62d0 100644
--- a/src/tools/clippy/tests/ui/lossy_float_literal.stderr
+++ b/src/tools/clippy/tests/ui/lossy_float_literal.stderr
@@ -8,8 +8,9 @@ LL |     let _: f32 = 16_777_217.0;
    = help: to override `-D warnings` add `#[allow(clippy::lossy_float_literal)]`
 help: consider changing the type or replacing it with
    |
-LL |     let _: f32 = 16_777_216.0;
-   |                  ~~~~~~~~~~~~
+LL -     let _: f32 = 16_777_217.0;
+LL +     let _: f32 = 16_777_216.0;
+   |
 
 error: literal cannot be represented as the underlying type without loss of precision
   --> tests/ui/lossy_float_literal.rs:15:18
@@ -19,8 +20,9 @@ LL |     let _: f32 = 16_777_219.0;
    |
 help: consider changing the type or replacing it with
    |
-LL |     let _: f32 = 16_777_220.0;
-   |                  ~~~~~~~~~~~~
+LL -     let _: f32 = 16_777_219.0;
+LL +     let _: f32 = 16_777_220.0;
+   |
 
 error: literal cannot be represented as the underlying type without loss of precision
   --> tests/ui/lossy_float_literal.rs:16:18
@@ -30,8 +32,9 @@ LL |     let _: f32 = 16_777_219.;
    |
 help: consider changing the type or replacing it with
    |
-LL |     let _: f32 = 16_777_220.0;
-   |                  ~~~~~~~~~~~~
+LL -     let _: f32 = 16_777_219.;
+LL +     let _: f32 = 16_777_220.0;
+   |
 
 error: literal cannot be represented as the underlying type without loss of precision
   --> tests/ui/lossy_float_literal.rs:17:18
@@ -41,8 +44,9 @@ LL |     let _: f32 = 16_777_219.000;
    |
 help: consider changing the type or replacing it with
    |
-LL |     let _: f32 = 16_777_220.0;
-   |                  ~~~~~~~~~~~~
+LL -     let _: f32 = 16_777_219.000;
+LL +     let _: f32 = 16_777_220.0;
+   |
 
 error: literal cannot be represented as the underlying type without loss of precision
   --> tests/ui/lossy_float_literal.rs:18:13
@@ -52,8 +56,9 @@ LL |     let _ = 16_777_219f32;
    |
 help: consider changing the type or replacing it with
    |
-LL |     let _ = 16_777_220_f32;
-   |             ~~~~~~~~~~~~~~
+LL -     let _ = 16_777_219f32;
+LL +     let _ = 16_777_220_f32;
+   |
 
 error: literal cannot be represented as the underlying type without loss of precision
   --> tests/ui/lossy_float_literal.rs:19:19
@@ -63,8 +68,9 @@ LL |     let _: f32 = -16_777_219.0;
    |
 help: consider changing the type or replacing it with
    |
-LL |     let _: f32 = -16_777_220.0;
-   |                   ~~~~~~~~~~~~
+LL -     let _: f32 = -16_777_219.0;
+LL +     let _: f32 = -16_777_220.0;
+   |
 
 error: literal cannot be represented as the underlying type without loss of precision
   --> tests/ui/lossy_float_literal.rs:21:18
@@ -74,8 +80,9 @@ LL |     let _: f64 = 9_007_199_254_740_993.0;
    |
 help: consider changing the type or replacing it with
    |
-LL |     let _: f64 = 9_007_199_254_740_992.0;
-   |                  ~~~~~~~~~~~~~~~~~~~~~~~
+LL -     let _: f64 = 9_007_199_254_740_993.0;
+LL +     let _: f64 = 9_007_199_254_740_992.0;
+   |
 
 error: literal cannot be represented as the underlying type without loss of precision
   --> tests/ui/lossy_float_literal.rs:22:18
@@ -85,8 +92,9 @@ LL |     let _: f64 = 9_007_199_254_740_993.;
    |
 help: consider changing the type or replacing it with
    |
-LL |     let _: f64 = 9_007_199_254_740_992.0;
-   |                  ~~~~~~~~~~~~~~~~~~~~~~~
+LL -     let _: f64 = 9_007_199_254_740_993.;
+LL +     let _: f64 = 9_007_199_254_740_992.0;
+   |
 
 error: literal cannot be represented as the underlying type without loss of precision
   --> tests/ui/lossy_float_literal.rs:23:18
@@ -96,8 +104,9 @@ LL |     let _: f64 = 9_007_199_254_740_993.00;
    |
 help: consider changing the type or replacing it with
    |
-LL |     let _: f64 = 9_007_199_254_740_992.0;
-   |                  ~~~~~~~~~~~~~~~~~~~~~~~
+LL -     let _: f64 = 9_007_199_254_740_993.00;
+LL +     let _: f64 = 9_007_199_254_740_992.0;
+   |
 
 error: literal cannot be represented as the underlying type without loss of precision
   --> tests/ui/lossy_float_literal.rs:24:13
@@ -107,8 +116,9 @@ LL |     let _ = 9_007_199_254_740_993f64;
    |
 help: consider changing the type or replacing it with
    |
-LL |     let _ = 9_007_199_254_740_992_f64;
-   |             ~~~~~~~~~~~~~~~~~~~~~~~~~
+LL -     let _ = 9_007_199_254_740_993f64;
+LL +     let _ = 9_007_199_254_740_992_f64;
+   |
 
 error: literal cannot be represented as the underlying type without loss of precision
   --> tests/ui/lossy_float_literal.rs:25:19
@@ -118,8 +128,9 @@ LL |     let _: f64 = -9_007_199_254_740_993.0;
    |
 help: consider changing the type or replacing it with
    |
-LL |     let _: f64 = -9_007_199_254_740_992.0;
-   |                   ~~~~~~~~~~~~~~~~~~~~~~~
+LL -     let _: f64 = -9_007_199_254_740_993.0;
+LL +     let _: f64 = -9_007_199_254_740_992.0;
+   |
 
 error: aborting due to 11 previous errors
 
diff --git a/src/tools/clippy/tests/ui/manual_assert.edition2018.stderr b/src/tools/clippy/tests/ui/manual_assert.edition2018.stderr
index 004463720e2..dfccf7e9939 100644
--- a/src/tools/clippy/tests/ui/manual_assert.edition2018.stderr
+++ b/src/tools/clippy/tests/ui/manual_assert.edition2018.stderr
@@ -79,7 +79,15 @@ LL | |     }
    |
 help: try instead
    |
-LL |     assert!(!(a > 2), "panic with comment");
+LL -     if a > 2 {
+LL -         // comment
+LL -         /* this is a
+LL -         multiline
+LL -         comment */
+LL -         /// Doc comment
+LL -         panic!("panic with comment") // comment after `panic!`
+LL -     }
+LL +     assert!(!(a > 2), "panic with comment");
    |
 
 error: only a `panic!` in `if`-then statement
diff --git a/src/tools/clippy/tests/ui/manual_assert.edition2021.stderr b/src/tools/clippy/tests/ui/manual_assert.edition2021.stderr
index 004463720e2..dfccf7e9939 100644
--- a/src/tools/clippy/tests/ui/manual_assert.edition2021.stderr
+++ b/src/tools/clippy/tests/ui/manual_assert.edition2021.stderr
@@ -79,7 +79,15 @@ LL | |     }
    |
 help: try instead
    |
-LL |     assert!(!(a > 2), "panic with comment");
+LL -     if a > 2 {
+LL -         // comment
+LL -         /* this is a
+LL -         multiline
+LL -         comment */
+LL -         /// Doc comment
+LL -         panic!("panic with comment") // comment after `panic!`
+LL -     }
+LL +     assert!(!(a > 2), "panic with comment");
    |
 
 error: only a `panic!` in `if`-then statement
diff --git a/src/tools/clippy/tests/ui/manual_async_fn.stderr b/src/tools/clippy/tests/ui/manual_async_fn.stderr
index 68a97243436..a7cfc30fb69 100644
--- a/src/tools/clippy/tests/ui/manual_async_fn.stderr
+++ b/src/tools/clippy/tests/ui/manual_async_fn.stderr
@@ -8,8 +8,9 @@ LL | fn fut() -> impl Future<Output = i32> {
    = help: to override `-D warnings` add `#[allow(clippy::manual_async_fn)]`
 help: make the function `async` and return the output of the future directly
    |
-LL | async fn fut() -> i32 { 42 }
-   | ~~~~~~~~~~~~~~~~~~~~~ ~~~~~~
+LL - fn fut() -> impl Future<Output = i32> {
+LL + async fn fut() -> i32 { 42 }
+   |
 
 error: this function can be simplified using the `async fn` syntax
   --> tests/ui/manual_async_fn.rs:11:1
@@ -19,8 +20,9 @@ LL | fn fut2() ->impl Future<Output = i32> {
    |
 help: make the function `async` and return the output of the future directly
    |
-LL | async fn fut2() -> i32 { 42 }
-   | ~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~
+LL - fn fut2() ->impl Future<Output = i32> {
+LL + async fn fut2() -> i32 { 42 }
+   |
 
 error: this function can be simplified using the `async fn` syntax
   --> tests/ui/manual_async_fn.rs:16:1
@@ -30,8 +32,9 @@ LL | fn fut3()-> impl Future<Output = i32> {
    |
 help: make the function `async` and return the output of the future directly
    |
-LL | async fn fut3() -> i32 { 42 }
-   | ~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~
+LL - fn fut3()-> impl Future<Output = i32> {
+LL + async fn fut3() -> i32 { 42 }
+   |
 
 error: this function can be simplified using the `async fn` syntax
   --> tests/ui/manual_async_fn.rs:20:1
@@ -41,8 +44,9 @@ LL | fn empty_fut() -> impl Future<Output = ()> {
    |
 help: make the function `async` and return the output of the future directly
    |
-LL | async fn empty_fut() {}
-   | ~~~~~~~~~~~~~~~~~~~~ ~~
+LL - fn empty_fut() -> impl Future<Output = ()> {
+LL + async fn empty_fut() {}
+   |
 
 error: this function can be simplified using the `async fn` syntax
   --> tests/ui/manual_async_fn.rs:25:1
@@ -52,8 +56,9 @@ LL | fn empty_fut2() ->impl Future<Output = ()> {
    |
 help: make the function `async` and return the output of the future directly
    |
-LL | async fn empty_fut2() {}
-   | ~~~~~~~~~~~~~~~~~~~~~ ~~
+LL - fn empty_fut2() ->impl Future<Output = ()> {
+LL + async fn empty_fut2() {}
+   |
 
 error: this function can be simplified using the `async fn` syntax
   --> tests/ui/manual_async_fn.rs:30:1
@@ -63,8 +68,9 @@ LL | fn empty_fut3()-> impl Future<Output = ()> {
    |
 help: make the function `async` and return the output of the future directly
    |
-LL | async fn empty_fut3() {}
-   | ~~~~~~~~~~~~~~~~~~~~~ ~~
+LL - fn empty_fut3()-> impl Future<Output = ()> {
+LL + async fn empty_fut3() {}
+   |
 
 error: this function can be simplified using the `async fn` syntax
   --> tests/ui/manual_async_fn.rs:34:1
@@ -74,8 +80,9 @@ LL | fn core_fut() -> impl core::future::Future<Output = i32> {
    |
 help: make the function `async` and return the output of the future directly
    |
-LL | async fn core_fut() -> i32 { 42 }
-   | ~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~
+LL - fn core_fut() -> impl core::future::Future<Output = i32> {
+LL + async fn core_fut() -> i32 { 42 }
+   |
 
 error: this function can be simplified using the `async fn` syntax
   --> tests/ui/manual_async_fn.rs:56:5
@@ -108,8 +115,9 @@ LL | fn elided(_: &i32) -> impl Future<Output = i32> + '_ {
    |
 help: make the function `async` and return the output of the future directly
    |
-LL | async fn elided(_: &i32) -> i32 { 42 }
-   | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~
+LL - fn elided(_: &i32) -> impl Future<Output = i32> + '_ {
+LL + async fn elided(_: &i32) -> i32 { 42 }
+   |
 
 error: this function can be simplified using the `async fn` syntax
   --> tests/ui/manual_async_fn.rs:101:1
@@ -119,8 +127,9 @@ LL | fn explicit<'a, 'b>(_: &'a i32, _: &'b i32) -> impl Future<Output = i32> +
    |
 help: make the function `async` and return the output of the future directly
    |
-LL | async fn explicit<'a, 'b>(_: &'a i32, _: &'b i32) -> i32 { 42 }
-   | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~
+LL - fn explicit<'a, 'b>(_: &'a i32, _: &'b i32) -> impl Future<Output = i32> + 'a + 'b {
+LL + async fn explicit<'a, 'b>(_: &'a i32, _: &'b i32) -> i32 { 42 }
+   |
 
 error: this function can be simplified using the `async fn` syntax
   --> tests/ui/manual_async_fn.rs:130:1
@@ -130,8 +139,9 @@ LL | pub fn issue_10450() -> impl Future<Output = i32> {
    |
 help: make the function `async` and return the output of the future directly
    |
-LL | pub async fn issue_10450() -> i32 { 42 }
-   | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~
+LL - pub fn issue_10450() -> impl Future<Output = i32> {
+LL + pub async fn issue_10450() -> i32 { 42 }
+   |
 
 error: this function can be simplified using the `async fn` syntax
   --> tests/ui/manual_async_fn.rs:134:1
@@ -141,8 +151,9 @@ LL | pub(crate) fn issue_10450_2() -> impl Future<Output = i32> {
    |
 help: make the function `async` and return the output of the future directly
    |
-LL | pub(crate) async fn issue_10450_2() -> i32 { 42 }
-   | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~
+LL - pub(crate) fn issue_10450_2() -> impl Future<Output = i32> {
+LL + pub(crate) async fn issue_10450_2() -> i32 { 42 }
+   |
 
 error: this function can be simplified using the `async fn` syntax
   --> tests/ui/manual_async_fn.rs:138:1
@@ -152,8 +163,9 @@ LL | pub(self) fn issue_10450_3() -> impl Future<Output = i32> {
    |
 help: make the function `async` and return the output of the future directly
    |
-LL | pub(self) async fn issue_10450_3() -> i32 { 42 }
-   | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~
+LL - pub(self) fn issue_10450_3() -> impl Future<Output = i32> {
+LL + pub(self) async fn issue_10450_3() -> i32 { 42 }
+   |
 
 error: aborting due to 13 previous errors
 
diff --git a/src/tools/clippy/tests/ui/manual_flatten.stderr b/src/tools/clippy/tests/ui/manual_flatten.stderr
index cf1b0a1c8bb..93f7f11b5e6 100644
--- a/src/tools/clippy/tests/ui/manual_flatten.stderr
+++ b/src/tools/clippy/tests/ui/manual_flatten.stderr
@@ -196,11 +196,9 @@ LL | |         }
    | |_________^
 help: try
    |
-LL ~     for n in vec![
-LL +
-LL +         Some(1),
-LL +         Some(2),
-LL +         Some(3)
+LL |     for n in vec![
+...
+LL |         Some(3)
 LL ~     ].iter().flatten() {
    |
 
diff --git a/src/tools/clippy/tests/ui/manual_float_methods.stderr b/src/tools/clippy/tests/ui/manual_float_methods.stderr
index 676a4485ab4..1352c5e73ca 100644
--- a/src/tools/clippy/tests/ui/manual_float_methods.stderr
+++ b/src/tools/clippy/tests/ui/manual_float_methods.stderr
@@ -17,16 +17,19 @@ LL |     if x != f32::INFINITY && x != f32::NEG_INFINITY {}
    = help: to override `-D warnings` add `#[allow(clippy::manual_is_finite)]`
 help: use the dedicated method instead
    |
-LL |     if x.is_finite() {}
-   |        ~~~~~~~~~~~~~
+LL -     if x != f32::INFINITY && x != f32::NEG_INFINITY {}
+LL +     if x.is_finite() {}
+   |
 help: this will alter how it handles NaN; if that is a problem, use instead
    |
-LL |     if x.is_finite() || x.is_nan() {}
-   |        ~~~~~~~~~~~~~~~~~~~~~~~~~~~
+LL -     if x != f32::INFINITY && x != f32::NEG_INFINITY {}
+LL +     if x.is_finite() || x.is_nan() {}
+   |
 help: or, for conciseness
    |
-LL |     if !x.is_infinite() {}
-   |        ~~~~~~~~~~~~~~~~
+LL -     if x != f32::INFINITY && x != f32::NEG_INFINITY {}
+LL +     if !x.is_infinite() {}
+   |
 
 error: manually checking if a float is infinite
   --> tests/ui/manual_float_methods.rs:26:8
@@ -42,16 +45,19 @@ LL |     if x != INFINITE && x != NEG_INFINITE {}
    |
 help: use the dedicated method instead
    |
-LL |     if x.is_finite() {}
-   |        ~~~~~~~~~~~~~
+LL -     if x != INFINITE && x != NEG_INFINITE {}
+LL +     if x.is_finite() {}
+   |
 help: this will alter how it handles NaN; if that is a problem, use instead
    |
-LL |     if x.is_finite() || x.is_nan() {}
-   |        ~~~~~~~~~~~~~~~~~~~~~~~~~~~
+LL -     if x != INFINITE && x != NEG_INFINITE {}
+LL +     if x.is_finite() || x.is_nan() {}
+   |
 help: or, for conciseness
    |
-LL |     if !x.is_infinite() {}
-   |        ~~~~~~~~~~~~~~~~
+LL -     if x != INFINITE && x != NEG_INFINITE {}
+LL +     if !x.is_infinite() {}
+   |
 
 error: manually checking if a float is infinite
   --> tests/ui/manual_float_methods.rs:29:8
@@ -67,16 +73,19 @@ LL |     if x != f64::INFINITY && x != f64::NEG_INFINITY {}
    |
 help: use the dedicated method instead
    |
-LL |     if x.is_finite() {}
-   |        ~~~~~~~~~~~~~
+LL -     if x != f64::INFINITY && x != f64::NEG_INFINITY {}
+LL +     if x.is_finite() {}
+   |
 help: this will alter how it handles NaN; if that is a problem, use instead
    |
-LL |     if x.is_finite() || x.is_nan() {}
-   |        ~~~~~~~~~~~~~~~~~~~~~~~~~~~
+LL -     if x != f64::INFINITY && x != f64::NEG_INFINITY {}
+LL +     if x.is_finite() || x.is_nan() {}
+   |
 help: or, for conciseness
    |
-LL |     if !x.is_infinite() {}
-   |        ~~~~~~~~~~~~~~~~
+LL -     if x != f64::INFINITY && x != f64::NEG_INFINITY {}
+LL +     if !x.is_infinite() {}
+   |
 
 error: manually checking if a float is infinite
   --> tests/ui/manual_float_methods.rs:44:12
diff --git a/src/tools/clippy/tests/ui/manual_ignore_case_cmp.stderr b/src/tools/clippy/tests/ui/manual_ignore_case_cmp.stderr
index 11e8b8aebb5..bc6393b66d5 100644
--- a/src/tools/clippy/tests/ui/manual_ignore_case_cmp.stderr
+++ b/src/tools/clippy/tests/ui/manual_ignore_case_cmp.stderr
@@ -11,8 +11,9 @@ LL | #![deny(clippy::manual_ignore_case_cmp)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 help: consider using `.eq_ignore_ascii_case()` instead
    |
-LL |     if a.eq_ignore_ascii_case(b) {
-   |        ~~~~~~~~~~~~~~~~~~~~~~~~~
+LL -     if a.to_ascii_lowercase() == b.to_ascii_lowercase() {
+LL +     if a.eq_ignore_ascii_case(b) {
+   |
 
 error: manual case-insensitive ASCII comparison
   --> tests/ui/manual_ignore_case_cmp.rs:12:8
@@ -22,8 +23,9 @@ 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) {
-   |        ~~~~~~~~~~~~~~~~~~~~~~~~~
+LL -     if a.to_ascii_uppercase() == b.to_ascii_uppercase() {
+LL +     if a.eq_ignore_ascii_case(b) {
+   |
 
 error: manual case-insensitive ASCII comparison
   --> tests/ui/manual_ignore_case_cmp.rs:15:13
@@ -33,8 +35,9 @@ 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);
-   |             ~~~~~~~~~~~~~~~~~~~~~~~~~
+LL -     let r = a.to_ascii_lowercase() == b.to_ascii_lowercase();
+LL +     let r = a.eq_ignore_ascii_case(b);
+   |
 
 error: manual case-insensitive ASCII comparison
   --> tests/ui/manual_ignore_case_cmp.rs:16:18
@@ -44,8 +47,9 @@ 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);
-   |                  ~~~~~~~~~~~~~~~~~~~~~~~~~
+LL -     let r = r || a.to_ascii_uppercase() == b.to_ascii_uppercase();
+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
@@ -55,8 +59,9 @@ 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());
-   |          ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+LL -     r && a.to_ascii_lowercase() == b.to_uppercase().to_ascii_lowercase();
+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
@@ -66,8 +71,9 @@ 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) {
-   |        ~~~~~~~~~~~~~~~~~~~~~~~~~~
+LL -     if a.to_ascii_lowercase() != b.to_ascii_lowercase() {
+LL +     if !a.eq_ignore_ascii_case(b) {
+   |
 
 error: manual case-insensitive ASCII comparison
   --> tests/ui/manual_ignore_case_cmp.rs:22:8
@@ -77,8 +83,9 @@ 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) {
-   |        ~~~~~~~~~~~~~~~~~~~~~~~~~~
+LL -     if a.to_ascii_uppercase() != b.to_ascii_uppercase() {
+LL +     if !a.eq_ignore_ascii_case(b) {
+   |
 
 error: manual case-insensitive ASCII comparison
   --> tests/ui/manual_ignore_case_cmp.rs:25:13
@@ -88,8 +95,9 @@ 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);
-   |             ~~~~~~~~~~~~~~~~~~~~~~~~~~
+LL -     let r = a.to_ascii_lowercase() != b.to_ascii_lowercase();
+LL +     let r = !a.eq_ignore_ascii_case(b);
+   |
 
 error: manual case-insensitive ASCII comparison
   --> tests/ui/manual_ignore_case_cmp.rs:26:18
@@ -99,8 +107,9 @@ 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);
-   |                  ~~~~~~~~~~~~~~~~~~~~~~~~~~
+LL -     let r = r || a.to_ascii_uppercase() != b.to_ascii_uppercase();
+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
@@ -110,8 +119,9 @@ 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());
-   |          ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+LL -     r && a.to_ascii_lowercase() != b.to_uppercase().to_ascii_lowercase();
+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
@@ -121,8 +131,9 @@ LL |     a.to_ascii_lowercase() == b.to_ascii_lowercase();
    |
 help: consider using `.eq_ignore_ascii_case()` instead
    |
-LL |     a.eq_ignore_ascii_case(&b);
-   |     ~~~~~~~~~~~~~~~~~~~~~~~~~~
+LL -     a.to_ascii_lowercase() == b.to_ascii_lowercase();
+LL +     a.eq_ignore_ascii_case(&b);
+   |
 
 error: manual case-insensitive ASCII comparison
   --> tests/ui/manual_ignore_case_cmp.rs:41:5
@@ -132,8 +143,9 @@ LL |     a.to_ascii_lowercase() == 'a';
    |
 help: consider using `.eq_ignore_ascii_case()` instead
    |
-LL |     a.eq_ignore_ascii_case(&'a');
-   |     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+LL -     a.to_ascii_lowercase() == 'a';
+LL +     a.eq_ignore_ascii_case(&'a');
+   |
 
 error: manual case-insensitive ASCII comparison
   --> tests/ui/manual_ignore_case_cmp.rs:42:5
@@ -143,8 +155,9 @@ LL |     'a' == b.to_ascii_lowercase();
    |
 help: consider using `.eq_ignore_ascii_case()` instead
    |
-LL |     'a'.eq_ignore_ascii_case(&b);
-   |     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+LL -     'a' == b.to_ascii_lowercase();
+LL +     'a'.eq_ignore_ascii_case(&b);
+   |
 
 error: manual case-insensitive ASCII comparison
   --> tests/ui/manual_ignore_case_cmp.rs:45:5
@@ -154,8 +167,9 @@ LL |     a.to_ascii_lowercase() == b.to_ascii_lowercase();
    |
 help: consider using `.eq_ignore_ascii_case()` instead
    |
-LL |     a.eq_ignore_ascii_case(&b);
-   |     ~~~~~~~~~~~~~~~~~~~~~~~~~~
+LL -     a.to_ascii_lowercase() == b.to_ascii_lowercase();
+LL +     a.eq_ignore_ascii_case(&b);
+   |
 
 error: manual case-insensitive ASCII comparison
   --> tests/ui/manual_ignore_case_cmp.rs:46:5
@@ -165,8 +179,9 @@ LL |     a.to_ascii_lowercase() == b'a';
    |
 help: consider using `.eq_ignore_ascii_case()` instead
    |
-LL |     a.eq_ignore_ascii_case(&b'a');
-   |     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+LL -     a.to_ascii_lowercase() == b'a';
+LL +     a.eq_ignore_ascii_case(&b'a');
+   |
 
 error: manual case-insensitive ASCII comparison
   --> tests/ui/manual_ignore_case_cmp.rs:47:5
@@ -176,8 +191,9 @@ LL |     b'a' == b.to_ascii_lowercase();
    |
 help: consider using `.eq_ignore_ascii_case()` instead
    |
-LL |     b'a'.eq_ignore_ascii_case(&b);
-   |     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+LL -     b'a' == b.to_ascii_lowercase();
+LL +     b'a'.eq_ignore_ascii_case(&b);
+   |
 
 error: manual case-insensitive ASCII comparison
   --> tests/ui/manual_ignore_case_cmp.rs:50:5
@@ -187,8 +203,9 @@ LL |     a.to_ascii_lowercase() == b.to_ascii_lowercase();
    |
 help: consider using `.eq_ignore_ascii_case()` instead
    |
-LL |     a.eq_ignore_ascii_case(b);
-   |     ~~~~~~~~~~~~~~~~~~~~~~~~~
+LL -     a.to_ascii_lowercase() == b.to_ascii_lowercase();
+LL +     a.eq_ignore_ascii_case(b);
+   |
 
 error: manual case-insensitive ASCII comparison
   --> tests/ui/manual_ignore_case_cmp.rs:51:5
@@ -198,8 +215,9 @@ 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);
-   |     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+LL -     a.to_uppercase().to_ascii_lowercase() == b.to_ascii_lowercase();
+LL +     a.to_uppercase().eq_ignore_ascii_case(b);
+   |
 
 error: manual case-insensitive ASCII comparison
   --> tests/ui/manual_ignore_case_cmp.rs:52:5
@@ -209,8 +227,9 @@ LL |     a.to_ascii_lowercase() == "a";
    |
 help: consider using `.eq_ignore_ascii_case()` instead
    |
-LL |     a.eq_ignore_ascii_case("a");
-   |     ~~~~~~~~~~~~~~~~~~~~~~~~~~~
+LL -     a.to_ascii_lowercase() == "a";
+LL +     a.eq_ignore_ascii_case("a");
+   |
 
 error: manual case-insensitive ASCII comparison
   --> tests/ui/manual_ignore_case_cmp.rs:53:5
@@ -220,8 +239,9 @@ LL |     "a" == b.to_ascii_lowercase();
    |
 help: consider using `.eq_ignore_ascii_case()` instead
    |
-LL |     "a".eq_ignore_ascii_case(b);
-   |     ~~~~~~~~~~~~~~~~~~~~~~~~~~~
+LL -     "a" == b.to_ascii_lowercase();
+LL +     "a".eq_ignore_ascii_case(b);
+   |
 
 error: manual case-insensitive ASCII comparison
   --> tests/ui/manual_ignore_case_cmp.rs:56:5
@@ -231,8 +251,9 @@ LL |     a.to_ascii_lowercase() == b.to_ascii_lowercase();
    |
 help: consider using `.eq_ignore_ascii_case()` instead
    |
-LL |     a.eq_ignore_ascii_case(b);
-   |     ~~~~~~~~~~~~~~~~~~~~~~~~~
+LL -     a.to_ascii_lowercase() == b.to_ascii_lowercase();
+LL +     a.eq_ignore_ascii_case(b);
+   |
 
 error: manual case-insensitive ASCII comparison
   --> tests/ui/manual_ignore_case_cmp.rs:57:5
@@ -242,8 +263,9 @@ 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);
-   |     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+LL -     a.to_uppercase().to_ascii_lowercase() == b.to_ascii_lowercase();
+LL +     a.to_uppercase().eq_ignore_ascii_case(b);
+   |
 
 error: manual case-insensitive ASCII comparison
   --> tests/ui/manual_ignore_case_cmp.rs:58:5
@@ -253,8 +275,9 @@ LL |     a.to_ascii_lowercase() == "a";
    |
 help: consider using `.eq_ignore_ascii_case()` instead
    |
-LL |     a.eq_ignore_ascii_case("a");
-   |     ~~~~~~~~~~~~~~~~~~~~~~~~~~~
+LL -     a.to_ascii_lowercase() == "a";
+LL +     a.eq_ignore_ascii_case("a");
+   |
 
 error: manual case-insensitive ASCII comparison
   --> tests/ui/manual_ignore_case_cmp.rs:59:5
@@ -264,8 +287,9 @@ LL |     "a" == b.to_ascii_lowercase();
    |
 help: consider using `.eq_ignore_ascii_case()` instead
    |
-LL |     "a".eq_ignore_ascii_case(b);
-   |     ~~~~~~~~~~~~~~~~~~~~~~~~~~~
+LL -     "a" == b.to_ascii_lowercase();
+LL +     "a".eq_ignore_ascii_case(b);
+   |
 
 error: manual case-insensitive ASCII comparison
   --> tests/ui/manual_ignore_case_cmp.rs:62:5
@@ -275,8 +299,9 @@ LL |     a.to_ascii_lowercase() == b.to_ascii_lowercase();
    |
 help: consider using `.eq_ignore_ascii_case()` instead
    |
-LL |     a.eq_ignore_ascii_case(&b);
-   |     ~~~~~~~~~~~~~~~~~~~~~~~~~~
+LL -     a.to_ascii_lowercase() == b.to_ascii_lowercase();
+LL +     a.eq_ignore_ascii_case(&b);
+   |
 
 error: manual case-insensitive ASCII comparison
   --> tests/ui/manual_ignore_case_cmp.rs:63:5
@@ -286,8 +311,9 @@ LL |     a.to_ascii_lowercase() == "a";
    |
 help: consider using `.eq_ignore_ascii_case()` instead
    |
-LL |     a.eq_ignore_ascii_case("a");
-   |     ~~~~~~~~~~~~~~~~~~~~~~~~~~~
+LL -     a.to_ascii_lowercase() == "a";
+LL +     a.eq_ignore_ascii_case("a");
+   |
 
 error: manual case-insensitive ASCII comparison
   --> tests/ui/manual_ignore_case_cmp.rs:64:5
@@ -297,8 +323,9 @@ LL |     "a" == b.to_ascii_lowercase();
    |
 help: consider using `.eq_ignore_ascii_case()` instead
    |
-LL |     "a".eq_ignore_ascii_case(&b);
-   |     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+LL -     "a" == b.to_ascii_lowercase();
+LL +     "a".eq_ignore_ascii_case(&b);
+   |
 
 error: manual case-insensitive ASCII comparison
   --> tests/ui/manual_ignore_case_cmp.rs:67:5
@@ -308,8 +335,9 @@ LL |     a.to_ascii_lowercase() == "a";
    |
 help: consider using `.eq_ignore_ascii_case()` instead
    |
-LL |     a.eq_ignore_ascii_case("a");
-   |     ~~~~~~~~~~~~~~~~~~~~~~~~~~~
+LL -     a.to_ascii_lowercase() == "a";
+LL +     a.eq_ignore_ascii_case("a");
+   |
 
 error: manual case-insensitive ASCII comparison
   --> tests/ui/manual_ignore_case_cmp.rs:68:5
@@ -319,8 +347,9 @@ LL |     "a" == b.to_ascii_lowercase();
    |
 help: consider using `.eq_ignore_ascii_case()` instead
    |
-LL |     "a".eq_ignore_ascii_case(&b);
-   |     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+LL -     "a" == b.to_ascii_lowercase();
+LL +     "a".eq_ignore_ascii_case(&b);
+   |
 
 error: manual case-insensitive ASCII comparison
   --> tests/ui/manual_ignore_case_cmp.rs:71:5
@@ -330,8 +359,9 @@ LL |     a.to_ascii_lowercase() == b.to_ascii_lowercase();
    |
 help: consider using `.eq_ignore_ascii_case()` instead
    |
-LL |     a.eq_ignore_ascii_case(b);
-   |     ~~~~~~~~~~~~~~~~~~~~~~~~~
+LL -     a.to_ascii_lowercase() == b.to_ascii_lowercase();
+LL +     a.eq_ignore_ascii_case(b);
+   |
 
 error: manual case-insensitive ASCII comparison
   --> tests/ui/manual_ignore_case_cmp.rs:72:5
@@ -341,8 +371,9 @@ LL |     a.to_ascii_lowercase() == "a";
    |
 help: consider using `.eq_ignore_ascii_case()` instead
    |
-LL |     a.eq_ignore_ascii_case("a");
-   |     ~~~~~~~~~~~~~~~~~~~~~~~~~~~
+LL -     a.to_ascii_lowercase() == "a";
+LL +     a.eq_ignore_ascii_case("a");
+   |
 
 error: manual case-insensitive ASCII comparison
   --> tests/ui/manual_ignore_case_cmp.rs:73:5
@@ -352,8 +383,9 @@ LL |     "a" == b.to_ascii_lowercase();
    |
 help: consider using `.eq_ignore_ascii_case()` instead
    |
-LL |     "a".eq_ignore_ascii_case(b);
-   |     ~~~~~~~~~~~~~~~~~~~~~~~~~~~
+LL -     "a" == b.to_ascii_lowercase();
+LL +     "a".eq_ignore_ascii_case(b);
+   |
 
 error: manual case-insensitive ASCII comparison
   --> tests/ui/manual_ignore_case_cmp.rs:75:5
@@ -363,8 +395,9 @@ LL |     b.to_ascii_lowercase() == a.to_ascii_lowercase();
    |
 help: consider using `.eq_ignore_ascii_case()` instead
    |
-LL |     b.eq_ignore_ascii_case(&a);
-   |     ~~~~~~~~~~~~~~~~~~~~~~~~~~
+LL -     b.to_ascii_lowercase() == a.to_ascii_lowercase();
+LL +     b.eq_ignore_ascii_case(&a);
+   |
 
 error: manual case-insensitive ASCII comparison
   --> tests/ui/manual_ignore_case_cmp.rs:76:5
@@ -374,8 +407,9 @@ LL |     b.to_ascii_lowercase() == "a";
    |
 help: consider using `.eq_ignore_ascii_case()` instead
    |
-LL |     b.eq_ignore_ascii_case("a");
-   |     ~~~~~~~~~~~~~~~~~~~~~~~~~~~
+LL -     b.to_ascii_lowercase() == "a";
+LL +     b.eq_ignore_ascii_case("a");
+   |
 
 error: manual case-insensitive ASCII comparison
   --> tests/ui/manual_ignore_case_cmp.rs:77:5
@@ -385,8 +419,9 @@ LL |     "a" == a.to_ascii_lowercase();
    |
 help: consider using `.eq_ignore_ascii_case()` instead
    |
-LL |     "a".eq_ignore_ascii_case(&a);
-   |     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+LL -     "a" == a.to_ascii_lowercase();
+LL +     "a".eq_ignore_ascii_case(&a);
+   |
 
 error: manual case-insensitive ASCII comparison
   --> tests/ui/manual_ignore_case_cmp.rs:80:5
@@ -396,8 +431,9 @@ LL |     a.to_ascii_lowercase() == b.to_ascii_lowercase();
    |
 help: consider using `.eq_ignore_ascii_case()` instead
    |
-LL |     a.eq_ignore_ascii_case(b);
-   |     ~~~~~~~~~~~~~~~~~~~~~~~~~
+LL -     a.to_ascii_lowercase() == b.to_ascii_lowercase();
+LL +     a.eq_ignore_ascii_case(b);
+   |
 
 error: manual case-insensitive ASCII comparison
   --> tests/ui/manual_ignore_case_cmp.rs:81:5
@@ -407,8 +443,9 @@ LL |     a.to_ascii_lowercase() == "a";
    |
 help: consider using `.eq_ignore_ascii_case()` instead
    |
-LL |     a.eq_ignore_ascii_case("a");
-   |     ~~~~~~~~~~~~~~~~~~~~~~~~~~~
+LL -     a.to_ascii_lowercase() == "a";
+LL +     a.eq_ignore_ascii_case("a");
+   |
 
 error: manual case-insensitive ASCII comparison
   --> tests/ui/manual_ignore_case_cmp.rs:82:5
@@ -418,8 +455,9 @@ LL |     "a" == b.to_ascii_lowercase();
    |
 help: consider using `.eq_ignore_ascii_case()` instead
    |
-LL |     "a".eq_ignore_ascii_case(b);
-   |     ~~~~~~~~~~~~~~~~~~~~~~~~~~~
+LL -     "a" == b.to_ascii_lowercase();
+LL +     "a".eq_ignore_ascii_case(b);
+   |
 
 error: manual case-insensitive ASCII comparison
   --> tests/ui/manual_ignore_case_cmp.rs:84:5
@@ -429,8 +467,9 @@ LL |     b.to_ascii_lowercase() == a.to_ascii_lowercase();
    |
 help: consider using `.eq_ignore_ascii_case()` instead
    |
-LL |     b.eq_ignore_ascii_case(&a);
-   |     ~~~~~~~~~~~~~~~~~~~~~~~~~~
+LL -     b.to_ascii_lowercase() == a.to_ascii_lowercase();
+LL +     b.eq_ignore_ascii_case(&a);
+   |
 
 error: manual case-insensitive ASCII comparison
   --> tests/ui/manual_ignore_case_cmp.rs:85:5
@@ -440,8 +479,9 @@ LL |     b.to_ascii_lowercase() == "a";
    |
 help: consider using `.eq_ignore_ascii_case()` instead
    |
-LL |     b.eq_ignore_ascii_case("a");
-   |     ~~~~~~~~~~~~~~~~~~~~~~~~~~~
+LL -     b.to_ascii_lowercase() == "a";
+LL +     b.eq_ignore_ascii_case("a");
+   |
 
 error: manual case-insensitive ASCII comparison
   --> tests/ui/manual_ignore_case_cmp.rs:86:5
@@ -451,8 +491,9 @@ LL |     "a" == a.to_ascii_lowercase();
    |
 help: consider using `.eq_ignore_ascii_case()` instead
    |
-LL |     "a".eq_ignore_ascii_case(&a);
-   |     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+LL -     "a" == a.to_ascii_lowercase();
+LL +     "a".eq_ignore_ascii_case(&a);
+   |
 
 error: manual case-insensitive ASCII comparison
   --> tests/ui/manual_ignore_case_cmp.rs:89:5
@@ -462,8 +503,9 @@ LL |     a.to_ascii_lowercase() == b.to_ascii_lowercase();
    |
 help: consider using `.eq_ignore_ascii_case()` instead
    |
-LL |     a.eq_ignore_ascii_case(b);
-   |     ~~~~~~~~~~~~~~~~~~~~~~~~~
+LL -     a.to_ascii_lowercase() == b.to_ascii_lowercase();
+LL +     a.eq_ignore_ascii_case(b);
+   |
 
 error: manual case-insensitive ASCII comparison
   --> tests/ui/manual_ignore_case_cmp.rs:92:5
@@ -473,8 +515,9 @@ LL |     a.to_ascii_lowercase() == b.to_ascii_lowercase();
    |
 help: consider using `.eq_ignore_ascii_case()` instead
    |
-LL |     a.eq_ignore_ascii_case(&b);
-   |     ~~~~~~~~~~~~~~~~~~~~~~~~~~
+LL -     a.to_ascii_lowercase() == b.to_ascii_lowercase();
+LL +     a.eq_ignore_ascii_case(&b);
+   |
 
 error: manual case-insensitive ASCII comparison
   --> tests/ui/manual_ignore_case_cmp.rs:95:5
@@ -484,8 +527,9 @@ LL |     a.to_ascii_lowercase() == b.to_ascii_lowercase();
    |
 help: consider using `.eq_ignore_ascii_case()` instead
    |
-LL |     a.eq_ignore_ascii_case(b);
-   |     ~~~~~~~~~~~~~~~~~~~~~~~~~
+LL -     a.to_ascii_lowercase() == b.to_ascii_lowercase();
+LL +     a.eq_ignore_ascii_case(b);
+   |
 
 error: manual case-insensitive ASCII comparison
   --> tests/ui/manual_ignore_case_cmp.rs:96:5
@@ -495,8 +539,9 @@ LL |     b.to_ascii_lowercase() == a.to_ascii_lowercase();
    |
 help: consider using `.eq_ignore_ascii_case()` instead
    |
-LL |     b.eq_ignore_ascii_case(&a);
-   |     ~~~~~~~~~~~~~~~~~~~~~~~~~~
+LL -     b.to_ascii_lowercase() == a.to_ascii_lowercase();
+LL +     b.eq_ignore_ascii_case(&a);
+   |
 
 error: manual case-insensitive ASCII comparison
   --> tests/ui/manual_ignore_case_cmp.rs:99:5
@@ -506,8 +551,9 @@ LL |     a.to_ascii_lowercase() == b.to_ascii_lowercase();
    |
 help: consider using `.eq_ignore_ascii_case()` instead
    |
-LL |     a.eq_ignore_ascii_case(b);
-   |     ~~~~~~~~~~~~~~~~~~~~~~~~~
+LL -     a.to_ascii_lowercase() == b.to_ascii_lowercase();
+LL +     a.eq_ignore_ascii_case(b);
+   |
 
 error: manual case-insensitive ASCII comparison
   --> tests/ui/manual_ignore_case_cmp.rs:102:5
@@ -517,8 +563,9 @@ LL |     a.to_ascii_lowercase() == b.to_ascii_lowercase();
    |
 help: consider using `.eq_ignore_ascii_case()` instead
    |
-LL |     a.eq_ignore_ascii_case(b);
-   |     ~~~~~~~~~~~~~~~~~~~~~~~~~
+LL -     a.to_ascii_lowercase() == b.to_ascii_lowercase();
+LL +     a.eq_ignore_ascii_case(b);
+   |
 
 error: manual case-insensitive ASCII comparison
   --> tests/ui/manual_ignore_case_cmp.rs:105:5
@@ -528,8 +575,9 @@ LL |     a.to_ascii_lowercase() == b.to_ascii_lowercase();
    |
 help: consider using `.eq_ignore_ascii_case()` instead
    |
-LL |     a.eq_ignore_ascii_case(b);
-   |     ~~~~~~~~~~~~~~~~~~~~~~~~~
+LL -     a.to_ascii_lowercase() == b.to_ascii_lowercase();
+LL +     a.eq_ignore_ascii_case(b);
+   |
 
 error: manual case-insensitive ASCII comparison
   --> tests/ui/manual_ignore_case_cmp.rs:106:5
@@ -539,8 +587,9 @@ LL |     b.to_ascii_lowercase() == a.to_ascii_lowercase();
    |
 help: consider using `.eq_ignore_ascii_case()` instead
    |
-LL |     b.eq_ignore_ascii_case(a);
-   |     ~~~~~~~~~~~~~~~~~~~~~~~~~
+LL -     b.to_ascii_lowercase() == a.to_ascii_lowercase();
+LL +     b.eq_ignore_ascii_case(a);
+   |
 
 error: aborting due to 49 previous errors
 
diff --git a/src/tools/clippy/tests/ui/manual_is_ascii_check.stderr b/src/tools/clippy/tests/ui/manual_is_ascii_check.stderr
index 92d93208006..7b3f0c938b0 100644
--- a/src/tools/clippy/tests/ui/manual_is_ascii_check.stderr
+++ b/src/tools/clippy/tests/ui/manual_is_ascii_check.stderr
@@ -153,8 +153,9 @@ LL |     take_while(|c| ('A'..='Z').contains(&c));
    |
 help: try
    |
-LL |     take_while(|c: char| c.is_ascii_uppercase());
-   |                 ~~~~~~~  ~~~~~~~~~~~~~~~~~~~~~~
+LL -     take_while(|c| ('A'..='Z').contains(&c));
+LL +     take_while(|c: char| c.is_ascii_uppercase());
+   |
 
 error: manual check for common ascii range
   --> tests/ui/manual_is_ascii_check.rs:82:20
@@ -164,8 +165,9 @@ LL |     take_while(|c| (b'A'..=b'Z').contains(&c));
    |
 help: try
    |
-LL |     take_while(|c: u8| c.is_ascii_uppercase());
-   |                 ~~~~~  ~~~~~~~~~~~~~~~~~~~~~~
+LL -     take_while(|c| (b'A'..=b'Z').contains(&c));
+LL +     take_while(|c: u8| c.is_ascii_uppercase());
+   |
 
 error: manual check for common ascii range
   --> tests/ui/manual_is_ascii_check.rs:83:26
@@ -181,8 +183,9 @@ LL |     let digits: Vec<&char> = ['1', 'A'].iter().take_while(|c| ('0'..='9').c
    |
 help: try
    |
-LL |     let digits: Vec<&char> = ['1', 'A'].iter().take_while(|c: &&char| c.is_ascii_digit()).collect();
-   |                                                            ~~~~~~~~~  ~~~~~~~~~~~~~~~~~~
+LL -     let digits: Vec<&char> = ['1', 'A'].iter().take_while(|c| ('0'..='9').contains(c)).collect();
+LL +     let digits: Vec<&char> = ['1', 'A'].iter().take_while(|c: &&char| c.is_ascii_digit()).collect();
+   |
 
 error: manual check for common ascii range
   --> tests/ui/manual_is_ascii_check.rs:88:71
@@ -192,8 +195,9 @@ LL |     let digits: Vec<&mut char> = ['1', 'A'].iter_mut().take_while(|c| ('0'.
    |
 help: try
    |
-LL |     let digits: Vec<&mut char> = ['1', 'A'].iter_mut().take_while(|c: &&mut char| c.is_ascii_digit()).collect();
-   |                                                                    ~~~~~~~~~~~~~  ~~~~~~~~~~~~~~~~~~
+LL -     let digits: Vec<&mut char> = ['1', 'A'].iter_mut().take_while(|c| ('0'..='9').contains(c)).collect();
+LL +     let digits: Vec<&mut char> = ['1', 'A'].iter_mut().take_while(|c: &&mut char| c.is_ascii_digit()).collect();
+   |
 
 error: aborting due to 29 previous errors
 
diff --git a/src/tools/clippy/tests/ui/map_all_any_identity.stderr b/src/tools/clippy/tests/ui/map_all_any_identity.stderr
index 98fdcc2a939..39df2a3d961 100644
--- a/src/tools/clippy/tests/ui/map_all_any_identity.stderr
+++ b/src/tools/clippy/tests/ui/map_all_any_identity.stderr
@@ -8,8 +8,9 @@ LL |     let _ = ["foo"].into_iter().map(|s| s == "foo").any(|a| a);
    = help: to override `-D warnings` add `#[allow(clippy::map_all_any_identity)]`
 help: use `.any(...)` instead
    |
-LL |     let _ = ["foo"].into_iter().any(|s| s == "foo");
-   |                                 ~~~~~~~~~~~~~~~~~~~
+LL -     let _ = ["foo"].into_iter().map(|s| s == "foo").any(|a| a);
+LL +     let _ = ["foo"].into_iter().any(|s| s == "foo");
+   |
 
 error: usage of `.map(...).all(identity)`
   --> tests/ui/map_all_any_identity.rs:6:33
@@ -19,8 +20,9 @@ LL |     let _ = ["foo"].into_iter().map(|s| s == "foo").all(std::convert::ident
    |
 help: use `.all(...)` instead
    |
-LL |     let _ = ["foo"].into_iter().all(|s| s == "foo");
-   |                                 ~~~~~~~~~~~~~~~~~~~
+LL -     let _ = ["foo"].into_iter().map(|s| s == "foo").all(std::convert::identity);
+LL +     let _ = ["foo"].into_iter().all(|s| s == "foo");
+   |
 
 error: aborting due to 2 previous errors
 
diff --git a/src/tools/clippy/tests/ui/map_with_unused_argument_over_ranges.stderr b/src/tools/clippy/tests/ui/map_with_unused_argument_over_ranges.stderr
index 0b56c6d9521..840515f95df 100644
--- a/src/tools/clippy/tests/ui/map_with_unused_argument_over_ranges.stderr
+++ b/src/tools/clippy/tests/ui/map_with_unused_argument_over_ranges.stderr
@@ -68,8 +68,9 @@ LL |     (0..10).map(|_| 3);
    |
 help: remove the explicit range and use `repeat_n`
    |
-LL |     std::iter::repeat_n(3, 10);
-   |     ~~~~~~~~~~~~~~~~~~~ ~~~~~
+LL -     (0..10).map(|_| 3);
+LL +     std::iter::repeat_n(3, 10);
+   |
 
 error: map of a closure that does not depend on its parameter over a range
   --> tests/ui/map_with_unused_argument_over_ranges.rs:31:5
@@ -216,8 +217,9 @@ LL |     (0..10).map(|_| 3);
    |
 help: remove the explicit range and use `repeat` and `take`
    |
-LL |     std::iter::repeat(3).take(10);
-   |     ~~~~~~~~~~~~~~~~~ ~ +++++++++
+LL -     (0..10).map(|_| 3);
+LL +     std::iter::repeat(3).take(10);
+   |
 
 error: aborting due to 18 previous errors
 
diff --git a/src/tools/clippy/tests/ui/map_with_unused_argument_over_ranges_nostd.stderr b/src/tools/clippy/tests/ui/map_with_unused_argument_over_ranges_nostd.stderr
index d47f3d09175..975ded83560 100644
--- a/src/tools/clippy/tests/ui/map_with_unused_argument_over_ranges_nostd.stderr
+++ b/src/tools/clippy/tests/ui/map_with_unused_argument_over_ranges_nostd.stderr
@@ -8,8 +8,9 @@ LL |     let _: Vec<_> = (0..10).map(|_| 3 + 1).collect();
    = help: to override `-D warnings` add `#[allow(clippy::map_with_unused_argument_over_ranges)]`
 help: remove the explicit range and use `repeat_n`
    |
-LL |     let _: Vec<_> = core::iter::repeat_n(3 + 1, 10).collect();
-   |                     ~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~
+LL -     let _: Vec<_> = (0..10).map(|_| 3 + 1).collect();
+LL +     let _: Vec<_> = core::iter::repeat_n(3 + 1, 10).collect();
+   |
 
 error: aborting due to 1 previous error
 
diff --git a/src/tools/clippy/tests/ui/match_result_ok.stderr b/src/tools/clippy/tests/ui/match_result_ok.stderr
index b5b91cbe553..18b23bd7845 100644
--- a/src/tools/clippy/tests/ui/match_result_ok.stderr
+++ b/src/tools/clippy/tests/ui/match_result_ok.stderr
@@ -8,8 +8,9 @@ LL |     if let Some(y) = x.parse().ok() { y } else { 0 }
    = help: to override `-D warnings` add `#[allow(clippy::match_result_ok)]`
 help: consider matching on `Ok(y)` and removing the call to `ok` instead
    |
-LL |     if let Ok(y) = x.parse() { y } else { 0 }
-   |     ~~~~~~~~~~~~~~~~~~~~~~~~
+LL -     if let Some(y) = x.parse().ok() { y } else { 0 }
+LL +     if let Ok(y) = x.parse() { y } else { 0 }
+   |
 
 error: matching on `Some` with `ok()` is redundant
   --> tests/ui/match_result_ok.rs:23:9
@@ -19,8 +20,9 @@ LL |         if let Some(y) = x   .   parse()   .   ok   ()    {
    |
 help: consider matching on `Ok(y)` and removing the call to `ok` instead
    |
-LL |         if let Ok(y) = x   .   parse()    {
-   |         ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+LL -         if let Some(y) = x   .   parse()   .   ok   ()    {
+LL +         if let Ok(y) = x   .   parse()    {
+   |
 
 error: matching on `Some` with `ok()` is redundant
   --> tests/ui/match_result_ok.rs:49:5
@@ -30,8 +32,9 @@ LL |     while let Some(a) = wat.next().ok() {
    |
 help: consider matching on `Ok(a)` and removing the call to `ok` instead
    |
-LL |     while let Ok(a) = wat.next() {
-   |     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+LL -     while let Some(a) = wat.next().ok() {
+LL +     while let Ok(a) = wat.next() {
+   |
 
 error: aborting due to 3 previous errors
 
diff --git a/src/tools/clippy/tests/ui/match_str_case_mismatch.stderr b/src/tools/clippy/tests/ui/match_str_case_mismatch.stderr
index 67e9ccaf6d2..5b14fd13a53 100644
--- a/src/tools/clippy/tests/ui/match_str_case_mismatch.stderr
+++ b/src/tools/clippy/tests/ui/match_str_case_mismatch.stderr
@@ -8,8 +8,9 @@ LL |         "Bar" => {},
    = help: to override `-D warnings` add `#[allow(clippy::match_str_case_mismatch)]`
 help: consider changing the case of this arm to respect `to_ascii_lowercase`
    |
-LL |         "bar" => {},
-   |         ~~~~~
+LL -         "Bar" => {},
+LL +         "bar" => {},
+   |
 
 error: this `match` arm has a differing case than its expression
   --> tests/ui/match_str_case_mismatch.rs:122:9
@@ -19,8 +20,9 @@ LL |         "~!@#$%^&*()-_=+Foo" => {},
    |
 help: consider changing the case of this arm to respect `to_ascii_lowercase` (notice the capitalization difference)
    |
-LL |         "~!@#$%^&*()-_=+foo" => {},
-   |         ~~~~~~~~~~~~~~~~~~~~
+LL -         "~!@#$%^&*()-_=+Foo" => {},
+LL +         "~!@#$%^&*()-_=+foo" => {},
+   |
 
 error: this `match` arm has a differing case than its expression
   --> tests/ui/match_str_case_mismatch.rs:134:9
@@ -30,8 +32,9 @@ LL |         "Воды" => {},
    |
 help: consider changing the case of this arm to respect `to_lowercase`
    |
-LL |         "воды" => {},
-   |         ~~~~~~
+LL -         "Воды" => {},
+LL +         "воды" => {},
+   |
 
 error: this `match` arm has a differing case than its expression
   --> tests/ui/match_str_case_mismatch.rs:145:9
@@ -41,8 +44,9 @@ LL |         "barDz" => {},
    |
 help: consider changing the case of this arm to respect `to_lowercase`
    |
-LL |         "bardz" => {},
-   |         ~~~~~~
+LL -         "barDz" => {},
+LL +         "bardz" => {},
+   |
 
 error: this `match` arm has a differing case than its expression
   --> tests/ui/match_str_case_mismatch.rs:155:9
@@ -52,8 +56,9 @@ LL |         "bARʁ" => {},
    |
 help: consider changing the case of this arm to respect `to_uppercase`
    |
-LL |         "BARʁ" => {},
-   |         ~~~~~~
+LL -         "bARʁ" => {},
+LL +         "BARʁ" => {},
+   |
 
 error: this `match` arm has a differing case than its expression
   --> tests/ui/match_str_case_mismatch.rs:165:9
@@ -63,8 +68,9 @@ LL |         "Bar" => {},
    |
 help: consider changing the case of this arm to respect `to_ascii_lowercase`
    |
-LL |         "bar" => {},
-   |         ~~~~~
+LL -         "Bar" => {},
+LL +         "bar" => {},
+   |
 
 error: this `match` arm has a differing case than its expression
   --> tests/ui/match_str_case_mismatch.rs:180:9
@@ -74,8 +80,9 @@ LL |         "bAR" => {},
    |
 help: consider changing the case of this arm to respect `to_ascii_uppercase`
    |
-LL |         "BAR" => {},
-   |         ~~~~~
+LL -         "bAR" => {},
+LL +         "BAR" => {},
+   |
 
 error: aborting due to 7 previous errors
 
diff --git a/src/tools/clippy/tests/ui/must_use_unit.fixed b/src/tools/clippy/tests/ui/must_use_unit.fixed
index b92d9379c90..7e2a7296049 100644
--- a/src/tools/clippy/tests/ui/must_use_unit.fixed
+++ b/src/tools/clippy/tests/ui/must_use_unit.fixed
@@ -24,8 +24,3 @@ fn main() {
     );
 }
 
-#[cfg_attr(all(), deprecated)]
-fn issue_12320() {}
-
-#[cfg_attr(all(), deprecated, doc = "foo")]
-fn issue_12320_2() {}
diff --git a/src/tools/clippy/tests/ui/must_use_unit.rs b/src/tools/clippy/tests/ui/must_use_unit.rs
index c77e7282750..f41b1a7c800 100644
--- a/src/tools/clippy/tests/ui/must_use_unit.rs
+++ b/src/tools/clippy/tests/ui/must_use_unit.rs
@@ -27,8 +27,3 @@ fn main() {
     );
 }
 
-#[cfg_attr(all(), must_use, deprecated)]
-fn issue_12320() {}
-
-#[cfg_attr(all(), deprecated, doc = "foo", must_use)]
-fn issue_12320_2() {}
diff --git a/src/tools/clippy/tests/ui/must_use_unit.stderr b/src/tools/clippy/tests/ui/must_use_unit.stderr
index b435568deea..c2ee2edda7d 100644
--- a/src/tools/clippy/tests/ui/must_use_unit.stderr
+++ b/src/tools/clippy/tests/ui/must_use_unit.stderr
@@ -25,21 +25,5 @@ LL | #[must_use = "With note"]
 LL | pub fn must_use_with_note() {}
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
-error: this unit-returning function has a `#[must_use]` attribute
-  --> tests/ui/must_use_unit.rs:31:1
-   |
-LL | #[cfg_attr(all(), must_use, deprecated)]
-   |                   -------------------- help: change these attributes to: `deprecated`
-LL | fn issue_12320() {}
-   | ^^^^^^^^^^^^^^^^
-
-error: this unit-returning function has a `#[must_use]` attribute
-  --> tests/ui/must_use_unit.rs:34:1
-   |
-LL | #[cfg_attr(all(), deprecated, doc = "foo", must_use)]
-   |                   --------------------------------- help: change these attributes to: `deprecated, doc = "foo"`
-LL | fn issue_12320_2() {}
-   | ^^^^^^^^^^^^^^^^^^
-
-error: aborting due to 5 previous errors
+error: aborting due to 3 previous errors
 
diff --git a/src/tools/clippy/tests/ui/must_use_unit_12320.rs b/src/tools/clippy/tests/ui/must_use_unit_12320.rs
new file mode 100644
index 00000000000..39dcafdb38b
--- /dev/null
+++ b/src/tools/clippy/tests/ui/must_use_unit_12320.rs
@@ -0,0 +1,11 @@
+//@aux-build:proc_macros.rs
+//@no-rustfix
+
+#![warn(clippy::must_use_unit)]
+#![allow(clippy::unused_unit)]
+
+#[cfg_attr(all(), must_use, deprecated)]
+fn issue_12320() {}
+
+#[cfg_attr(all(), deprecated, doc = "foo", must_use)]
+fn issue_12320_2() {}
diff --git a/src/tools/clippy/tests/ui/must_use_unit_12320.stderr b/src/tools/clippy/tests/ui/must_use_unit_12320.stderr
new file mode 100644
index 00000000000..b3e1cbc0457
--- /dev/null
+++ b/src/tools/clippy/tests/ui/must_use_unit_12320.stderr
@@ -0,0 +1,28 @@
+error: this unit-returning function has a `#[must_use]` attribute
+  --> tests/ui/must_use_unit_12320.rs:8:1
+   |
+LL | fn issue_12320() {}
+   | ^^^^^^^^^^^^^^^^
+   |
+help: remove `must_use`
+  --> tests/ui/must_use_unit_12320.rs:7:19
+   |
+LL | #[cfg_attr(all(), must_use, deprecated)]
+   |                   ^^^^^^^^
+   = note: `-D clippy::double-must-use` implied by `-D warnings`
+   = help: to override `-D warnings` add `#[allow(clippy::double_must_use)]`
+
+error: this unit-returning function has a `#[must_use]` attribute
+  --> tests/ui/must_use_unit_12320.rs:11:1
+   |
+LL | fn issue_12320_2() {}
+   | ^^^^^^^^^^^^^^^^^^
+   |
+help: remove `must_use`
+  --> tests/ui/must_use_unit_12320.rs:10:44
+   |
+LL | #[cfg_attr(all(), deprecated, doc = "foo", must_use)]
+   |                                            ^^^^^^^^
+
+error: aborting due to 2 previous errors
+
diff --git a/src/tools/clippy/tests/ui/needless_borrow_pat.stderr b/src/tools/clippy/tests/ui/needless_borrow_pat.stderr
index 2ad69449039..035376cabaf 100644
--- a/src/tools/clippy/tests/ui/needless_borrow_pat.stderr
+++ b/src/tools/clippy/tests/ui/needless_borrow_pat.stderr
@@ -15,8 +15,9 @@ LL |         Some(ref x) => *x,
    |
 help: try
    |
-LL |         Some(x) => x,
-   |              ~     ~
+LL -         Some(ref x) => *x,
+LL +         Some(x) => x,
+   |
 
 error: this pattern creates a reference to a reference
   --> tests/ui/needless_borrow_pat.rs:74:14
@@ -71,8 +72,9 @@ LL |         E::A(ref x) | E::B(ref x) => *x,
    |
 help: try
    |
-LL |         E::A(x) | E::B(x) => x,
-   |              ~         ~     ~
+LL -         E::A(ref x) | E::B(ref x) => *x,
+LL +         E::A(x) | E::B(x) => x,
+   |
 
 error: this pattern creates a reference to a reference
   --> tests/ui/needless_borrow_pat.rs:126:21
diff --git a/src/tools/clippy/tests/ui/needless_for_each_unfixable.stderr b/src/tools/clippy/tests/ui/needless_for_each_unfixable.stderr
index 682140a1dfd..722016b1212 100644
--- a/src/tools/clippy/tests/ui/needless_for_each_unfixable.stderr
+++ b/src/tools/clippy/tests/ui/needless_for_each_unfixable.stderr
@@ -25,8 +25,9 @@ LL +     }
    |
 help: ...and replace `return` with `continue`
    |
-LL |             continue;
-   |             ~~~~~~~~
+LL -             return;
+LL +             continue;
+   |
 
 error: aborting due to 1 previous error
 
diff --git a/src/tools/clippy/tests/ui/needless_pass_by_value.stderr b/src/tools/clippy/tests/ui/needless_pass_by_value.stderr
index 2587d3f8c52..2c90da51252 100644
--- a/src/tools/clippy/tests/ui/needless_pass_by_value.stderr
+++ b/src/tools/clippy/tests/ui/needless_pass_by_value.stderr
@@ -63,12 +63,14 @@ LL | fn issue_2114(s: String, t: String, u: Vec<i32>, v: Vec<i32>) {
    |
 help: consider changing the type to
    |
-LL | fn issue_2114(s: String, t: &str, u: Vec<i32>, v: Vec<i32>) {
-   |                             ~~~~
+LL - fn issue_2114(s: String, t: String, u: Vec<i32>, v: Vec<i32>) {
+LL + fn issue_2114(s: String, t: &str, u: Vec<i32>, v: Vec<i32>) {
+   |
 help: change `t.clone()` to
    |
-LL |     let _ = t.to_string();
-   |             ~~~~~~~~~~~~~
+LL -     let _ = t.clone();
+LL +     let _ = t.to_string();
+   |
 
 error: this argument is passed by value, but not consumed in the function body
   --> tests/ui/needless_pass_by_value.rs:91:40
@@ -84,12 +86,14 @@ LL | fn issue_2114(s: String, t: String, u: Vec<i32>, v: Vec<i32>) {
    |
 help: consider changing the type to
    |
-LL | fn issue_2114(s: String, t: String, u: Vec<i32>, v: &[i32]) {
-   |                                                     ~~~~~~
+LL - fn issue_2114(s: String, t: String, u: Vec<i32>, v: Vec<i32>) {
+LL + fn issue_2114(s: String, t: String, u: Vec<i32>, v: &[i32]) {
+   |
 help: change `v.clone()` to
    |
-LL |     let _ = v.to_owned();
-   |             ~~~~~~~~~~~~
+LL -     let _ = v.clone();
+LL +     let _ = v.to_owned();
+   |
 
 error: this argument is passed by value, but not consumed in the function body
   --> tests/ui/needless_pass_by_value.rs:108:12
diff --git a/src/tools/clippy/tests/ui/needless_range_loop.stderr b/src/tools/clippy/tests/ui/needless_range_loop.stderr
index 503d796e5e8..831b8511e43 100644
--- a/src/tools/clippy/tests/ui/needless_range_loop.stderr
+++ b/src/tools/clippy/tests/ui/needless_range_loop.stderr
@@ -8,8 +8,9 @@ LL |     for i in 0..vec.len() {
    = help: to override `-D warnings` add `#[allow(clippy::needless_range_loop)]`
 help: consider using an iterator
    |
-LL |     for <item> in &vec {
-   |         ~~~~~~    ~~~~
+LL -     for i in 0..vec.len() {
+LL +     for <item> in &vec {
+   |
 
 error: the loop variable `i` is only used to index `vec`
   --> tests/ui/needless_range_loop.rs:27:14
@@ -19,8 +20,9 @@ LL |     for i in 0..vec.len() {
    |
 help: consider using an iterator
    |
-LL |     for <item> in &vec {
-   |         ~~~~~~    ~~~~
+LL -     for i in 0..vec.len() {
+LL +     for <item> in &vec {
+   |
 
 error: the loop variable `j` is only used to index `STATIC`
   --> tests/ui/needless_range_loop.rs:33:14
@@ -30,8 +32,9 @@ LL |     for j in 0..4 {
    |
 help: consider using an iterator
    |
-LL |     for <item> in &STATIC {
-   |         ~~~~~~    ~~~~~~~
+LL -     for j in 0..4 {
+LL +     for <item> in &STATIC {
+   |
 
 error: the loop variable `j` is only used to index `CONST`
   --> tests/ui/needless_range_loop.rs:38:14
@@ -41,8 +44,9 @@ LL |     for j in 0..4 {
    |
 help: consider using an iterator
    |
-LL |     for <item> in &CONST {
-   |         ~~~~~~    ~~~~~~
+LL -     for j in 0..4 {
+LL +     for <item> in &CONST {
+   |
 
 error: the loop variable `i` is used to index `vec`
   --> tests/ui/needless_range_loop.rs:43:14
@@ -52,8 +56,9 @@ LL |     for i in 0..vec.len() {
    |
 help: consider using an iterator and enumerate()
    |
-LL |     for (i, <item>) in vec.iter().enumerate() {
-   |         ~~~~~~~~~~~    ~~~~~~~~~~~~~~~~~~~~~~
+LL -     for i in 0..vec.len() {
+LL +     for (i, <item>) in vec.iter().enumerate() {
+   |
 
 error: the loop variable `i` is only used to index `vec2`
   --> tests/ui/needless_range_loop.rs:52:14
@@ -63,8 +68,9 @@ LL |     for i in 0..vec.len() {
    |
 help: consider using an iterator
    |
-LL |     for <item> in vec2.iter().take(vec.len()) {
-   |         ~~~~~~    ~~~~~~~~~~~~~~~~~~~~~~~~~~~
+LL -     for i in 0..vec.len() {
+LL +     for <item> in vec2.iter().take(vec.len()) {
+   |
 
 error: the loop variable `i` is only used to index `vec`
   --> tests/ui/needless_range_loop.rs:57:14
@@ -74,8 +80,9 @@ LL |     for i in 5..vec.len() {
    |
 help: consider using an iterator
    |
-LL |     for <item> in vec.iter().skip(5) {
-   |         ~~~~~~    ~~~~~~~~~~~~~~~~~~
+LL -     for i in 5..vec.len() {
+LL +     for <item> in vec.iter().skip(5) {
+   |
 
 error: the loop variable `i` is only used to index `vec`
   --> tests/ui/needless_range_loop.rs:62:14
@@ -85,8 +92,9 @@ LL |     for i in 0..MAX_LEN {
    |
 help: consider using an iterator
    |
-LL |     for <item> in vec.iter().take(MAX_LEN) {
-   |         ~~~~~~    ~~~~~~~~~~~~~~~~~~~~~~~~
+LL -     for i in 0..MAX_LEN {
+LL +     for <item> in vec.iter().take(MAX_LEN) {
+   |
 
 error: the loop variable `i` is only used to index `vec`
   --> tests/ui/needless_range_loop.rs:67:14
@@ -96,8 +104,9 @@ LL |     for i in 0..=MAX_LEN {
    |
 help: consider using an iterator
    |
-LL |     for <item> in vec.iter().take(MAX_LEN + 1) {
-   |         ~~~~~~    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+LL -     for i in 0..=MAX_LEN {
+LL +     for <item> in vec.iter().take(MAX_LEN + 1) {
+   |
 
 error: the loop variable `i` is only used to index `vec`
   --> tests/ui/needless_range_loop.rs:72:14
@@ -107,8 +116,9 @@ LL |     for i in 5..10 {
    |
 help: consider using an iterator
    |
-LL |     for <item> in vec.iter().take(10).skip(5) {
-   |         ~~~~~~    ~~~~~~~~~~~~~~~~~~~~~~~~~~~
+LL -     for i in 5..10 {
+LL +     for <item> in vec.iter().take(10).skip(5) {
+   |
 
 error: the loop variable `i` is only used to index `vec`
   --> tests/ui/needless_range_loop.rs:77:14
@@ -118,8 +128,9 @@ LL |     for i in 5..=10 {
    |
 help: consider using an iterator
    |
-LL |     for <item> in vec.iter().take(10 + 1).skip(5) {
-   |         ~~~~~~    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+LL -     for i in 5..=10 {
+LL +     for <item> in vec.iter().take(10 + 1).skip(5) {
+   |
 
 error: the loop variable `i` is used to index `vec`
   --> tests/ui/needless_range_loop.rs:82:14
@@ -129,8 +140,9 @@ LL |     for i in 5..vec.len() {
    |
 help: consider using an iterator and enumerate()
    |
-LL |     for (i, <item>) in vec.iter().enumerate().skip(5) {
-   |         ~~~~~~~~~~~    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+LL -     for i in 5..vec.len() {
+LL +     for (i, <item>) in vec.iter().enumerate().skip(5) {
+   |
 
 error: the loop variable `i` is used to index `vec`
   --> tests/ui/needless_range_loop.rs:87:14
@@ -140,8 +152,9 @@ LL |     for i in 5..10 {
    |
 help: consider using an iterator and enumerate()
    |
-LL |     for (i, <item>) in vec.iter().enumerate().take(10).skip(5) {
-   |         ~~~~~~~~~~~    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+LL -     for i in 5..10 {
+LL +     for (i, <item>) in vec.iter().enumerate().take(10).skip(5) {
+   |
 
 error: the loop variable `i` is used to index `vec`
   --> tests/ui/needless_range_loop.rs:93:14
@@ -151,8 +164,9 @@ LL |     for i in 0..vec.len() {
    |
 help: consider using an iterator and enumerate()
    |
-LL |     for (i, <item>) in vec.iter_mut().enumerate() {
-   |         ~~~~~~~~~~~    ~~~~~~~~~~~~~~~~~~~~~~~~~~
+LL -     for i in 0..vec.len() {
+LL +     for (i, <item>) in vec.iter_mut().enumerate() {
+   |
 
 error: aborting due to 14 previous errors
 
diff --git a/src/tools/clippy/tests/ui/needless_range_loop2.stderr b/src/tools/clippy/tests/ui/needless_range_loop2.stderr
index 353f30b1b26..f37e1f2872d 100644
--- a/src/tools/clippy/tests/ui/needless_range_loop2.stderr
+++ b/src/tools/clippy/tests/ui/needless_range_loop2.stderr
@@ -8,8 +8,9 @@ LL |     for i in 3..10 {
    = help: to override `-D warnings` add `#[allow(clippy::needless_range_loop)]`
 help: consider using an iterator
    |
-LL |     for <item> in ns.iter().take(10).skip(3) {
-   |         ~~~~~~    ~~~~~~~~~~~~~~~~~~~~~~~~~~
+LL -     for i in 3..10 {
+LL +     for <item> in ns.iter().take(10).skip(3) {
+   |
 
 error: the loop variable `i` is only used to index `ms`
   --> tests/ui/needless_range_loop2.rs:34:14
@@ -19,8 +20,9 @@ LL |     for i in 0..ms.len() {
    |
 help: consider using an iterator
    |
-LL |     for <item> in &mut ms {
-   |         ~~~~~~    ~~~~~~~
+LL -     for i in 0..ms.len() {
+LL +     for <item> in &mut ms {
+   |
 
 error: the loop variable `i` is only used to index `ms`
   --> tests/ui/needless_range_loop2.rs:41:14
@@ -30,8 +32,9 @@ LL |     for i in 0..ms.len() {
    |
 help: consider using an iterator
    |
-LL |     for <item> in &mut ms {
-   |         ~~~~~~    ~~~~~~~
+LL -     for i in 0..ms.len() {
+LL +     for <item> in &mut ms {
+   |
 
 error: the loop variable `i` is only used to index `vec`
   --> tests/ui/needless_range_loop2.rs:66:14
@@ -41,8 +44,9 @@ LL |     for i in x..x + 4 {
    |
 help: consider using an iterator
    |
-LL |     for <item> in vec.iter_mut().skip(x).take(4) {
-   |         ~~~~~~    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+LL -     for i in x..x + 4 {
+LL +     for <item> in vec.iter_mut().skip(x).take(4) {
+   |
 
 error: the loop variable `i` is only used to index `vec`
   --> tests/ui/needless_range_loop2.rs:74:14
@@ -52,8 +56,9 @@ LL |     for i in x..=x + 4 {
    |
 help: consider using an iterator
    |
-LL |     for <item> in vec.iter_mut().skip(x).take(4 + 1) {
-   |         ~~~~~~    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+LL -     for i in x..=x + 4 {
+LL +     for <item> in vec.iter_mut().skip(x).take(4 + 1) {
+   |
 
 error: the loop variable `i` is only used to index `arr`
   --> tests/ui/needless_range_loop2.rs:81:14
@@ -63,8 +68,9 @@ LL |     for i in 0..3 {
    |
 help: consider using an iterator
    |
-LL |     for <item> in &arr {
-   |         ~~~~~~    ~~~~
+LL -     for i in 0..3 {
+LL +     for <item> in &arr {
+   |
 
 error: the loop variable `i` is only used to index `arr`
   --> tests/ui/needless_range_loop2.rs:86:14
@@ -74,8 +80,9 @@ LL |     for i in 0..2 {
    |
 help: consider using an iterator
    |
-LL |     for <item> in arr.iter().take(2) {
-   |         ~~~~~~    ~~~~~~~~~~~~~~~~~~
+LL -     for i in 0..2 {
+LL +     for <item> in arr.iter().take(2) {
+   |
 
 error: the loop variable `i` is only used to index `arr`
   --> tests/ui/needless_range_loop2.rs:91:14
@@ -85,8 +92,9 @@ LL |     for i in 1..3 {
    |
 help: consider using an iterator
    |
-LL |     for <item> in arr.iter().skip(1) {
-   |         ~~~~~~    ~~~~~~~~~~~~~~~~~~
+LL -     for i in 1..3 {
+LL +     for <item> in arr.iter().skip(1) {
+   |
 
 error: aborting due to 8 previous errors
 
diff --git a/src/tools/clippy/tests/ui/needless_return.stderr b/src/tools/clippy/tests/ui/needless_return.stderr
index d3c2a6badc0..8d8b5b9e713 100644
--- a/src/tools/clippy/tests/ui/needless_return.stderr
+++ b/src/tools/clippy/tests/ui/needless_return.stderr
@@ -80,8 +80,9 @@ LL |         true => return false,
    |
 help: remove `return`
    |
-LL |         true => false,
-   |                 ~~~~~
+LL -         true => return false,
+LL +         true => false,
+   |
 
 error: unneeded `return` statement
   --> tests/ui/needless_return.rs:58:13
@@ -115,8 +116,9 @@ LL |     let _ = || return true;
    |
 help: remove `return`
    |
-LL |     let _ = || true;
-   |                ~~~~
+LL -     let _ = || return true;
+LL +     let _ = || true;
+   |
 
 error: unneeded `return` statement
   --> tests/ui/needless_return.rs:71:5
@@ -183,8 +185,9 @@ LL |         _ => return,
    |
 help: replace `return` with a unit value
    |
-LL |         _ => (),
-   |              ~~
+LL -         _ => return,
+LL +         _ => (),
+   |
 
 error: unneeded `return` statement
   --> tests/ui/needless_return.rs:97:24
@@ -209,8 +212,9 @@ LL |         _ => return,
    |
 help: replace `return` with a unit value
    |
-LL |         _ => (),
-   |              ~~
+LL -         _ => return,
+LL +         _ => (),
+   |
 
 error: unneeded `return` statement
   --> tests/ui/needless_return.rs:113:9
@@ -244,8 +248,9 @@ LL |         bar.unwrap_or_else(|_| return)
    |
 help: replace `return` with an empty block
    |
-LL |         bar.unwrap_or_else(|_| {})
-   |                                ~~
+LL -         bar.unwrap_or_else(|_| return)
+LL +         bar.unwrap_or_else(|_| {})
+   |
 
 error: unneeded `return` statement
   --> tests/ui/needless_return.rs:141:21
@@ -270,8 +275,9 @@ LL |         let _ = || return;
    |
 help: replace `return` with an empty block
    |
-LL |         let _ = || {};
-   |                    ~~
+LL -         let _ = || return;
+LL +         let _ = || {};
+   |
 
 error: unneeded `return` statement
   --> tests/ui/needless_return.rs:150:32
@@ -281,8 +287,9 @@ LL |         res.unwrap_or_else(|_| return Foo)
    |
 help: remove `return`
    |
-LL |         res.unwrap_or_else(|_| Foo)
-   |                                ~~~
+LL -         res.unwrap_or_else(|_| return Foo)
+LL +         res.unwrap_or_else(|_| Foo)
+   |
 
 error: unneeded `return` statement
   --> tests/ui/needless_return.rs:159:5
@@ -340,8 +347,9 @@ LL |         true => return false,
    |
 help: remove `return`
    |
-LL |         true => false,
-   |                 ~~~~~
+LL -         true => return false,
+LL +         true => false,
+   |
 
 error: unneeded `return` statement
   --> tests/ui/needless_return.rs:178:13
@@ -375,8 +383,9 @@ LL |     let _ = || return true;
    |
 help: remove `return`
    |
-LL |     let _ = || true;
-   |                ~~~~
+LL -     let _ = || return true;
+LL +     let _ = || true;
+   |
 
 error: unneeded `return` statement
   --> tests/ui/needless_return.rs:191:5
@@ -443,8 +452,9 @@ LL |         _ => return,
    |
 help: replace `return` with a unit value
    |
-LL |         _ => (),
-   |              ~~
+LL -         _ => return,
+LL +         _ => (),
+   |
 
 error: unneeded `return` statement
   --> tests/ui/needless_return.rs:222:9
diff --git a/src/tools/clippy/tests/ui/never_loop.stderr b/src/tools/clippy/tests/ui/never_loop.stderr
index dab3488af10..203e3325822 100644
--- a/src/tools/clippy/tests/ui/never_loop.stderr
+++ b/src/tools/clippy/tests/ui/never_loop.stderr
@@ -77,8 +77,9 @@ LL | |     }
    |
 help: if you need the first element of the iterator, try writing
    |
-LL |     if let Some(x) = (0..10).next() {
-   |     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+LL -     for x in 0..10 {
+LL +     if let Some(x) = (0..10).next() {
+   |
 
 error: this loop never actually loops
   --> tests/ui/never_loop.rs:167:5
@@ -145,8 +146,9 @@ LL | |             }
    |
 help: if you need the first element of the iterator, try writing
    |
-LL |             if let Some(_) = (0..20).next() {
-   |             ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+LL -             for _ in 0..20 {
+LL +             if let Some(_) = (0..20).next() {
+   |
 
 error: this loop never actually loops
   --> tests/ui/never_loop.rs:378:13
diff --git a/src/tools/clippy/tests/ui/non_canonical_partial_ord_impl.stderr b/src/tools/clippy/tests/ui/non_canonical_partial_ord_impl.stderr
index a15379c5b1a..9f0c2ec4301 100644
--- a/src/tools/clippy/tests/ui/non_canonical_partial_ord_impl.stderr
+++ b/src/tools/clippy/tests/ui/non_canonical_partial_ord_impl.stderr
@@ -25,8 +25,9 @@ LL | | }
    |
 help: change this to
    |
-LL |     fn partial_cmp(&self, other: &Self) -> Option<Ordering> { Some(self.cmp(other)) }
-   |                           ~~~~~                             ~~~~~~~~~~~~~~~~~~~~~~~~~
+LL -     fn partial_cmp(&self, _: &Self) -> Option<Ordering> {
+LL +     fn partial_cmp(&self, other: &Self) -> Option<Ordering> { Some(self.cmp(other)) }
+   |
 
 error: aborting due to 2 previous errors
 
diff --git a/src/tools/clippy/tests/ui/non_std_lazy_static/non_std_lazy_static_fixable.stderr b/src/tools/clippy/tests/ui/non_std_lazy_static/non_std_lazy_static_fixable.stderr
index f956f4b8d52..333052ae1c1 100644
--- a/src/tools/clippy/tests/ui/non_std_lazy_static/non_std_lazy_static_fixable.stderr
+++ b/src/tools/clippy/tests/ui/non_std_lazy_static/non_std_lazy_static_fixable.stderr
@@ -8,8 +8,9 @@ LL | static LAZY_FOO: Lazy<String> = Lazy::new(|| "foo".to_uppercase());
    = help: to override `-D warnings` add `#[allow(clippy::non_std_lazy_statics)]`
 help: use `std::sync::LazyLock` instead
    |
-LL | static LAZY_FOO: std::sync::LazyLock<String> = std::sync::LazyLock::new(|| "foo".to_uppercase());
-   |                  ~~~~~~~~~~~~~~~~~~~           ~~~~~~~~~~~~~~~~~~~~~~~~
+LL - static LAZY_FOO: Lazy<String> = Lazy::new(|| "foo".to_uppercase());
+LL + static LAZY_FOO: std::sync::LazyLock<String> = std::sync::LazyLock::new(|| "foo".to_uppercase());
+   |
 
 error: this type has been superceded by `LazyLock` in the standard library
   --> tests/ui/non_std_lazy_static/non_std_lazy_static_fixable.rs:13:18
@@ -19,8 +20,9 @@ LL | static LAZY_BAR: Lazy<String> = Lazy::new(|| {
    |
 help: use `std::sync::LazyLock` instead
    |
-LL | static LAZY_BAR: std::sync::LazyLock<String> = std::sync::LazyLock::new(|| {
-   |                  ~~~~~~~~~~~~~~~~~~~           ~~~~~~~~~~~~~~~~~~~~~~~~
+LL - static LAZY_BAR: Lazy<String> = Lazy::new(|| {
+LL + static LAZY_BAR: std::sync::LazyLock<String> = std::sync::LazyLock::new(|| {
+   |
 
 error: this type has been superceded by `LazyLock` in the standard library
   --> tests/ui/non_std_lazy_static/non_std_lazy_static_fixable.rs:18:18
@@ -30,8 +32,9 @@ LL | static LAZY_BAZ: Lazy<String> = { Lazy::new(|| "baz".to_uppercase()) };
    |
 help: use `std::sync::LazyLock` instead
    |
-LL | static LAZY_BAZ: std::sync::LazyLock<String> = { std::sync::LazyLock::new(|| "baz".to_uppercase()) };
-   |                  ~~~~~~~~~~~~~~~~~~~             ~~~~~~~~~~~~~~~~~~~~~~~~
+LL - static LAZY_BAZ: Lazy<String> = { Lazy::new(|| "baz".to_uppercase()) };
+LL + static LAZY_BAZ: std::sync::LazyLock<String> = { std::sync::LazyLock::new(|| "baz".to_uppercase()) };
+   |
 
 error: this type has been superceded by `LazyLock` in the standard library
   --> tests/ui/non_std_lazy_static/non_std_lazy_static_fixable.rs:20:18
diff --git a/src/tools/clippy/tests/ui/non_std_lazy_static/non_std_lazy_static_unfixable.stderr b/src/tools/clippy/tests/ui/non_std_lazy_static/non_std_lazy_static_unfixable.stderr
index 66dc435f982..216190ae4ca 100644
--- a/src/tools/clippy/tests/ui/non_std_lazy_static/non_std_lazy_static_unfixable.stderr
+++ b/src/tools/clippy/tests/ui/non_std_lazy_static/non_std_lazy_static_unfixable.stderr
@@ -37,8 +37,9 @@ LL |     static LAZY_FOO: Lazy<String> = Lazy::new(|| "foo".to_uppercase());
    |
 help: use `std::sync::LazyLock` instead
    |
-LL |     static LAZY_FOO: std::sync::LazyLock<String> = std::sync::LazyLock::new(|| "foo".to_uppercase());
-   |                      ~~~~~~~~~~~~~~~~~~~           ~~~~~~~~~~~~~~~~~~~~~~~~
+LL -     static LAZY_FOO: Lazy<String> = Lazy::new(|| "foo".to_uppercase());
+LL +     static LAZY_FOO: std::sync::LazyLock<String> = std::sync::LazyLock::new(|| "foo".to_uppercase());
+   |
 
 error: this type has been superceded by `LazyLock` in the standard library
   --> tests/ui/non_std_lazy_static/non_std_lazy_static_unfixable.rs:13:26
@@ -48,8 +49,9 @@ LL |     static mut LAZY_BAR: Lazy<String> = Lazy::new(|| "bar".to_uppercase());
    |
 help: use `std::sync::LazyLock` instead
    |
-LL |     static mut LAZY_BAR: std::sync::LazyLock<String> = std::sync::LazyLock::new(|| "bar".to_uppercase());
-   |                          ~~~~~~~~~~~~~~~~~~~           ~~~~~~~~~~~~~~~~~~~~~~~~
+LL -     static mut LAZY_BAR: Lazy<String> = Lazy::new(|| "bar".to_uppercase());
+LL +     static mut LAZY_BAR: std::sync::LazyLock<String> = std::sync::LazyLock::new(|| "bar".to_uppercase());
+   |
 
 error: this type has been superceded by `LazyLock` in the standard library
   --> tests/ui/non_std_lazy_static/non_std_lazy_static_unfixable.rs:15:26
@@ -59,8 +61,9 @@ LL |     static mut LAZY_BAZ: Lazy<String> = Lazy::new(|| "baz".to_uppercase());
    |
 help: use `std::sync::LazyLock` instead
    |
-LL |     static mut LAZY_BAZ: std::sync::LazyLock<String> = std::sync::LazyLock::new(|| "baz".to_uppercase());
-   |                          ~~~~~~~~~~~~~~~~~~~           ~~~~~~~~~~~~~~~~~~~~~~~~
+LL -     static mut LAZY_BAZ: Lazy<String> = Lazy::new(|| "baz".to_uppercase());
+LL +     static mut LAZY_BAZ: std::sync::LazyLock<String> = std::sync::LazyLock::new(|| "baz".to_uppercase());
+   |
 
 error: aborting due to 6 previous errors
 
diff --git a/src/tools/clippy/tests/ui/nonminimal_bool.stderr b/src/tools/clippy/tests/ui/nonminimal_bool.stderr
index 578f918f013..56d6eb10ac0 100644
--- a/src/tools/clippy/tests/ui/nonminimal_bool.stderr
+++ b/src/tools/clippy/tests/ui/nonminimal_bool.stderr
@@ -51,10 +51,12 @@ LL |     let _ = a == b && c == 5 && a == b;
    |
 help: try
    |
-LL |     let _ = !(a != b || c != 5);
-   |             ~~~~~~~~~~~~~~~~~~~
-LL |     let _ = a == b && c == 5;
-   |             ~~~~~~~~~~~~~~~~
+LL -     let _ = a == b && c == 5 && a == b;
+LL +     let _ = !(a != b || c != 5);
+   |
+LL -     let _ = a == b && c == 5 && a == b;
+LL +     let _ = a == b && c == 5;
+   |
 
 error: this boolean expression can be simplified
   --> tests/ui/nonminimal_bool.rs:44:13
@@ -64,10 +66,12 @@ LL |     let _ = a == b || c == 5 || a == b;
    |
 help: try
    |
-LL |     let _ = !(a != b && c != 5);
-   |             ~~~~~~~~~~~~~~~~~~~
-LL |     let _ = a == b || c == 5;
-   |             ~~~~~~~~~~~~~~~~
+LL -     let _ = a == b || c == 5 || a == b;
+LL +     let _ = !(a != b && c != 5);
+   |
+LL -     let _ = a == b || c == 5 || a == b;
+LL +     let _ = a == b || c == 5;
+   |
 
 error: this boolean expression can be simplified
   --> tests/ui/nonminimal_bool.rs:46:13
@@ -77,10 +81,12 @@ LL |     let _ = a == b && c == 5 && b == a;
    |
 help: try
    |
-LL |     let _ = !(a != b || c != 5);
-   |             ~~~~~~~~~~~~~~~~~~~
-LL |     let _ = a == b && c == 5;
-   |             ~~~~~~~~~~~~~~~~
+LL -     let _ = a == b && c == 5 && b == a;
+LL +     let _ = !(a != b || c != 5);
+   |
+LL -     let _ = a == b && c == 5 && b == a;
+LL +     let _ = a == b && c == 5;
+   |
 
 error: this boolean expression can be simplified
   --> tests/ui/nonminimal_bool.rs:48:13
@@ -90,10 +96,12 @@ LL |     let _ = a != b || !(a != b || c == d);
    |
 help: try
    |
-LL |     let _ = !(a == b && c == d);
-   |             ~~~~~~~~~~~~~~~~~~~
-LL |     let _ = a != b || c != d;
-   |             ~~~~~~~~~~~~~~~~
+LL -     let _ = a != b || !(a != b || c == d);
+LL +     let _ = !(a == b && c == d);
+   |
+LL -     let _ = a != b || !(a != b || c == d);
+LL +     let _ = a != b || c != d;
+   |
 
 error: this boolean expression can be simplified
   --> tests/ui/nonminimal_bool.rs:50:13
@@ -103,10 +111,12 @@ LL |     let _ = a != b && !(a != b && c == d);
    |
 help: try
    |
-LL |     let _ = !(a == b || c == d);
-   |             ~~~~~~~~~~~~~~~~~~~
-LL |     let _ = a != b && c != d;
-   |             ~~~~~~~~~~~~~~~~
+LL -     let _ = a != b && !(a != b && c == d);
+LL +     let _ = !(a == b || c == d);
+   |
+LL -     let _ = a != b && !(a != b && c == d);
+LL +     let _ = a != b && c != d;
+   |
 
 error: this boolean expression can be simplified
   --> tests/ui/nonminimal_bool.rs:81:8
diff --git a/src/tools/clippy/tests/ui/octal_escapes.stderr b/src/tools/clippy/tests/ui/octal_escapes.stderr
index 9343ba64a30..61c781e316e 100644
--- a/src/tools/clippy/tests/ui/octal_escapes.stderr
+++ b/src/tools/clippy/tests/ui/octal_escapes.stderr
@@ -9,12 +9,13 @@ LL |     let _bad1 = "\033[0m";
    = help: to override `-D warnings` add `#[allow(clippy::octal_escapes)]`
 help: if an octal escape is intended, use a hex escape instead
    |
-LL |     let _bad1 = "\x1b[0m";
-   |                  ~~~~
+LL -     let _bad1 = "\033[0m";
+LL +     let _bad1 = "\x1b[0m";
+   |
 help: if a null escape is intended, disambiguate using
    |
 LL |     let _bad1 = "\x0033[0m";
-   |                  ~~~~~~
+   |                   ++
 
 error: octal-looking escape in a literal
   --> tests/ui/octal_escapes.rs:6:19
@@ -24,12 +25,13 @@ LL |     let _bad2 = b"\033[0m";
    |
 help: if an octal escape is intended, use a hex escape instead
    |
-LL |     let _bad2 = b"\x1b[0m";
-   |                   ~~~~
+LL -     let _bad2 = b"\033[0m";
+LL +     let _bad2 = b"\x1b[0m";
+   |
 help: if a null escape is intended, disambiguate using
    |
 LL |     let _bad2 = b"\x0033[0m";
-   |                   ~~~~~~
+   |                    ++
 
 error: octal-looking escape in a literal
   --> tests/ui/octal_escapes.rs:7:20
@@ -39,12 +41,13 @@ LL |     let _bad3 = "\\\033[0m";
    |
 help: if an octal escape is intended, use a hex escape instead
    |
-LL |     let _bad3 = "\\\x1b[0m";
-   |                    ~~~~
+LL -     let _bad3 = "\\\033[0m";
+LL +     let _bad3 = "\\\x1b[0m";
+   |
 help: if a null escape is intended, disambiguate using
    |
 LL |     let _bad3 = "\\\x0033[0m";
-   |                    ~~~~~~
+   |                     ++
 
 error: octal-looking escape in a literal
   --> tests/ui/octal_escapes.rs:9:18
@@ -54,12 +57,13 @@ LL |     let _bad4 = "\01234567";
    |
 help: if an octal escape is intended, use a hex escape instead
    |
-LL |     let _bad4 = "\x0a34567";
-   |                  ~~~~
+LL -     let _bad4 = "\01234567";
+LL +     let _bad4 = "\x0a34567";
+   |
 help: if a null escape is intended, disambiguate using
    |
 LL |     let _bad4 = "\x001234567";
-   |                  ~~~~~~
+   |                   ++
 
 error: octal-looking escape in a literal
   --> tests/ui/octal_escapes.rs:10:20
@@ -70,11 +74,11 @@ LL |     let _bad5 = "\0\03";
 help: if an octal escape is intended, use a hex escape instead
    |
 LL |     let _bad5 = "\0\x03";
-   |                    ~~~~
+   |                     +
 help: if a null escape is intended, disambiguate using
    |
 LL |     let _bad5 = "\0\x0003";
-   |                    ~~~~~~
+   |                     +++
 
 error: octal-looking escape in a literal
   --> tests/ui/octal_escapes.rs:11:23
@@ -84,12 +88,13 @@ LL |     let _bad6 = "Text-\055\077-MoreText";
    |
 help: if an octal escape is intended, use a hex escape instead
    |
-LL |     let _bad6 = "Text-\x2d\077-MoreText";
-   |                       ~~~~
+LL -     let _bad6 = "Text-\055\077-MoreText";
+LL +     let _bad6 = "Text-\x2d\077-MoreText";
+   |
 help: if a null escape is intended, disambiguate using
    |
 LL |     let _bad6 = "Text-\x0055\077-MoreText";
-   |                       ~~~~~~
+   |                        ++
 
 error: octal-looking escape in a literal
   --> tests/ui/octal_escapes.rs:11:27
@@ -99,12 +104,13 @@ LL |     let _bad6 = "Text-\055\077-MoreText";
    |
 help: if an octal escape is intended, use a hex escape instead
    |
-LL |     let _bad6 = "Text-\055\x3f-MoreText";
-   |                           ~~~~
+LL -     let _bad6 = "Text-\055\077-MoreText";
+LL +     let _bad6 = "Text-\055\x3f-MoreText";
+   |
 help: if a null escape is intended, disambiguate using
    |
 LL |     let _bad6 = "Text-\055\x0077-MoreText";
-   |                           ~~~~~~
+   |                            ++
 
 error: octal-looking escape in a literal
   --> tests/ui/octal_escapes.rs:14:31
@@ -115,11 +121,11 @@ LL |     let _bad7 = "EvenMoreText-\01\02-ShortEscapes";
 help: if an octal escape is intended, use a hex escape instead
    |
 LL |     let _bad7 = "EvenMoreText-\x01\02-ShortEscapes";
-   |                               ~~~~
+   |                                +
 help: if a null escape is intended, disambiguate using
    |
 LL |     let _bad7 = "EvenMoreText-\x0001\02-ShortEscapes";
-   |                               ~~~~~~
+   |                                +++
 
 error: octal-looking escape in a literal
   --> tests/ui/octal_escapes.rs:14:34
@@ -130,11 +136,11 @@ LL |     let _bad7 = "EvenMoreText-\01\02-ShortEscapes";
 help: if an octal escape is intended, use a hex escape instead
    |
 LL |     let _bad7 = "EvenMoreText-\01\x02-ShortEscapes";
-   |                                  ~~~~
+   |                                   +
 help: if a null escape is intended, disambiguate using
    |
 LL |     let _bad7 = "EvenMoreText-\01\x0002-ShortEscapes";
-   |                                  ~~~~~~
+   |                                   +++
 
 error: octal-looking escape in a literal
   --> tests/ui/octal_escapes.rs:17:19
@@ -145,11 +151,11 @@ LL |     let _bad8 = "锈\01锈";
 help: if an octal escape is intended, use a hex escape instead
    |
 LL |     let _bad8 = "锈\x01锈";
-   |                    ~~~~
+   |                     +
 help: if a null escape is intended, disambiguate using
    |
 LL |     let _bad8 = "锈\x0001锈";
-   |                    ~~~~~~
+   |                     +++
 
 error: octal-looking escape in a literal
   --> tests/ui/octal_escapes.rs:18:19
@@ -159,12 +165,13 @@ LL |     let _bad9 = "锈\011锈";
    |
 help: if an octal escape is intended, use a hex escape instead
    |
-LL |     let _bad9 = "锈\x09锈";
-   |                    ~~~~
+LL -     let _bad9 = "锈\011锈";
+LL +     let _bad9 = "锈\x09锈";
+   |
 help: if a null escape is intended, disambiguate using
    |
 LL |     let _bad9 = "锈\x0011锈";
-   |                    ~~~~~~
+   |                     ++
 
 error: aborting due to 11 previous errors
 
diff --git a/src/tools/clippy/tests/ui/op_ref.stderr b/src/tools/clippy/tests/ui/op_ref.stderr
index c5b68730a8f..ad002437c0c 100644
--- a/src/tools/clippy/tests/ui/op_ref.stderr
+++ b/src/tools/clippy/tests/ui/op_ref.stderr
@@ -8,8 +8,9 @@ LL |     let foo = &5 - &6;
    = help: to override `-D warnings` add `#[allow(clippy::op_ref)]`
 help: use the values directly
    |
-LL |     let foo = 5 - 6;
-   |               ~   ~
+LL -     let foo = &5 - &6;
+LL +     let foo = 5 - 6;
+   |
 
 error: taken reference of right operand
   --> tests/ui/op_ref.rs:58:13
diff --git a/src/tools/clippy/tests/ui/option_as_ref_cloned.stderr b/src/tools/clippy/tests/ui/option_as_ref_cloned.stderr
index 5892f2bdec5..0eda42b91b9 100644
--- a/src/tools/clippy/tests/ui/option_as_ref_cloned.stderr
+++ b/src/tools/clippy/tests/ui/option_as_ref_cloned.stderr
@@ -8,8 +8,9 @@ LL |     let _: Option<String> = x.as_ref().cloned();
    = help: to override `-D warnings` add `#[allow(clippy::option_as_ref_cloned)]`
 help: this can be written more concisely by cloning the `Option<_>` directly
    |
-LL |     let _: Option<String> = x.clone();
-   |                               ~~~~~
+LL -     let _: Option<String> = x.as_ref().cloned();
+LL +     let _: Option<String> = x.clone();
+   |
 
 error: cloning an `Option<_>` using `.as_mut().cloned()`
   --> tests/ui/option_as_ref_cloned.rs:8:31
@@ -19,8 +20,9 @@ LL |     let _: Option<String> = x.as_mut().cloned();
    |
 help: this can be written more concisely by cloning the `Option<_>` directly
    |
-LL |     let _: Option<String> = x.clone();
-   |                               ~~~~~
+LL -     let _: Option<String> = x.as_mut().cloned();
+LL +     let _: Option<String> = x.clone();
+   |
 
 error: cloning an `Option<_>` using `.as_ref().cloned()`
   --> tests/ui/option_as_ref_cloned.rs:11:32
@@ -30,8 +32,9 @@ LL |     let _: Option<&String> = y.as_ref().cloned();
    |
 help: this can be written more concisely by cloning the `Option<_>` directly
    |
-LL |     let _: Option<&String> = y.clone();
-   |                                ~~~~~
+LL -     let _: Option<&String> = y.as_ref().cloned();
+LL +     let _: Option<&String> = y.clone();
+   |
 
 error: aborting due to 3 previous errors
 
diff --git a/src/tools/clippy/tests/ui/redundant_guards.stderr b/src/tools/clippy/tests/ui/redundant_guards.stderr
index 7512546450b..a10cd5c2f48 100644
--- a/src/tools/clippy/tests/ui/redundant_guards.stderr
+++ b/src/tools/clippy/tests/ui/redundant_guards.stderr
@@ -44,8 +44,9 @@ LL |         Some(x) if matches!(x, Some(1) if true) => ..,
    |
 help: try
    |
-LL |         Some(Some(1)) if true => ..,
-   |              ~~~~~~~  ~~~~~~~
+LL -         Some(x) if matches!(x, Some(1) if true) => ..,
+LL +         Some(Some(1)) if true => ..,
+   |
 
 error: redundant guard
   --> tests/ui/redundant_guards.rs:50:20
diff --git a/src/tools/clippy/tests/ui/ref_binding_to_reference.stderr b/src/tools/clippy/tests/ui/ref_binding_to_reference.stderr
index 25ab9822382..233416351a0 100644
--- a/src/tools/clippy/tests/ui/ref_binding_to_reference.stderr
+++ b/src/tools/clippy/tests/ui/ref_binding_to_reference.stderr
@@ -8,8 +8,9 @@ LL |         Some(ref x) => x,
    = help: to override `-D warnings` add `#[allow(clippy::ref_binding_to_reference)]`
 help: try
    |
-LL |         Some(x) => &x,
-   |              ~     ~~
+LL -         Some(ref x) => x,
+LL +         Some(x) => &x,
+   |
 
 error: this pattern creates a reference to a reference
   --> tests/ui/ref_binding_to_reference.rs:38:14
@@ -34,8 +35,9 @@ LL |         Some(ref x) => m2!(x),
    |
 help: try
    |
-LL |         Some(x) => m2!(&x),
-   |              ~         ~~
+LL -         Some(ref x) => m2!(x),
+LL +         Some(x) => m2!(&x),
+   |
 
 error: this pattern creates a reference to a reference
   --> tests/ui/ref_binding_to_reference.rs:55:15
diff --git a/src/tools/clippy/tests/ui/ref_option/ref_option.all.stderr b/src/tools/clippy/tests/ui/ref_option/ref_option.all.stderr
index b4c69ac6296..fd30628bdd8 100644
--- a/src/tools/clippy/tests/ui/ref_option/ref_option.all.stderr
+++ b/src/tools/clippy/tests/ui/ref_option/ref_option.all.stderr
@@ -55,8 +55,9 @@ LL | fn mult_string(a: &Option<String>, b: &Option<Vec<u8>>) {}
    |
 help: change this to
    |
-LL | fn mult_string(a: Option<&String>, b: Option<&Vec<u8>>) {}
-   |                   ~~~~~~~~~~~~~~~     ~~~~~~~~~~~~~~~~
+LL - fn mult_string(a: &Option<String>, b: &Option<Vec<u8>>) {}
+LL + fn mult_string(a: Option<&String>, b: Option<&Vec<u8>>) {}
+   |
 
 error: it is more idiomatic to use `Option<&T>` instead of `&Option<T>`
   --> tests/ui/ref_option/ref_option.rs:18:1
@@ -85,8 +86,9 @@ LL | pub fn pub_mult_string(a: &Option<String>, b: &Option<Vec<u8>>) {}
    |
 help: change this to
    |
-LL | pub fn pub_mult_string(a: Option<&String>, b: Option<&Vec<u8>>) {}
-   |                           ~~~~~~~~~~~~~~~     ~~~~~~~~~~~~~~~~
+LL - pub fn pub_mult_string(a: &Option<String>, b: &Option<Vec<u8>>) {}
+LL + pub fn pub_mult_string(a: Option<&String>, b: Option<&Vec<u8>>) {}
+   |
 
 error: it is more idiomatic to use `Option<&T>` instead of `&Option<T>`
   --> tests/ui/ref_option/ref_option.rs:26:5
diff --git a/src/tools/clippy/tests/ui/ref_option/ref_option.private.stderr b/src/tools/clippy/tests/ui/ref_option/ref_option.private.stderr
index 17c90536da3..d3428f1891f 100644
--- a/src/tools/clippy/tests/ui/ref_option/ref_option.private.stderr
+++ b/src/tools/clippy/tests/ui/ref_option/ref_option.private.stderr
@@ -55,8 +55,9 @@ LL | fn mult_string(a: &Option<String>, b: &Option<Vec<u8>>) {}
    |
 help: change this to
    |
-LL | fn mult_string(a: Option<&String>, b: Option<&Vec<u8>>) {}
-   |                   ~~~~~~~~~~~~~~~     ~~~~~~~~~~~~~~~~
+LL - fn mult_string(a: &Option<String>, b: &Option<Vec<u8>>) {}
+LL + fn mult_string(a: Option<&String>, b: Option<&Vec<u8>>) {}
+   |
 
 error: it is more idiomatic to use `Option<&T>` instead of `&Option<T>`
   --> tests/ui/ref_option/ref_option.rs:18:1
diff --git a/src/tools/clippy/tests/ui/repeat_vec_with_capacity.stderr b/src/tools/clippy/tests/ui/repeat_vec_with_capacity.stderr
index 43027c9cb89..05513a8859a 100644
--- a/src/tools/clippy/tests/ui/repeat_vec_with_capacity.stderr
+++ b/src/tools/clippy/tests/ui/repeat_vec_with_capacity.stderr
@@ -9,8 +9,9 @@ LL |         vec![Vec::<()>::with_capacity(42); 123];
    = help: to override `-D warnings` add `#[allow(clippy::repeat_vec_with_capacity)]`
 help: if you intended to initialize multiple `Vec`s with an initial capacity, try
    |
-LL |         (0..123).map(|_| Vec::<()>::with_capacity(42)).collect::<Vec<_>>();
-   |         ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+LL -         vec![Vec::<()>::with_capacity(42); 123];
+LL +         (0..123).map(|_| Vec::<()>::with_capacity(42)).collect::<Vec<_>>();
+   |
 
 error: repeating `Vec::with_capacity` using `vec![x; n]`, which does not retain capacity
   --> tests/ui/repeat_vec_with_capacity.rs:12:9
@@ -21,8 +22,9 @@ LL |         vec![Vec::<()>::with_capacity(42); n];
    = note: only the last `Vec` will have the capacity
 help: if you intended to initialize multiple `Vec`s with an initial capacity, try
    |
-LL |         (0..n).map(|_| Vec::<()>::with_capacity(42)).collect::<Vec<_>>();
-   |         ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+LL -         vec![Vec::<()>::with_capacity(42); n];
+LL +         (0..n).map(|_| Vec::<()>::with_capacity(42)).collect::<Vec<_>>();
+   |
 
 error: repeating `Vec::with_capacity` using `iter::repeat`, which does not retain capacity
   --> tests/ui/repeat_vec_with_capacity.rs:27:9
@@ -33,8 +35,9 @@ LL |         std::iter::repeat(Vec::<()>::with_capacity(42));
    = note: none of the yielded `Vec`s will have the requested capacity
 help: if you intended to create an iterator that yields `Vec`s with an initial capacity, try
    |
-LL |         std::iter::repeat_with(|| Vec::<()>::with_capacity(42));
-   |         ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+LL -         std::iter::repeat(Vec::<()>::with_capacity(42));
+LL +         std::iter::repeat_with(|| Vec::<()>::with_capacity(42));
+   |
 
 error: aborting due to 3 previous errors
 
diff --git a/src/tools/clippy/tests/ui/repeat_vec_with_capacity_nostd.stderr b/src/tools/clippy/tests/ui/repeat_vec_with_capacity_nostd.stderr
index 39364d09b96..092167485ce 100644
--- a/src/tools/clippy/tests/ui/repeat_vec_with_capacity_nostd.stderr
+++ b/src/tools/clippy/tests/ui/repeat_vec_with_capacity_nostd.stderr
@@ -9,8 +9,9 @@ LL |     let _: Vec<Vec<u8>> = iter::repeat(Vec::with_capacity(42)).take(123).co
    = help: to override `-D warnings` add `#[allow(clippy::repeat_vec_with_capacity)]`
 help: if you intended to create an iterator that yields `Vec`s with an initial capacity, try
    |
-LL |     let _: Vec<Vec<u8>> = core::iter::repeat_with(|| Vec::with_capacity(42)).take(123).collect();
-   |                           ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+LL -     let _: Vec<Vec<u8>> = iter::repeat(Vec::with_capacity(42)).take(123).collect();
+LL +     let _: Vec<Vec<u8>> = core::iter::repeat_with(|| Vec::with_capacity(42)).take(123).collect();
+   |
 
 error: aborting due to 1 previous error
 
diff --git a/src/tools/clippy/tests/ui/reversed_empty_ranges_fixable.stderr b/src/tools/clippy/tests/ui/reversed_empty_ranges_fixable.stderr
index 3747eb9deeb..24cb959c96a 100644
--- a/src/tools/clippy/tests/ui/reversed_empty_ranges_fixable.stderr
+++ b/src/tools/clippy/tests/ui/reversed_empty_ranges_fixable.stderr
@@ -8,8 +8,9 @@ LL |     (42..=21).for_each(|x| println!("{}", x));
    = help: to override `-D warnings` add `#[allow(clippy::reversed_empty_ranges)]`
 help: consider using the following if you are attempting to iterate over this range in reverse
    |
-LL |     (21..=42).rev().for_each(|x| println!("{}", x));
-   |     ~~~~~~~~~~~~~~~
+LL -     (42..=21).for_each(|x| println!("{}", x));
+LL +     (21..=42).rev().for_each(|x| println!("{}", x));
+   |
 
 error: this range is empty so it will yield no values
   --> tests/ui/reversed_empty_ranges_fixable.rs:10:13
@@ -19,8 +20,9 @@ LL |     let _ = (ANSWER..21).filter(|x| x % 2 == 0).take(10).collect::<Vec<_>>(
    |
 help: consider using the following if you are attempting to iterate over this range in reverse
    |
-LL |     let _ = (21..ANSWER).rev().filter(|x| x % 2 == 0).take(10).collect::<Vec<_>>();
-   |             ~~~~~~~~~~~~~~~~~~
+LL -     let _ = (ANSWER..21).filter(|x| x % 2 == 0).take(10).collect::<Vec<_>>();
+LL +     let _ = (21..ANSWER).rev().filter(|x| x % 2 == 0).take(10).collect::<Vec<_>>();
+   |
 
 error: this range is empty so it will yield no values
   --> tests/ui/reversed_empty_ranges_fixable.rs:12:14
@@ -30,8 +32,9 @@ LL |     for _ in -21..=-42 {}
    |
 help: consider using the following if you are attempting to iterate over this range in reverse
    |
-LL |     for _ in (-42..=-21).rev() {}
-   |              ~~~~~~~~~~~~~~~~~
+LL -     for _ in -21..=-42 {}
+LL +     for _ in (-42..=-21).rev() {}
+   |
 
 error: this range is empty so it will yield no values
   --> tests/ui/reversed_empty_ranges_fixable.rs:13:14
@@ -41,8 +44,9 @@ LL |     for _ in 42u32..21u32 {}
    |
 help: consider using the following if you are attempting to iterate over this range in reverse
    |
-LL |     for _ in (21u32..42u32).rev() {}
-   |              ~~~~~~~~~~~~~~~~~~~~
+LL -     for _ in 42u32..21u32 {}
+LL +     for _ in (21u32..42u32).rev() {}
+   |
 
 error: aborting due to 4 previous errors
 
diff --git a/src/tools/clippy/tests/ui/reversed_empty_ranges_loops_fixable.stderr b/src/tools/clippy/tests/ui/reversed_empty_ranges_loops_fixable.stderr
index d5df34c42f4..3e9ccb653fe 100644
--- a/src/tools/clippy/tests/ui/reversed_empty_ranges_loops_fixable.stderr
+++ b/src/tools/clippy/tests/ui/reversed_empty_ranges_loops_fixable.stderr
@@ -8,8 +8,9 @@ LL |     for i in 10..0 {
    = help: to override `-D warnings` add `#[allow(clippy::reversed_empty_ranges)]`
 help: consider using the following if you are attempting to iterate over this range in reverse
    |
-LL |     for i in (0..10).rev() {
-   |              ~~~~~~~~~~~~~
+LL -     for i in 10..0 {
+LL +     for i in (0..10).rev() {
+   |
 
 error: this range is empty so it will yield no values
   --> tests/ui/reversed_empty_ranges_loops_fixable.rs:11:14
@@ -19,8 +20,9 @@ LL |     for i in 10..=0 {
    |
 help: consider using the following if you are attempting to iterate over this range in reverse
    |
-LL |     for i in (0..=10).rev() {
-   |              ~~~~~~~~~~~~~~
+LL -     for i in 10..=0 {
+LL +     for i in (0..=10).rev() {
+   |
 
 error: this range is empty so it will yield no values
   --> tests/ui/reversed_empty_ranges_loops_fixable.rs:15:14
@@ -30,8 +32,9 @@ LL |     for i in MAX_LEN..0 {
    |
 help: consider using the following if you are attempting to iterate over this range in reverse
    |
-LL |     for i in (0..MAX_LEN).rev() {
-   |              ~~~~~~~~~~~~~~~~~~
+LL -     for i in MAX_LEN..0 {
+LL +     for i in (0..MAX_LEN).rev() {
+   |
 
 error: this range is empty so it will yield no values
   --> tests/ui/reversed_empty_ranges_loops_fixable.rs:34:14
@@ -41,8 +44,9 @@ LL |     for i in (10..0).map(|x| x * 2) {
    |
 help: consider using the following if you are attempting to iterate over this range in reverse
    |
-LL |     for i in (0..10).rev().map(|x| x * 2) {
-   |              ~~~~~~~~~~~~~
+LL -     for i in (10..0).map(|x| x * 2) {
+LL +     for i in (0..10).rev().map(|x| x * 2) {
+   |
 
 error: this range is empty so it will yield no values
   --> tests/ui/reversed_empty_ranges_loops_fixable.rs:39:14
@@ -52,8 +56,9 @@ LL |     for i in 10..5 + 4 {
    |
 help: consider using the following if you are attempting to iterate over this range in reverse
    |
-LL |     for i in (5 + 4..10).rev() {
-   |              ~~~~~~~~~~~~~~~~~
+LL -     for i in 10..5 + 4 {
+LL +     for i in (5 + 4..10).rev() {
+   |
 
 error: this range is empty so it will yield no values
   --> tests/ui/reversed_empty_ranges_loops_fixable.rs:43:14
@@ -63,8 +68,9 @@ LL |     for i in (5 + 2)..(3 - 1) {
    |
 help: consider using the following if you are attempting to iterate over this range in reverse
    |
-LL |     for i in ((3 - 1)..(5 + 2)).rev() {
-   |              ~~~~~~~~~~~~~~~~~~~~~~~~
+LL -     for i in (5 + 2)..(3 - 1) {
+LL +     for i in ((3 - 1)..(5 + 2)).rev() {
+   |
 
 error: aborting due to 6 previous errors
 
diff --git a/src/tools/clippy/tests/ui/single_range_in_vec_init.stderr b/src/tools/clippy/tests/ui/single_range_in_vec_init.stderr
index 9c125adb51a..b3bc8dd4aca 100644
--- a/src/tools/clippy/tests/ui/single_range_in_vec_init.stderr
+++ b/src/tools/clippy/tests/ui/single_range_in_vec_init.stderr
@@ -8,12 +8,14 @@ LL |     [0..200];
    = help: to override `-D warnings` add `#[allow(clippy::single_range_in_vec_init)]`
 help: if you wanted a `Vec` that contains the entire range, try
    |
-LL |     (0..200).collect::<std::vec::Vec<i32>>();
-   |     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+LL -     [0..200];
+LL +     (0..200).collect::<std::vec::Vec<i32>>();
+   |
 help: if you wanted an array of len 200, try
    |
-LL |     [0; 200];
-   |      ~~~~~~
+LL -     [0..200];
+LL +     [0; 200];
+   |
 
 error: a `Vec` of `Range` that is only one element
   --> tests/ui/single_range_in_vec_init.rs:27:5
@@ -23,12 +25,14 @@ LL |     vec![0..200];
    |
 help: if you wanted a `Vec` that contains the entire range, try
    |
-LL |     (0..200).collect::<std::vec::Vec<i32>>();
-   |     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+LL -     vec![0..200];
+LL +     (0..200).collect::<std::vec::Vec<i32>>();
+   |
 help: if you wanted a `Vec` of len 200, try
    |
-LL |     vec![0; 200];
-   |          ~~~~~~
+LL -     vec![0..200];
+LL +     vec![0; 200];
+   |
 
 error: an array of `Range` that is only one element
   --> tests/ui/single_range_in_vec_init.rs:28:5
@@ -38,12 +42,14 @@ LL |     [0u8..200];
    |
 help: if you wanted a `Vec` that contains the entire range, try
    |
-LL |     (0u8..200).collect::<std::vec::Vec<u8>>();
-   |     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+LL -     [0u8..200];
+LL +     (0u8..200).collect::<std::vec::Vec<u8>>();
+   |
 help: if you wanted an array of len 200, try
    |
-LL |     [0u8; 200];
-   |      ~~~~~~~~
+LL -     [0u8..200];
+LL +     [0u8; 200];
+   |
 
 error: an array of `Range` that is only one element
   --> tests/ui/single_range_in_vec_init.rs:29:5
@@ -53,12 +59,14 @@ LL |     [0usize..200];
    |
 help: if you wanted a `Vec` that contains the entire range, try
    |
-LL |     (0usize..200).collect::<std::vec::Vec<usize>>();
-   |     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+LL -     [0usize..200];
+LL +     (0usize..200).collect::<std::vec::Vec<usize>>();
+   |
 help: if you wanted an array of len 200, try
    |
-LL |     [0usize; 200];
-   |      ~~~~~~~~~~~
+LL -     [0usize..200];
+LL +     [0usize; 200];
+   |
 
 error: an array of `Range` that is only one element
   --> tests/ui/single_range_in_vec_init.rs:30:5
@@ -68,12 +76,14 @@ LL |     [0..200usize];
    |
 help: if you wanted a `Vec` that contains the entire range, try
    |
-LL |     (0..200usize).collect::<std::vec::Vec<usize>>();
-   |     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+LL -     [0..200usize];
+LL +     (0..200usize).collect::<std::vec::Vec<usize>>();
+   |
 help: if you wanted an array of len 200usize, try
    |
-LL |     [0; 200usize];
-   |      ~~~~~~~~~~~
+LL -     [0..200usize];
+LL +     [0; 200usize];
+   |
 
 error: a `Vec` of `Range` that is only one element
   --> tests/ui/single_range_in_vec_init.rs:31:5
@@ -83,12 +93,14 @@ LL |     vec![0u8..200];
    |
 help: if you wanted a `Vec` that contains the entire range, try
    |
-LL |     (0u8..200).collect::<std::vec::Vec<u8>>();
-   |     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+LL -     vec![0u8..200];
+LL +     (0u8..200).collect::<std::vec::Vec<u8>>();
+   |
 help: if you wanted a `Vec` of len 200, try
    |
-LL |     vec![0u8; 200];
-   |          ~~~~~~~~
+LL -     vec![0u8..200];
+LL +     vec![0u8; 200];
+   |
 
 error: a `Vec` of `Range` that is only one element
   --> tests/ui/single_range_in_vec_init.rs:32:5
@@ -98,12 +110,14 @@ LL |     vec![0usize..200];
    |
 help: if you wanted a `Vec` that contains the entire range, try
    |
-LL |     (0usize..200).collect::<std::vec::Vec<usize>>();
-   |     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+LL -     vec![0usize..200];
+LL +     (0usize..200).collect::<std::vec::Vec<usize>>();
+   |
 help: if you wanted a `Vec` of len 200, try
    |
-LL |     vec![0usize; 200];
-   |          ~~~~~~~~~~~
+LL -     vec![0usize..200];
+LL +     vec![0usize; 200];
+   |
 
 error: a `Vec` of `Range` that is only one element
   --> tests/ui/single_range_in_vec_init.rs:33:5
@@ -113,12 +127,14 @@ LL |     vec![0..200usize];
    |
 help: if you wanted a `Vec` that contains the entire range, try
    |
-LL |     (0..200usize).collect::<std::vec::Vec<usize>>();
-   |     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+LL -     vec![0..200usize];
+LL +     (0..200usize).collect::<std::vec::Vec<usize>>();
+   |
 help: if you wanted a `Vec` of len 200usize, try
    |
-LL |     vec![0; 200usize];
-   |          ~~~~~~~~~~~
+LL -     vec![0..200usize];
+LL +     vec![0; 200usize];
+   |
 
 error: an array of `Range` that is only one element
   --> tests/ui/single_range_in_vec_init.rs:35:5
@@ -128,8 +144,9 @@ LL |     [0..200isize];
    |
 help: if you wanted a `Vec` that contains the entire range, try
    |
-LL |     (0..200isize).collect::<std::vec::Vec<isize>>();
-   |     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+LL -     [0..200isize];
+LL +     (0..200isize).collect::<std::vec::Vec<isize>>();
+   |
 
 error: a `Vec` of `Range` that is only one element
   --> tests/ui/single_range_in_vec_init.rs:36:5
@@ -139,8 +156,9 @@ LL |     vec![0..200isize];
    |
 help: if you wanted a `Vec` that contains the entire range, try
    |
-LL |     (0..200isize).collect::<std::vec::Vec<isize>>();
-   |     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+LL -     vec![0..200isize];
+LL +     (0..200isize).collect::<std::vec::Vec<isize>>();
+   |
 
 error: aborting due to 10 previous errors
 
diff --git a/src/tools/clippy/tests/ui/string_lit_chars_any.stderr b/src/tools/clippy/tests/ui/string_lit_chars_any.stderr
index 4d3ca98e623..1e28ae7b163 100644
--- a/src/tools/clippy/tests/ui/string_lit_chars_any.stderr
+++ b/src/tools/clippy/tests/ui/string_lit_chars_any.stderr
@@ -8,8 +8,9 @@ LL |     "\\.+*?()|[]{}^$#&-~".chars().any(|x| x == c);
    = help: to override `-D warnings` add `#[allow(clippy::string_lit_chars_any)]`
 help: use `matches!(...)` instead
    |
-LL |     matches!(c, '\\' | '.' | '+' | '*' | '?' | '(' | ')' | '|' | '[' | ']' | '{' | '}' | '^' | '$' | '#' | '&' | '-' | '~');
-   |     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+LL -     "\\.+*?()|[]{}^$#&-~".chars().any(|x| x == c);
+LL +     matches!(c, '\\' | '.' | '+' | '*' | '?' | '(' | ')' | '|' | '[' | ']' | '{' | '}' | '^' | '$' | '#' | '&' | '-' | '~');
+   |
 
 error: usage of `.chars().any(...)` to check if a char matches any from a string literal
   --> tests/ui/string_lit_chars_any.rs:19:5
@@ -19,8 +20,9 @@ LL |     r#"\.+*?()|[]{}^$#&-~"#.chars().any(|x| x == c);
    |
 help: use `matches!(...)` instead
    |
-LL |     matches!(c, '\\' | '.' | '+' | '*' | '?' | '(' | ')' | '|' | '[' | ']' | '{' | '}' | '^' | '$' | '#' | '&' | '-' | '~');
-   |     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+LL -     r#"\.+*?()|[]{}^$#&-~"#.chars().any(|x| x == c);
+LL +     matches!(c, '\\' | '.' | '+' | '*' | '?' | '(' | ')' | '|' | '[' | ']' | '{' | '}' | '^' | '$' | '#' | '&' | '-' | '~');
+   |
 
 error: usage of `.chars().any(...)` to check if a char matches any from a string literal
   --> tests/ui/string_lit_chars_any.rs:20:5
@@ -30,8 +32,9 @@ LL |     "\\.+*?()|[]{}^$#&-~".chars().any(|x| c == x);
    |
 help: use `matches!(...)` instead
    |
-LL |     matches!(c, '\\' | '.' | '+' | '*' | '?' | '(' | ')' | '|' | '[' | ']' | '{' | '}' | '^' | '$' | '#' | '&' | '-' | '~');
-   |     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+LL -     "\\.+*?()|[]{}^$#&-~".chars().any(|x| c == x);
+LL +     matches!(c, '\\' | '.' | '+' | '*' | '?' | '(' | ')' | '|' | '[' | ']' | '{' | '}' | '^' | '$' | '#' | '&' | '-' | '~');
+   |
 
 error: usage of `.chars().any(...)` to check if a char matches any from a string literal
   --> tests/ui/string_lit_chars_any.rs:21:5
@@ -41,8 +44,9 @@ LL |     r#"\.+*?()|[]{}^$#&-~"#.chars().any(|x| c == x);
    |
 help: use `matches!(...)` instead
    |
-LL |     matches!(c, '\\' | '.' | '+' | '*' | '?' | '(' | ')' | '|' | '[' | ']' | '{' | '}' | '^' | '$' | '#' | '&' | '-' | '~');
-   |     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+LL -     r#"\.+*?()|[]{}^$#&-~"#.chars().any(|x| c == x);
+LL +     matches!(c, '\\' | '.' | '+' | '*' | '?' | '(' | ')' | '|' | '[' | ']' | '{' | '}' | '^' | '$' | '#' | '&' | '-' | '~');
+   |
 
 error: usage of `.chars().any(...)` to check if a char matches any from a string literal
   --> tests/ui/string_lit_chars_any.rs:23:5
@@ -52,8 +56,9 @@ LL |     "\\.+*?()|[]{}^$#&-~".chars().any(|x| { x == c });
    |
 help: use `matches!(...)` instead
    |
-LL |     matches!(c, '\\' | '.' | '+' | '*' | '?' | '(' | ')' | '|' | '[' | ']' | '{' | '}' | '^' | '$' | '#' | '&' | '-' | '~');
-   |     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+LL -     "\\.+*?()|[]{}^$#&-~".chars().any(|x| { x == c });
+LL +     matches!(c, '\\' | '.' | '+' | '*' | '?' | '(' | ')' | '|' | '[' | ']' | '{' | '}' | '^' | '$' | '#' | '&' | '-' | '~');
+   |
 
 error: aborting due to 5 previous errors
 
diff --git a/src/tools/clippy/tests/ui/suspicious_command_arg_space.stderr b/src/tools/clippy/tests/ui/suspicious_command_arg_space.stderr
index 6fd07d07d7b..8952a3ffe4b 100644
--- a/src/tools/clippy/tests/ui/suspicious_command_arg_space.stderr
+++ b/src/tools/clippy/tests/ui/suspicious_command_arg_space.stderr
@@ -8,8 +8,9 @@ LL |     std::process::Command::new("echo").arg("-n hello").spawn().unwrap();
    = help: to override `-D warnings` add `#[allow(clippy::suspicious_command_arg_space)]`
 help: consider splitting the argument
    |
-LL |     std::process::Command::new("echo").args(["-n", "hello"]).spawn().unwrap();
-   |                                        ~~~~ ~~~~~~~~~~~~~~~
+LL -     std::process::Command::new("echo").arg("-n hello").spawn().unwrap();
+LL +     std::process::Command::new("echo").args(["-n", "hello"]).spawn().unwrap();
+   |
 
 error: single argument that looks like it should be multiple arguments
   --> tests/ui/suspicious_command_arg_space.rs:7:43
@@ -19,8 +20,9 @@ LL |     std::process::Command::new("cat").arg("--number file").spawn().unwrap()
    |
 help: consider splitting the argument
    |
-LL |     std::process::Command::new("cat").args(["--number", "file"]).spawn().unwrap();
-   |                                       ~~~~ ~~~~~~~~~~~~~~~~~~~~
+LL -     std::process::Command::new("cat").arg("--number file").spawn().unwrap();
+LL +     std::process::Command::new("cat").args(["--number", "file"]).spawn().unwrap();
+   |
 
 error: aborting due to 2 previous errors
 
diff --git a/src/tools/clippy/tests/ui/suspicious_doc_comments.stderr b/src/tools/clippy/tests/ui/suspicious_doc_comments.stderr
index c34e39cd0fc..7e5933df237 100644
--- a/src/tools/clippy/tests/ui/suspicious_doc_comments.stderr
+++ b/src/tools/clippy/tests/ui/suspicious_doc_comments.stderr
@@ -8,7 +8,8 @@ LL | ///! Fake module documentation.
    = help: to override `-D warnings` add `#[allow(clippy::suspicious_doc_comments)]`
 help: use an inner doc comment to document the parent module or crate
    |
-LL | //! Fake module documentation.
+LL - ///! Fake module documentation.
+LL + //! Fake module documentation.
    |
 
 error: this is an outer doc comment and does not apply to the parent module or crate
@@ -19,7 +20,8 @@ LL |     ///! This module contains useful functions.
    |
 help: use an inner doc comment to document the parent module or crate
    |
-LL |     //! This module contains useful functions.
+LL -     ///! This module contains useful functions.
+LL +     //! This module contains useful functions.
    |
 
 error: this is an outer doc comment and does not apply to the parent module or crate
@@ -71,7 +73,8 @@ LL |     ///! a
    |
 help: use an inner doc comment to document the parent module or crate
    |
-LL |     //! a
+LL -     ///! a
+LL +     //! a
    |
 
 error: this is an outer doc comment and does not apply to the parent module or crate
@@ -97,7 +100,8 @@ LL |     ///! Very cool macro
    |
 help: use an inner doc comment to document the parent module or crate
    |
-LL |     //! Very cool macro
+LL -     ///! Very cool macro
+LL +     //! Very cool macro
    |
 
 error: this is an outer doc comment and does not apply to the parent module or crate
@@ -108,7 +112,8 @@ LL |     ///! Huh.
    |
 help: use an inner doc comment to document the parent module or crate
    |
-LL |     //! Huh.
+LL -     ///! Huh.
+LL +     //! Huh.
    |
 
 error: aborting due to 9 previous errors
diff --git a/src/tools/clippy/tests/ui/suspicious_to_owned.stderr b/src/tools/clippy/tests/ui/suspicious_to_owned.stderr
index 255f211e655..2c26565d5ef 100644
--- a/src/tools/clippy/tests/ui/suspicious_to_owned.stderr
+++ b/src/tools/clippy/tests/ui/suspicious_to_owned.stderr
@@ -9,11 +9,12 @@ LL |     let _ = cow.to_owned();
 help: depending on intent, either make the Cow an Owned variant
    |
 LL |     let _ = cow.into_owned();
-   |             ~~~~~~~~~~~~~~~~
+   |                 ++
 help: or clone the Cow itself
    |
-LL |     let _ = cow.clone();
-   |             ~~~~~~~~~~~
+LL -     let _ = cow.to_owned();
+LL +     let _ = cow.clone();
+   |
 
 error: this `to_owned` call clones the Cow<'_, [char; 3]> itself and does not cause the Cow<'_, [char; 3]> contents to become owned
   --> tests/ui/suspicious_to_owned.rs:29:13
@@ -24,11 +25,12 @@ LL |     let _ = cow.to_owned();
 help: depending on intent, either make the Cow an Owned variant
    |
 LL |     let _ = cow.into_owned();
-   |             ~~~~~~~~~~~~~~~~
+   |                 ++
 help: or clone the Cow itself
    |
-LL |     let _ = cow.clone();
-   |             ~~~~~~~~~~~
+LL -     let _ = cow.to_owned();
+LL +     let _ = cow.clone();
+   |
 
 error: this `to_owned` call clones the Cow<'_, Vec<char>> itself and does not cause the Cow<'_, Vec<char>> contents to become owned
   --> tests/ui/suspicious_to_owned.rs:40:13
@@ -39,11 +41,12 @@ LL |     let _ = cow.to_owned();
 help: depending on intent, either make the Cow an Owned variant
    |
 LL |     let _ = cow.into_owned();
-   |             ~~~~~~~~~~~~~~~~
+   |                 ++
 help: or clone the Cow itself
    |
-LL |     let _ = cow.clone();
-   |             ~~~~~~~~~~~
+LL -     let _ = cow.to_owned();
+LL +     let _ = cow.clone();
+   |
 
 error: this `to_owned` call clones the Cow<'_, str> itself and does not cause the Cow<'_, str> contents to become owned
   --> tests/ui/suspicious_to_owned.rs:51:13
@@ -54,11 +57,12 @@ LL |     let _ = cow.to_owned();
 help: depending on intent, either make the Cow an Owned variant
    |
 LL |     let _ = cow.into_owned();
-   |             ~~~~~~~~~~~~~~~~
+   |                 ++
 help: or clone the Cow itself
    |
-LL |     let _ = cow.clone();
-   |             ~~~~~~~~~~~
+LL -     let _ = cow.to_owned();
+LL +     let _ = cow.clone();
+   |
 
 error: implicitly cloning a `String` by calling `to_owned` on its dereferenced type
   --> tests/ui/suspicious_to_owned.rs:66:13
diff --git a/src/tools/clippy/tests/ui/suspicious_xor_used_as_pow.stderr b/src/tools/clippy/tests/ui/suspicious_xor_used_as_pow.stderr
index 43b03676b1d..2a153169bd3 100644
--- a/src/tools/clippy/tests/ui/suspicious_xor_used_as_pow.stderr
+++ b/src/tools/clippy/tests/ui/suspicious_xor_used_as_pow.stderr
@@ -8,8 +8,9 @@ LL |     let _ = 2 ^ 5;
    = help: to override `-D warnings` add `#[allow(clippy::suspicious_xor_used_as_pow)]`
 help: did you mean to write
    |
-LL |     let _ = 2.pow(5);
-   |             ~~~~~~~~
+LL -     let _ = 2 ^ 5;
+LL +     let _ = 2.pow(5);
+   |
 
 error: `^` is not the exponentiation operator
   --> tests/ui/suspicious_xor_used_as_pow.rs:22:13
@@ -19,8 +20,9 @@ LL |     let _ = 2i32 ^ 9i32;
    |
 help: did you mean to write
    |
-LL |     let _ = 2i32.pow(9i32);
-   |             ~~~~~~~~~~~~~~
+LL -     let _ = 2i32 ^ 9i32;
+LL +     let _ = 2i32.pow(9i32);
+   |
 
 error: `^` is not the exponentiation operator
   --> tests/ui/suspicious_xor_used_as_pow.rs:24:13
@@ -30,8 +32,9 @@ LL |     let _ = 2i32 ^ 2i32;
    |
 help: did you mean to write
    |
-LL |     let _ = 2i32.pow(2i32);
-   |             ~~~~~~~~~~~~~~
+LL -     let _ = 2i32 ^ 2i32;
+LL +     let _ = 2i32.pow(2i32);
+   |
 
 error: `^` is not the exponentiation operator
   --> tests/ui/suspicious_xor_used_as_pow.rs:26:13
@@ -41,8 +44,9 @@ LL |     let _ = 50i32 ^ 3i32;
    |
 help: did you mean to write
    |
-LL |     let _ = 50i32.pow(3i32);
-   |             ~~~~~~~~~~~~~~~
+LL -     let _ = 50i32 ^ 3i32;
+LL +     let _ = 50i32.pow(3i32);
+   |
 
 error: `^` is not the exponentiation operator
   --> tests/ui/suspicious_xor_used_as_pow.rs:28:13
@@ -52,8 +56,9 @@ LL |     let _ = 5i32 ^ 8i32;
    |
 help: did you mean to write
    |
-LL |     let _ = 5i32.pow(8i32);
-   |             ~~~~~~~~~~~~~~
+LL -     let _ = 5i32 ^ 8i32;
+LL +     let _ = 5i32.pow(8i32);
+   |
 
 error: `^` is not the exponentiation operator
   --> tests/ui/suspicious_xor_used_as_pow.rs:30:13
@@ -63,8 +68,9 @@ LL |     let _ = 2i32 ^ 32i32;
    |
 help: did you mean to write
    |
-LL |     let _ = 2i32.pow(32i32);
-   |             ~~~~~~~~~~~~~~~
+LL -     let _ = 2i32 ^ 32i32;
+LL +     let _ = 2i32.pow(32i32);
+   |
 
 error: `^` is not the exponentiation operator
   --> tests/ui/suspicious_xor_used_as_pow.rs:13:9
@@ -78,8 +84,9 @@ LL |     macro_test_inside!();
    = note: this error originates in the macro `macro_test_inside` (in Nightly builds, run with -Z macro-backtrace for more info)
 help: did you mean to write
    |
-LL |         1.pow(2) // should warn even if inside macro
-   |         ~~~~~~~~
+LL -         1 ^ 2 // should warn even if inside macro
+LL +         1.pow(2) // should warn even if inside macro
+   |
 
 error: aborting due to 7 previous errors
 
diff --git a/src/tools/clippy/tests/ui/too_long_first_doc_paragraph-fix.stderr b/src/tools/clippy/tests/ui/too_long_first_doc_paragraph-fix.stderr
index 6ef333f0cfd..84a574017a9 100644
--- a/src/tools/clippy/tests/ui/too_long_first_doc_paragraph-fix.stderr
+++ b/src/tools/clippy/tests/ui/too_long_first_doc_paragraph-fix.stderr
@@ -12,7 +12,7 @@ LL | | /// 200 characters so I needed to write something longeeeeeeer.
    = help: to override `-D warnings` add `#[allow(clippy::too_long_first_doc_paragraph)]`
 help: add an empty line
    |
-LL ~ /// A very short summary.
+LL | /// A very short summary.
 LL + ///
    |
 
diff --git a/src/tools/clippy/tests/ui/too_long_first_doc_paragraph.stderr b/src/tools/clippy/tests/ui/too_long_first_doc_paragraph.stderr
index 95f42349b9b..8bc853132ec 100644
--- a/src/tools/clippy/tests/ui/too_long_first_doc_paragraph.stderr
+++ b/src/tools/clippy/tests/ui/too_long_first_doc_paragraph.stderr
@@ -12,8 +12,8 @@ LL | |     //! 200 characters so I needed to write something longeeeeeeer.
    = help: to override `-D warnings` add `#[allow(clippy::too_long_first_doc_paragraph)]`
 help: add an empty line
    |
-LL ~     //! A very short summary.
-LL +     //!
+LL |     //! A very short summary.
+LL ~     //!
 LL ~     //! A much longer explanation that goes into a lot more detail about
    |
 
diff --git a/src/tools/clippy/tests/ui/transmute_ptr_to_ptr.stderr b/src/tools/clippy/tests/ui/transmute_ptr_to_ptr.stderr
index 8801eb943ce..f4f83cd7ac6 100644
--- a/src/tools/clippy/tests/ui/transmute_ptr_to_ptr.stderr
+++ b/src/tools/clippy/tests/ui/transmute_ptr_to_ptr.stderr
@@ -8,8 +8,9 @@ LL |         let _: *const f32 = transmute(ptr);
    = help: to override `-D warnings` add `#[allow(clippy::transmute_ptr_to_ptr)]`
 help: use `pointer::cast` instead
    |
-LL |         let _: *const f32 = ptr.cast::<f32>();
-   |                             ~~~~~~~~~~~~~~~~~
+LL -         let _: *const f32 = transmute(ptr);
+LL +         let _: *const f32 = ptr.cast::<f32>();
+   |
 
 error: transmute from a pointer to a pointer
   --> tests/ui/transmute_ptr_to_ptr.rs:34:27
@@ -19,8 +20,9 @@ LL |         let _: *mut f32 = transmute(mut_ptr);
    |
 help: use `pointer::cast` instead
    |
-LL |         let _: *mut f32 = mut_ptr.cast::<f32>();
-   |                           ~~~~~~~~~~~~~~~~~~~~~
+LL -         let _: *mut f32 = transmute(mut_ptr);
+LL +         let _: *mut f32 = mut_ptr.cast::<f32>();
+   |
 
 error: transmute from a reference to a reference
   --> tests/ui/transmute_ptr_to_ptr.rs:37:23
@@ -60,8 +62,9 @@ LL |         let _: *const u32 = transmute(mut_ptr);
    |
 help: use `pointer::cast_const` instead
    |
-LL |         let _: *const u32 = mut_ptr.cast_const();
-   |                             ~~~~~~~~~~~~~~~~~~~~
+LL -         let _: *const u32 = transmute(mut_ptr);
+LL +         let _: *const u32 = mut_ptr.cast_const();
+   |
 
 error: transmute from a pointer to a pointer
   --> tests/ui/transmute_ptr_to_ptr.rs:52:27
@@ -71,8 +74,9 @@ LL |         let _: *mut u32 = transmute(ptr);
    |
 help: use `pointer::cast_mut` instead
    |
-LL |         let _: *mut u32 = ptr.cast_mut();
-   |                           ~~~~~~~~~~~~~~
+LL -         let _: *mut u32 = transmute(ptr);
+LL +         let _: *mut u32 = ptr.cast_mut();
+   |
 
 error: transmute from a pointer to a pointer
   --> tests/ui/transmute_ptr_to_ptr.rs:64:14
@@ -82,8 +86,9 @@ LL |     unsafe { transmute(v) }
    |
 help: use an `as` cast instead
    |
-LL |     unsafe { v as *const &() }
-   |              ~~~~~~~~~~~~~~~
+LL -     unsafe { transmute(v) }
+LL +     unsafe { v as *const &() }
+   |
 
 error: transmute from a pointer to a pointer
   --> tests/ui/transmute_ptr_to_ptr.rs:79:28
@@ -93,8 +98,9 @@ LL |         let _: *const i8 = transmute(ptr);
    |
 help: use an `as` cast instead
    |
-LL |         let _: *const i8 = ptr as *const i8;
-   |                            ~~~~~~~~~~~~~~~~
+LL -         let _: *const i8 = transmute(ptr);
+LL +         let _: *const i8 = ptr as *const i8;
+   |
 
 error: transmute from a pointer to a pointer
   --> tests/ui/transmute_ptr_to_ptr.rs:86:28
@@ -104,8 +110,9 @@ LL |         let _: *const i8 = transmute(ptr);
    |
 help: use `pointer::cast` instead
    |
-LL |         let _: *const i8 = ptr.cast::<i8>();
-   |                            ~~~~~~~~~~~~~~~~
+LL -         let _: *const i8 = transmute(ptr);
+LL +         let _: *const i8 = ptr.cast::<i8>();
+   |
 
 error: transmute from a pointer to a pointer
   --> tests/ui/transmute_ptr_to_ptr.rs:93:26
@@ -115,8 +122,9 @@ LL |         let _: *mut u8 = transmute(ptr);
    |
 help: use an `as` cast instead
    |
-LL |         let _: *mut u8 = ptr as *mut u8;
-   |                          ~~~~~~~~~~~~~~
+LL -         let _: *mut u8 = transmute(ptr);
+LL +         let _: *mut u8 = ptr as *mut u8;
+   |
 
 error: transmute from a pointer to a pointer
   --> tests/ui/transmute_ptr_to_ptr.rs:94:28
@@ -126,8 +134,9 @@ LL |         let _: *const u8 = transmute(mut_ptr);
    |
 help: use an `as` cast instead
    |
-LL |         let _: *const u8 = mut_ptr as *const u8;
-   |                            ~~~~~~~~~~~~~~~~~~~~
+LL -         let _: *const u8 = transmute(mut_ptr);
+LL +         let _: *const u8 = mut_ptr as *const u8;
+   |
 
 error: transmute from a pointer to a pointer
   --> tests/ui/transmute_ptr_to_ptr.rs:101:26
@@ -137,8 +146,9 @@ LL |         let _: *mut u8 = transmute(ptr);
    |
 help: use `pointer::cast_mut` instead
    |
-LL |         let _: *mut u8 = ptr.cast_mut();
-   |                          ~~~~~~~~~~~~~~
+LL -         let _: *mut u8 = transmute(ptr);
+LL +         let _: *mut u8 = ptr.cast_mut();
+   |
 
 error: transmute from a pointer to a pointer
   --> tests/ui/transmute_ptr_to_ptr.rs:102:28
@@ -148,8 +158,9 @@ LL |         let _: *const u8 = transmute(mut_ptr);
    |
 help: use `pointer::cast_const` instead
    |
-LL |         let _: *const u8 = mut_ptr.cast_const();
-   |                            ~~~~~~~~~~~~~~~~~~~~
+LL -         let _: *const u8 = transmute(mut_ptr);
+LL +         let _: *const u8 = mut_ptr.cast_const();
+   |
 
 error: aborting due to 16 previous errors
 
diff --git a/src/tools/clippy/tests/ui/transmutes_expressible_as_ptr_casts.stderr b/src/tools/clippy/tests/ui/transmutes_expressible_as_ptr_casts.stderr
index 2d74967ede5..21edd39e7ad 100644
--- a/src/tools/clippy/tests/ui/transmutes_expressible_as_ptr_casts.stderr
+++ b/src/tools/clippy/tests/ui/transmutes_expressible_as_ptr_casts.stderr
@@ -17,8 +17,9 @@ LL |     let _ptr_i8_transmute = unsafe { transmute::<*const i32, *const i8>(ptr
    = help: to override `-D warnings` add `#[allow(clippy::transmute_ptr_to_ptr)]`
 help: use `pointer::cast` instead
    |
-LL |     let _ptr_i8_transmute = unsafe { ptr_i32.cast::<i8>() };
-   |                                      ~~~~~~~~~~~~~~~~~~~~
+LL -     let _ptr_i8_transmute = unsafe { transmute::<*const i32, *const i8>(ptr_i32) };
+LL +     let _ptr_i8_transmute = unsafe { ptr_i32.cast::<i8>() };
+   |
 
 error: transmute from a pointer to a pointer
   --> tests/ui/transmutes_expressible_as_ptr_casts.rs:27:46
@@ -28,8 +29,9 @@ LL |     let _ptr_to_unsized_transmute = unsafe { transmute::<*const [i32], *con
    |
 help: use an `as` cast instead
    |
-LL |     let _ptr_to_unsized_transmute = unsafe { slice_ptr as *const [u32] };
-   |                                              ~~~~~~~~~~~~~~~~~~~~~~~~~
+LL -     let _ptr_to_unsized_transmute = unsafe { transmute::<*const [i32], *const [u32]>(slice_ptr) };
+LL +     let _ptr_to_unsized_transmute = unsafe { slice_ptr as *const [u32] };
+   |
 
 error: transmute from `*const i32` to `usize` which could be expressed as a pointer cast instead
   --> tests/ui/transmutes_expressible_as_ptr_casts.rs:33:50
diff --git a/src/tools/clippy/tests/ui/unit_arg.stderr b/src/tools/clippy/tests/ui/unit_arg.stderr
index 41ad1a2d383..bf79e93e444 100644
--- a/src/tools/clippy/tests/ui/unit_arg.stderr
+++ b/src/tools/clippy/tests/ui/unit_arg.stderr
@@ -10,7 +10,8 @@ LL | |     });
    = help: to override `-D warnings` add `#[allow(clippy::unit_arg)]`
 help: remove the semicolon from the last statement in the block
    |
-LL |         1
+LL -         1;
+LL +         1
    |
 help: or move the expression in front of the call and replace it with the unit literal `()`
    |
@@ -43,7 +44,8 @@ LL | |     });
    |
 help: remove the semicolon from the last statement in the block
    |
-LL |         foo(2)
+LL -         foo(2);
+LL +         foo(2)
    |
 help: or move the expression in front of the call and replace it with the unit literal `()`
    |
@@ -64,7 +66,8 @@ LL | |     });
    |
 help: remove the semicolon from the last statement in the block
    |
-LL |         1
+LL -         1;
+LL +         1
    |
 help: or move the expression in front of the call and replace it with the unit literal `()`
    |
@@ -98,7 +101,8 @@ LL | |     });
    |
 help: remove the semicolon from the last statement in the block
    |
-LL |         foo(2)
+LL -         foo(2);
+LL +         foo(2)
    |
 help: or move the expressions in front of the call and replace them with the unit literal `()`
    |
@@ -124,11 +128,13 @@ LL | |     );
    |
 help: remove the semicolon from the last statement in the block
    |
-LL |             foo(1)
+LL -             foo(1);
+LL +             foo(1)
    |
 help: remove the semicolon from the last statement in the block
    |
-LL |             foo(3)
+LL -             foo(3);
+LL +             foo(3)
    |
 help: or move the expressions in front of the call and replace them with the unit literal `()`
    |
diff --git a/src/tools/clippy/tests/ui/unknown_clippy_lints.stderr b/src/tools/clippy/tests/ui/unknown_clippy_lints.stderr
index aa2c2f3c0e2..ea925cd3a9f 100644
--- a/src/tools/clippy/tests/ui/unknown_clippy_lints.stderr
+++ b/src/tools/clippy/tests/ui/unknown_clippy_lints.stderr
@@ -39,8 +39,9 @@ LL | #[warn(clippy::dead_cod)]
    |
 help: a lint with a similar name exists in `rustc` lints
    |
-LL | #[warn(dead_code)]
-   |        ~~~~~~~~~
+LL - #[warn(clippy::dead_cod)]
+LL + #[warn(dead_code)]
+   |
 
 error: unknown lint: `clippy::unused_colle`
   --> tests/ui/unknown_clippy_lints.rs:13:8
@@ -62,8 +63,9 @@ LL | #[warn(clippy::missing_docs)]
    |
 help: a lint with a similar name exists in `rustc` lints
    |
-LL | #[warn(missing_docs)]
-   |        ~~~~~~~~~~~~
+LL - #[warn(clippy::missing_docs)]
+LL + #[warn(missing_docs)]
+   |
 
 error: aborting due to 9 previous errors
 
diff --git a/src/tools/clippy/tests/ui/unnecessary_lazy_eval.stderr b/src/tools/clippy/tests/ui/unnecessary_lazy_eval.stderr
index 35a2144c389..9bb1b71f0ed 100644
--- a/src/tools/clippy/tests/ui/unnecessary_lazy_eval.stderr
+++ b/src/tools/clippy/tests/ui/unnecessary_lazy_eval.stderr
@@ -8,8 +8,9 @@ LL |     let _ = opt.unwrap_or_else(|| 2);
    = help: to override `-D warnings` add `#[allow(clippy::unnecessary_lazy_evaluations)]`
 help: use `unwrap_or` instead
    |
-LL |     let _ = opt.unwrap_or(2);
-   |                 ~~~~~~~~~~~~
+LL -     let _ = opt.unwrap_or_else(|| 2);
+LL +     let _ = opt.unwrap_or(2);
+   |
 
 error: unnecessary closure used to substitute value for `Option::None`
   --> tests/ui/unnecessary_lazy_eval.rs:84:13
@@ -19,8 +20,9 @@ LL |     let _ = opt.unwrap_or_else(|| astronomers_pi);
    |
 help: use `unwrap_or` instead
    |
-LL |     let _ = opt.unwrap_or(astronomers_pi);
-   |                 ~~~~~~~~~~~~~~~~~~~~~~~~~
+LL -     let _ = opt.unwrap_or_else(|| astronomers_pi);
+LL +     let _ = opt.unwrap_or(astronomers_pi);
+   |
 
 error: unnecessary closure used to substitute value for `Option::None`
   --> tests/ui/unnecessary_lazy_eval.rs:85:13
@@ -30,8 +32,9 @@ LL |     let _ = opt.unwrap_or_else(|| ext_str.some_field);
    |
 help: use `unwrap_or` instead
    |
-LL |     let _ = opt.unwrap_or(ext_str.some_field);
-   |                 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+LL -     let _ = opt.unwrap_or_else(|| ext_str.some_field);
+LL +     let _ = opt.unwrap_or(ext_str.some_field);
+   |
 
 error: unnecessary closure used to substitute value for `Option::None`
   --> tests/ui/unnecessary_lazy_eval.rs:87:13
@@ -41,8 +44,9 @@ LL |     let _ = opt.and_then(|_| ext_opt);
    |
 help: use `and` instead
    |
-LL |     let _ = opt.and(ext_opt);
-   |                 ~~~~~~~~~~~~
+LL -     let _ = opt.and_then(|_| ext_opt);
+LL +     let _ = opt.and(ext_opt);
+   |
 
 error: unnecessary closure used to substitute value for `Option::None`
   --> tests/ui/unnecessary_lazy_eval.rs:88:13
@@ -52,8 +56,9 @@ LL |     let _ = opt.or_else(|| ext_opt);
    |
 help: use `or` instead
    |
-LL |     let _ = opt.or(ext_opt);
-   |                 ~~~~~~~~~~~
+LL -     let _ = opt.or_else(|| ext_opt);
+LL +     let _ = opt.or(ext_opt);
+   |
 
 error: unnecessary closure used to substitute value for `Option::None`
   --> tests/ui/unnecessary_lazy_eval.rs:89:13
@@ -63,8 +68,9 @@ LL |     let _ = opt.or_else(|| None);
    |
 help: use `or` instead
    |
-LL |     let _ = opt.or(None);
-   |                 ~~~~~~~~
+LL -     let _ = opt.or_else(|| None);
+LL +     let _ = opt.or(None);
+   |
 
 error: unnecessary closure used to substitute value for `Option::None`
   --> tests/ui/unnecessary_lazy_eval.rs:90:13
@@ -74,8 +80,9 @@ LL |     let _ = opt.get_or_insert_with(|| 2);
    |
 help: use `get_or_insert` instead
    |
-LL |     let _ = opt.get_or_insert(2);
-   |                 ~~~~~~~~~~~~~~~~
+LL -     let _ = opt.get_or_insert_with(|| 2);
+LL +     let _ = opt.get_or_insert(2);
+   |
 
 error: unnecessary closure used to substitute value for `Option::None`
   --> tests/ui/unnecessary_lazy_eval.rs:91:13
@@ -85,8 +92,9 @@ LL |     let _ = opt.ok_or_else(|| 2);
    |
 help: use `ok_or` instead
    |
-LL |     let _ = opt.ok_or(2);
-   |                 ~~~~~~~~
+LL -     let _ = opt.ok_or_else(|| 2);
+LL +     let _ = opt.ok_or(2);
+   |
 
 error: unnecessary closure used to substitute value for `Option::None`
   --> tests/ui/unnecessary_lazy_eval.rs:92:13
@@ -96,8 +104,9 @@ LL |     let _ = nested_tuple_opt.unwrap_or_else(|| Some((1, 2)));
    |
 help: use `unwrap_or` instead
    |
-LL |     let _ = nested_tuple_opt.unwrap_or(Some((1, 2)));
-   |                              ~~~~~~~~~~~~~~~~~~~~~~~
+LL -     let _ = nested_tuple_opt.unwrap_or_else(|| Some((1, 2)));
+LL +     let _ = nested_tuple_opt.unwrap_or(Some((1, 2)));
+   |
 
 error: unnecessary closure used with `bool::then`
   --> tests/ui/unnecessary_lazy_eval.rs:93:13
@@ -107,8 +116,9 @@ LL |     let _ = cond.then(|| astronomers_pi);
    |
 help: use `then_some` instead
    |
-LL |     let _ = cond.then_some(astronomers_pi);
-   |                  ~~~~~~~~~~~~~~~~~~~~~~~~~
+LL -     let _ = cond.then(|| astronomers_pi);
+LL +     let _ = cond.then_some(astronomers_pi);
+   |
 
 error: unnecessary closure used with `bool::then`
   --> tests/ui/unnecessary_lazy_eval.rs:94:13
@@ -118,8 +128,9 @@ LL |     let _ = true.then(|| -> _ {});
    |
 help: use `then_some` instead
    |
-LL |     let _ = true.then_some({});
-   |                  ~~~~~~~~~~~~~
+LL -     let _ = true.then(|| -> _ {});
+LL +     let _ = true.then_some({});
+   |
 
 error: unnecessary closure used with `bool::then`
   --> tests/ui/unnecessary_lazy_eval.rs:95:13
@@ -129,8 +140,9 @@ LL |     let _ = true.then(|| {});
    |
 help: use `then_some` instead
    |
-LL |     let _ = true.then_some({});
-   |                  ~~~~~~~~~~~~~
+LL -     let _ = true.then(|| {});
+LL +     let _ = true.then_some({});
+   |
 
 error: unnecessary closure used to substitute value for `Option::None`
   --> tests/ui/unnecessary_lazy_eval.rs:99:13
@@ -140,8 +152,9 @@ LL |     let _ = Some(1).unwrap_or_else(|| *r);
    |
 help: use `unwrap_or` instead
    |
-LL |     let _ = Some(1).unwrap_or(*r);
-   |                     ~~~~~~~~~~~~~
+LL -     let _ = Some(1).unwrap_or_else(|| *r);
+LL +     let _ = Some(1).unwrap_or(*r);
+   |
 
 error: unnecessary closure used to substitute value for `Option::None`
   --> tests/ui/unnecessary_lazy_eval.rs:101:13
@@ -151,8 +164,9 @@ LL |     let _ = Some(1).unwrap_or_else(|| *b);
    |
 help: use `unwrap_or` instead
    |
-LL |     let _ = Some(1).unwrap_or(*b);
-   |                     ~~~~~~~~~~~~~
+LL -     let _ = Some(1).unwrap_or_else(|| *b);
+LL +     let _ = Some(1).unwrap_or(*b);
+   |
 
 error: unnecessary closure used to substitute value for `Option::None`
   --> tests/ui/unnecessary_lazy_eval.rs:103:13
@@ -162,8 +176,9 @@ LL |     let _ = Some(1).as_ref().unwrap_or_else(|| &r);
    |
 help: use `unwrap_or` instead
    |
-LL |     let _ = Some(1).as_ref().unwrap_or(&r);
-   |                              ~~~~~~~~~~~~~
+LL -     let _ = Some(1).as_ref().unwrap_or_else(|| &r);
+LL +     let _ = Some(1).as_ref().unwrap_or(&r);
+   |
 
 error: unnecessary closure used to substitute value for `Option::None`
   --> tests/ui/unnecessary_lazy_eval.rs:104:13
@@ -173,8 +188,9 @@ LL |     let _ = Some(1).as_ref().unwrap_or_else(|| &b);
    |
 help: use `unwrap_or` instead
    |
-LL |     let _ = Some(1).as_ref().unwrap_or(&b);
-   |                              ~~~~~~~~~~~~~
+LL -     let _ = Some(1).as_ref().unwrap_or_else(|| &b);
+LL +     let _ = Some(1).as_ref().unwrap_or(&b);
+   |
 
 error: unnecessary closure used to substitute value for `Option::None`
   --> tests/ui/unnecessary_lazy_eval.rs:107:13
@@ -184,8 +200,9 @@ LL |     let _ = Some(10).unwrap_or_else(|| 2);
    |
 help: use `unwrap_or` instead
    |
-LL |     let _ = Some(10).unwrap_or(2);
-   |                      ~~~~~~~~~~~~
+LL -     let _ = Some(10).unwrap_or_else(|| 2);
+LL +     let _ = Some(10).unwrap_or(2);
+   |
 
 error: unnecessary closure used to substitute value for `Option::None`
   --> tests/ui/unnecessary_lazy_eval.rs:108:13
@@ -195,8 +212,9 @@ LL |     let _ = Some(10).and_then(|_| ext_opt);
    |
 help: use `and` instead
    |
-LL |     let _ = Some(10).and(ext_opt);
-   |                      ~~~~~~~~~~~~
+LL -     let _ = Some(10).and_then(|_| ext_opt);
+LL +     let _ = Some(10).and(ext_opt);
+   |
 
 error: unnecessary closure used to substitute value for `Option::None`
   --> tests/ui/unnecessary_lazy_eval.rs:109:28
@@ -206,8 +224,9 @@ LL |     let _: Option<usize> = None.or_else(|| ext_opt);
    |
 help: use `or` instead
    |
-LL |     let _: Option<usize> = None.or(ext_opt);
-   |                                 ~~~~~~~~~~~
+LL -     let _: Option<usize> = None.or_else(|| ext_opt);
+LL +     let _: Option<usize> = None.or(ext_opt);
+   |
 
 error: unnecessary closure used to substitute value for `Option::None`
   --> tests/ui/unnecessary_lazy_eval.rs:110:13
@@ -217,8 +236,9 @@ LL |     let _ = None.get_or_insert_with(|| 2);
    |
 help: use `get_or_insert` instead
    |
-LL |     let _ = None.get_or_insert(2);
-   |                  ~~~~~~~~~~~~~~~~
+LL -     let _ = None.get_or_insert_with(|| 2);
+LL +     let _ = None.get_or_insert(2);
+   |
 
 error: unnecessary closure used to substitute value for `Option::None`
   --> tests/ui/unnecessary_lazy_eval.rs:111:35
@@ -228,8 +248,9 @@ LL |     let _: Result<usize, usize> = None.ok_or_else(|| 2);
    |
 help: use `ok_or` instead
    |
-LL |     let _: Result<usize, usize> = None.ok_or(2);
-   |                                        ~~~~~~~~
+LL -     let _: Result<usize, usize> = None.ok_or_else(|| 2);
+LL +     let _: Result<usize, usize> = None.ok_or(2);
+   |
 
 error: unnecessary closure used to substitute value for `Option::None`
   --> tests/ui/unnecessary_lazy_eval.rs:112:28
@@ -239,8 +260,9 @@ LL |     let _: Option<usize> = None.or_else(|| None);
    |
 help: use `or` instead
    |
-LL |     let _: Option<usize> = None.or(None);
-   |                                 ~~~~~~~~
+LL -     let _: Option<usize> = None.or_else(|| None);
+LL +     let _: Option<usize> = None.or(None);
+   |
 
 error: unnecessary closure used to substitute value for `Option::None`
   --> tests/ui/unnecessary_lazy_eval.rs:115:13
@@ -250,8 +272,9 @@ LL |     let _ = deep.0.unwrap_or_else(|| 2);
    |
 help: use `unwrap_or` instead
    |
-LL |     let _ = deep.0.unwrap_or(2);
-   |                    ~~~~~~~~~~~~
+LL -     let _ = deep.0.unwrap_or_else(|| 2);
+LL +     let _ = deep.0.unwrap_or(2);
+   |
 
 error: unnecessary closure used to substitute value for `Option::None`
   --> tests/ui/unnecessary_lazy_eval.rs:116:13
@@ -261,8 +284,9 @@ LL |     let _ = deep.0.and_then(|_| ext_opt);
    |
 help: use `and` instead
    |
-LL |     let _ = deep.0.and(ext_opt);
-   |                    ~~~~~~~~~~~~
+LL -     let _ = deep.0.and_then(|_| ext_opt);
+LL +     let _ = deep.0.and(ext_opt);
+   |
 
 error: unnecessary closure used to substitute value for `Option::None`
   --> tests/ui/unnecessary_lazy_eval.rs:117:13
@@ -272,8 +296,9 @@ LL |     let _ = deep.0.or_else(|| None);
    |
 help: use `or` instead
    |
-LL |     let _ = deep.0.or(None);
-   |                    ~~~~~~~~
+LL -     let _ = deep.0.or_else(|| None);
+LL +     let _ = deep.0.or(None);
+   |
 
 error: unnecessary closure used to substitute value for `Option::None`
   --> tests/ui/unnecessary_lazy_eval.rs:118:13
@@ -283,8 +308,9 @@ LL |     let _ = deep.0.get_or_insert_with(|| 2);
    |
 help: use `get_or_insert` instead
    |
-LL |     let _ = deep.0.get_or_insert(2);
-   |                    ~~~~~~~~~~~~~~~~
+LL -     let _ = deep.0.get_or_insert_with(|| 2);
+LL +     let _ = deep.0.get_or_insert(2);
+   |
 
 error: unnecessary closure used to substitute value for `Option::None`
   --> tests/ui/unnecessary_lazy_eval.rs:119:13
@@ -294,8 +320,9 @@ LL |     let _ = deep.0.ok_or_else(|| 2);
    |
 help: use `ok_or` instead
    |
-LL |     let _ = deep.0.ok_or(2);
-   |                    ~~~~~~~~
+LL -     let _ = deep.0.ok_or_else(|| 2);
+LL +     let _ = deep.0.ok_or(2);
+   |
 
 error: unnecessary closure used to substitute value for `Option::None`
   --> tests/ui/unnecessary_lazy_eval.rs:150:28
@@ -305,8 +332,9 @@ LL |     let _: Option<usize> = None.or_else(|| Some(3));
    |
 help: use `or` instead
    |
-LL |     let _: Option<usize> = None.or(Some(3));
-   |                                 ~~~~~~~~~~~
+LL -     let _: Option<usize> = None.or_else(|| Some(3));
+LL +     let _: Option<usize> = None.or(Some(3));
+   |
 
 error: unnecessary closure used to substitute value for `Option::None`
   --> tests/ui/unnecessary_lazy_eval.rs:151:13
@@ -316,8 +344,9 @@ LL |     let _ = deep.0.or_else(|| Some(3));
    |
 help: use `or` instead
    |
-LL |     let _ = deep.0.or(Some(3));
-   |                    ~~~~~~~~~~~
+LL -     let _ = deep.0.or_else(|| Some(3));
+LL +     let _ = deep.0.or(Some(3));
+   |
 
 error: unnecessary closure used to substitute value for `Option::None`
   --> tests/ui/unnecessary_lazy_eval.rs:152:13
@@ -327,8 +356,9 @@ LL |     let _ = opt.or_else(|| Some(3));
    |
 help: use `or` instead
    |
-LL |     let _ = opt.or(Some(3));
-   |                 ~~~~~~~~~~~
+LL -     let _ = opt.or_else(|| Some(3));
+LL +     let _ = opt.or(Some(3));
+   |
 
 error: unnecessary closure used to substitute value for `Result::Err`
   --> tests/ui/unnecessary_lazy_eval.rs:158:13
@@ -338,8 +368,9 @@ LL |     let _ = res2.unwrap_or_else(|_| 2);
    |
 help: use `unwrap_or` instead
    |
-LL |     let _ = res2.unwrap_or(2);
-   |                  ~~~~~~~~~~~~
+LL -     let _ = res2.unwrap_or_else(|_| 2);
+LL +     let _ = res2.unwrap_or(2);
+   |
 
 error: unnecessary closure used to substitute value for `Result::Err`
   --> tests/ui/unnecessary_lazy_eval.rs:159:13
@@ -349,8 +380,9 @@ LL |     let _ = res2.unwrap_or_else(|_| astronomers_pi);
    |
 help: use `unwrap_or` instead
    |
-LL |     let _ = res2.unwrap_or(astronomers_pi);
-   |                  ~~~~~~~~~~~~~~~~~~~~~~~~~
+LL -     let _ = res2.unwrap_or_else(|_| astronomers_pi);
+LL +     let _ = res2.unwrap_or(astronomers_pi);
+   |
 
 error: unnecessary closure used to substitute value for `Result::Err`
   --> tests/ui/unnecessary_lazy_eval.rs:160:13
@@ -360,8 +392,9 @@ LL |     let _ = res2.unwrap_or_else(|_| ext_str.some_field);
    |
 help: use `unwrap_or` instead
    |
-LL |     let _ = res2.unwrap_or(ext_str.some_field);
-   |                  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+LL -     let _ = res2.unwrap_or_else(|_| ext_str.some_field);
+LL +     let _ = res2.unwrap_or(ext_str.some_field);
+   |
 
 error: unnecessary closure used to substitute value for `Result::Err`
   --> tests/ui/unnecessary_lazy_eval.rs:182:35
@@ -371,8 +404,9 @@ LL |     let _: Result<usize, usize> = res.and_then(|_| Err(2));
    |
 help: use `and` instead
    |
-LL |     let _: Result<usize, usize> = res.and(Err(2));
-   |                                       ~~~~~~~~~~~
+LL -     let _: Result<usize, usize> = res.and_then(|_| Err(2));
+LL +     let _: Result<usize, usize> = res.and(Err(2));
+   |
 
 error: unnecessary closure used to substitute value for `Result::Err`
   --> tests/ui/unnecessary_lazy_eval.rs:183:35
@@ -382,8 +416,9 @@ LL |     let _: Result<usize, usize> = res.and_then(|_| Err(astronomers_pi));
    |
 help: use `and` instead
    |
-LL |     let _: Result<usize, usize> = res.and(Err(astronomers_pi));
-   |                                       ~~~~~~~~~~~~~~~~~~~~~~~~
+LL -     let _: Result<usize, usize> = res.and_then(|_| Err(astronomers_pi));
+LL +     let _: Result<usize, usize> = res.and(Err(astronomers_pi));
+   |
 
 error: unnecessary closure used to substitute value for `Result::Err`
   --> tests/ui/unnecessary_lazy_eval.rs:184:35
@@ -393,8 +428,9 @@ LL |     let _: Result<usize, usize> = res.and_then(|_| Err(ext_str.some_field))
    |
 help: use `and` instead
    |
-LL |     let _: Result<usize, usize> = res.and(Err(ext_str.some_field));
-   |                                       ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+LL -     let _: Result<usize, usize> = res.and_then(|_| Err(ext_str.some_field));
+LL +     let _: Result<usize, usize> = res.and(Err(ext_str.some_field));
+   |
 
 error: unnecessary closure used to substitute value for `Result::Err`
   --> tests/ui/unnecessary_lazy_eval.rs:186:35
@@ -404,8 +440,9 @@ LL |     let _: Result<usize, usize> = res.or_else(|_| Ok(2));
    |
 help: use `or` instead
    |
-LL |     let _: Result<usize, usize> = res.or(Ok(2));
-   |                                       ~~~~~~~~~
+LL -     let _: Result<usize, usize> = res.or_else(|_| Ok(2));
+LL +     let _: Result<usize, usize> = res.or(Ok(2));
+   |
 
 error: unnecessary closure used to substitute value for `Result::Err`
   --> tests/ui/unnecessary_lazy_eval.rs:187:35
@@ -415,8 +452,9 @@ LL |     let _: Result<usize, usize> = res.or_else(|_| Ok(astronomers_pi));
    |
 help: use `or` instead
    |
-LL |     let _: Result<usize, usize> = res.or(Ok(astronomers_pi));
-   |                                       ~~~~~~~~~~~~~~~~~~~~~~
+LL -     let _: Result<usize, usize> = res.or_else(|_| Ok(astronomers_pi));
+LL +     let _: Result<usize, usize> = res.or(Ok(astronomers_pi));
+   |
 
 error: unnecessary closure used to substitute value for `Result::Err`
   --> tests/ui/unnecessary_lazy_eval.rs:188:35
@@ -426,8 +464,9 @@ LL |     let _: Result<usize, usize> = res.or_else(|_| Ok(ext_str.some_field));
    |
 help: use `or` instead
    |
-LL |     let _: Result<usize, usize> = res.or(Ok(ext_str.some_field));
-   |                                       ~~~~~~~~~~~~~~~~~~~~~~~~~~
+LL -     let _: Result<usize, usize> = res.or_else(|_| Ok(ext_str.some_field));
+LL +     let _: Result<usize, usize> = res.or(Ok(ext_str.some_field));
+   |
 
 error: unnecessary closure used to substitute value for `Result::Err`
   --> tests/ui/unnecessary_lazy_eval.rs:189:35
@@ -440,8 +479,9 @@ LL | |     or_else(|_| Ok(ext_str.some_field));
    |
 help: use `or` instead
    |
-LL |     or(Ok(ext_str.some_field));
-   |     ~~~~~~~~~~~~~~~~~~~~~~~~~~
+LL -     or_else(|_| Ok(ext_str.some_field));
+LL +     or(Ok(ext_str.some_field));
+   |
 
 error: unnecessary closure used with `bool::then`
   --> tests/ui/unnecessary_lazy_eval.rs:219:14
@@ -451,8 +491,9 @@ LL |     let _x = false.then(|| i32::MAX + 1);
    |
 help: use `then_some` instead
    |
-LL |     let _x = false.then_some(i32::MAX + 1);
-   |                    ~~~~~~~~~~~~~~~~~~~~~~~
+LL -     let _x = false.then(|| i32::MAX + 1);
+LL +     let _x = false.then_some(i32::MAX + 1);
+   |
 
 error: unnecessary closure used with `bool::then`
   --> tests/ui/unnecessary_lazy_eval.rs:221:14
@@ -462,8 +503,9 @@ LL |     let _x = false.then(|| i32::MAX * 2);
    |
 help: use `then_some` instead
    |
-LL |     let _x = false.then_some(i32::MAX * 2);
-   |                    ~~~~~~~~~~~~~~~~~~~~~~~
+LL -     let _x = false.then(|| i32::MAX * 2);
+LL +     let _x = false.then_some(i32::MAX * 2);
+   |
 
 error: unnecessary closure used with `bool::then`
   --> tests/ui/unnecessary_lazy_eval.rs:223:14
@@ -473,8 +515,9 @@ LL |     let _x = false.then(|| i32::MAX - 1);
    |
 help: use `then_some` instead
    |
-LL |     let _x = false.then_some(i32::MAX - 1);
-   |                    ~~~~~~~~~~~~~~~~~~~~~~~
+LL -     let _x = false.then(|| i32::MAX - 1);
+LL +     let _x = false.then_some(i32::MAX - 1);
+   |
 
 error: unnecessary closure used with `bool::then`
   --> tests/ui/unnecessary_lazy_eval.rs:225:14
@@ -484,8 +527,9 @@ LL |     let _x = false.then(|| i32::MIN - 1);
    |
 help: use `then_some` instead
    |
-LL |     let _x = false.then_some(i32::MIN - 1);
-   |                    ~~~~~~~~~~~~~~~~~~~~~~~
+LL -     let _x = false.then(|| i32::MIN - 1);
+LL +     let _x = false.then_some(i32::MIN - 1);
+   |
 
 error: unnecessary closure used with `bool::then`
   --> tests/ui/unnecessary_lazy_eval.rs:227:14
@@ -495,8 +539,9 @@ LL |     let _x = false.then(|| (1 + 2 * 3 - 2 / 3 + 9) << 2);
    |
 help: use `then_some` instead
    |
-LL |     let _x = false.then_some((1 + 2 * 3 - 2 / 3 + 9) << 2);
-   |                    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+LL -     let _x = false.then(|| (1 + 2 * 3 - 2 / 3 + 9) << 2);
+LL +     let _x = false.then_some((1 + 2 * 3 - 2 / 3 + 9) << 2);
+   |
 
 error: unnecessary closure used with `bool::then`
   --> tests/ui/unnecessary_lazy_eval.rs:229:14
@@ -506,8 +551,9 @@ LL |     let _x = false.then(|| 255u8 << 7);
    |
 help: use `then_some` instead
    |
-LL |     let _x = false.then_some(255u8 << 7);
-   |                    ~~~~~~~~~~~~~~~~~~~~~
+LL -     let _x = false.then(|| 255u8 << 7);
+LL +     let _x = false.then_some(255u8 << 7);
+   |
 
 error: unnecessary closure used with `bool::then`
   --> tests/ui/unnecessary_lazy_eval.rs:231:14
@@ -517,8 +563,9 @@ LL |     let _x = false.then(|| 255u8 << 8);
    |
 help: use `then_some` instead
    |
-LL |     let _x = false.then_some(255u8 << 8);
-   |                    ~~~~~~~~~~~~~~~~~~~~~
+LL -     let _x = false.then(|| 255u8 << 8);
+LL +     let _x = false.then_some(255u8 << 8);
+   |
 
 error: unnecessary closure used with `bool::then`
   --> tests/ui/unnecessary_lazy_eval.rs:233:14
@@ -528,8 +575,9 @@ LL |     let _x = false.then(|| 255u8 >> 8);
    |
 help: use `then_some` instead
    |
-LL |     let _x = false.then_some(255u8 >> 8);
-   |                    ~~~~~~~~~~~~~~~~~~~~~
+LL -     let _x = false.then(|| 255u8 >> 8);
+LL +     let _x = false.then_some(255u8 >> 8);
+   |
 
 error: unnecessary closure used with `bool::then`
   --> tests/ui/unnecessary_lazy_eval.rs:236:14
@@ -539,8 +587,9 @@ LL |     let _x = false.then(|| i32::MAX + -1);
    |
 help: use `then_some` instead
    |
-LL |     let _x = false.then_some(i32::MAX + -1);
-   |                    ~~~~~~~~~~~~~~~~~~~~~~~~
+LL -     let _x = false.then(|| i32::MAX + -1);
+LL +     let _x = false.then_some(i32::MAX + -1);
+   |
 
 error: unnecessary closure used with `bool::then`
   --> tests/ui/unnecessary_lazy_eval.rs:238:14
@@ -550,8 +599,9 @@ LL |     let _x = false.then(|| -i32::MAX);
    |
 help: use `then_some` instead
    |
-LL |     let _x = false.then_some(-i32::MAX);
-   |                    ~~~~~~~~~~~~~~~~~~~~
+LL -     let _x = false.then(|| -i32::MAX);
+LL +     let _x = false.then_some(-i32::MAX);
+   |
 
 error: unnecessary closure used with `bool::then`
   --> tests/ui/unnecessary_lazy_eval.rs:240:14
@@ -561,8 +611,9 @@ LL |     let _x = false.then(|| -i32::MIN);
    |
 help: use `then_some` instead
    |
-LL |     let _x = false.then_some(-i32::MIN);
-   |                    ~~~~~~~~~~~~~~~~~~~~
+LL -     let _x = false.then(|| -i32::MIN);
+LL +     let _x = false.then_some(-i32::MIN);
+   |
 
 error: unnecessary closure used with `bool::then`
   --> tests/ui/unnecessary_lazy_eval.rs:243:14
@@ -572,8 +623,9 @@ LL |     let _x = false.then(|| 255 >> -7);
    |
 help: use `then_some` instead
    |
-LL |     let _x = false.then_some(255 >> -7);
-   |                    ~~~~~~~~~~~~~~~~~~~~
+LL -     let _x = false.then(|| 255 >> -7);
+LL +     let _x = false.then_some(255 >> -7);
+   |
 
 error: unnecessary closure used with `bool::then`
   --> tests/ui/unnecessary_lazy_eval.rs:245:14
@@ -583,8 +635,9 @@ LL |     let _x = false.then(|| 255 << -1);
    |
 help: use `then_some` instead
    |
-LL |     let _x = false.then_some(255 << -1);
-   |                    ~~~~~~~~~~~~~~~~~~~~
+LL -     let _x = false.then(|| 255 << -1);
+LL +     let _x = false.then_some(255 << -1);
+   |
 
 error: unnecessary closure used with `bool::then`
   --> tests/ui/unnecessary_lazy_eval.rs:247:14
@@ -594,8 +647,9 @@ LL |     let _x = false.then(|| 1 / 0);
    |
 help: use `then_some` instead
    |
-LL |     let _x = false.then_some(1 / 0);
-   |                    ~~~~~~~~~~~~~~~~
+LL -     let _x = false.then(|| 1 / 0);
+LL +     let _x = false.then_some(1 / 0);
+   |
 
 error: unnecessary closure used with `bool::then`
   --> tests/ui/unnecessary_lazy_eval.rs:249:14
@@ -605,8 +659,9 @@ LL |     let _x = false.then(|| x << -1);
    |
 help: use `then_some` instead
    |
-LL |     let _x = false.then_some(x << -1);
-   |                    ~~~~~~~~~~~~~~~~~~
+LL -     let _x = false.then(|| x << -1);
+LL +     let _x = false.then_some(x << -1);
+   |
 
 error: unnecessary closure used with `bool::then`
   --> tests/ui/unnecessary_lazy_eval.rs:251:14
@@ -616,8 +671,9 @@ LL |     let _x = false.then(|| x << 2);
    |
 help: use `then_some` instead
    |
-LL |     let _x = false.then_some(x << 2);
-   |                    ~~~~~~~~~~~~~~~~~
+LL -     let _x = false.then(|| x << 2);
+LL +     let _x = false.then_some(x << 2);
+   |
 
 error: unnecessary closure used with `bool::then`
   --> tests/ui/unnecessary_lazy_eval.rs:261:14
@@ -627,8 +683,9 @@ LL |     let _x = false.then(|| x / 0);
    |
 help: use `then_some` instead
    |
-LL |     let _x = false.then_some(x / 0);
-   |                    ~~~~~~~~~~~~~~~~
+LL -     let _x = false.then(|| x / 0);
+LL +     let _x = false.then_some(x / 0);
+   |
 
 error: unnecessary closure used with `bool::then`
   --> tests/ui/unnecessary_lazy_eval.rs:263:14
@@ -638,8 +695,9 @@ LL |     let _x = false.then(|| x % 0);
    |
 help: use `then_some` instead
    |
-LL |     let _x = false.then_some(x % 0);
-   |                    ~~~~~~~~~~~~~~~~
+LL -     let _x = false.then(|| x % 0);
+LL +     let _x = false.then_some(x % 0);
+   |
 
 error: unnecessary closure used with `bool::then`
   --> tests/ui/unnecessary_lazy_eval.rs:266:14
@@ -649,8 +707,9 @@ LL |     let _x = false.then(|| 1 / -1);
    |
 help: use `then_some` instead
    |
-LL |     let _x = false.then_some(1 / -1);
-   |                    ~~~~~~~~~~~~~~~~~
+LL -     let _x = false.then(|| 1 / -1);
+LL +     let _x = false.then_some(1 / -1);
+   |
 
 error: unnecessary closure used with `bool::then`
   --> tests/ui/unnecessary_lazy_eval.rs:268:14
@@ -660,8 +719,9 @@ LL |     let _x = false.then(|| i32::MIN / -1);
    |
 help: use `then_some` instead
    |
-LL |     let _x = false.then_some(i32::MIN / -1);
-   |                    ~~~~~~~~~~~~~~~~~~~~~~~~
+LL -     let _x = false.then(|| i32::MIN / -1);
+LL +     let _x = false.then_some(i32::MIN / -1);
+   |
 
 error: unnecessary closure used with `bool::then`
   --> tests/ui/unnecessary_lazy_eval.rs:271:14
@@ -671,8 +731,9 @@ LL |     let _x = false.then(|| i32::MIN / 0);
    |
 help: use `then_some` instead
    |
-LL |     let _x = false.then_some(i32::MIN / 0);
-   |                    ~~~~~~~~~~~~~~~~~~~~~~~
+LL -     let _x = false.then(|| i32::MIN / 0);
+LL +     let _x = false.then_some(i32::MIN / 0);
+   |
 
 error: unnecessary closure used with `bool::then`
   --> tests/ui/unnecessary_lazy_eval.rs:273:14
@@ -682,8 +743,9 @@ LL |     let _x = false.then(|| 4 / 2);
    |
 help: use `then_some` instead
    |
-LL |     let _x = false.then_some(4 / 2);
-   |                    ~~~~~~~~~~~~~~~~
+LL -     let _x = false.then(|| 4 / 2);
+LL +     let _x = false.then_some(4 / 2);
+   |
 
 error: unnecessary closure used with `bool::then`
   --> tests/ui/unnecessary_lazy_eval.rs:281:14
@@ -693,8 +755,9 @@ LL |     let _x = false.then(|| f1 + f2);
    |
 help: use `then_some` instead
    |
-LL |     let _x = false.then_some(f1 + f2);
-   |                    ~~~~~~~~~~~~~~~~~~
+LL -     let _x = false.then(|| f1 + f2);
+LL +     let _x = false.then_some(f1 + f2);
+   |
 
 error: aborting due to 63 previous errors
 
diff --git a/src/tools/clippy/tests/ui/unnecessary_lazy_eval_unfixable.stderr b/src/tools/clippy/tests/ui/unnecessary_lazy_eval_unfixable.stderr
index 390235b2124..9688c44c914 100644
--- a/src/tools/clippy/tests/ui/unnecessary_lazy_eval_unfixable.stderr
+++ b/src/tools/clippy/tests/ui/unnecessary_lazy_eval_unfixable.stderr
@@ -8,8 +8,9 @@ LL |     let _ = Ok(1).unwrap_or_else(|()| 2);
    = help: to override `-D warnings` add `#[allow(clippy::unnecessary_lazy_evaluations)]`
 help: use `unwrap_or` instead
    |
-LL |     let _ = Ok(1).unwrap_or(2);
-   |                   ~~~~~~~~~~~~
+LL -     let _ = Ok(1).unwrap_or_else(|()| 2);
+LL +     let _ = Ok(1).unwrap_or(2);
+   |
 
 error: unnecessary closure used to substitute value for `Result::Err`
   --> tests/ui/unnecessary_lazy_eval_unfixable.rs:19:13
@@ -19,8 +20,9 @@ LL |     let _ = Ok(1).unwrap_or_else(|e::E| 2);
    |
 help: use `unwrap_or` instead
    |
-LL |     let _ = Ok(1).unwrap_or(2);
-   |                   ~~~~~~~~~~~~
+LL -     let _ = Ok(1).unwrap_or_else(|e::E| 2);
+LL +     let _ = Ok(1).unwrap_or(2);
+   |
 
 error: unnecessary closure used to substitute value for `Result::Err`
   --> tests/ui/unnecessary_lazy_eval_unfixable.rs:21:13
@@ -30,8 +32,9 @@ LL |     let _ = Ok(1).unwrap_or_else(|SomeStruct { .. }| 2);
    |
 help: use `unwrap_or` instead
    |
-LL |     let _ = Ok(1).unwrap_or(2);
-   |                   ~~~~~~~~~~~~
+LL -     let _ = Ok(1).unwrap_or_else(|SomeStruct { .. }| 2);
+LL +     let _ = Ok(1).unwrap_or(2);
+   |
 
 error: unnecessary closure used with `bool::then`
   --> tests/ui/unnecessary_lazy_eval_unfixable.rs:31:13
@@ -41,8 +44,9 @@ LL |     let _ = true.then(|| -> &[u8] { &[] });
    |
 help: use `then_some` instead
    |
-LL |     let _ = true.then_some({ &[] });
-   |                  ~~~~~~~~~~~~~~~~~~
+LL -     let _ = true.then(|| -> &[u8] { &[] });
+LL +     let _ = true.then_some({ &[] });
+   |
 
 error: aborting due to 4 previous errors
 
diff --git a/src/tools/clippy/tests/ui/unnecessary_literal_unwrap.stderr b/src/tools/clippy/tests/ui/unnecessary_literal_unwrap.stderr
index 37ee9195fce..631bf083726 100644
--- a/src/tools/clippy/tests/ui/unnecessary_literal_unwrap.stderr
+++ b/src/tools/clippy/tests/ui/unnecessary_literal_unwrap.stderr
@@ -62,8 +62,9 @@ LL |     let _val = None::<()>.expect("this always happens");
    |
 help: remove the `None` and `expect()`
    |
-LL |     let _val = panic!("this always happens");
-   |                ~~~~~~~
+LL -     let _val = None::<()>.expect("this always happens");
+LL +     let _val = panic!("this always happens");
+   |
 
 error: used `unwrap_or_default()` on `None` value
   --> tests/ui/unnecessary_literal_unwrap.rs:22:24
@@ -133,8 +134,9 @@ LL |     None::<()>.expect("this always happens");
    |
 help: remove the `None` and `expect()`
    |
-LL |     panic!("this always happens");
-   |     ~~~~~~~
+LL -     None::<()>.expect("this always happens");
+LL +     panic!("this always happens");
+   |
 
 error: used `unwrap_or_default()` on `None` value
   --> tests/ui/unnecessary_literal_unwrap.rs:30:5
@@ -222,8 +224,9 @@ LL |     let _val = Ok::<_, ()>(1).unwrap_err();
    |
 help: remove the `Ok` and `unwrap_err()`
    |
-LL |     let _val = panic!("{:?}", 1);
-   |                ~~~~~~~~~~~~~~  ~
+LL -     let _val = Ok::<_, ()>(1).unwrap_err();
+LL +     let _val = panic!("{:?}", 1);
+   |
 
 error: used `expect_err()` on `Ok` value
   --> tests/ui/unnecessary_literal_unwrap.rs:41:16
@@ -233,8 +236,9 @@ LL |     let _val = Ok::<_, ()>(1).expect_err("this always happens");
    |
 help: remove the `Ok` and `expect_err()`
    |
-LL |     let _val = panic!("{1}: {:?}", 1, "this always happens");
-   |                ~~~~~~~~~~~~~~~~~~~  ~
+LL -     let _val = Ok::<_, ()>(1).expect_err("this always happens");
+LL +     let _val = panic!("{1}: {:?}", 1, "this always happens");
+   |
 
 error: used `unwrap()` on `Ok` value
   --> tests/ui/unnecessary_literal_unwrap.rs:43:5
@@ -268,8 +272,9 @@ LL |     Ok::<_, ()>(1).unwrap_err();
    |
 help: remove the `Ok` and `unwrap_err()`
    |
-LL |     panic!("{:?}", 1);
-   |     ~~~~~~~~~~~~~~  ~
+LL -     Ok::<_, ()>(1).unwrap_err();
+LL +     panic!("{:?}", 1);
+   |
 
 error: used `expect_err()` on `Ok` value
   --> tests/ui/unnecessary_literal_unwrap.rs:46:5
@@ -279,8 +284,9 @@ LL |     Ok::<_, ()>(1).expect_err("this always happens");
    |
 help: remove the `Ok` and `expect_err()`
    |
-LL |     panic!("{1}: {:?}", 1, "this always happens");
-   |     ~~~~~~~~~~~~~~~~~~~  ~
+LL -     Ok::<_, ()>(1).expect_err("this always happens");
+LL +     panic!("{1}: {:?}", 1, "this always happens");
+   |
 
 error: used `unwrap_err()` on `Err` value
   --> tests/ui/unnecessary_literal_unwrap.rs:50:16
@@ -314,8 +320,9 @@ LL |     let _val = Err::<(), _>(1).unwrap();
    |
 help: remove the `Err` and `unwrap()`
    |
-LL |     let _val = panic!("{:?}", 1);
-   |                ~~~~~~~~~~~~~~  ~
+LL -     let _val = Err::<(), _>(1).unwrap();
+LL +     let _val = panic!("{:?}", 1);
+   |
 
 error: used `expect()` on `Err` value
   --> tests/ui/unnecessary_literal_unwrap.rs:53:16
@@ -325,8 +332,9 @@ LL |     let _val = Err::<(), _>(1).expect("this always happens");
    |
 help: remove the `Err` and `expect()`
    |
-LL |     let _val = panic!("{1}: {:?}", 1, "this always happens");
-   |                ~~~~~~~~~~~~~~~~~~~  ~
+LL -     let _val = Err::<(), _>(1).expect("this always happens");
+LL +     let _val = panic!("{1}: {:?}", 1, "this always happens");
+   |
 
 error: used `unwrap_err()` on `Err` value
   --> tests/ui/unnecessary_literal_unwrap.rs:55:5
@@ -360,8 +368,9 @@ LL |     Err::<(), _>(1).unwrap();
    |
 help: remove the `Err` and `unwrap()`
    |
-LL |     panic!("{:?}", 1);
-   |     ~~~~~~~~~~~~~~  ~
+LL -     Err::<(), _>(1).unwrap();
+LL +     panic!("{:?}", 1);
+   |
 
 error: used `expect()` on `Err` value
   --> tests/ui/unnecessary_literal_unwrap.rs:58:5
@@ -371,8 +380,9 @@ LL |     Err::<(), _>(1).expect("this always happens");
    |
 help: remove the `Err` and `expect()`
    |
-LL |     panic!("{1}: {:?}", 1, "this always happens");
-   |     ~~~~~~~~~~~~~~~~~~~  ~
+LL -     Err::<(), _>(1).expect("this always happens");
+LL +     panic!("{1}: {:?}", 1, "this always happens");
+   |
 
 error: used `unwrap_or()` on `Some` value
   --> tests/ui/unnecessary_literal_unwrap.rs:62:16
diff --git a/src/tools/clippy/tests/ui/unnecessary_map_or.stderr b/src/tools/clippy/tests/ui/unnecessary_map_or.stderr
index 2ae327f0bf8..9f38b8c8d93 100644
--- a/src/tools/clippy/tests/ui/unnecessary_map_or.stderr
+++ b/src/tools/clippy/tests/ui/unnecessary_map_or.stderr
@@ -8,8 +8,9 @@ LL |     let _ = Some(5).map_or(false, |n| n == 5);
    = help: to override `-D warnings` add `#[allow(clippy::unnecessary_map_or)]`
 help: use a standard comparison instead
    |
-LL |     let _ = Some(5) == Some(5);
-   |             ~~~~~~~~~~~~~~~~~~
+LL -     let _ = Some(5).map_or(false, |n| n == 5);
+LL +     let _ = Some(5) == Some(5);
+   |
 
 error: this `map_or` can be simplified
   --> tests/ui/unnecessary_map_or.rs:14:13
@@ -19,8 +20,9 @@ LL |     let _ = Some(5).map_or(true, |n| n != 5);
    |
 help: use a standard comparison instead
    |
-LL |     let _ = Some(5) != Some(5);
-   |             ~~~~~~~~~~~~~~~~~~
+LL -     let _ = Some(5).map_or(true, |n| n != 5);
+LL +     let _ = Some(5) != Some(5);
+   |
 
 error: this `map_or` can be simplified
   --> tests/ui/unnecessary_map_or.rs:15:13
@@ -34,8 +36,12 @@ LL | |     });
    |
 help: use a standard comparison instead
    |
-LL |     let _ = Some(5) == Some(5);
-   |             ~~~~~~~~~~~~~~~~~~
+LL -     let _ = Some(5).map_or(false, |n| {
+LL -         let _ = 1;
+LL -         n == 5
+LL -     });
+LL +     let _ = Some(5) == Some(5);
+   |
 
 error: this `map_or` can be simplified
   --> tests/ui/unnecessary_map_or.rs:19:13
@@ -121,8 +127,9 @@ LL |     let _ = Ok::<i32, i32>(5).map_or(false, |n| n == 5);
    |
 help: use a standard comparison instead
    |
-LL |     let _ = Ok::<i32, i32>(5) == Ok(5);
-   |             ~~~~~~~~~~~~~~~~~~~~~~~~~~
+LL -     let _ = Ok::<i32, i32>(5).map_or(false, |n| n == 5);
+LL +     let _ = Ok::<i32, i32>(5) == Ok(5);
+   |
 
 error: this `map_or` can be simplified
   --> tests/ui/unnecessary_map_or.rs:29:13
@@ -132,8 +139,9 @@ LL |     let _ = Some(5).map_or(false, |n| n == 5).then(|| 1);
    |
 help: use a standard comparison instead
    |
-LL |     let _ = (Some(5) == Some(5)).then(|| 1);
-   |             ~~~~~~~~~~~~~~~~~~~~
+LL -     let _ = Some(5).map_or(false, |n| n == 5).then(|| 1);
+LL +     let _ = (Some(5) == Some(5)).then(|| 1);
+   |
 
 error: this `map_or` can be simplified
   --> tests/ui/unnecessary_map_or.rs:30:13
@@ -167,8 +175,9 @@ LL |     let _ = !Some(5).map_or(false, |n| n == 5);
    |
 help: use a standard comparison instead
    |
-LL |     let _ = !(Some(5) == Some(5));
-   |              ~~~~~~~~~~~~~~~~~~~~
+LL -     let _ = !Some(5).map_or(false, |n| n == 5);
+LL +     let _ = !(Some(5) == Some(5));
+   |
 
 error: this `map_or` can be simplified
   --> tests/ui/unnecessary_map_or.rs:33:13
@@ -178,8 +187,9 @@ LL |     let _ = Some(5).map_or(false, |n| n == 5) || false;
    |
 help: use a standard comparison instead
    |
-LL |     let _ = (Some(5) == Some(5)) || false;
-   |             ~~~~~~~~~~~~~~~~~~~~
+LL -     let _ = Some(5).map_or(false, |n| n == 5) || false;
+LL +     let _ = (Some(5) == Some(5)) || false;
+   |
 
 error: this `map_or` can be simplified
   --> tests/ui/unnecessary_map_or.rs:34:13
@@ -189,8 +199,9 @@ LL |     let _ = Some(5).map_or(false, |n| n == 5) as usize;
    |
 help: use a standard comparison instead
    |
-LL |     let _ = (Some(5) == Some(5)) as usize;
-   |             ~~~~~~~~~~~~~~~~~~~~
+LL -     let _ = Some(5).map_or(false, |n| n == 5) as usize;
+LL +     let _ = (Some(5) == Some(5)) as usize;
+   |
 
 error: this `map_or` can be simplified
   --> tests/ui/unnecessary_map_or.rs:58:13
@@ -248,8 +259,9 @@ LL |     let _ = r.map_or(false, |x| x == 8);
    |
 help: use a standard comparison instead
    |
-LL |     let _ = r == Ok(8);
-   |             ~~~~~~~~~~
+LL -     let _ = r.map_or(false, |x| x == 8);
+LL +     let _ = r == Ok(8);
+   |
 
 error: this `map_or` can be simplified
   --> tests/ui/unnecessary_map_or.rs:90:5
diff --git a/src/tools/clippy/tests/ui/unnecessary_wraps.stderr b/src/tools/clippy/tests/ui/unnecessary_wraps.stderr
index b304d4dce6e..b06ab91dc8d 100644
--- a/src/tools/clippy/tests/ui/unnecessary_wraps.stderr
+++ b/src/tools/clippy/tests/ui/unnecessary_wraps.stderr
@@ -13,8 +13,9 @@ LL | | }
    = help: to override `-D warnings` add `#[allow(clippy::unnecessary_wraps)]`
 help: remove `Option` from the return type...
    |
-LL | fn func1(a: bool, b: bool) -> i32 {
-   |                               ~~~
+LL - fn func1(a: bool, b: bool) -> Option<i32> {
+LL + fn func1(a: bool, b: bool) -> i32 {
+   |
 help: ...and then change returning expressions
    |
 LL ~         return 42;
@@ -40,8 +41,9 @@ LL | | }
    |
 help: remove `Option` from the return type...
    |
-LL | fn func2(a: bool, b: bool) -> i32 {
-   |                               ~~~
+LL - fn func2(a: bool, b: bool) -> Option<i32> {
+LL + fn func2(a: bool, b: bool) -> i32 {
+   |
 help: ...and then change returning expressions
    |
 LL ~         return 10;
@@ -60,11 +62,13 @@ LL | | }
    |
 help: remove `Option` from the return type...
    |
-LL | fn func5() -> i32 {
-   |               ~~~
+LL - fn func5() -> Option<i32> {
+LL + fn func5() -> i32 {
+   |
 help: ...and then change returning expressions
    |
-LL |     1
+LL -     Some(1)
+LL +     1
    |
 
 error: this function's return value is unnecessarily wrapped by `Result`
@@ -78,11 +82,13 @@ LL | | }
    |
 help: remove `Result` from the return type...
    |
-LL | fn func7() -> i32 {
-   |               ~~~
+LL - fn func7() -> Result<i32, ()> {
+LL + fn func7() -> i32 {
+   |
 help: ...and then change returning expressions
    |
-LL |     1
+LL -     Ok(1)
+LL +     1
    |
 
 error: this function's return value is unnecessarily wrapped by `Option`
@@ -96,11 +102,13 @@ LL | |     }
    |
 help: remove `Option` from the return type...
    |
-LL |     fn func12() -> i32 {
-   |                    ~~~
+LL -     fn func12() -> Option<i32> {
+LL +     fn func12() -> i32 {
+   |
 help: ...and then change returning expressions
    |
-LL |         1
+LL -         Some(1)
+LL +         1
    |
 
 error: this function's return value is unnecessary
@@ -116,8 +124,9 @@ LL | | }
    |
 help: remove the return type...
    |
-LL | fn issue_6640_1(a: bool, b: bool) -> () {
-   |                                      ~~
+LL - fn issue_6640_1(a: bool, b: bool) -> Option<()> {
+LL + fn issue_6640_1(a: bool, b: bool) -> () {
+   |
 help: ...and then remove returned values
    |
 LL ~         return ;
@@ -142,8 +151,9 @@ LL | | }
    |
 help: remove the return type...
    |
-LL | fn issue_6640_2(a: bool, b: bool) -> () {
-   |                                      ~~
+LL - fn issue_6640_2(a: bool, b: bool) -> Result<(), i32> {
+LL + fn issue_6640_2(a: bool, b: bool) -> () {
+   |
 help: ...and then remove returned values
    |
 LL ~         return ;
diff --git a/src/tools/clippy/tests/ui/unnested_or_patterns.stderr b/src/tools/clippy/tests/ui/unnested_or_patterns.stderr
index bd15ef62368..4325df14304 100644
--- a/src/tools/clippy/tests/ui/unnested_or_patterns.stderr
+++ b/src/tools/clippy/tests/ui/unnested_or_patterns.stderr
@@ -8,8 +8,9 @@ LL |     if let box 0 | box 2 = Box::new(0) {}
    = help: to override `-D warnings` add `#[allow(clippy::unnested_or_patterns)]`
 help: nest the patterns
    |
-LL |     if let box (0 | 2) = Box::new(0) {}
-   |            ~~~~~~~~~~~
+LL -     if let box 0 | box 2 = Box::new(0) {}
+LL +     if let box (0 | 2) = Box::new(0) {}
+   |
 
 error: unnested or-patterns
   --> tests/ui/unnested_or_patterns.rs:17:12
@@ -19,8 +20,9 @@ LL |     if let box ((0 | 1)) | box (2 | 3) | box 4 = Box::new(0) {}
    |
 help: nest the patterns
    |
-LL |     if let box (0 | 1 | 2 | 3 | 4) = Box::new(0) {}
-   |            ~~~~~~~~~~~~~~~~~~~~~~~
+LL -     if let box ((0 | 1)) | box (2 | 3) | box 4 = Box::new(0) {}
+LL +     if let box (0 | 1 | 2 | 3 | 4) = Box::new(0) {}
+   |
 
 error: unnested or-patterns
   --> tests/ui/unnested_or_patterns.rs:19:12
@@ -30,8 +32,9 @@ LL |     if let Some(1) | C0 | Some(2) = None {}
    |
 help: nest the patterns
    |
-LL |     if let Some(1 | 2) | C0 = None {}
-   |            ~~~~~~~~~~~~~~~~
+LL -     if let Some(1) | C0 | Some(2) = None {}
+LL +     if let Some(1 | 2) | C0 = None {}
+   |
 
 error: unnested or-patterns
   --> tests/ui/unnested_or_patterns.rs:20:12
@@ -41,8 +44,9 @@ LL |     if let &mut 0 | &mut 2 = &mut 0 {}
    |
 help: nest the patterns
    |
-LL |     if let &mut (0 | 2) = &mut 0 {}
-   |            ~~~~~~~~~~~~
+LL -     if let &mut 0 | &mut 2 = &mut 0 {}
+LL +     if let &mut (0 | 2) = &mut 0 {}
+   |
 
 error: unnested or-patterns
   --> tests/ui/unnested_or_patterns.rs:21:12
@@ -52,8 +56,9 @@ LL |     if let x @ 0 | x @ 2 = 0 {}
    |
 help: nest the patterns
    |
-LL |     if let x @ (0 | 2) = 0 {}
-   |            ~~~~~~~~~~~
+LL -     if let x @ 0 | x @ 2 = 0 {}
+LL +     if let x @ (0 | 2) = 0 {}
+   |
 
 error: unnested or-patterns
   --> tests/ui/unnested_or_patterns.rs:22:12
@@ -63,8 +68,9 @@ LL |     if let (0, 1) | (0, 2) | (0, 3) = (0, 0) {}
    |
 help: nest the patterns
    |
-LL |     if let (0, 1 | 2 | 3) = (0, 0) {}
-   |            ~~~~~~~~~~~~~~
+LL -     if let (0, 1) | (0, 2) | (0, 3) = (0, 0) {}
+LL +     if let (0, 1 | 2 | 3) = (0, 0) {}
+   |
 
 error: unnested or-patterns
   --> tests/ui/unnested_or_patterns.rs:23:12
@@ -74,8 +80,9 @@ LL |     if let (1, 0) | (2, 0) | (3, 0) = (0, 0) {}
    |
 help: nest the patterns
    |
-LL |     if let (1 | 2 | 3, 0) = (0, 0) {}
-   |            ~~~~~~~~~~~~~~
+LL -     if let (1, 0) | (2, 0) | (3, 0) = (0, 0) {}
+LL +     if let (1 | 2 | 3, 0) = (0, 0) {}
+   |
 
 error: unnested or-patterns
   --> tests/ui/unnested_or_patterns.rs:24:12
@@ -85,8 +92,9 @@ LL |     if let (x, ..) | (x, 1) | (x, 2) = (0, 1) {}
    |
 help: nest the patterns
    |
-LL |     if let (x, ..) | (x, 1 | 2) = (0, 1) {}
-   |            ~~~~~~~~~~~~~~~~~~~~
+LL -     if let (x, ..) | (x, 1) | (x, 2) = (0, 1) {}
+LL +     if let (x, ..) | (x, 1 | 2) = (0, 1) {}
+   |
 
 error: unnested or-patterns
   --> tests/ui/unnested_or_patterns.rs:25:12
@@ -96,8 +104,9 @@ LL |     if let [0] | [1] = [0] {}
    |
 help: nest the patterns
    |
-LL |     if let [0 | 1] = [0] {}
-   |            ~~~~~~~
+LL -     if let [0] | [1] = [0] {}
+LL +     if let [0 | 1] = [0] {}
+   |
 
 error: unnested or-patterns
   --> tests/ui/unnested_or_patterns.rs:26:12
@@ -107,8 +116,9 @@ LL |     if let [x, 0] | [x, 1] = [0, 1] {}
    |
 help: nest the patterns
    |
-LL |     if let [x, 0 | 1] = [0, 1] {}
-   |            ~~~~~~~~~~
+LL -     if let [x, 0] | [x, 1] = [0, 1] {}
+LL +     if let [x, 0 | 1] = [0, 1] {}
+   |
 
 error: unnested or-patterns
   --> tests/ui/unnested_or_patterns.rs:27:12
@@ -118,8 +128,9 @@ LL |     if let [x, 0] | [x, 1] | [x, 2] = [0, 1] {}
    |
 help: nest the patterns
    |
-LL |     if let [x, 0 | 1 | 2] = [0, 1] {}
-   |            ~~~~~~~~~~~~~~
+LL -     if let [x, 0] | [x, 1] | [x, 2] = [0, 1] {}
+LL +     if let [x, 0 | 1 | 2] = [0, 1] {}
+   |
 
 error: unnested or-patterns
   --> tests/ui/unnested_or_patterns.rs:28:12
@@ -129,8 +140,9 @@ LL |     if let [x, ..] | [x, 1] | [x, 2] = [0, 1] {}
    |
 help: nest the patterns
    |
-LL |     if let [x, ..] | [x, 1 | 2] = [0, 1] {}
-   |            ~~~~~~~~~~~~~~~~~~~~
+LL -     if let [x, ..] | [x, 1] | [x, 2] = [0, 1] {}
+LL +     if let [x, ..] | [x, 1 | 2] = [0, 1] {}
+   |
 
 error: unnested or-patterns
   --> tests/ui/unnested_or_patterns.rs:30:12
@@ -140,8 +152,9 @@ LL |     if let TS(0, x) | TS(1, x) = TS(0, 0) {}
    |
 help: nest the patterns
    |
-LL |     if let TS(0 | 1, x) = TS(0, 0) {}
-   |            ~~~~~~~~~~~~
+LL -     if let TS(0, x) | TS(1, x) = TS(0, 0) {}
+LL +     if let TS(0 | 1, x) = TS(0, 0) {}
+   |
 
 error: unnested or-patterns
   --> tests/ui/unnested_or_patterns.rs:31:12
@@ -151,8 +164,9 @@ LL |     if let TS(1, 0) | TS(2, 0) | TS(3, 0) = TS(0, 0) {}
    |
 help: nest the patterns
    |
-LL |     if let TS(1 | 2 | 3, 0) = TS(0, 0) {}
-   |            ~~~~~~~~~~~~~~~~
+LL -     if let TS(1, 0) | TS(2, 0) | TS(3, 0) = TS(0, 0) {}
+LL +     if let TS(1 | 2 | 3, 0) = TS(0, 0) {}
+   |
 
 error: unnested or-patterns
   --> tests/ui/unnested_or_patterns.rs:32:12
@@ -162,8 +176,9 @@ LL |     if let TS(x, ..) | TS(x, 1) | TS(x, 2) = TS(0, 0) {}
    |
 help: nest the patterns
    |
-LL |     if let TS(x, ..) | TS(x, 1 | 2) = TS(0, 0) {}
-   |            ~~~~~~~~~~~~~~~~~~~~~~~~
+LL -     if let TS(x, ..) | TS(x, 1) | TS(x, 2) = TS(0, 0) {}
+LL +     if let TS(x, ..) | TS(x, 1 | 2) = TS(0, 0) {}
+   |
 
 error: unnested or-patterns
   --> tests/ui/unnested_or_patterns.rs:37:12
@@ -173,8 +188,9 @@ LL |     if let S { x: 0, y } | S { y, x: 1 } = (S { x: 0, y: 1 }) {}
    |
 help: nest the patterns
    |
-LL |     if let S { x: 0 | 1, y } = (S { x: 0, y: 1 }) {}
-   |            ~~~~~~~~~~~~~~~~~
+LL -     if let S { x: 0, y } | S { y, x: 1 } = (S { x: 0, y: 1 }) {}
+LL +     if let S { x: 0 | 1, y } = (S { x: 0, y: 1 }) {}
+   |
 
 error: unnested or-patterns
   --> tests/ui/unnested_or_patterns.rs:48:12
@@ -184,8 +200,9 @@ LL |     if let [1] | [53] = [0] {}
    |
 help: nest the patterns
    |
-LL |     if let [1 | 53] = [0] {}
-   |            ~~~~~~~~
+LL -     if let [1] | [53] = [0] {}
+LL +     if let [1 | 53] = [0] {}
+   |
 
 error: aborting due to 17 previous errors
 
diff --git a/src/tools/clippy/tests/ui/unnested_or_patterns2.stderr b/src/tools/clippy/tests/ui/unnested_or_patterns2.stderr
index 54f03937508..3d8968551b9 100644
--- a/src/tools/clippy/tests/ui/unnested_or_patterns2.stderr
+++ b/src/tools/clippy/tests/ui/unnested_or_patterns2.stderr
@@ -8,8 +8,9 @@ LL |     if let Some(Some(0)) | Some(Some(1)) = None {}
    = help: to override `-D warnings` add `#[allow(clippy::unnested_or_patterns)]`
 help: nest the patterns
    |
-LL |     if let Some(Some(0 | 1)) = None {}
-   |            ~~~~~~~~~~~~~~~~~
+LL -     if let Some(Some(0)) | Some(Some(1)) = None {}
+LL +     if let Some(Some(0 | 1)) = None {}
+   |
 
 error: unnested or-patterns
   --> tests/ui/unnested_or_patterns2.rs:13:12
@@ -19,8 +20,9 @@ LL |     if let Some(Some(0)) | Some(Some(1) | Some(2)) = None {}
    |
 help: nest the patterns
    |
-LL |     if let Some(Some(0 | 1 | 2)) = None {}
-   |            ~~~~~~~~~~~~~~~~~~~~~
+LL -     if let Some(Some(0)) | Some(Some(1) | Some(2)) = None {}
+LL +     if let Some(Some(0 | 1 | 2)) = None {}
+   |
 
 error: unnested or-patterns
   --> tests/ui/unnested_or_patterns2.rs:14:12
@@ -30,8 +32,9 @@ LL |     if let Some(Some(0 | 1) | Some(2)) | Some(Some(3) | Some(4)) = None {}
    |
 help: nest the patterns
    |
-LL |     if let Some(Some(0 | 1 | 2 | 3 | 4)) = None {}
-   |            ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+LL -     if let Some(Some(0 | 1) | Some(2)) | Some(Some(3) | Some(4)) = None {}
+LL +     if let Some(Some(0 | 1 | 2 | 3 | 4)) = None {}
+   |
 
 error: unnested or-patterns
   --> tests/ui/unnested_or_patterns2.rs:15:12
@@ -41,8 +44,9 @@ LL |     if let Some(Some(0) | Some(1 | 2)) = None {}
    |
 help: nest the patterns
    |
-LL |     if let Some(Some(0 | 1 | 2)) = None {}
-   |            ~~~~~~~~~~~~~~~~~~~~~
+LL -     if let Some(Some(0) | Some(1 | 2)) = None {}
+LL +     if let Some(Some(0 | 1 | 2)) = None {}
+   |
 
 error: unnested or-patterns
   --> tests/ui/unnested_or_patterns2.rs:16:12
@@ -52,8 +56,9 @@ LL |     if let ((0,),) | ((1,) | (2,),) = ((0,),) {}
    |
 help: nest the patterns
    |
-LL |     if let ((0 | 1 | 2,),) = ((0,),) {}
-   |            ~~~~~~~~~~~~~~~
+LL -     if let ((0,),) | ((1,) | (2,),) = ((0,),) {}
+LL +     if let ((0 | 1 | 2,),) = ((0,),) {}
+   |
 
 error: unnested or-patterns
   --> tests/ui/unnested_or_patterns2.rs:17:12
@@ -63,8 +68,9 @@ LL |     if let 0 | (1 | 2) = 0 {}
    |
 help: nest the patterns
    |
-LL |     if let 0 | 1 | 2 = 0 {}
-   |            ~~~~~~~~~
+LL -     if let 0 | (1 | 2) = 0 {}
+LL +     if let 0 | 1 | 2 = 0 {}
+   |
 
 error: unnested or-patterns
   --> tests/ui/unnested_or_patterns2.rs:18:12
@@ -74,8 +80,9 @@ LL |     if let box (0 | 1) | (box 2 | box (3 | 4)) = Box::new(0) {}
    |
 help: nest the patterns
    |
-LL |     if let box (0 | 1 | 2 | 3 | 4) = Box::new(0) {}
-   |            ~~~~~~~~~~~~~~~~~~~~~~~
+LL -     if let box (0 | 1) | (box 2 | box (3 | 4)) = Box::new(0) {}
+LL +     if let box (0 | 1 | 2 | 3 | 4) = Box::new(0) {}
+   |
 
 error: unnested or-patterns
   --> tests/ui/unnested_or_patterns2.rs:19:12
@@ -85,8 +92,9 @@ LL |     if let box box 0 | box (box 2 | box 4) = Box::new(Box::new(0)) {}
    |
 help: nest the patterns
    |
-LL |     if let box box (0 | 2 | 4) = Box::new(Box::new(0)) {}
-   |            ~~~~~~~~~~~~~~~~~~~
+LL -     if let box box 0 | box (box 2 | box 4) = Box::new(Box::new(0)) {}
+LL +     if let box box (0 | 2 | 4) = Box::new(Box::new(0)) {}
+   |
 
 error: aborting due to 8 previous errors
 
diff --git a/src/tools/clippy/tests/ui/unused_enumerate_index.stderr b/src/tools/clippy/tests/ui/unused_enumerate_index.stderr
index 6ec07dcbff0..02d65f06430 100644
--- a/src/tools/clippy/tests/ui/unused_enumerate_index.stderr
+++ b/src/tools/clippy/tests/ui/unused_enumerate_index.stderr
@@ -8,8 +8,9 @@ LL |     for (_, x) in v.iter().enumerate() {
    = help: to override `-D warnings` add `#[allow(clippy::unused_enumerate_index)]`
 help: remove the `.enumerate()` call
    |
-LL |     for x in v.iter() {
-   |         ~    ~~~~~~~~
+LL -     for (_, x) in v.iter().enumerate() {
+LL +     for x in v.iter() {
+   |
 
 error: you seem to use `.enumerate()` and immediately discard the index
   --> tests/ui/unused_enumerate_index.rs:59:19
@@ -19,8 +20,9 @@ LL |     for (_, x) in dummy.enumerate() {
    |
 help: remove the `.enumerate()` call
    |
-LL |     for x in dummy {
-   |         ~    ~~~~~
+LL -     for (_, x) in dummy.enumerate() {
+LL +     for x in dummy {
+   |
 
 error: you seem to use `.enumerate()` and immediately discard the index
   --> tests/ui/unused_enumerate_index.rs:63:39
diff --git a/src/tools/clippy/tests/ui/unused_format_specs.stderr b/src/tools/clippy/tests/ui/unused_format_specs.stderr
index df61d59130e..d3c0530ced4 100644
--- a/src/tools/clippy/tests/ui/unused_format_specs.stderr
+++ b/src/tools/clippy/tests/ui/unused_format_specs.stderr
@@ -8,8 +8,9 @@ LL |     println!("{:5}.", format_args!(""));
    = help: to override `-D warnings` add `#[allow(clippy::unused_format_specs)]`
 help: for the width to apply consider using `format!()`
    |
-LL |     println!("{:5}.", format!(""));
-   |                       ~~~~~~
+LL -     println!("{:5}.", format_args!(""));
+LL +     println!("{:5}.", format!(""));
+   |
 help: if the current behavior is intentional, remove the format specifiers
    |
 LL -     println!("{:5}.", format_args!(""));
@@ -24,8 +25,9 @@ LL |     println!("{:.3}", format_args!("abcde"));
    |
 help: for the precision to apply consider using `format!()`
    |
-LL |     println!("{:.3}", format!("abcde"));
-   |                       ~~~~~~
+LL -     println!("{:.3}", format_args!("abcde"));
+LL +     println!("{:.3}", format!("abcde"));
+   |
 help: if the current behavior is intentional, remove the format specifiers
    |
 LL -     println!("{:.3}", format_args!("abcde"));
@@ -66,8 +68,9 @@ LL |     usr_println!(true, "{:5}.", format_args!(""));
    |
 help: for the width to apply consider using `format!()`
    |
-LL |     usr_println!(true, "{:5}.", format!(""));
-   |                                 ~~~~~~
+LL -     usr_println!(true, "{:5}.", format_args!(""));
+LL +     usr_println!(true, "{:5}.", format!(""));
+   |
 help: if the current behavior is intentional, remove the format specifiers
    |
 LL -     usr_println!(true, "{:5}.", format_args!(""));
@@ -82,8 +85,9 @@ LL |     usr_println!(true, "{:.3}", format_args!("abcde"));
    |
 help: for the precision to apply consider using `format!()`
    |
-LL |     usr_println!(true, "{:.3}", format!("abcde"));
-   |                                 ~~~~~~
+LL -     usr_println!(true, "{:.3}", format_args!("abcde"));
+LL +     usr_println!(true, "{:.3}", format!("abcde"));
+   |
 help: if the current behavior is intentional, remove the format specifiers
    |
 LL -     usr_println!(true, "{:.3}", format_args!("abcde"));
diff --git a/src/tools/clippy/tests/ui/unused_result_ok.stderr b/src/tools/clippy/tests/ui/unused_result_ok.stderr
index 241e0c71261..024aafa6bbb 100644
--- a/src/tools/clippy/tests/ui/unused_result_ok.stderr
+++ b/src/tools/clippy/tests/ui/unused_result_ok.stderr
@@ -8,8 +8,9 @@ LL |     x.parse::<u32>().ok();
    = help: to override `-D warnings` add `#[allow(clippy::unused_result_ok)]`
 help: consider using `let _ =` and removing the call to `.ok()` instead
    |
-LL |     let _ = x.parse::<u32>();
-   |     ~~~~~~~~~~~~~~~~~~~~~~~~
+LL -     x.parse::<u32>().ok();
+LL +     let _ = x.parse::<u32>();
+   |
 
 error: ignoring a result with `.ok()` is misleading
   --> tests/ui/unused_result_ok.rs:18:5
@@ -19,8 +20,9 @@ LL |     x   .   parse::<i32>()   .   ok   ();
    |
 help: consider using `let _ =` and removing the call to `.ok()` instead
    |
-LL |     let _ = x   .   parse::<i32>();
-   |     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+LL -     x   .   parse::<i32>()   .   ok   ();
+LL +     let _ = x   .   parse::<i32>();
+   |
 
 error: ignoring a result with `.ok()` is misleading
   --> tests/ui/unused_result_ok.rs:34:5
@@ -30,8 +32,9 @@ LL |     v!().ok();
    |
 help: consider using `let _ =` and removing the call to `.ok()` instead
    |
-LL |     let _ = v!();
-   |     ~~~~~~~~~~~~
+LL -     v!().ok();
+LL +     let _ = v!();
+   |
 
 error: ignoring a result with `.ok()` is misleading
   --> tests/ui/unused_result_ok.rs:29:9
@@ -45,8 +48,9 @@ LL |     w!();
    = note: this error originates in the macro `w` (in Nightly builds, run with -Z macro-backtrace for more info)
 help: consider using `let _ =` and removing the call to `.ok()` instead
    |
-LL |         let _ = Ok::<(), ()>(());
-   |         ~~~~~~~~~~~~~~~~~~~~~~~~
+LL -         Ok::<(), ()>(()).ok();
+LL +         let _ = Ok::<(), ()>(());
+   |
 
 error: aborting due to 4 previous errors
 
diff --git a/src/tools/compiletest/src/common.rs b/src/tools/compiletest/src/common.rs
index 00821fc9f9d..98ab7adf5a7 100644
--- a/src/tools/compiletest/src/common.rs
+++ b/src/tools/compiletest/src/common.rs
@@ -215,8 +215,10 @@ pub struct Config {
     /// `None` then these tests will be ignored.
     pub run_clang_based_tests_with: Option<String>,
 
-    /// The directory containing the tests to run
-    pub src_base: PathBuf,
+    /// The directory containing the sources.
+    pub src_root: PathBuf,
+    /// The directory containing the test suite sources. Must be a subdirectory of `src_root`.
+    pub src_test_suite_root: PathBuf,
 
     /// The directory where programs should be built
     pub build_base: PathBuf,
@@ -224,7 +226,9 @@ pub struct Config {
     /// The directory containing the compiler sysroot
     pub sysroot_base: PathBuf,
 
-    /// The name of the stage being built (stage1, etc)
+    /// The number of the stage under test.
+    pub stage: u32,
+    /// The id of the stage under test (stage1-xxx, etc).
     pub stage_id: String,
 
     /// The test mode, e.g. ui or debuginfo.
@@ -515,6 +519,7 @@ pub struct TargetCfgs {
     pub all_abis: HashSet<String>,
     pub all_families: HashSet<String>,
     pub all_pointer_widths: HashSet<String>,
+    pub all_rustc_abis: HashSet<String>,
 }
 
 impl TargetCfgs {
@@ -534,6 +539,9 @@ impl TargetCfgs {
         let mut all_abis = HashSet::new();
         let mut all_families = HashSet::new();
         let mut all_pointer_widths = HashSet::new();
+        // NOTE: for distinction between `abi` and `rustc_abi`, see comment on
+        // `TargetCfg::rustc_abi`.
+        let mut all_rustc_abis = HashSet::new();
 
         // If current target is not included in the `--print=all-target-specs-json` output,
         // we check whether it is a custom target from the user or a synthetic target from bootstrap.
@@ -574,7 +582,9 @@ impl TargetCfgs {
                 all_families.insert(family.clone());
             }
             all_pointer_widths.insert(format!("{}bit", cfg.pointer_width));
-
+            if let Some(rustc_abi) = &cfg.rustc_abi {
+                all_rustc_abis.insert(rustc_abi.clone());
+            }
             all_targets.insert(target.clone());
         }
 
@@ -588,6 +598,7 @@ impl TargetCfgs {
             all_abis,
             all_families,
             all_pointer_widths,
+            all_rustc_abis,
         }
     }
 
@@ -674,6 +685,10 @@ pub struct TargetCfg {
     pub(crate) xray: bool,
     #[serde(default = "default_reloc_model")]
     pub(crate) relocation_model: String,
+    // NOTE: `rustc_abi` should not be confused with `abi`. `rustc_abi` was introduced in #137037 to
+    // make SSE2 *required* by the ABI (kind of a hack to make a target feature *required* via the
+    // target spec).
+    pub(crate) rustc_abi: Option<String>,
 
     // Not present in target cfg json output, additional derived information.
     #[serde(skip)]
diff --git a/src/tools/compiletest/src/directive-list.rs b/src/tools/compiletest/src/directive-list.rs
index a7ac875d0a3..8c909bcb195 100644
--- a/src/tools/compiletest/src/directive-list.rs
+++ b/src/tools/compiletest/src/directive-list.rs
@@ -87,6 +87,7 @@ const KNOWN_DIRECTIVE_NAMES: &[&str] = &[
     "ignore-remote",
     "ignore-riscv64",
     "ignore-rustc-debug-assertions",
+    "ignore-rustc_abi-x86-sse2",
     "ignore-s390x",
     "ignore-sgx",
     "ignore-sparc64",
@@ -198,6 +199,7 @@ const KNOWN_DIRECTIVE_NAMES: &[&str] = &[
     "only-nvptx64",
     "only-powerpc",
     "only-riscv64",
+    "only-rustc_abi-x86-sse2",
     "only-s390x",
     "only-sparc",
     "only-sparc64",
diff --git a/src/tools/compiletest/src/header.rs b/src/tools/compiletest/src/header.rs
index 452a2e9a9d5..3bdf37a1f29 100644
--- a/src/tools/compiletest/src/header.rs
+++ b/src/tools/compiletest/src/header.rs
@@ -1026,19 +1026,6 @@ impl Config {
         }
     }
 
-    pub fn find_rust_src_root(&self) -> Option<PathBuf> {
-        let mut path = self.src_base.clone();
-        let path_postfix = Path::new("src/etc/lldb_batchmode.py");
-
-        while path.pop() {
-            if path.join(&path_postfix).is_file() {
-                return Some(path);
-            }
-        }
-
-        None
-    }
-
     fn parse_edition(&self, line: &str) -> Option<String> {
         self.parse_name_value_directive(line, "edition")
     }
@@ -1098,7 +1085,7 @@ fn expand_variables(mut value: String, config: &Config) -> String {
     }
 
     if value.contains(SRC_BASE) {
-        value = value.replace(SRC_BASE, &config.src_base.to_string_lossy());
+        value = value.replace(SRC_BASE, &config.src_test_suite_root.to_str().unwrap());
     }
 
     if value.contains(BUILD_BASE) {
diff --git a/src/tools/compiletest/src/header/cfg.rs b/src/tools/compiletest/src/header/cfg.rs
index 6e5ced17c20..72a3b9d85c8 100644
--- a/src/tools/compiletest/src/header/cfg.rs
+++ b/src/tools/compiletest/src/header/cfg.rs
@@ -192,7 +192,7 @@ fn parse_cfg_name_directive<'a>(
         message: "on big-endian targets",
     }
     condition! {
-        name: config.stage_id.split('-').next().unwrap(),
+        name: format!("stage{}", config.stage).as_str(),
         allowed_names: &["stage0", "stage1", "stage2"],
         message: "when the bootstrapping stage is {name}",
     }
@@ -234,6 +234,14 @@ fn parse_cfg_name_directive<'a>(
         allowed_names: ["coverage-map", "coverage-run"],
         message: "when the test mode is {name}",
     }
+    condition! {
+        name: target_cfg.rustc_abi.as_ref().map(|abi| format!("rustc_abi-{abi}")).unwrap_or_default(),
+        allowed_names: ContainsPrefixed {
+            prefix: "rustc_abi-",
+            inner: target_cfgs.all_rustc_abis.clone(),
+        },
+        message: "when the target `rustc_abi` is {name}",
+    }
 
     condition! {
         name: "dist",
diff --git a/src/tools/compiletest/src/header/tests.rs b/src/tools/compiletest/src/header/tests.rs
index 023658a3dd4..84c1cb0963e 100644
--- a/src/tools/compiletest/src/header/tests.rs
+++ b/src/tools/compiletest/src/header/tests.rs
@@ -72,6 +72,7 @@ struct ConfigBuilder {
     channel: Option<String>,
     host: Option<String>,
     target: Option<String>,
+    stage: Option<u32>,
     stage_id: Option<String>,
     llvm_version: Option<String>,
     git_hash: bool,
@@ -102,6 +103,11 @@ impl ConfigBuilder {
         self
     }
 
+    fn stage(&mut self, n: u32) -> &mut Self {
+        self.stage = Some(n);
+        self
+    }
+
     fn stage_id(&mut self, s: &str) -> &mut Self {
         self.stage_id = Some(s.to_owned());
         self
@@ -147,7 +153,8 @@ impl ConfigBuilder {
             "--run-lib-path=",
             "--python=",
             "--jsondocck-path=",
-            "--src-base=",
+            "--src-root=",
+            "--src-test-suite-root=",
             "--build-base=",
             "--sysroot-base=",
             "--cc=c",
@@ -156,6 +163,8 @@ impl ConfigBuilder {
             "--cxxflags=",
             "--llvm-components=",
             "--android-cross-path=",
+            "--stage",
+            &self.stage.unwrap_or(2).to_string(),
             "--stage-id",
             self.stage_id.as_deref().unwrap_or("stage2-x86_64-unknown-linux-gnu"),
             "--channel",
@@ -388,7 +397,7 @@ fn std_debug_assertions() {
 
 #[test]
 fn stage() {
-    let config: Config = cfg().stage_id("stage1-x86_64-unknown-linux-gnu").build();
+    let config: Config = cfg().stage(1).stage_id("stage1-x86_64-unknown-linux-gnu").build();
 
     assert!(check_ignore(&config, "//@ ignore-stage1"));
     assert!(!check_ignore(&config, "//@ ignore-stage2"));
@@ -457,7 +466,10 @@ fn profiler_runtime() {
 #[test]
 fn asm_support() {
     let asms = [
+        #[cfg(bootstrap)]
         ("avr-unknown-gnu-atmega328", false),
+        #[cfg(not(bootstrap))]
+        ("avr-none", false),
         ("i686-unknown-netbsd", true),
         ("riscv32gc-unknown-linux-gnu", true),
         ("riscv64imac-unknown-none-elf", true),
@@ -881,3 +893,15 @@ fn test_needs_target_has_atomic() {
     assert!(!check_ignore(&config, "//@ needs-target-has-atomic: 8, ptr"));
     assert!(check_ignore(&config, "//@ needs-target-has-atomic: 8, ptr, 128"));
 }
+
+#[test]
+fn test_rustc_abi() {
+    let config = cfg().target("i686-unknown-linux-gnu").build();
+    assert_eq!(config.target_cfg().rustc_abi, Some("x86-sse2".to_string()));
+    assert!(check_ignore(&config, "//@ ignore-rustc_abi-x86-sse2"));
+    assert!(!check_ignore(&config, "//@ only-rustc_abi-x86-sse2"));
+    let config = cfg().target("x86_64-unknown-linux-gnu").build();
+    assert_eq!(config.target_cfg().rustc_abi, None);
+    assert!(!check_ignore(&config, "//@ ignore-rustc_abi-x86-sse2"));
+    assert!(check_ignore(&config, "//@ only-rustc_abi-x86-sse2"));
+}
diff --git a/src/tools/compiletest/src/lib.rs b/src/tools/compiletest/src/lib.rs
index 27a046ba5bc..97982170138 100644
--- a/src/tools/compiletest/src/lib.rs
+++ b/src/tools/compiletest/src/lib.rs
@@ -61,9 +61,11 @@ pub fn parse_config(args: Vec<String>) -> Config {
         .optopt("", "jsondoclint-path", "path to jsondoclint to use for doc tests", "PATH")
         .optopt("", "run-clang-based-tests-with", "path to Clang executable", "PATH")
         .optopt("", "llvm-filecheck", "path to LLVM's FileCheck binary", "DIR")
-        .reqopt("", "src-base", "directory to scan for test files", "PATH")
+        .reqopt("", "src-root", "directory containing sources", "PATH")
+        .reqopt("", "src-test-suite-root", "directory containing test suite sources", "PATH")
         .reqopt("", "build-base", "directory to deposit test outputs", "PATH")
         .reqopt("", "sysroot-base", "directory containing the compiler sysroot", "PATH")
+        .reqopt("", "stage", "stage number under test", "N")
         .reqopt("", "stage-id", "the target-stage identifier", "stageN-TARGET")
         .reqopt(
             "",
@@ -242,7 +244,6 @@ pub fn parse_config(args: Vec<String>) -> Config {
             || header::extract_llvm_version_from_binary(&matches.opt_str("llvm-filecheck")?),
         );
 
-    let src_base = opt_path(matches, "src-base");
     let run_ignored = matches.opt_present("ignored");
     let with_rustc_debug_assertions = matches.opt_present("with-rustc-debug-assertions");
     let with_std_debug_assertions = matches.opt_present("with-std-debug-assertions");
@@ -294,6 +295,20 @@ pub fn parse_config(args: Vec<String>) -> Config {
         panic!("`--nocapture` is deprecated; please use `--no-capture`");
     }
 
+    let stage = match matches.opt_str("stage") {
+        Some(stage) => stage.parse::<u32>().expect("expected `--stage` to be an unsigned integer"),
+        None => panic!("`--stage` is required"),
+    };
+
+    let src_root = opt_path(matches, "src-root");
+    let src_test_suite_root = opt_path(matches, "src-test-suite-root");
+    assert!(
+        src_test_suite_root.starts_with(&src_root),
+        "`src-root` must be a parent of `src-test-suite-root`: `src-root`=`{}`, `src-test-suite-root` = `{}`",
+        src_root.display(),
+        src_test_suite_root.display()
+    );
+
     Config {
         bless: matches.opt_present("bless"),
         compile_lib_path: make_absolute(opt_path(matches, "compile-lib-path")),
@@ -308,10 +323,16 @@ pub fn parse_config(args: Vec<String>) -> Config {
         run_clang_based_tests_with: matches.opt_str("run-clang-based-tests-with"),
         llvm_filecheck: matches.opt_str("llvm-filecheck").map(PathBuf::from),
         llvm_bin_dir: matches.opt_str("llvm-bin-dir").map(PathBuf::from),
-        src_base,
+
+        src_root,
+        src_test_suite_root,
+
         build_base: opt_path(matches, "build-base"),
         sysroot_base: opt_path(matches, "sysroot-base"),
+
+        stage,
         stage_id: matches.opt_str("stage-id").unwrap(),
+
         mode,
         suite: matches.opt_str("suite").unwrap(),
         debugger: matches.opt_str("debugger").map(|debugger| {
@@ -413,8 +434,12 @@ pub fn log_config(config: &Config) {
     logv(c, format!("rustc_path: {:?}", config.rustc_path.display()));
     logv(c, format!("cargo_path: {:?}", config.cargo_path));
     logv(c, format!("rustdoc_path: {:?}", config.rustdoc_path));
-    logv(c, format!("src_base: {:?}", config.src_base.display()));
+
+    logv(c, format!("src_root: {}", config.src_root.display()));
+    logv(c, format!("src_test_suite_root: {}", config.src_test_suite_root.display()));
+
     logv(c, format!("build_base: {:?}", config.build_base.display()));
+    logv(c, format!("stage: {}", config.stage));
     logv(c, format!("stage_id: {}", config.stage_id));
     logv(c, format!("mode: {}", config.mode));
     logv(c, format!("run_ignored: {}", config.run_ignored));
@@ -610,20 +635,29 @@ struct TestCollector {
 /// regardless of whether any filters/tests were specified on the command-line,
 /// because filtering is handled later by libtest.
 pub fn collect_and_make_tests(config: Arc<Config>) -> Vec<test::TestDescAndFn> {
-    debug!("making tests from {:?}", config.src_base.display());
+    debug!("making tests from {}", config.src_test_suite_root.display());
     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 modified_tests =
+        modified_tests(&config, &config.src_test_suite_root).unwrap_or_else(|err| {
+            panic!(
+                "modified_tests got error from dir: {}, error: {}",
+                config.src_test_suite_root.display(),
+                err
+            )
+        });
     let cache = HeadersCache::load(&config);
 
     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, Path::new("")).unwrap_or_else(
-        |reason| panic!("Could not read tests from {}: {reason}", cx.config.src_base.display()),
-    );
+    collect_tests_from_dir(&cx, &mut collector, &cx.config.src_test_suite_root, Path::new(""))
+        .unwrap_or_else(|reason| {
+            panic!(
+                "Could not read tests from {}: {reason}",
+                cx.config.src_test_suite_root.display()
+            )
+        });
 
     let TestCollector { tests, found_path_stems, poisoned } = collector;
 
@@ -645,7 +679,7 @@ pub fn collect_and_make_tests(config: Arc<Config>) -> Vec<test::TestDescAndFn> {
 /// 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");
+    let src_root = &config.src_root;
 
     let mut stamp = Stamp::from_path(&config.rustc_path);
 
@@ -660,17 +694,17 @@ fn common_inputs_stamp(config: &Config) -> Stamp {
         "src/etc/lldb_providers.py",
     ];
     for file in &pretty_printer_files {
-        let path = rust_src_dir.join(file);
+        let path = src_root.join(file);
         stamp.add_path(&path);
     }
 
-    stamp.add_dir(&rust_src_dir.join("src/etc/natvis"));
+    stamp.add_dir(&src_root.join("src/etc/natvis"));
 
     stamp.add_dir(&config.run_lib_path);
 
     if let Some(ref rustdoc_path) = config.rustdoc_path {
         stamp.add_path(&rustdoc_path);
-        stamp.add_path(&rust_src_dir.join("src/etc/htmldocck.py"));
+        stamp.add_path(&src_root.join("src/etc/htmldocck.py"));
     }
 
     // Re-run coverage tests if the `coverage-dump` tool was modified,
@@ -679,10 +713,10 @@ fn common_inputs_stamp(config: &Config) -> Stamp {
         stamp.add_path(coverage_dump_path)
     }
 
-    stamp.add_dir(&rust_src_dir.join("src/tools/run-make-support"));
+    stamp.add_dir(&src_root.join("src/tools/run-make-support"));
 
     // Compiletest itself.
-    stamp.add_dir(&rust_src_dir.join("src/tools/compiletest/"));
+    stamp.add_dir(&src_root.join("src/tools/compiletest"));
 
     stamp
 }
@@ -923,10 +957,7 @@ fn files_related_to_test(
     }
 
     // `minicore.rs` test auxiliary: we need to make sure tests get rerun if this changes.
-    //
-    // FIXME(jieyouxu): untangle these paths, we should provide both a path to root `tests/` or
-    // `tests/auxiliary/` and the test suite in question. `src_base` is also a terrible name.
-    related.push(config.src_base.parent().unwrap().join("auxiliary").join("minicore.rs"));
+    related.push(config.src_root.join("tests").join("auxiliary").join("minicore.rs"));
 
     related
 }
@@ -1016,10 +1047,8 @@ fn make_test_name(
     testpaths: &TestPaths,
     revision: Option<&str>,
 ) -> test::TestName {
-    // Print the name of the file, relative to the repository root.
-    // `src_base` looks like `/path/to/rust/tests/ui`
-    let root_directory = config.src_base.parent().unwrap().parent().unwrap();
-    let path = testpaths.file.strip_prefix(root_directory).unwrap();
+    // Print the name of the file, relative to the sources root.
+    let path = testpaths.file.strip_prefix(&config.src_root).unwrap();
     let debugger = match config.debugger {
         Some(d) => format!("-{}", d),
         None => String::new(),
diff --git a/src/tools/compiletest/src/runtest.rs b/src/tools/compiletest/src/runtest.rs
index 0e2da2b02ca..536e19bc493 100644
--- a/src/tools/compiletest/src/runtest.rs
+++ b/src/tools/compiletest/src/runtest.rs
@@ -1365,7 +1365,7 @@ impl<'test> TestCx<'test> {
         //
         // Note: avoid adding a subdirectory of an already filtered directory here, otherwise the
         // same slice of text will be double counted and the truncation might not happen.
-        add_path(&self.config.src_base);
+        add_path(&self.config.src_test_suite_root);
         add_path(&self.config.build_base);
 
         read2_abbreviated(child, &filter_paths_from_len).expect("failed to read output")
@@ -1471,7 +1471,7 @@ impl<'test> TestCx<'test> {
         // Similarly, vendored sources shouldn't be shown when running from a dist tarball.
         rustc.arg("-Z").arg(format!(
             "ignore-directory-in-diagnostics-source-blocks={}",
-            self.config.find_rust_src_root().unwrap().join("vendor").display(),
+            self.config.src_root.join("vendor").to_str().unwrap(),
         ));
 
         // Optionally prevent default --sysroot if specified in test compile-flags.
@@ -1632,7 +1632,7 @@ impl<'test> TestCx<'test> {
         if self.props.remap_src_base {
             rustc.arg(format!(
                 "--remap-path-prefix={}={}",
-                self.config.src_base.display(),
+                self.config.src_test_suite_root.to_str().unwrap(),
                 FAKE_SRC_BASE,
             ));
         }
diff --git a/src/tools/compiletest/src/runtest/debuginfo.rs b/src/tools/compiletest/src/runtest/debuginfo.rs
index b236b067569..170b8a80996 100644
--- a/src/tools/compiletest/src/runtest/debuginfo.rs
+++ b/src/tools/compiletest/src/runtest/debuginfo.rs
@@ -257,11 +257,8 @@ impl TestCx<'_> {
                 println!("Adb process is already finished.");
             }
         } else {
-            let rust_src_root =
-                self.config.find_rust_src_root().expect("Could not find Rust source root");
-            let rust_pp_module_rel_path = Path::new("./src/etc");
-            let rust_pp_module_abs_path =
-                rust_src_root.join(rust_pp_module_rel_path).to_str().unwrap().to_owned();
+            let rust_pp_module_abs_path = self.config.src_root.join("src").join("etc");
+            let rust_pp_module_abs_path = rust_pp_module_abs_path.to_str().unwrap();
             // write debugger script
             let mut script_str = String::with_capacity(2048);
             script_str.push_str(&format!("set charset {}\n", Self::charset()));
@@ -338,7 +335,7 @@ impl TestCx<'_> {
             let pythonpath = if let Ok(pp) = std::env::var("PYTHONPATH") {
                 format!("{pp}:{rust_pp_module_abs_path}")
             } else {
-                rust_pp_module_abs_path
+                rust_pp_module_abs_path.to_string()
             };
             gdb.args(debugger_opts).env("PYTHONPATH", pythonpath);
 
@@ -407,11 +404,8 @@ impl TestCx<'_> {
         // Make LLDB emit its version, so we have it documented in the test output
         script_str.push_str("version\n");
 
-        // Switch LLDB into "Rust mode"
-        let rust_src_root =
-            self.config.find_rust_src_root().expect("Could not find Rust source root");
-        let rust_pp_module_rel_path = Path::new("./src/etc");
-        let rust_pp_module_abs_path = rust_src_root.join(rust_pp_module_rel_path);
+        // Switch LLDB into "Rust mode".
+        let rust_pp_module_abs_path = self.config.src_root.join("src/etc");
 
         script_str.push_str(&format!(
             "command script import {}/lldb_lookup.py\n",
@@ -445,7 +439,7 @@ impl TestCx<'_> {
         let debugger_script = self.make_out_name("debugger.script");
 
         // Let LLDB execute the script via lldb_batchmode.py
-        let debugger_run_result = self.run_lldb(&exe_file, &debugger_script, &rust_src_root);
+        let debugger_run_result = self.run_lldb(&exe_file, &debugger_script);
 
         if !debugger_run_result.status.success() {
             self.fatal_proc_rec("Error while running LLDB", &debugger_run_result);
@@ -456,18 +450,13 @@ impl TestCx<'_> {
         }
     }
 
-    fn run_lldb(
-        &self,
-        test_executable: &Path,
-        debugger_script: &Path,
-        rust_src_root: &Path,
-    ) -> ProcRes {
+    fn run_lldb(&self, test_executable: &Path, debugger_script: &Path) -> ProcRes {
         // Prepare the lldb_batchmode which executes the debugger script
-        let lldb_script_path = rust_src_root.join("src/etc/lldb_batchmode.py");
+        let lldb_script_path = self.config.src_root.join("src/etc/lldb_batchmode.py");
         let pythonpath = if let Ok(pp) = std::env::var("PYTHONPATH") {
             format!("{pp}:{}", self.config.lldb_python_dir.as_ref().unwrap())
         } else {
-            self.config.lldb_python_dir.as_ref().unwrap().to_string()
+            self.config.lldb_python_dir.clone().unwrap()
         };
         self.run_command_to_procres(
             Command::new(&self.config.python)
diff --git a/src/tools/compiletest/src/runtest/js_doc.rs b/src/tools/compiletest/src/runtest/js_doc.rs
index a83bcd70c87..d630affbec1 100644
--- a/src/tools/compiletest/src/runtest/js_doc.rs
+++ b/src/tools/compiletest/src/runtest/js_doc.rs
@@ -9,12 +9,11 @@ impl TestCx<'_> {
 
             self.document(&out_dir, &self.testpaths);
 
-            let root = self.config.find_rust_src_root().unwrap();
             let file_stem =
                 self.testpaths.file.file_stem().and_then(|f| f.to_str()).expect("no file stem");
             let res = self.run_command_to_procres(
                 Command::new(&nodejs)
-                    .arg(root.join("src/tools/rustdoc-js/tester.js"))
+                    .arg(self.config.src_root.join("src/tools/rustdoc-js/tester.js"))
                     .arg("--doc-folder")
                     .arg(out_dir)
                     .arg("--crate-name")
diff --git a/src/tools/compiletest/src/runtest/run_make.rs b/src/tools/compiletest/src/runtest/run_make.rs
index 7ef16e4a966..74e6af36ea1 100644
--- a/src/tools/compiletest/src/runtest/run_make.rs
+++ b/src/tools/compiletest/src/runtest/run_make.rs
@@ -21,8 +21,6 @@ impl TestCx<'_> {
 
     fn run_rmake_legacy_test(&self) {
         let cwd = env::current_dir().unwrap();
-        let src_root = self.config.src_base.parent().unwrap().parent().unwrap();
-        let src_root = cwd.join(&src_root);
 
         // FIXME(Zalathar): This should probably be `output_base_dir` to avoid
         // an unnecessary extra subdirectory, but since legacy Makefile tests
@@ -51,7 +49,7 @@ impl TestCx<'_> {
             .stderr(Stdio::piped())
             .env("TARGET", &self.config.target)
             .env("PYTHON", &self.config.python)
-            .env("S", src_root)
+            .env("S", &self.config.src_root)
             .env("RUST_BUILD_STAGE", &self.config.stage_id)
             .env("RUSTC", cwd.join(&self.config.rustc_path))
             .env("TMPDIR", &tmpdir)
@@ -181,28 +179,10 @@ impl TestCx<'_> {
         //    library.
         // 2. We need to run the recipe binary.
 
-        // So we assume the rust-lang/rust project setup looks like the following (our `.` is the
-        // top-level directory, irrelevant entries to our purposes omitted):
-        //
-        // ```
-        // .                               // <- `source_root`
-        // ├── build/                      // <- `build_root`
-        // ├── compiler/
-        // ├── library/
-        // ├── src/
-        // │  └── tools/
-        // │     └── run_make_support/
-        // └── tests
-        //    └── run-make/
-        // ```
-
-        // `source_root` is the top-level directory containing the rust-lang/rust checkout.
-        let source_root =
-            self.config.find_rust_src_root().expect("could not determine rust source root");
         // `self.config.build_base` is actually the build base folder + "test" + test suite name, it
         // looks like `build/<host_triple>/test/run-make`. But we want `build/<host_triple>/`. Note
         // that the `build` directory does not need to be called `build`, nor does it need to be
-        // under `source_root`, so we must compute it based off of `self.config.build_base`.
+        // under `src_root`, so we must compute it based off of `self.config.build_base`.
         let build_root =
             self.config.build_base.parent().and_then(Path::parent).unwrap().to_path_buf();
 
@@ -239,30 +219,6 @@ impl TestCx<'_> {
             }
         }
 
-        // `self.config.stage_id` looks like `stage1-<target_triple>`, but we only want
-        // the `stage1` part as that is what the output directories of bootstrap are prefixed with.
-        // Note that this *assumes* build layout from bootstrap is produced as:
-        //
-        // ```
-        // build/<target_triple>/          // <- this is `build_root`
-        // ├── stage0
-        // ├── stage0-bootstrap-tools
-        // ├── stage0-codegen
-        // ├── stage0-rustc
-        // ├── stage0-std
-        // ├── stage0-sysroot
-        // ├── stage0-tools
-        // ├── stage0-tools-bin
-        // ├── stage1
-        // ├── stage1-std
-        // ├── stage1-tools
-        // ├── stage1-tools-bin
-        // └── test
-        // ```
-        // FIXME(jieyouxu): improve the communication between bootstrap and compiletest here so
-        // we don't have to hack out a `stageN`.
-        let stage = self.config.stage_id.split('-').next().unwrap();
-
         // In order to link in the support library as a rlib when compiling recipes, we need three
         // paths:
         // 1. Path of the built support library rlib itself.
@@ -284,10 +240,12 @@ impl TestCx<'_> {
         // support lib and its deps are organized, can't we copy them to the tools-bin dir as
         // well?), but this seems to work for now.
 
-        let stage_tools_bin = build_root.join(format!("{stage}-tools-bin"));
+        let stage_number = self.config.stage;
+
+        let stage_tools_bin = build_root.join(format!("stage{stage_number}-tools-bin"));
         let support_lib_path = stage_tools_bin.join("librun_make_support.rlib");
 
-        let stage_tools = build_root.join(format!("{stage}-tools"));
+        let stage_tools = build_root.join(format!("stage{stage_number}-tools"));
         let support_lib_deps = stage_tools.join(&self.config.host).join("release").join("deps");
         let support_lib_deps_deps = stage_tools.join("release").join("deps");
 
@@ -368,7 +326,7 @@ impl TestCx<'_> {
         // provided through env vars.
 
         // Compute stage-specific standard library paths.
-        let stage_std_path = build_root.join(&stage).join("lib");
+        let stage_std_path = build_root.join(format!("stage{stage_number}")).join("lib");
 
         // Compute dynamic library search paths for recipes.
         let recipe_dylib_search_paths = {
@@ -411,10 +369,9 @@ impl TestCx<'_> {
             .env("TARGET", &self.config.target)
             // Some tests unfortunately still need Python, so provide path to a Python interpreter.
             .env("PYTHON", &self.config.python)
-            // Provide path to checkout root. This is the top-level directory containing
-            // rust-lang/rust checkout.
-            .env("SOURCE_ROOT", &source_root)
-            // Path to the build directory. This is usually the same as `source_root.join("build").join("host")`.
+            // Provide path to sources root.
+            .env("SOURCE_ROOT", &self.config.src_root)
+            // Path to the host build directory.
             .env("BUILD_ROOT", &build_root)
             // Provide path to stage-corresponding rustc.
             .env("RUSTC", &self.config.rustc_path)
@@ -430,11 +387,11 @@ impl TestCx<'_> {
             .env("LLVM_COMPONENTS", &self.config.llvm_components);
 
         if let Some(ref cargo) = self.config.cargo_path {
-            cmd.env("CARGO", source_root.join(cargo));
+            cmd.env("CARGO", cargo);
         }
 
         if let Some(ref rustdoc) = self.config.rustdoc_path {
-            cmd.env("RUSTDOC", source_root.join(rustdoc));
+            cmd.env("RUSTDOC", rustdoc);
         }
 
         if let Some(ref node) = self.config.nodejs {
diff --git a/src/tools/compiletest/src/runtest/rustdoc.rs b/src/tools/compiletest/src/runtest/rustdoc.rs
index 3f33862d2cf..2583ae96a67 100644
--- a/src/tools/compiletest/src/runtest/rustdoc.rs
+++ b/src/tools/compiletest/src/runtest/rustdoc.rs
@@ -17,9 +17,10 @@ impl TestCx<'_> {
         if self.props.check_test_line_numbers_match {
             self.check_rustdoc_test_option(proc_res);
         } else {
-            let root = self.config.find_rust_src_root().unwrap();
             let mut cmd = Command::new(&self.config.python);
-            cmd.arg(root.join("src/etc/htmldocck.py")).arg(&out_dir).arg(&self.testpaths.file);
+            cmd.arg(self.config.src_root.join("src/etc/htmldocck.py"))
+                .arg(&out_dir)
+                .arg(&self.testpaths.file);
             if self.config.bless {
                 cmd.arg("--bless");
             }
diff --git a/src/tools/enzyme b/src/tools/enzyme
-Subproject 0e5fa4a3d475f4dece489c9e06b11164f83789f
+Subproject 5004a8f6f5d8468b64fae457afb7d96e1784c78
diff --git a/src/tools/error_index_generator/Cargo.toml b/src/tools/error_index_generator/Cargo.toml
index f4dac6e947e..54fe7f6eb5a 100644
--- a/src/tools/error_index_generator/Cargo.toml
+++ b/src/tools/error_index_generator/Cargo.toml
@@ -2,6 +2,7 @@
 name = "error_index_generator"
 version = "0.0.0"
 edition = "2021"
+workspace = "../rustbook"
 
 [dependencies]
 mdbook = { version = "0.4", default-features = false, features = ["search"] }
diff --git a/src/tools/generate-copyright/src/cargo_metadata.rs b/src/tools/generate-copyright/src/cargo_metadata.rs
index 51e353e9b22..b717bd53eb1 100644
--- a/src/tools/generate-copyright/src/cargo_metadata.rs
+++ b/src/tools/generate-copyright/src/cargo_metadata.rs
@@ -11,10 +11,6 @@ pub enum Error {
     Io(#[from] std::io::Error),
     #[error("Failed get output from cargo-metadata: {0:?}")]
     GettingMetadata(#[from] cargo_metadata::Error),
-    #[error("Failed to run cargo vendor: {0:?}")]
-    LaunchingVendor(std::io::Error),
-    #[error("Failed to complete cargo vendor")]
-    RunningVendor,
     #[error("Bad path {0:?} whilst scraping files")]
     Scraping(PathBuf),
 }
@@ -43,25 +39,19 @@ pub struct PackageMetadata {
     pub is_in_libstd: Option<bool>,
 }
 
-/// Use `cargo metadata` and `cargo vendor` to get a list of dependencies and their license data.
+/// Use `cargo metadata` to get a list of dependencies and their license data. License files will
+/// also be pulled from the vendor path (generated by bootstrap).
 ///
-/// This will involve running `cargo vendor` into `vendor_path` so we can
-/// grab the license files.
-///
-/// Any dependency with a path beginning with `root_path` is ignored, as we
-/// assume `reuse` has covered it already.
+/// Any dependency with a path beginning with `root_path` is ignored, as we assume `reuse` has
+/// covered it already.
 pub fn get_metadata_and_notices(
     cargo: &Path,
     vendor_path: &Path,
     root_path: &Path,
-    manifest_paths: &[&Path],
+    manifest_paths: &[PathBuf],
 ) -> Result<BTreeMap<Package, PackageMetadata>, Error> {
     let mut output = get_metadata(cargo, root_path, manifest_paths)?;
 
-    // Now do a cargo-vendor and grab everything
-    println!("Vendoring deps into {}...", vendor_path.display());
-    run_cargo_vendor(cargo, &vendor_path, manifest_paths)?;
-
     // Now for each dependency we found, go and grab any important looking files
     for (package, metadata) in output.iter_mut() {
         load_important_files(package, metadata, &vendor_path)?;
@@ -77,7 +67,7 @@ pub fn get_metadata_and_notices(
 pub fn get_metadata(
     cargo: &Path,
     root_path: &Path,
-    manifest_paths: &[&Path],
+    manifest_paths: &[PathBuf],
 ) -> Result<BTreeMap<Package, PackageMetadata>, Error> {
     let mut output = BTreeMap::new();
     // Look at the metadata for each manifest
@@ -113,28 +103,6 @@ pub fn get_metadata(
     Ok(output)
 }
 
-/// Run cargo-vendor, fetching into the given dir
-fn run_cargo_vendor(cargo: &Path, dest: &Path, manifest_paths: &[&Path]) -> Result<(), Error> {
-    let mut vendor_command = std::process::Command::new(cargo);
-    vendor_command.env("RUSTC_BOOTSTRAP", "1");
-    vendor_command.arg("vendor");
-    vendor_command.arg("--quiet");
-    vendor_command.arg("--versioned-dirs");
-    for manifest_path in manifest_paths {
-        vendor_command.arg("-s");
-        vendor_command.arg(manifest_path);
-    }
-    vendor_command.arg(dest);
-
-    let vendor_status = vendor_command.status().map_err(Error::LaunchingVendor)?;
-
-    if !vendor_status.success() {
-        return Err(Error::RunningVendor);
-    }
-
-    Ok(())
-}
-
 /// Add important files off disk into this dependency.
 ///
 /// Maybe one-day Cargo.toml will contain enough information that we don't need
diff --git a/src/tools/generate-copyright/src/main.rs b/src/tools/generate-copyright/src/main.rs
index 7a014989e68..79e90d88f44 100644
--- a/src/tools/generate-copyright/src/main.rs
+++ b/src/tools/generate-copyright/src/main.rs
@@ -17,29 +17,36 @@ mod cargo_metadata;
 fn main() -> Result<(), Error> {
     let dest_file = env_path("DEST")?;
     let libstd_dest_file = env_path("DEST_LIBSTD")?;
-    let out_dir = env_path("OUT_DIR")?;
+    let src_dir = env_path("SRC_DIR")?;
+    let vendor_dir = env_path("VENDOR_DIR")?;
     let cargo = env_path("CARGO")?;
     let license_metadata = env_path("LICENSE_METADATA")?;
 
-    let root_path = std::path::absolute(".")?;
+    let cargo_manifests = env_string("CARGO_MANIFESTS")?
+        .split(",")
+        .map(|manifest| manifest.into())
+        .collect::<Vec<PathBuf>>();
+    let library_manifests = cargo_manifests
+        .iter()
+        .filter(|path| {
+            if let Ok(stripped) = path.strip_prefix(&src_dir) {
+                stripped.starts_with("library")
+            } else {
+                panic!("manifest {path:?} not relative to source dir {src_dir:?}");
+            }
+        })
+        .cloned()
+        .collect::<Vec<_>>();
 
     // Scan Cargo dependencies
-    let mut collected_cargo_metadata = cargo_metadata::get_metadata_and_notices(
-        &cargo,
-        &out_dir.join("vendor"),
-        &root_path,
-        &[
-            Path::new("./Cargo.toml"),
-            Path::new("./src/tools/cargo/Cargo.toml"),
-            Path::new("./library/Cargo.toml"),
-        ],
-    )?;
+    let mut collected_cargo_metadata =
+        cargo_metadata::get_metadata_and_notices(&cargo, &vendor_dir, &src_dir, &cargo_manifests)?;
 
     let library_collected_cargo_metadata = cargo_metadata::get_metadata_and_notices(
         &cargo,
-        &out_dir.join("library-vendor"),
-        &root_path,
-        &[Path::new("./library/Cargo.toml")],
+        &vendor_dir,
+        &src_dir,
+        &library_manifests,
     )?;
 
     for (key, value) in collected_cargo_metadata.iter_mut() {
@@ -54,7 +61,7 @@ fn main() -> Result<(), Error> {
     let library_collected_tree_metadata = Metadata {
         files: collected_tree_metadata
             .files
-            .trim_clone(&Path::new("./library"), &Path::new("."))
+            .trim_clone(&src_dir.join("library"), &src_dir)
             .unwrap(),
     };
 
@@ -193,6 +200,17 @@ struct License {
     copyright: Vec<String>,
 }
 
+/// Grab an environment variable as string, or fail nicely.
+fn env_string(var: &str) -> Result<String, Error> {
+    match std::env::var(var) {
+        Ok(var) => Ok(var),
+        Err(std::env::VarError::NotUnicode(_)) => {
+            anyhow::bail!("environment variable {var} is not utf-8")
+        }
+        Err(std::env::VarError::NotPresent) => anyhow::bail!("missing environment variable {var}"),
+    }
+}
+
 /// Grab an environment variable as a PathBuf, or fail nicely.
 fn env_path(var: &str) -> Result<PathBuf, Error> {
     if let Some(var) = std::env::var_os(var) {
diff --git a/src/tools/generate-windows-sys/Cargo.toml b/src/tools/generate-windows-sys/Cargo.toml
index 882d3d63525..f5c0e56bb3c 100644
--- a/src/tools/generate-windows-sys/Cargo.toml
+++ b/src/tools/generate-windows-sys/Cargo.toml
@@ -4,4 +4,4 @@ version = "0.1.0"
 edition = "2021"
 
 [dependencies.windows-bindgen]
-version = "0.58.0"
+version = "0.59.0"
diff --git a/src/tools/generate-windows-sys/src/main.rs b/src/tools/generate-windows-sys/src/main.rs
index 6dbf29d957f..6bf47e84062 100644
--- a/src/tools/generate-windows-sys/src/main.rs
+++ b/src/tools/generate-windows-sys/src/main.rs
@@ -29,8 +29,7 @@ fn main() -> Result<(), Box<dyn Error>> {
 
     sort_bindings("bindings.txt")?;
 
-    let info = windows_bindgen::bindgen(["--etc", "bindings.txt"])?;
-    println!("{info}");
+    windows_bindgen::bindgen(["--etc", "bindings.txt"]);
 
     let mut f = std::fs::File::options().append(true).open("windows_sys.rs")?;
     f.write_all(ARM32_SHIM.as_bytes())?;
diff --git a/src/tools/llvm-bitcode-linker/src/bin/llvm-bitcode-linker.rs b/src/tools/llvm-bitcode-linker/src/bin/llvm-bitcode-linker.rs
index fdbf6171c53..608c6605304 100644
--- a/src/tools/llvm-bitcode-linker/src/bin/llvm-bitcode-linker.rs
+++ b/src/tools/llvm-bitcode-linker/src/bin/llvm-bitcode-linker.rs
@@ -27,6 +27,10 @@ pub struct Args {
     #[arg(long)]
     target_cpu: Option<String>,
 
+    /// The target features
+    #[arg(long)]
+    target_feature: Option<String>,
+
     /// Write output to the filename
     #[arg(short, long)]
     output: PathBuf,
@@ -49,7 +53,7 @@ fn main() -> anyhow::Result<()> {
 
     let args = Args::parse();
 
-    let mut linker = Session::new(args.target, args.target_cpu, args.output);
+    let mut linker = Session::new(args.target, args.target_cpu, args.target_feature, args.output);
 
     linker.add_exported_symbols(args.export_symbol);
 
diff --git a/src/tools/llvm-bitcode-linker/src/linker.rs b/src/tools/llvm-bitcode-linker/src/linker.rs
index 9f579d10094..dafd847e768 100644
--- a/src/tools/llvm-bitcode-linker/src/linker.rs
+++ b/src/tools/llvm-bitcode-linker/src/linker.rs
@@ -8,6 +8,7 @@ use crate::{Optimization, Target};
 pub struct Session {
     target: Target,
     cpu: Option<String>,
+    feature: Option<String>,
     symbols: Vec<String>,
 
     /// A file that `llvm-link` supports, like a bitcode file or an archive.
@@ -21,7 +22,12 @@ pub struct Session {
 }
 
 impl Session {
-    pub fn new(target: crate::Target, cpu: Option<String>, out_path: PathBuf) -> Self {
+    pub fn new(
+        target: crate::Target,
+        cpu: Option<String>,
+        feature: Option<String>,
+        out_path: PathBuf,
+    ) -> Self {
         let link_path = out_path.with_extension("o");
         let opt_path = out_path.with_extension("optimized.o");
         let sym_path = out_path.with_extension("symbols.txt");
@@ -29,6 +35,7 @@ impl Session {
         Session {
             target,
             cpu,
+            feature,
             symbols: Vec::new(),
             files: Vec::new(),
             link_path,
@@ -134,6 +141,10 @@ impl Session {
             lcc_command.arg("--mcpu").arg(mcpu);
         }
 
+        if let Some(mattr) = &self.feature {
+            lcc_command.arg(&format!("--mattr={}", mattr));
+        }
+
         let lcc_output = lcc_command
             .arg(&self.opt_path)
             .arg("-o").arg(&self.out_path)
diff --git a/src/tools/miri/src/intrinsics/atomic.rs b/src/tools/miri/src/intrinsics/atomic.rs
index 8507b0f49de..6d2a575ed76 100644
--- a/src/tools/miri/src/intrinsics/atomic.rs
+++ b/src/tools/miri/src/intrinsics/atomic.rs
@@ -189,7 +189,7 @@ trait EvalContextPrivExt<'tcx>: MiriInterpCxExt<'tcx> {
         let place = this.deref_pointer(place)?;
         let rhs = this.read_immediate(rhs)?;
 
-        if !place.layout.ty.is_integral() && !place.layout.ty.is_unsafe_ptr() {
+        if !place.layout.ty.is_integral() && !place.layout.ty.is_raw_ptr() {
             span_bug!(
                 this.cur_span(),
                 "atomic arithmetic operations only work on integer and raw pointer types",
diff --git a/src/tools/miri/src/intrinsics/mod.rs b/src/tools/miri/src/intrinsics/mod.rs
index bce78adcaea..0faad0f7621 100644
--- a/src/tools/miri/src/intrinsics/mod.rs
+++ b/src/tools/miri/src/intrinsics/mod.rs
@@ -145,7 +145,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
                 this.write_scalar(Scalar::from_bool(branch), dest)?;
             }
 
-            "floorf16" | "ceilf16" | "truncf16" | "roundf16" | "rintf16" => {
+            "floorf16" | "ceilf16" | "truncf16" | "roundf16" | "round_ties_even_f16" => {
                 let [f] = check_arg_count(args)?;
                 let f = this.read_scalar(f)?.to_f16()?;
                 let mode = match intrinsic_name {
@@ -153,14 +153,14 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
                     "ceilf16" => Round::TowardPositive,
                     "truncf16" => Round::TowardZero,
                     "roundf16" => Round::NearestTiesToAway,
-                    "rintf16" => Round::NearestTiesToEven,
+                    "round_ties_even_f16" => Round::NearestTiesToEven,
                     _ => bug!(),
                 };
                 let res = f.round_to_integral(mode).value;
                 let res = this.adjust_nan(res, &[f]);
                 this.write_scalar(res, dest)?;
             }
-            "floorf32" | "ceilf32" | "truncf32" | "roundf32" | "rintf32" => {
+            "floorf32" | "ceilf32" | "truncf32" | "roundf32" | "round_ties_even_f32" => {
                 let [f] = check_arg_count(args)?;
                 let f = this.read_scalar(f)?.to_f32()?;
                 let mode = match intrinsic_name {
@@ -168,14 +168,14 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
                     "ceilf32" => Round::TowardPositive,
                     "truncf32" => Round::TowardZero,
                     "roundf32" => Round::NearestTiesToAway,
-                    "rintf32" => Round::NearestTiesToEven,
+                    "round_ties_even_f32" => Round::NearestTiesToEven,
                     _ => bug!(),
                 };
                 let res = f.round_to_integral(mode).value;
                 let res = this.adjust_nan(res, &[f]);
                 this.write_scalar(res, dest)?;
             }
-            "floorf64" | "ceilf64" | "truncf64" | "roundf64" | "rintf64" => {
+            "floorf64" | "ceilf64" | "truncf64" | "roundf64" | "round_ties_even_f64" => {
                 let [f] = check_arg_count(args)?;
                 let f = this.read_scalar(f)?.to_f64()?;
                 let mode = match intrinsic_name {
@@ -183,14 +183,14 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
                     "ceilf64" => Round::TowardPositive,
                     "truncf64" => Round::TowardZero,
                     "roundf64" => Round::NearestTiesToAway,
-                    "rintf64" => Round::NearestTiesToEven,
+                    "round_ties_even_f64" => Round::NearestTiesToEven,
                     _ => bug!(),
                 };
                 let res = f.round_to_integral(mode).value;
                 let res = this.adjust_nan(res, &[f]);
                 this.write_scalar(res, dest)?;
             }
-            "floorf128" | "ceilf128" | "truncf128" | "roundf128" | "rintf128" => {
+            "floorf128" | "ceilf128" | "truncf128" | "roundf128" | "round_ties_even_f128" => {
                 let [f] = check_arg_count(args)?;
                 let f = this.read_scalar(f)?.to_f128()?;
                 let mode = match intrinsic_name {
@@ -198,7 +198,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
                     "ceilf128" => Round::TowardPositive,
                     "truncf128" => Round::TowardZero,
                     "roundf128" => Round::NearestTiesToAway,
-                    "rintf128" => Round::NearestTiesToEven,
+                    "round_ties_even_f128" => Round::NearestTiesToEven,
                     _ => bug!(),
                 };
                 let res = f.round_to_integral(mode).value;
diff --git a/src/tools/miri/src/intrinsics/simd.rs b/src/tools/miri/src/intrinsics/simd.rs
index 45e316b190a..3e8b3ef46e4 100644
--- a/src/tools/miri/src/intrinsics/simd.rs
+++ b/src/tools/miri/src/intrinsics/simd.rs
@@ -633,7 +633,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
                     this.write_immediate(*val, &dest)?;
                 }
             }
-            "shuffle_generic" => {
+            "shuffle_const_generic" => {
                 let [left, right] = check_arg_count(args)?;
                 let (left, left_len) = this.project_to_simd(left)?;
                 let (right, right_len) = this.project_to_simd(right)?;
@@ -657,7 +657,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
                         this.read_immediate(&this.project_index(&right, right_idx)?)?
                     } else {
                         throw_ub_format!(
-                            "`simd_shuffle_generic` index {src_index} is out-of-bounds for 2 vectors with length {dest_len}"
+                            "`simd_shuffle_const_generic` index {src_index} is out-of-bounds for 2 vectors with length {dest_len}"
                         );
                     };
                     this.write_immediate(*val, &dest)?;
diff --git a/src/tools/miri/src/lib.rs b/src/tools/miri/src/lib.rs
index a717d8ccf28..9ac19a2a5f2 100644
--- a/src/tools/miri/src/lib.rs
+++ b/src/tools/miri/src/lib.rs
@@ -1,7 +1,7 @@
-#![cfg_attr(bootstrap, feature(trait_upcasting))]
 #![feature(rustc_private)]
 #![feature(cell_update)]
 #![feature(float_gamma)]
+#![feature(float_erf)]
 #![feature(map_try_insert)]
 #![feature(never_type)]
 #![feature(try_blocks)]
@@ -15,8 +15,6 @@
 #![feature(unqualified_local_imports)]
 #![feature(derive_coerce_pointee)]
 #![feature(arbitrary_self_types)]
-#![feature(unsigned_is_multiple_of)]
-#![feature(extract_if)]
 // Configure clippy and other lints
 #![allow(
     clippy::collapsible_else_if,
diff --git a/src/tools/miri/src/operator.rs b/src/tools/miri/src/operator.rs
index c588b6fc7f1..81f22b2d0b2 100644
--- a/src/tools/miri/src/operator.rs
+++ b/src/tools/miri/src/operator.rs
@@ -52,8 +52,8 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
             // Some more operations are possible with atomics.
             // The return value always has the provenance of the *left* operand.
             Add | Sub | BitOr | BitAnd | BitXor => {
-                assert!(left.layout.ty.is_unsafe_ptr());
-                assert!(right.layout.ty.is_unsafe_ptr());
+                assert!(left.layout.ty.is_raw_ptr());
+                assert!(right.layout.ty.is_raw_ptr());
                 let ptr = left.to_scalar().to_pointer(this)?;
                 // We do the actual operation with usize-typed scalars.
                 let left = ImmTy::from_uint(ptr.addr().bytes(), this.machine.layouts.usize);
diff --git a/src/tools/miri/src/shims/foreign_items.rs b/src/tools/miri/src/shims/foreign_items.rs
index 97bfb04f1f4..bedc1ebdc95 100644
--- a/src/tools/miri/src/shims/foreign_items.rs
+++ b/src/tools/miri/src/shims/foreign_items.rs
@@ -742,6 +742,8 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
             | "log1pf"
             | "expm1f"
             | "tgammaf"
+            | "erff"
+            | "erfcf"
             => {
                 let [f] = this.check_shim(abi, Conv::C , link_name, args)?;
                 let f = this.read_scalar(f)?.to_f32()?;
@@ -759,6 +761,8 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
                     "log1pf" => f_host.ln_1p(),
                     "expm1f" => f_host.exp_m1(),
                     "tgammaf" => f_host.gamma(),
+                    "erff" => f_host.erf(),
+                    "erfcf" => f_host.erfc(),
                     _ => bug!(),
                 };
                 let res = res.to_soft();
@@ -799,6 +803,8 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
             | "log1p"
             | "expm1"
             | "tgamma"
+            | "erf"
+            | "erfc"
             => {
                 let [f] = this.check_shim(abi, Conv::C , link_name, args)?;
                 let f = this.read_scalar(f)?.to_f64()?;
@@ -816,6 +822,8 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
                     "log1p" => f_host.ln_1p(),
                     "expm1" => f_host.exp_m1(),
                     "tgamma" => f_host.gamma(),
+                    "erf" => f_host.erf(),
+                    "erfc" => f_host.erfc(),
                     _ => bug!(),
                 };
                 let res = res.to_soft();
diff --git a/src/tools/miri/tests/fail/intrinsics/ptr_offset_from_unsigned_neg.rs b/src/tools/miri/tests/fail/intrinsics/ptr_offset_from_unsigned_neg.rs
index 13eb5bfb342..562d72b0ca0 100644
--- a/src/tools/miri/tests/fail/intrinsics/ptr_offset_from_unsigned_neg.rs
+++ b/src/tools/miri/tests/fail/intrinsics/ptr_offset_from_unsigned_neg.rs
@@ -1,9 +1,7 @@
 //@normalize-stderr-test: "\d+ < \d+" -> "$$ADDR < $$ADDR"
-#![feature(ptr_sub_ptr)]
-
 fn main() {
     let arr = [0u8; 8];
     let ptr1 = arr.as_ptr();
     let ptr2 = ptr1.wrapping_add(4);
-    let _val = unsafe { ptr1.sub_ptr(ptr2) }; //~ERROR: first pointer has smaller address than second
+    let _val = unsafe { ptr1.offset_from_unsigned(ptr2) }; //~ERROR: first pointer has smaller address than second
 }
diff --git a/src/tools/miri/tests/fail/intrinsics/ptr_offset_from_unsigned_neg.stderr b/src/tools/miri/tests/fail/intrinsics/ptr_offset_from_unsigned_neg.stderr
index a0a8e97e7fa..80e3f2c22a1 100644
--- a/src/tools/miri/tests/fail/intrinsics/ptr_offset_from_unsigned_neg.stderr
+++ b/src/tools/miri/tests/fail/intrinsics/ptr_offset_from_unsigned_neg.stderr
@@ -1,8 +1,8 @@
 error: Undefined Behavior: `ptr_offset_from_unsigned` called when first pointer has smaller address than second: $ADDR < $ADDR
   --> tests/fail/intrinsics/ptr_offset_from_unsigned_neg.rs:LL:CC
    |
-LL |     let _val = unsafe { ptr1.sub_ptr(ptr2) };
-   |                         ^^^^^^^^^^^^^^^^^^ `ptr_offset_from_unsigned` called when first pointer has smaller address than second: $ADDR < $ADDR
+LL |     let _val = unsafe { ptr1.offset_from_unsigned(ptr2) };
+   |                         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `ptr_offset_from_unsigned` called when first pointer has smaller address than second: $ADDR < $ADDR
    |
    = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior
    = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information
diff --git a/src/tools/miri/tests/fail/panic/panic_abort1.rs b/src/tools/miri/tests/fail/panic/panic_abort1.rs
index 300bfa32ecb..7552c7b7e80 100644
--- a/src/tools/miri/tests/fail/panic/panic_abort1.rs
+++ b/src/tools/miri/tests/fail/panic/panic_abort1.rs
@@ -1,6 +1,6 @@
 //@error-in-other-file: the program aborted execution
 //@normalize-stderr-test: "\| +\^+" -> "| ^"
-//@normalize-stderr-test: "libc::abort\(\);|core::intrinsics::abort\(\);" -> "ABORT();"
+//@normalize-stderr-test: "unsafe \{ libc::abort\(\); \}|core::intrinsics::abort\(\);" -> "ABORT();"
 //@compile-flags: -C panic=abort
 
 fn main() {
diff --git a/src/tools/miri/tests/fail/panic/panic_abort2.rs b/src/tools/miri/tests/fail/panic/panic_abort2.rs
index 5d691350577..624f9933545 100644
--- a/src/tools/miri/tests/fail/panic/panic_abort2.rs
+++ b/src/tools/miri/tests/fail/panic/panic_abort2.rs
@@ -1,6 +1,6 @@
 //@error-in-other-file: the program aborted execution
 //@normalize-stderr-test: "\| +\^+" -> "| ^"
-//@normalize-stderr-test: "libc::abort\(\);|core::intrinsics::abort\(\);" -> "ABORT();"
+//@normalize-stderr-test: "unsafe \{ libc::abort\(\); \}|core::intrinsics::abort\(\);" -> "ABORT();"
 //@compile-flags: -C panic=abort
 
 fn main() {
diff --git a/src/tools/miri/tests/fail/panic/panic_abort3.rs b/src/tools/miri/tests/fail/panic/panic_abort3.rs
index 25afc315628..d1435b55946 100644
--- a/src/tools/miri/tests/fail/panic/panic_abort3.rs
+++ b/src/tools/miri/tests/fail/panic/panic_abort3.rs
@@ -1,6 +1,6 @@
 //@error-in-other-file: the program aborted execution
 //@normalize-stderr-test: "\| +\^+" -> "| ^"
-//@normalize-stderr-test: "libc::abort\(\);|core::intrinsics::abort\(\);" -> "ABORT();"
+//@normalize-stderr-test: "unsafe \{ libc::abort\(\); \}|core::intrinsics::abort\(\);" -> "ABORT();"
 //@compile-flags: -C panic=abort
 
 fn main() {
diff --git a/src/tools/miri/tests/fail/panic/panic_abort4.rs b/src/tools/miri/tests/fail/panic/panic_abort4.rs
index 025b51a5cf5..54b9c9cbfdb 100644
--- a/src/tools/miri/tests/fail/panic/panic_abort4.rs
+++ b/src/tools/miri/tests/fail/panic/panic_abort4.rs
@@ -1,6 +1,6 @@
 //@error-in-other-file: the program aborted execution
 //@normalize-stderr-test: "\| +\^+" -> "| ^"
-//@normalize-stderr-test: "libc::abort\(\);|core::intrinsics::abort\(\);" -> "ABORT();"
+//@normalize-stderr-test: "unsafe \{ libc::abort\(\); \}|core::intrinsics::abort\(\);" -> "ABORT();"
 //@compile-flags: -C panic=abort
 
 fn main() {
diff --git a/src/tools/miri/tests/fail/ptr_swap_nonoverlapping.rs b/src/tools/miri/tests/fail/ptr_swap_nonoverlapping.rs
new file mode 100644
index 00000000000..6f627c416b0
--- /dev/null
+++ b/src/tools/miri/tests/fail/ptr_swap_nonoverlapping.rs
@@ -0,0 +1,15 @@
+//! This is a regression test for <https://github.com/rust-lang/miri/issues/4188>: The precondition
+//! check in `ptr::swap_nonoverlapping` was incorrectly disabled in Miri.
+//@normalize-stderr-test: "unsafe \{ libc::abort\(\) \}|crate::intrinsics::abort\(\);" -> "ABORT();"
+//@normalize-stderr-test: "\| +\^+" -> "| ^"
+//@normalize-stderr-test: "\n +[0-9]+:[^\n]+" -> ""
+//@normalize-stderr-test: "\n +at [^\n]+" -> ""
+//@error-in-other-file: aborted execution
+
+fn main() {
+    let mut data = 0usize;
+    let ptr = std::ptr::addr_of_mut!(data);
+    unsafe {
+        std::ptr::swap_nonoverlapping(ptr, ptr, 1);
+    }
+}
diff --git a/src/tools/miri/tests/fail/ptr_swap_nonoverlapping.stderr b/src/tools/miri/tests/fail/ptr_swap_nonoverlapping.stderr
new file mode 100644
index 00000000000..80dd2f39b42
--- /dev/null
+++ b/src/tools/miri/tests/fail/ptr_swap_nonoverlapping.stderr
@@ -0,0 +1,33 @@
+
+thread 'main' panicked at RUSTLIB/core/src/panicking.rs:LL:CC:
+unsafe precondition(s) violated: ptr::swap_nonoverlapping requires that both pointer arguments are aligned and non-null and the specified memory ranges do not overlap
+
+This indicates a bug in the program. This Undefined Behavior check is optional, and cannot be relied on for safety.
+note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
+note: in Miri, you may have to set `MIRIFLAGS=-Zmiri-env-forward=RUST_BACKTRACE` for the environment variable to have an effect
+thread caused non-unwinding panic. aborting.
+error: abnormal termination: the program aborted execution
+  --> RUSTLIB/std/src/sys/pal/PLATFORM/mod.rs:LL:CC
+   |
+LL |     ABORT();
+   | ^ the program aborted execution
+   |
+   = note: BACKTRACE:
+   = note: inside `std::sys::pal::PLATFORM::abort_internal` at RUSTLIB/std/src/sys/pal/PLATFORM/mod.rs:LL:CC
+   = note: inside `std::panicking::rust_panic_with_hook` at RUSTLIB/std/src/panicking.rs:LL:CC
+   = note: inside closure at RUSTLIB/std/src/panicking.rs:LL:CC
+   = note: inside `std::sys::backtrace::__rust_end_short_backtrace::<{closure@std::panicking::begin_panic_handler::{closure#0}}, !>` at RUSTLIB/std/src/sys/backtrace.rs:LL:CC
+   = note: inside `std::panicking::begin_panic_handler` at RUSTLIB/std/src/panicking.rs:LL:CC
+   = note: inside `core::panicking::panic_nounwind` at RUSTLIB/core/src/panicking.rs:LL:CC
+   = note: inside `std::ptr::swap_nonoverlapping::precondition_check` at RUSTLIB/core/src/ub_checks.rs:LL:CC
+   = note: inside `std::ptr::swap_nonoverlapping::<usize>` at RUSTLIB/core/src/ub_checks.rs:LL:CC
+note: inside `main`
+  --> tests/fail/ptr_swap_nonoverlapping.rs:LL:CC
+   |
+LL |         std::ptr::swap_nonoverlapping(ptr, ptr, 1);
+   | ^
+
+note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
+
+error: aborting due to 1 previous error
+
diff --git a/src/tools/miri/tests/fail/validity/dyn-transmute-inner-binder.rs b/src/tools/miri/tests/fail/validity/dyn-transmute-inner-binder.rs
new file mode 100644
index 00000000000..7de4aef422a
--- /dev/null
+++ b/src/tools/miri/tests/fail/validity/dyn-transmute-inner-binder.rs
@@ -0,0 +1,30 @@
+// Test that transmuting from `&dyn Trait<fn(&'static ())>` to `&dyn Trait<for<'a> fn(&'a ())>` is UB.
+//
+// The vtable of `() as Trait<fn(&'static ())>` and `() as Trait<for<'a> fn(&'a ())>` can have
+// different entries and, because in the former the entry for `foo` is vacant, this test will
+// segfault at runtime.
+
+trait Trait<U> {
+    fn foo(&self)
+    where
+        U: HigherRanked,
+    {
+    }
+}
+impl<T, U> Trait<U> for T {}
+
+trait HigherRanked {}
+impl HigherRanked for for<'a> fn(&'a ()) {}
+
+// 2nd candidate is required so that selecting `(): Trait<fn(&'static ())>` will
+// evaluate the candidates and fail the leak check instead of returning the
+// only applicable candidate.
+trait Unsatisfied {}
+impl<T: Unsatisfied> HigherRanked for T {}
+
+fn main() {
+    let x: &dyn Trait<fn(&'static ())> = &();
+    let y: &dyn Trait<for<'a> fn(&'a ())> = unsafe { std::mem::transmute(x) };
+    //~^ ERROR: wrong trait in wide pointer vtable
+    y.foo();
+}
diff --git a/src/tools/miri/tests/fail/validity/dyn-transmute-inner-binder.stderr b/src/tools/miri/tests/fail/validity/dyn-transmute-inner-binder.stderr
new file mode 100644
index 00000000000..cfdf279a605
--- /dev/null
+++ b/src/tools/miri/tests/fail/validity/dyn-transmute-inner-binder.stderr
@@ -0,0 +1,15 @@
+error: Undefined Behavior: constructing invalid value: wrong trait in wide pointer vtable: expected `Trait<for<'a> fn(&'a ())>`, but encountered `Trait<fn(&())>`
+  --> tests/fail/validity/dyn-transmute-inner-binder.rs:LL:CC
+   |
+LL |     let y: &dyn Trait<for<'a> fn(&'a ())> = unsafe { std::mem::transmute(x) };
+   |                                                      ^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value: wrong trait in wide pointer vtable: expected `Trait<for<'a> fn(&'a ())>`, but encountered `Trait<fn(&())>`
+   |
+   = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior
+   = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information
+   = note: BACKTRACE:
+   = note: inside `main` at tests/fail/validity/dyn-transmute-inner-binder.rs:LL:CC
+
+note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
+
+error: aborting due to 1 previous error
+
diff --git a/src/tools/miri/tests/pass/dyn-upcast.rs b/src/tools/miri/tests/pass/dyn-upcast.rs
index 6f8adc09640..875891df233 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() {
     drop_principal();
     modulo_binder();
     modulo_assoc();
+    bidirectional_subtyping();
 }
 
 fn vtable_nop_cast() {
@@ -531,3 +532,32 @@ fn modulo_assoc() {
 
     (&() as &dyn Trait as &dyn Middle<()>).say_hello(&0);
 }
+
+fn bidirectional_subtyping() {
+    // Test that transmuting between subtypes of dyn traits is fine, even in the
+    // "wrong direction", i.e. going from a lower-ranked to a higher-ranked dyn trait.
+    // Note that compared to the `dyn-transmute-inner-binder` test, the `for` is on the
+    // *outside* here!
+
+    trait Trait<U: ?Sized> {}
+    impl<T, U: ?Sized> Trait<U> for T {}
+
+    struct Wrapper<T: ?Sized>(T);
+
+    let x: &dyn Trait<fn(&'static ())> = &();
+    let _y: &dyn for<'a> Trait<fn(&'a ())> = unsafe { std::mem::transmute(x) };
+
+    let x: &dyn for<'a> Trait<fn(&'a ())> = &();
+    let _y: &dyn Trait<fn(&'static ())> = unsafe { std::mem::transmute(x) };
+
+    let x: &dyn Trait<dyn Trait<fn(&'static ())>> = &();
+    let _y: &dyn for<'a> Trait<dyn Trait<fn(&'a ())>> = unsafe { std::mem::transmute(x) };
+
+    let x: &dyn for<'a> Trait<dyn Trait<fn(&'a ())>> = &();
+    let _y: &dyn Trait<dyn Trait<fn(&'static ())>> = unsafe { std::mem::transmute(x) };
+
+    // This lowers to a ptr-to-ptr cast (which behaves like a transmute)
+    // and not an unsizing coercion:
+    let x: *const dyn for<'a> Trait<&'a ()> = &();
+    let _y: *const Wrapper<dyn Trait<&'static ()>> = x as _;
+}
diff --git a/src/tools/miri/tests/pass/float.rs b/src/tools/miri/tests/pass/float.rs
index 2f4f64b1aa8..51cafbb3f43 100644
--- a/src/tools/miri/tests/pass/float.rs
+++ b/src/tools/miri/tests/pass/float.rs
@@ -1,4 +1,5 @@
 #![feature(stmt_expr_attributes)]
+#![feature(float_erf)]
 #![feature(float_gamma)]
 #![feature(core_intrinsics)]
 #![feature(f128)]
@@ -1076,6 +1077,11 @@ pub fn libm() {
     let (val, sign) = (-0.5f64).ln_gamma();
     assert_approx_eq!(val, (2.0 * f64::consts::PI.sqrt()).ln());
     assert_eq!(sign, -1);
+
+    assert_approx_eq!(1.0f32.erf(), 0.84270079294971486934122063508260926f32);
+    assert_approx_eq!(1.0f64.erf(), 0.84270079294971486934122063508260926f64);
+    assert_approx_eq!(1.0f32.erfc(), 0.15729920705028513065877936491739074f32);
+    assert_approx_eq!(1.0f64.erfc(), 0.15729920705028513065877936491739074f64);
 }
 
 fn test_fast() {
diff --git a/src/tools/miri/tests/pass/intrinsics/portable-simd.rs b/src/tools/miri/tests/pass/intrinsics/portable-simd.rs
index 0d0d79098d5..f043bb7ce9f 100644
--- a/src/tools/miri/tests/pass/intrinsics/portable-simd.rs
+++ b/src/tools/miri/tests/pass/intrinsics/portable-simd.rs
@@ -16,7 +16,7 @@ use std::simd::prelude::*;
 
 #[rustc_intrinsic]
 #[rustc_nounwind]
-pub unsafe fn simd_shuffle_generic<T, U, const IDX: &'static [u32]>(_x: T, _y: T) -> U;
+pub unsafe fn simd_shuffle_const_generic<T, U, const IDX: &'static [u32]>(_x: T, _y: T) -> U;
 
 fn simd_ops_f32() {
     let a = f32x4::splat(10.0);
@@ -299,27 +299,6 @@ fn simd_mask() {
         }
     }
 
-    // This used to cause an ICE. It exercises simd_select_bitmask with an array as input.
-    let bitmask = u8x4::from_array([0b00001101, 0, 0, 0]);
-    assert_eq!(
-        mask32x4::from_bitmask_vector(bitmask),
-        mask32x4::from_array([true, false, true, true]),
-    );
-    let bitmask = u8x8::from_array([0b01000101, 0, 0, 0, 0, 0, 0, 0]);
-    assert_eq!(
-        mask32x8::from_bitmask_vector(bitmask),
-        mask32x8::from_array([true, false, true, false, false, false, true, false]),
-    );
-    let bitmask =
-        u8x16::from_array([0b01000101, 0b11110000, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]);
-    assert_eq!(
-        mask32x16::from_bitmask_vector(bitmask),
-        mask32x16::from_array([
-            true, false, true, false, false, false, true, false, false, false, false, false, true,
-            true, true, true,
-        ]),
-    );
-
     // Also directly call simd_select_bitmask, to test both kinds of argument types.
     unsafe {
         // These masks are exactly the results we got out above in the `simd_bitmask` tests.
@@ -640,13 +619,13 @@ fn simd_intrinsics() {
             simd_select(i8x4::from_array([0, -1, -1, 0]), b, a),
             i32x4::from_array([10, 2, 10, 10])
         );
-        assert_eq!(simd_shuffle_generic::<_, i32x4, { &[3, 1, 0, 2] }>(a, b), a,);
+        assert_eq!(simd_shuffle_const_generic::<_, i32x4, { &[3, 1, 0, 2] }>(a, b), a,);
         assert_eq!(
             simd_shuffle::<_, _, i32x4>(a, b, const { u32x4::from_array([3u32, 1, 0, 2]) }),
             a,
         );
         assert_eq!(
-            simd_shuffle_generic::<_, i32x4, { &[7, 5, 4, 6] }>(a, b),
+            simd_shuffle_const_generic::<_, i32x4, { &[7, 5, 4, 6] }>(a, b),
             i32x4::from_array([4, 2, 1, 10]),
         );
         assert_eq!(
diff --git a/src/tools/miri/tests/pass/ptr_offset.rs b/src/tools/miri/tests/pass/ptr_offset.rs
index 92b275b0032..89aff7bf635 100644
--- a/src/tools/miri/tests/pass/ptr_offset.rs
+++ b/src/tools/miri/tests/pass/ptr_offset.rs
@@ -1,5 +1,4 @@
 //@compile-flags: -Zmiri-permissive-provenance
-#![feature(ptr_sub_ptr)]
 use std::{mem, ptr};
 
 fn main() {
@@ -22,7 +21,7 @@ fn smoke() {
     let _val = ptr.wrapping_sub(0);
     let _val = unsafe { ptr.sub(0) };
     let _val = unsafe { ptr.offset_from(ptr) };
-    let _val = unsafe { ptr.sub_ptr(ptr) };
+    let _val = unsafe { ptr.offset_from_unsigned(ptr) };
 }
 
 fn test_offset_from() {
@@ -33,14 +32,14 @@ fn test_offset_from() {
         let y = x.offset(12);
 
         assert_eq!(y.offset_from(x), 12);
-        assert_eq!(y.sub_ptr(x), 12);
+        assert_eq!(y.offset_from_unsigned(x), 12);
         assert_eq!(x.offset_from(y), -12);
         assert_eq!((y as *const u32).offset_from(x as *const u32), 12 / 4);
         assert_eq!((x as *const u32).offset_from(y as *const u32), -12 / 4);
 
         let x = (((x as usize) * 2) / 2) as *const u8;
         assert_eq!(y.offset_from(x), 12);
-        assert_eq!(y.sub_ptr(x), 12);
+        assert_eq!(y.offset_from_unsigned(x), 12);
         assert_eq!(x.offset_from(y), -12);
     }
 }
diff --git a/src/tools/miri/tests/pass/shims/x86/intrinsics-x86-pause-without-sse2.stderr b/src/tools/miri/tests/pass/shims/x86/intrinsics-x86-pause-without-sse2.stderr
new file mode 100644
index 00000000000..171bf0c82d5
--- /dev/null
+++ b/src/tools/miri/tests/pass/shims/x86/intrinsics-x86-pause-without-sse2.stderr
@@ -0,0 +1,5 @@
+warning: target feature `sse2` must be enabled to ensure that the ABI of the current target can be implemented correctly
+   |
+   = note: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
+   = note: for more information, see issue #116344 <https://github.com/rust-lang/rust/issues/116344>
+
diff --git a/src/tools/run-make-support/src/external_deps/rustc.rs b/src/tools/run-make-support/src/external_deps/rustc.rs
index 33dc57cbc07..710ba025830 100644
--- a/src/tools/run-make-support/src/external_deps/rustc.rs
+++ b/src/tools/run-make-support/src/external_deps/rustc.rs
@@ -84,7 +84,7 @@ impl Rustc {
         self
     }
 
-    /// Specify default optimization level `-O` (alias for `-C opt-level=2`).
+    /// Specify default optimization level `-O` (alias for `-C opt-level=3`).
     pub fn opt(&mut self) -> &mut Self {
         self.cmd.arg("-O");
         self
@@ -253,6 +253,13 @@ impl Rustc {
         self
     }
 
+    /// Specify the target CPU.
+    pub fn target_cpu<S: AsRef<str>>(&mut self, target_cpu: S) -> &mut Self {
+        let target_cpu = target_cpu.as_ref();
+        self.cmd.arg(format!("-Ctarget-cpu={target_cpu}"));
+        self
+    }
+
     /// Specify the crate type.
     pub fn crate_type(&mut self, crate_type: &str) -> &mut Self {
         self.cmd.arg("--crate-type");
diff --git a/src/tools/rust-analyzer/.git-blame-ignore-revs b/src/tools/rust-analyzer/.git-blame-ignore-revs
index 2ccdc8c0425..651502965ef 100644
--- a/src/tools/rust-analyzer/.git-blame-ignore-revs
+++ b/src/tools/rust-analyzer/.git-blame-ignore-revs
@@ -14,3 +14,4 @@ f247090558c9ba3c551566eae5882b7ca865225f
 b2f6fd4f961fc7e4fbfdb80cae2e6065f8436f15
 c48062fe2ab9a2d913d1985a6b0aec4bf936bfc1
 f532576ac53ddcc666bc8d59e0b6437065e2f599
+4704881b641884de50645637108b6b6f5b68aaf9
diff --git a/src/tools/rust-analyzer/.github/workflows/autopublish.yaml b/src/tools/rust-analyzer/.github/workflows/autopublish.yaml
index 5258d9ddd3a..e4fa94643ba 100644
--- a/src/tools/rust-analyzer/.github/workflows/autopublish.yaml
+++ b/src/tools/rust-analyzer/.github/workflows/autopublish.yaml
@@ -11,6 +11,7 @@ on:
 
 jobs:
   publish:
+    if: ${{ github.repository == 'rust-lang/rust-analyzer' || github.event.action == 'workflow_dispatch' }}
     name: publish
     runs-on: ubuntu-latest
     steps:
diff --git a/src/tools/rust-analyzer/.github/workflows/ci.yaml b/src/tools/rust-analyzer/.github/workflows/ci.yaml
index ec33009239c..81b55712d7f 100644
--- a/src/tools/rust-analyzer/.github/workflows/ci.yaml
+++ b/src/tools/rust-analyzer/.github/workflows/ci.yaml
@@ -64,7 +64,11 @@ jobs:
         run: |
           rustup update --no-self-update ${{ env.RUST_CHANNEL }}
           rustup default ${{ env.RUST_CHANNEL }}
-          rustup component add --toolchain ${{ env.RUST_CHANNEL }} rustfmt rust-src
+          rustup component add --toolchain ${{ env.RUST_CHANNEL }} rust-src
+          # We always use a nightly rustfmt, regardless of channel, because we need
+          # --file-lines.
+          rustup toolchain add nightly --profile minimal
+          rustup component add --toolchain nightly rustfmt
       # https://github.com/actions-rust-lang/setup-rust-toolchain/blob/main/rust.json
       - name: Install Rust Problem Matcher
         if: matrix.os == 'ubuntu-latest'
diff --git a/src/tools/rust-analyzer/.github/workflows/publish-libs.yaml b/src/tools/rust-analyzer/.github/workflows/publish-libs.yaml
index f1533bf26e5..5023a634fde 100644
--- a/src/tools/rust-analyzer/.github/workflows/publish-libs.yaml
+++ b/src/tools/rust-analyzer/.github/workflows/publish-libs.yaml
@@ -9,6 +9,7 @@ on:
 
 jobs:
   publish-libs:
+    if: ${{ github.repository == 'rust-lang/rust-analyzer' || github.event.action == 'workflow_dispatch' }}
     name: publish
     runs-on: ubuntu-latest
     steps:
diff --git a/src/tools/rust-analyzer/.github/workflows/release.yaml b/src/tools/rust-analyzer/.github/workflows/release.yaml
index 39ac652de0f..fe090267dc9 100644
--- a/src/tools/rust-analyzer/.github/workflows/release.yaml
+++ b/src/tools/rust-analyzer/.github/workflows/release.yaml
@@ -22,6 +22,7 @@ env:
 
 jobs:
   dist:
+    if: ${{ github.repository == 'rust-lang/rust-analyzer' || github.event.action == 'workflow_dispatch' }}
     strategy:
       matrix:
         include:
@@ -33,14 +34,14 @@ jobs:
           - os: windows-latest
             target: aarch64-pc-windows-msvc
             code-target: win32-arm64
-          - os: ubuntu-20.04
+          - os: ubuntu-latest
             target: x86_64-unknown-linux-gnu
             code-target: linux-x64
             container: rockylinux:8
-          - os: ubuntu-20.04
+          - os: ubuntu-latest
             target: aarch64-unknown-linux-gnu
             code-target: linux-arm64
-          - os: ubuntu-20.04
+          - os: ubuntu-latest
             target: arm-unknown-linux-gnueabihf
             code-target: linux-armhf
           - os: macos-13
@@ -138,6 +139,7 @@ jobs:
           path: ./dist
 
   dist-x86_64-unknown-linux-musl:
+    if: ${{ github.repository == 'rust-lang/rust-analyzer' || github.event.action == 'workflow_dispatch' }}
     name: dist (x86_64-unknown-linux-musl)
     runs-on: ubuntu-latest
     env:
@@ -183,6 +185,7 @@ jobs:
           path: ./dist
 
   publish:
+    if: ${{ github.repository == 'rust-lang/rust-analyzer' || github.event.action == 'workflow_dispatch' }}
     name: publish
     runs-on: ubuntu-latest
     needs: ["dist", "dist-x86_64-unknown-linux-musl"]
@@ -257,24 +260,24 @@ jobs:
         working-directory: ./editors/code
 
       - name: Publish Extension (Code Marketplace, release)
-        if: github.ref == 'refs/heads/release' && (github.repository == 'rust-analyzer/rust-analyzer' || github.repository == 'rust-lang/rust-analyzer')
+        if: github.ref == 'refs/heads/release' && github.repository == 'rust-lang/rust-analyzer'
         working-directory: ./editors/code
         # token from https://dev.azure.com/rust-analyzer/
         run: npx vsce publish --pat ${{ secrets.MARKETPLACE_TOKEN }} --packagePath ../../dist/rust-analyzer-*.vsix
 
       - name: Publish Extension (OpenVSX, release)
-        if: github.ref == 'refs/heads/release' && (github.repository == 'rust-analyzer/rust-analyzer' || github.repository == 'rust-lang/rust-analyzer')
+        if: github.ref == 'refs/heads/release' && github.repository == 'rust-lang/rust-analyzer'
         working-directory: ./editors/code
         run: npx ovsx publish --pat ${{ secrets.OPENVSX_TOKEN }} --packagePath ../../dist/rust-analyzer-*.vsix
         timeout-minutes: 2
 
       - name: Publish Extension (Code Marketplace, nightly)
-        if: github.ref != 'refs/heads/release' && (github.repository == 'rust-analyzer/rust-analyzer' || github.repository == 'rust-lang/rust-analyzer')
+        if: github.ref != 'refs/heads/release' && github.repository == 'rust-lang/rust-analyzer'
         working-directory: ./editors/code
         run: npx vsce publish --pat ${{ secrets.MARKETPLACE_TOKEN }} --packagePath ../../dist/rust-analyzer-*.vsix --pre-release
 
       - name: Publish Extension (OpenVSX, nightly)
-        if: github.ref != 'refs/heads/release' && (github.repository == 'rust-analyzer/rust-analyzer' || github.repository == 'rust-lang/rust-analyzer')
+        if: github.ref != 'refs/heads/release' && github.repository == 'rust-lang/rust-analyzer'
         working-directory: ./editors/code
         run: npx ovsx publish --pat ${{ secrets.OPENVSX_TOKEN }} --packagePath ../../dist/rust-analyzer-*.vsix
         timeout-minutes: 2
diff --git a/src/tools/rust-analyzer/.gitignore b/src/tools/rust-analyzer/.gitignore
index c4470a45078..7192e685e29 100644
--- a/src/tools/rust-analyzer/.gitignore
+++ b/src/tools/rust-analyzer/.gitignore
@@ -6,10 +6,11 @@ target/
 *.log
 *.iml
 .vscode/settings.json
-generated_assists.adoc
-generated_features.adoc
-generated_diagnostic.adoc
 .DS_Store
 /out/
 /dump.lsif
 .envrc
+docs/book/book
+docs/book/src/assists_generated.md
+docs/book/src/diagnostics_generated.md
+docs/book/src/features_generated.md
diff --git a/src/tools/rust-analyzer/CONTRIBUTING.md b/src/tools/rust-analyzer/CONTRIBUTING.md
index da65b034be3..6f270fc63ba 100644
--- a/src/tools/rust-analyzer/CONTRIBUTING.md
+++ b/src/tools/rust-analyzer/CONTRIBUTING.md
@@ -4,7 +4,7 @@ Thank you for your interest in contributing to rust-analyzer! There are many way
 and we appreciate all of them.
 
 To get a quick overview of the crates and structure of the project take a look at the
-[./docs/dev](./docs/dev) folder.
+[Contributing](https://rust-analyzer.github.io/book/contributing) section of the manual.
 
 If you have any questions please ask them in the [rust-analyzer zulip stream](
 https://rust-lang.zulipchat.com/#narrow/stream/185405-t-compiler.2Frust-analyzer) or if unsure where
diff --git a/src/tools/rust-analyzer/Cargo.lock b/src/tools/rust-analyzer/Cargo.lock
index 2dfca7c4803..01e6a39f7c9 100644
--- a/src/tools/rust-analyzer/Cargo.lock
+++ b/src/tools/rust-analyzer/Cargo.lock
@@ -559,9 +559,9 @@ dependencies = [
  "intern",
  "itertools",
  "la-arena 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
- "limit",
  "mbe",
  "ra-ap-rustc_abi",
+ "ra-ap-rustc_hashes",
  "ra-ap-rustc_parse_format",
  "rustc-hash 2.0.0",
  "rustc_apfloat",
@@ -591,7 +591,6 @@ dependencies = [
  "intern",
  "itertools",
  "la-arena 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
- "limit",
  "mbe",
  "parser",
  "rustc-hash 2.0.0",
@@ -626,11 +625,11 @@ dependencies = [
  "intern",
  "itertools",
  "la-arena 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
- "limit",
  "nohash-hasher",
  "oorandom",
  "project-model",
  "ra-ap-rustc_abi",
+ "ra-ap-rustc_hashes",
  "ra-ap-rustc_index",
  "ra-ap-rustc_pattern_analysis",
  "rustc-hash 2.0.0",
@@ -744,7 +743,6 @@ dependencies = [
  "hir",
  "indexmap",
  "itertools",
- "limit",
  "line-index 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
  "memchr",
  "nohash-hasher",
@@ -944,10 +942,6 @@ dependencies = [
 ]
 
 [[package]]
-name = "limit"
-version = "0.0.0"
-
-[[package]]
 name = "line-index"
 version = "0.1.2"
 dependencies = [
@@ -1279,7 +1273,6 @@ dependencies = [
  "drop_bomb",
  "edition",
  "expect-test",
- "limit",
  "ra-ap-rustc_lexer",
  "stdx",
  "tracing",
@@ -1408,9 +1401,9 @@ dependencies = [
 
 [[package]]
 name = "proc-macro2"
-version = "1.0.86"
+version = "1.0.93"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77"
+checksum = "60946a68e5f9d28b0dc1c21bb8a97ee7d018a8b322fa57838ba31cc878e22d99"
 dependencies = [
  "unicode-ident",
 ]
@@ -1514,20 +1507,30 @@ dependencies = [
 
 [[package]]
 name = "ra-ap-rustc_abi"
-version = "0.91.0"
+version = "0.97.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d5246e9e1f450333a990877eabbc36fe0567e7cedd56d5365db319e14079cf2a"
+checksum = "3829c3355d1681ffeaf1450ec71edcdace6820fe2e86469d8fc1ad45e2c96460"
 dependencies = [
  "bitflags 2.7.0",
+ "ra-ap-rustc_hashes",
  "ra-ap-rustc_index",
  "tracing",
 ]
 
 [[package]]
+name = "ra-ap-rustc_hashes"
+version = "0.97.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1bd4d6d4c434bec08e02370a4f64a4985312097215a62e82d0f757f3a98e502e"
+dependencies = [
+ "rustc-stable-hash",
+]
+
+[[package]]
 name = "ra-ap-rustc_index"
-version = "0.91.0"
+version = "0.97.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "59fd8e4f5b34c434ec111efb0e0614954db048b9307d3b2e4cc3c915da9d2160"
+checksum = "bad6fc4bd7522e31096e2de5b0351144fe0684b608791ee26c842bf2da1b19ae"
 dependencies = [
  "ra-ap-rustc_index_macros",
  "smallvec",
@@ -1535,9 +1538,9 @@ dependencies = [
 
 [[package]]
 name = "ra-ap-rustc_index_macros"
-version = "0.91.0"
+version = "0.97.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2d34973fe081392bd1edb022e865e9952fcaa093f9cdae183edce64472e5e889"
+checksum = "cfb234e1f84b92be45276c3025bee18789e9bc95bec8789bec961e78edb01c52"
 dependencies = [
  "proc-macro2",
  "quote",
@@ -1546,19 +1549,20 @@ dependencies = [
 
 [[package]]
 name = "ra-ap-rustc_lexer"
-version = "0.91.0"
+version = "0.97.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "52fa42c582e21b35e8f61a5afe3c63a9c722d995826762eb19b18beeccf5157f"
+checksum = "7a3a40bd11dc43d1cb110e730b80620cf8102f4cca8920a02b65954da0ed931f"
 dependencies = [
+ "memchr",
  "unicode-properties",
  "unicode-xid",
 ]
 
 [[package]]
 name = "ra-ap-rustc_parse_format"
-version = "0.91.0"
+version = "0.97.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "740383328d7033393e5385f4a6073b880d5811b0fc0fd2559e481f905940f2f8"
+checksum = "5feb877478994cb4c0c0c7a5116a352eefc0634aefc8636feb00a893fa5b7135"
 dependencies = [
  "ra-ap-rustc_index",
  "ra-ap-rustc_lexer",
@@ -1566,9 +1570,9 @@ dependencies = [
 
 [[package]]
 name = "ra-ap-rustc_pattern_analysis"
-version = "0.91.0"
+version = "0.97.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c39f544728f32cebffb1a8b92ba3c1f3dcb4144081438d192137ed197d479a9d"
+checksum = "a76774d35934d464c4115908cde16f76a4f7e540fe1eea6b79336c556e37bdd3"
 dependencies = [
  "ra-ap-rustc_index",
  "rustc-hash 2.0.0",
@@ -1744,6 +1748,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "583034fd73374156e66797ed8e5b0d5690409c9226b22d87cb7f19821c05d152"
 
 [[package]]
+name = "rustc-stable-hash"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2febf9acc5ee5e99d1ad0afcdbccc02d87aa3f857a1f01f825b80eacf8edfcd1"
+
+[[package]]
 name = "rustc_apfloat"
 version = "0.2.1+llvm-462a31f5a5ab"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -2001,9 +2011,9 @@ dependencies = [
 
 [[package]]
 name = "tenthash"
-version = "0.4.0"
+version = "1.0.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d67f9f3cf70e0852941d7bc3cb884b49b24b8ee956baf91ad0abae31f5ef11fb"
+checksum = "2d092d622df8bb64e5de8dc86a3667702d5f1e0fe2f0604c6035540703c8cd1e"
 
 [[package]]
 name = "test-fixture"
diff --git a/src/tools/rust-analyzer/Cargo.toml b/src/tools/rust-analyzer/Cargo.toml
index c42ae171d86..1ff36a68e8f 100644
--- a/src/tools/rust-analyzer/Cargo.toml
+++ b/src/tools/rust-analyzer/Cargo.toml
@@ -64,7 +64,6 @@ ide-db = { path = "./crates/ide-db", version = "0.0.0" }
 ide-diagnostics = { path = "./crates/ide-diagnostics", version = "0.0.0" }
 ide-ssr = { path = "./crates/ide-ssr", version = "0.0.0" }
 intern = { path = "./crates/intern", version = "0.0.0" }
-limit = { path = "./crates/limit", version = "0.0.0" }
 load-cargo = { path = "./crates/load-cargo", version = "0.0.0" }
 mbe = { path = "./crates/mbe", version = "0.0.0" }
 parser = { path = "./crates/parser", version = "0.0.0" }
@@ -87,11 +86,12 @@ vfs-notify = { path = "./crates/vfs-notify", version = "0.0.0" }
 vfs = { path = "./crates/vfs", version = "0.0.0" }
 edition = { path = "./crates/edition", version = "0.0.0" }
 
-ra-ap-rustc_lexer = { version = "0.91", default-features = false }
-ra-ap-rustc_parse_format = { version = "0.91", default-features = false }
-ra-ap-rustc_index = { version = "0.91", default-features = false }
-ra-ap-rustc_abi = { version = "0.91", default-features = false }
-ra-ap-rustc_pattern_analysis = { version = "0.91", default-features = false }
+ra-ap-rustc_hashes = { version = "0.97", default-features = false }
+ra-ap-rustc_lexer = { version = "0.97", default-features = false }
+ra-ap-rustc_parse_format = { version = "0.97", default-features = false }
+ra-ap-rustc_index = { version = "0.97", default-features = false }
+ra-ap-rustc_abi = { version = "0.97", default-features = false }
+ra-ap-rustc_pattern_analysis = { version = "0.97", default-features = false }
 
 # local crates that aren't published to crates.io. These should not have versions.
 
diff --git a/src/tools/rust-analyzer/PRIVACY.md b/src/tools/rust-analyzer/PRIVACY.md
index 89e252be731..ef9c2437ab7 100644
--- a/src/tools/rust-analyzer/PRIVACY.md
+++ b/src/tools/rust-analyzer/PRIVACY.md
@@ -1 +1 @@
-See the [Privacy](https://rust-analyzer.github.io/manual.html#privacy) section of the user manual.
+See the [Privacy](https://rust-analyzer.github.io/book/privacy.html) section of the user manual.
diff --git a/src/tools/rust-analyzer/README.md b/src/tools/rust-analyzer/README.md
index 552f71f1518..4360dea4a11 100644
--- a/src/tools/rust-analyzer/README.md
+++ b/src/tools/rust-analyzer/README.md
@@ -1,6 +1,6 @@
 <p align="center">
   <img
-    src="https://raw.githubusercontent.com/rust-analyzer/rust-analyzer/master/assets/logo-wide.svg"
+    src="https://raw.githubusercontent.com/rust-lang/rust-analyzer/master/assets/logo-wide.svg"
     alt="rust-analyzer logo">
 </p>
 
@@ -9,21 +9,22 @@ It is a part of a larger rls-2.0 effort to create excellent IDE support for Rust
 
 ## Quick Start
 
-https://rust-analyzer.github.io/manual.html#installation
+https://rust-analyzer.github.io/book/installation.html
 
 ## Documentation
 
 If you want to **contribute** to rust-analyzer check out the [CONTRIBUTING.md](./CONTRIBUTING.md) or
-if you are just curious about how things work under the hood, check the [./docs/dev](./docs/dev)
-folder.
+if you are just curious about how things work under the hood, see the
+[Contributing](https://rust-analyzer.github.io/book/contributing) section of the manual.
 
 If you want to **use** rust-analyzer's language server with your editor of
-choice, check [the manual](https://rust-analyzer.github.io/manual.html) folder.
+choice, check [the manual](https://rust-analyzer.github.io/book/).
 It also contains some tips & tricks to help you be more productive when using rust-analyzer.
 
 ## Security and Privacy
 
-See the corresponding sections of [the manual](https://rust-analyzer.github.io/manual.html#security).
+See the [security](https://rust-analyzer.github.io/book/security.html) and
+[privacy](https://rust-analyzer.github.io/book/privacy.html) sections of the manual.
 
 ## Communication
 
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 a0fc8c31eaf..bd08387b582 100644
--- a/src/tools/rust-analyzer/crates/base-db/src/input.rs
+++ b/src/tools/rust-analyzer/crates/base-db/src/input.rs
@@ -129,9 +129,9 @@ impl fmt::Display for CrateName {
 }
 
 impl ops::Deref for CrateName {
-    type Target = str;
-    fn deref(&self) -> &str {
-        self.0.as_str()
+    type Target = Symbol;
+    fn deref(&self) -> &Symbol {
+        &self.0
     }
 }
 
@@ -230,8 +230,8 @@ impl fmt::Display for CrateDisplayName {
 }
 
 impl ops::Deref for CrateDisplayName {
-    type Target = str;
-    fn deref(&self) -> &str {
+    type Target = Symbol;
+    fn deref(&self) -> &Symbol {
         &self.crate_name
     }
 }
@@ -296,6 +296,9 @@ pub struct CrateData {
     pub dependencies: Vec<Dependency>,
     pub origin: CrateOrigin,
     pub is_proc_macro: bool,
+    /// The working directory to run proc-macros in. This is the workspace root of the cargo workspace
+    /// for workspace members, the crate manifest dir otherwise.
+    pub proc_macro_cwd: Option<AbsPathBuf>,
 }
 
 #[derive(Default, Clone, PartialEq, Eq)]
@@ -360,8 +363,9 @@ impl CrateGraph {
         cfg_options: Arc<CfgOptions>,
         potential_cfg_options: Option<Arc<CfgOptions>>,
         mut env: Env,
-        is_proc_macro: bool,
         origin: CrateOrigin,
+        is_proc_macro: bool,
+        proc_macro_cwd: Option<AbsPathBuf>,
     ) -> CrateId {
         env.entries.shrink_to_fit();
         let data = CrateData {
@@ -375,6 +379,7 @@ impl CrateGraph {
             dependencies: Vec::new(),
             origin,
             is_proc_macro,
+            proc_macro_cwd,
         };
         self.arena.alloc(data)
     }
@@ -698,8 +703,9 @@ mod tests {
             Default::default(),
             Default::default(),
             Env::default(),
-            false,
             CrateOrigin::Local { repo: None, name: None },
+            false,
+            None,
         );
         let crate2 = graph.add_crate_root(
             FileId::from_raw(2u32),
@@ -709,8 +715,9 @@ mod tests {
             Default::default(),
             Default::default(),
             Env::default(),
-            false,
             CrateOrigin::Local { repo: None, name: None },
+            false,
+            None,
         );
         let crate3 = graph.add_crate_root(
             FileId::from_raw(3u32),
@@ -720,8 +727,9 @@ mod tests {
             Default::default(),
             Default::default(),
             Env::default(),
-            false,
             CrateOrigin::Local { repo: None, name: None },
+            false,
+            None,
         );
         assert!(graph
             .add_dep(crate1, Dependency::new(CrateName::new("crate2").unwrap(), crate2,))
@@ -745,8 +753,9 @@ mod tests {
             Default::default(),
             Default::default(),
             Env::default(),
-            false,
             CrateOrigin::Local { repo: None, name: None },
+            false,
+            None,
         );
         let crate2 = graph.add_crate_root(
             FileId::from_raw(2u32),
@@ -756,8 +765,9 @@ mod tests {
             Default::default(),
             Default::default(),
             Env::default(),
-            false,
             CrateOrigin::Local { repo: None, name: None },
+            false,
+            None,
         );
         assert!(graph
             .add_dep(crate1, Dependency::new(CrateName::new("crate2").unwrap(), crate2,))
@@ -778,8 +788,9 @@ mod tests {
             Default::default(),
             Default::default(),
             Env::default(),
-            false,
             CrateOrigin::Local { repo: None, name: None },
+            false,
+            None,
         );
         let crate2 = graph.add_crate_root(
             FileId::from_raw(2u32),
@@ -789,8 +800,9 @@ mod tests {
             Default::default(),
             Default::default(),
             Env::default(),
-            false,
             CrateOrigin::Local { repo: None, name: None },
+            false,
+            None,
         );
         let crate3 = graph.add_crate_root(
             FileId::from_raw(3u32),
@@ -800,8 +812,9 @@ mod tests {
             Default::default(),
             Default::default(),
             Env::default(),
-            false,
             CrateOrigin::Local { repo: None, name: None },
+            false,
+            None,
         );
         assert!(graph
             .add_dep(crate1, Dependency::new(CrateName::new("crate2").unwrap(), crate2,))
@@ -822,8 +835,9 @@ mod tests {
             Default::default(),
             Default::default(),
             Env::default(),
-            false,
             CrateOrigin::Local { repo: None, name: None },
+            false,
+            None,
         );
         let crate2 = graph.add_crate_root(
             FileId::from_raw(2u32),
@@ -833,8 +847,9 @@ mod tests {
             Default::default(),
             Default::default(),
             Env::default(),
-            false,
             CrateOrigin::Local { repo: None, name: None },
+            false,
+            None,
         );
         assert!(graph
             .add_dep(
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 c7e4168f6bc..eed8c886839 100644
--- a/src/tools/rust-analyzer/crates/base-db/src/lib.rs
+++ b/src/tools/rust-analyzer/crates/base-db/src/lib.rs
@@ -10,7 +10,7 @@ use rustc_hash::FxHashMap;
 use span::EditionedFileId;
 use syntax::{ast, Parse, SourceFile, SyntaxError};
 use triomphe::Arc;
-use vfs::{AbsPathBuf, FileId};
+use vfs::FileId;
 
 pub use crate::{
     change::FileChange,
@@ -85,8 +85,6 @@ pub trait SourceDatabase: FileLoader + std::fmt::Debug {
 /// Crate related data shared by the whole workspace.
 #[derive(Debug, PartialEq, Eq, Hash, Clone)]
 pub struct CrateWorkspaceData {
-    /// The working directory to run proc-macros in. This is usually the workspace root of cargo workspaces.
-    pub proc_macro_cwd: Option<AbsPathBuf>,
     // FIXME: Consider removing this, making HirDatabase::target_data_layout an input query
     pub data_layout: TargetLayoutLoadResult,
     /// Toolchain version used to compile the crate.
diff --git a/src/tools/rust-analyzer/crates/hir-def/Cargo.toml b/src/tools/rust-analyzer/crates/hir-def/Cargo.toml
index 375f18d9fe1..9a448ec14ea 100644
--- a/src/tools/rust-analyzer/crates/hir-def/Cargo.toml
+++ b/src/tools/rust-analyzer/crates/hir-def/Cargo.toml
@@ -31,6 +31,7 @@ triomphe.workspace = true
 rustc_apfloat = "0.2.0"
 text-size.workspace = true
 
+ra-ap-rustc_hashes.workspace = true
 ra-ap-rustc_parse_format.workspace = true
 ra-ap-rustc_abi.workspace = true
 
@@ -43,7 +44,6 @@ hir-expand.workspace = true
 mbe.workspace = true
 cfg.workspace = true
 tt.workspace = true
-limit.workspace = true
 span.workspace = true
 
 
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 12f5f6ad79a..bec66278772 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/data.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/data.rs
@@ -95,10 +95,14 @@ impl FunctionData {
             .map(Box::new);
         let rustc_allow_incoherent_impl = attrs.by_key(&sym::rustc_allow_incoherent_impl).exists();
         if flags.contains(FnFlags::HAS_UNSAFE_KW)
-            && !crate_graph[krate].edition.at_least_2024()
             && attrs.by_key(&sym::rustc_deprecated_safe_2024).exists()
         {
             flags.remove(FnFlags::HAS_UNSAFE_KW);
+            flags.insert(FnFlags::DEPRECATED_SAFE_2024);
+        }
+
+        if attrs.by_key(&sym::target_feature).exists() {
+            flags.insert(FnFlags::HAS_TARGET_FEATURE);
         }
 
         Arc::new(FunctionData {
@@ -148,6 +152,10 @@ impl FunctionData {
         self.flags.contains(FnFlags::HAS_UNSAFE_KW)
     }
 
+    pub fn is_deprecated_safe_2024(&self) -> bool {
+        self.flags.contains(FnFlags::DEPRECATED_SAFE_2024)
+    }
+
     pub fn is_safe(&self) -> bool {
         self.flags.contains(FnFlags::HAS_SAFE_KW)
     }
@@ -155,6 +163,10 @@ impl FunctionData {
     pub fn is_varargs(&self) -> bool {
         self.flags.contains(FnFlags::IS_VARARGS)
     }
+
+    pub fn has_target_feature(&self) -> bool {
+        self.flags.contains(FnFlags::HAS_TARGET_FEATURE)
+    }
 }
 
 fn parse_rustc_legacy_const_generics(tt: &crate::tt::TopSubtree) -> Box<[u32]> {
@@ -238,6 +250,7 @@ bitflags::bitflags! {
         const RUSTC_HAS_INCOHERENT_INHERENT_IMPLS = 1 << 3;
         const SKIP_ARRAY_DURING_METHOD_DISPATCH = 1 << 4;
         const SKIP_BOXED_SLICE_DURING_METHOD_DISPATCH = 1 << 5;
+        const RUSTC_PAREN_SUGAR = 1 << 6;
     }
 }
 
@@ -282,6 +295,9 @@ impl TraitData {
         if attrs.by_key(&sym::rustc_has_incoherent_inherent_impls).exists() {
             flags |= TraitFlags::RUSTC_HAS_INCOHERENT_INHERENT_IMPLS;
         }
+        if attrs.by_key(&sym::rustc_paren_sugar).exists() {
+            flags |= TraitFlags::RUSTC_PAREN_SUGAR;
+        }
 
         let mut skip_array_during_method_dispatch =
             attrs.by_key(&sym::rustc_skip_array_during_method_dispatch).exists();
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/data/adt.rs b/src/tools/rust-analyzer/crates/hir-def/src/data/adt.rs
index 8fc19854033..c94622016d3 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/data/adt.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/data/adt.rs
@@ -9,6 +9,7 @@ use hir_expand::name::Name;
 use intern::sym;
 use la_arena::Arena;
 use rustc_abi::{Align, Integer, IntegerType, ReprFlags, ReprOptions};
+use rustc_hashes::Hash64;
 use triomphe::Arc;
 use tt::iter::TtElement;
 
@@ -172,7 +173,13 @@ fn parse_repr_tt(tt: &TopSubtree) -> Option<ReprOptions> {
         }
     }
 
-    Some(ReprOptions { int, align: max_align, pack: min_pack, flags, field_shuffle_seed: 0 })
+    Some(ReprOptions {
+        int,
+        align: max_align,
+        pack: min_pack,
+        flags,
+        field_shuffle_seed: Hash64::ZERO,
+    })
 }
 
 impl StructData {
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 bf6cc1dcade..598a850898b 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/db.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/db.rs
@@ -10,12 +10,12 @@ use triomphe::Arc;
 
 use crate::{
     attr::{Attrs, AttrsWithOwner},
-    body::{scope::ExprScopes, Body, BodySourceMap},
     data::{
         adt::{EnumData, EnumVariantData, StructData, VariantData},
         ConstData, ExternCrateDeclData, FunctionData, ImplData, Macro2Data, MacroRulesData,
         ProcMacroData, StaticData, TraitAliasData, TraitData, TypeAliasData,
     },
+    expr_store::{scope::ExprScopes, Body, BodySourceMap},
     generics::GenericParams,
     import_map::ImportMap,
     item_tree::{AttrOwner, ItemTree, ItemTreeSourceMaps},
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/dyn_map.rs b/src/tools/rust-analyzer/crates/hir-def/src/dyn_map.rs
index 0f73595347b..e9318d146dd 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/dyn_map.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/dyn_map.rs
@@ -31,9 +31,9 @@ pub mod keys {
 
     use crate::{
         dyn_map::{DynMap, Policy},
-        BlockId, ConstId, EnumId, EnumVariantId, ExternCrateId, FieldId, FunctionId, ImplId,
-        LifetimeParamId, Macro2Id, MacroRulesId, ProcMacroId, StaticId, StructId, TraitAliasId,
-        TraitId, TypeAliasId, TypeOrConstParamId, UnionId, UseId,
+        BlockId, ConstId, EnumId, EnumVariantId, ExternBlockId, ExternCrateId, FieldId, FunctionId,
+        ImplId, LifetimeParamId, Macro2Id, MacroRulesId, ProcMacroId, StaticId, StructId,
+        TraitAliasId, TraitId, TypeAliasId, TypeOrConstParamId, UnionId, UseId,
     };
 
     pub type Key<K, V> = crate::dyn_map::Key<AstPtr<K>, V, AstPtrPolicy<K, V>>;
@@ -44,6 +44,7 @@ pub mod keys {
     pub const STATIC: Key<ast::Static, StaticId> = Key::new();
     pub const TYPE_ALIAS: Key<ast::TypeAlias, TypeAliasId> = Key::new();
     pub const IMPL: Key<ast::Impl, ImplId> = Key::new();
+    pub const EXTERN_BLOCK: Key<ast::ExternBlock, ExternBlockId> = Key::new();
     pub const TRAIT: Key<ast::Trait, TraitId> = Key::new();
     pub const TRAIT_ALIAS: Key<ast::TraitAlias, TraitAliasId> = Key::new();
     pub const STRUCT: Key<ast::Struct, StructId> = Key::new();
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/expander.rs b/src/tools/rust-analyzer/crates/hir-def/src/expander.rs
index 108258d5a11..a1b3123c991 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/expander.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/expander.rs
@@ -9,7 +9,6 @@ use hir_expand::{
     attrs::RawAttrs, mod_path::ModPath, span_map::SpanMap, ExpandError, ExpandErrorKind,
     ExpandResult, HirFileId, InFile, Lookup, MacroCallId,
 };
-use limit::Limit;
 use span::{Edition, SyntaxContextId};
 use syntax::{ast, Parse};
 use triomphe::Arc;
@@ -28,18 +27,18 @@ pub struct Expander {
     pub(crate) module: ModuleId,
     /// `recursion_depth == usize::MAX` indicates that the recursion limit has been reached.
     recursion_depth: u32,
-    recursion_limit: Limit,
+    recursion_limit: usize,
 }
 
 impl Expander {
     pub fn new(db: &dyn DefDatabase, current_file_id: HirFileId, module: ModuleId) -> Expander {
         let recursion_limit = module.def_map(db).recursion_limit() as usize;
-        let recursion_limit = Limit::new(if cfg!(test) {
+        let recursion_limit = if cfg!(test) {
             // Without this, `body::tests::your_stack_belongs_to_me` stack-overflows in debug
             std::cmp::min(32, recursion_limit)
         } else {
             recursion_limit
-        });
+        };
         Expander {
             current_file_id,
             module,
@@ -194,7 +193,7 @@ impl Expander {
         let Some(call_id) = value else {
             return ExpandResult { value: None, err };
         };
-        if self.recursion_limit.check(self.recursion_depth as usize + 1).is_err() {
+        if self.recursion_depth as usize > self.recursion_limit {
             self.recursion_depth = u32::MAX;
             cov_mark::hit!(your_stack_belongs_to_me);
             return ExpandResult::only_err(ExpandError::new(
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/body.rs b/src/tools/rust-analyzer/crates/hir-def/src/expr_store.rs
index de439249306..5ff6a7ffe56 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/body.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/expr_store.rs
@@ -1,18 +1,19 @@
-//! Defines `Body`: a lowered representation of bodies of functions, statics and
+//! Defines `ExpressionStore`: a lowered representation of functions, statics and
 //! consts.
+mod body;
 mod lower;
 mod pretty;
 pub mod scope;
+
 #[cfg(test)]
 mod tests;
 
 use std::ops::{Deref, Index};
 
-use base_db::CrateId;
 use cfg::{CfgExpr, CfgOptions};
 use either::Either;
 use hir_expand::{name::Name, ExpandError, InFile};
-use la_arena::{Arena, ArenaMap, Idx, RawIdx};
+use la_arena::{Arena, ArenaMap};
 use rustc_hash::FxHashMap;
 use smallvec::SmallVec;
 use span::{Edition, MacroFileId, SyntaxContextData};
@@ -22,19 +23,18 @@ use tt::TextRange;
 
 use crate::{
     db::DefDatabase,
-    expander::Expander,
     hir::{
-        dummy_expr_id, Array, AsmOperand, Binding, BindingId, Expr, ExprId, ExprOrPatId, Label,
-        LabelId, Pat, PatId, RecordFieldPat, Statement,
+        Array, AsmOperand, Binding, BindingId, Expr, ExprId, ExprOrPatId, Label, LabelId, Pat,
+        PatId, RecordFieldPat, Statement,
     },
-    item_tree::AttrOwner,
     nameres::DefMap,
     path::{ModPath, Path},
-    src::HasSource,
     type_ref::{TypeRef, TypeRefId, TypesMap, TypesSourceMap},
-    BlockId, DefWithBodyId, HasModule, Lookup, SyntheticSyntax,
+    BlockId, DefWithBodyId, Lookup, SyntheticSyntax,
 };
 
+pub use self::body::{Body, BodySourceMap};
+
 /// A wrapper around [`span::SyntaxContextId`] that is intended only for comparisons.
 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
 pub struct HygieneId(span::SyntaxContextId);
@@ -58,9 +58,29 @@ impl HygieneId {
     }
 }
 
-/// The body of an item (function, const etc.).
+pub type ExprPtr = AstPtr<ast::Expr>;
+pub type ExprSource = InFile<ExprPtr>;
+
+pub type PatPtr = AstPtr<ast::Pat>;
+pub type PatSource = InFile<PatPtr>;
+
+pub type LabelPtr = AstPtr<ast::Label>;
+pub type LabelSource = InFile<LabelPtr>;
+
+pub type FieldPtr = AstPtr<ast::RecordExprField>;
+pub type FieldSource = InFile<FieldPtr>;
+
+pub type PatFieldPtr = AstPtr<Either<ast::RecordExprField, ast::RecordPatField>>;
+pub type PatFieldSource = InFile<PatFieldPtr>;
+
+pub type ExprOrPatPtr = AstPtr<Either<ast::Expr, ast::Pat>>;
+pub type ExprOrPatSource = InFile<ExprOrPatPtr>;
+
+pub type SelfParamPtr = AstPtr<ast::SelfParam>;
+pub type MacroCallPtr = AstPtr<ast::MacroCall>;
+
 #[derive(Debug, Eq, PartialEq)]
-pub struct Body {
+pub struct ExpressionStore {
     pub exprs: Arena<Expr>,
     pub pats: Arena<Pat>,
     pub bindings: Arena<Binding>,
@@ -68,19 +88,9 @@ pub struct Body {
     /// Id of the closure/coroutine that owns the corresponding binding. If a binding is owned by the
     /// top level expression, it will not be listed in here.
     pub binding_owners: FxHashMap<BindingId, ExprId>,
-    /// The patterns for the function's parameters. While the parameter types are
-    /// part of the function signature, the patterns are not (they don't change
-    /// the external type of the function).
-    ///
-    /// If this `Body` is for the body of a constant, this will just be
-    /// empty.
-    pub params: Box<[PatId]>,
-    pub self_param: Option<BindingId>,
-    /// The `ExprId` of the actual body expression.
-    pub body_expr: ExprId,
     pub types: TypesMap,
-    /// Block expressions in this body that may contain inner items.
-    block_scopes: Vec<BlockId>,
+    /// Block expressions in this store that may contain inner items.
+    block_scopes: Box<[BlockId]>,
 
     /// A map from binding to its hygiene ID.
     ///
@@ -92,56 +102,24 @@ pub struct Body {
     binding_hygiene: FxHashMap<BindingId, HygieneId>,
     /// A map from an variable usages to their hygiene ID.
     ///
-    /// Expressions that can be recorded here are single segment path, although not all single segments path refer
+    /// Expressions (and destructuing patterns) that can be recorded here are single segment path, although not all single segments path refer
     /// to variables and have hygiene (some refer to items, we don't know at this stage).
-    expr_hygiene: FxHashMap<ExprId, HygieneId>,
-    /// A map from a destructuring assignment possible variable usages to their hygiene ID.
-    pat_hygiene: FxHashMap<PatId, HygieneId>,
+    ident_hygiene: FxHashMap<ExprOrPatId, HygieneId>,
 }
 
-pub type ExprPtr = AstPtr<ast::Expr>;
-pub type ExprSource = InFile<ExprPtr>;
-
-pub type PatPtr = AstPtr<ast::Pat>;
-pub type PatSource = InFile<PatPtr>;
-
-pub type LabelPtr = AstPtr<ast::Label>;
-pub type LabelSource = InFile<LabelPtr>;
-
-pub type FieldPtr = AstPtr<ast::RecordExprField>;
-pub type FieldSource = InFile<FieldPtr>;
-
-pub type PatFieldPtr = AstPtr<Either<ast::RecordExprField, ast::RecordPatField>>;
-pub type PatFieldSource = InFile<PatFieldPtr>;
-
-pub type ExprOrPatPtr = AstPtr<Either<ast::Expr, ast::Pat>>;
-pub type ExprOrPatSource = InFile<ExprOrPatPtr>;
-
-/// An item body together with the mapping from syntax nodes to HIR expression
-/// IDs. This is needed to go from e.g. a position in a file to the HIR
-/// expression containing it; but for type inference etc., we want to operate on
-/// a structure that is agnostic to the actual positions of expressions in the
-/// file, so that we don't recompute types whenever some whitespace is typed.
-///
-/// One complication here is that, due to macro expansion, a single `Body` might
-/// be spread across several files. So, for each ExprId and PatId, we record
-/// both the HirFileId and the position inside the file. However, we only store
-/// AST -> ExprId mapping for non-macro files, as it is not clear how to handle
-/// this properly for macros.
-#[derive(Default, Debug, Eq, PartialEq)]
-pub struct BodySourceMap {
+#[derive(Debug, Eq, PartialEq, Default)]
+pub struct ExpressionStoreSourceMap {
     // AST expressions can create patterns in destructuring assignments. Therefore, `ExprSource` can also map
     // to `PatId`, and `PatId` can also map to `ExprSource` (the other way around is unaffected).
     expr_map: FxHashMap<ExprSource, ExprOrPatId>,
-    expr_map_back: ArenaMap<ExprId, ExprSource>,
+    expr_map_back: ArenaMap<ExprId, ExprOrPatSource>,
 
-    pat_map: FxHashMap<PatSource, PatId>,
+    pat_map: FxHashMap<PatSource, ExprOrPatId>,
     pat_map_back: ArenaMap<PatId, ExprOrPatSource>,
 
     label_map: FxHashMap<LabelSource, LabelId>,
     label_map_back: ArenaMap<LabelId, LabelSource>,
 
-    self_param: Option<InFile<AstPtr<ast::SelfParam>>>,
     binding_definitions: FxHashMap<BindingId, SmallVec<[PatId; 4]>>,
 
     /// We don't create explicit nodes for record fields (`S { record_field: 92 }`).
@@ -153,11 +131,25 @@ pub struct BodySourceMap {
 
     template_map: Option<Box<FormatTemplate>>,
 
-    expansions: FxHashMap<InFile<AstPtr<ast::MacroCall>>, MacroFileId>,
+    expansions: FxHashMap<InFile<MacroCallPtr>, MacroFileId>,
 
-    /// Diagnostics accumulated during body lowering. These contain `AstPtr`s and so are stored in
+    /// Diagnostics accumulated during lowering. These contain `AstPtr`s and so are stored in
     /// the source map (since they're just as volatile).
-    diagnostics: Vec<BodyDiagnostic>,
+    diagnostics: Vec<ExpressionStoreDiagnostics>,
+}
+
+/// The body of an item (function, const etc.).
+#[derive(Debug, Eq, PartialEq, Default)]
+pub struct ExpressionStoreBuilder {
+    pub exprs: Arena<Expr>,
+    pub pats: Arena<Pat>,
+    pub bindings: Arena<Binding>,
+    pub labels: Arena<Label>,
+    pub binding_owners: FxHashMap<BindingId, ExprId>,
+    pub types: TypesMap,
+    block_scopes: Vec<BlockId>,
+    binding_hygiene: FxHashMap<BindingId, HygieneId>,
+    ident_hygiene: FxHashMap<ExprOrPatId, HygieneId>,
 }
 
 #[derive(Default, Debug, Eq, PartialEq)]
@@ -171,166 +163,62 @@ struct FormatTemplate {
     /// The value stored for each capture is its template literal and offset inside it. The template literal
     /// is from the `format_args[_nl]!()` macro and so needs to be mapped up once to go to the user-written
     /// template.
-    implicit_capture_to_source: FxHashMap<ExprId, InFile<(AstPtr<ast::Expr>, TextRange)>>,
+    implicit_capture_to_source: FxHashMap<ExprId, InFile<(ExprPtr, TextRange)>>,
 }
 
 #[derive(Debug, Eq, PartialEq)]
-pub enum BodyDiagnostic {
+pub enum ExpressionStoreDiagnostics {
     InactiveCode { node: InFile<SyntaxNodePtr>, cfg: CfgExpr, opts: CfgOptions },
-    MacroError { node: InFile<AstPtr<ast::MacroCall>>, err: ExpandError },
-    UnresolvedMacroCall { node: InFile<AstPtr<ast::MacroCall>>, path: ModPath },
+    MacroError { node: InFile<MacroCallPtr>, err: ExpandError },
+    UnresolvedMacroCall { node: InFile<MacroCallPtr>, path: ModPath },
     UnreachableLabel { node: InFile<AstPtr<ast::Lifetime>>, name: Name },
     AwaitOutsideOfAsync { node: InFile<AstPtr<ast::AwaitExpr>>, location: String },
     UndeclaredLabel { node: InFile<AstPtr<ast::Lifetime>>, name: Name },
 }
 
-impl Body {
-    pub(crate) fn body_with_source_map_query(
-        db: &dyn DefDatabase,
-        def: DefWithBodyId,
-    ) -> (Arc<Body>, Arc<BodySourceMap>) {
-        let _p = tracing::info_span!("body_with_source_map_query").entered();
-        let mut params = None;
-
-        let mut is_async_fn = false;
-        let InFile { file_id, value: body } = {
-            match def {
-                DefWithBodyId::FunctionId(f) => {
-                    let data = db.function_data(f);
-                    let f = f.lookup(db);
-                    let src = f.source(db);
-                    params = src.value.param_list().map(move |param_list| {
-                        let item_tree = f.id.item_tree(db);
-                        let func = &item_tree[f.id.value];
-                        let krate = f.container.module(db).krate;
-                        let crate_graph = db.crate_graph();
-                        (
-                            param_list,
-                            (0..func.params.len()).map(move |idx| {
-                                item_tree
-                                    .attrs(
-                                        db,
-                                        krate,
-                                        AttrOwner::Param(
-                                            f.id.value,
-                                            Idx::from_raw(RawIdx::from(idx as u32)),
-                                        ),
-                                    )
-                                    .is_cfg_enabled(&crate_graph[krate].cfg_options)
-                            }),
-                        )
-                    });
-                    is_async_fn = data.is_async();
-                    src.map(|it| it.body().map(ast::Expr::from))
-                }
-                DefWithBodyId::ConstId(c) => {
-                    let c = c.lookup(db);
-                    let src = c.source(db);
-                    src.map(|it| it.body())
-                }
-                DefWithBodyId::StaticId(s) => {
-                    let s = s.lookup(db);
-                    let src = s.source(db);
-                    src.map(|it| it.body())
-                }
-                DefWithBodyId::VariantId(v) => {
-                    let s = v.lookup(db);
-                    let src = s.source(db);
-                    src.map(|it| it.expr())
-                }
-                DefWithBodyId::InTypeConstId(c) => c.lookup(db).id.map(|_| c.source(db).expr()),
-            }
-        };
-        let module = def.module(db);
-        let expander = Expander::new(db, file_id, module);
-        let (mut body, mut source_map) =
-            Body::new(db, def, expander, params, body, module.krate, is_async_fn);
-        body.shrink_to_fit();
-        source_map.shrink_to_fit();
-
-        (Arc::new(body), Arc::new(source_map))
-    }
-
-    pub(crate) fn body_query(db: &dyn DefDatabase, def: DefWithBodyId) -> Arc<Body> {
-        db.body_with_source_map(def).0
-    }
-
-    /// Returns an iterator over all block expressions in this body that define inner items.
-    pub fn blocks<'a>(
-        &'a self,
-        db: &'a dyn DefDatabase,
-    ) -> impl Iterator<Item = (BlockId, Arc<DefMap>)> + 'a {
-        self.block_scopes.iter().map(move |&block| (block, db.block_def_map(block)))
-    }
-
-    pub fn pretty_print(
-        &self,
-        db: &dyn DefDatabase,
-        owner: DefWithBodyId,
-        edition: Edition,
-    ) -> String {
-        pretty::print_body_hir(db, self, owner, edition)
-    }
-
-    pub fn pretty_print_expr(
-        &self,
-        db: &dyn DefDatabase,
-        owner: DefWithBodyId,
-        expr: ExprId,
-        edition: Edition,
-    ) -> String {
-        pretty::print_expr_hir(db, self, owner, expr, edition)
-    }
-
-    pub fn pretty_print_pat(
-        &self,
-        db: &dyn DefDatabase,
-        owner: DefWithBodyId,
-        pat: PatId,
-        oneline: bool,
-        edition: Edition,
-    ) -> String {
-        pretty::print_pat_hir(db, self, owner, pat, oneline, edition)
-    }
-
-    fn new(
-        db: &dyn DefDatabase,
-        owner: DefWithBodyId,
-        expander: Expander,
-        params: Option<(ast::ParamList, impl Iterator<Item = bool>)>,
-        body: Option<ast::Expr>,
-        krate: CrateId,
-        is_async_fn: bool,
-    ) -> (Body, BodySourceMap) {
-        lower::lower(db, owner, expander, params, body, krate, is_async_fn)
-    }
-
-    fn shrink_to_fit(&mut self) {
+impl ExpressionStoreBuilder {
+    fn finish(self) -> ExpressionStore {
         let Self {
-            body_expr: _,
-            params: _,
-            self_param: _,
             block_scopes,
-            exprs,
-            labels,
-            pats,
-            bindings,
-            binding_owners,
-            binding_hygiene,
-            expr_hygiene,
-            pat_hygiene,
-            types,
+            mut exprs,
+            mut labels,
+            mut pats,
+            mut bindings,
+            mut binding_owners,
+            mut binding_hygiene,
+            mut ident_hygiene,
+            mut types,
         } = self;
-        block_scopes.shrink_to_fit();
         exprs.shrink_to_fit();
         labels.shrink_to_fit();
         pats.shrink_to_fit();
         bindings.shrink_to_fit();
         binding_owners.shrink_to_fit();
         binding_hygiene.shrink_to_fit();
-        expr_hygiene.shrink_to_fit();
-        pat_hygiene.shrink_to_fit();
+        ident_hygiene.shrink_to_fit();
         types.shrink_to_fit();
+
+        ExpressionStore {
+            exprs,
+            pats,
+            bindings,
+            labels,
+            binding_owners,
+            types,
+            block_scopes: block_scopes.into_boxed_slice(),
+            binding_hygiene,
+            ident_hygiene,
+        }
+    }
+}
+
+impl ExpressionStore {
+    /// Returns an iterator over all block expressions in this store that define inner items.
+    pub fn blocks<'a>(
+        &'a self,
+        db: &'a dyn DefDatabase,
+    ) -> impl Iterator<Item = (BlockId, Arc<DefMap>)> + 'a {
+        self.block_scopes.iter().map(move |&block| (block, db.block_def_map(block)))
     }
 
     pub fn walk_bindings_in_pat(&self, pat_id: PatId, mut f: impl FnMut(BindingId)) {
@@ -658,11 +546,11 @@ impl Body {
     }
 
     pub fn expr_path_hygiene(&self, expr: ExprId) -> HygieneId {
-        self.expr_hygiene.get(&expr).copied().unwrap_or(HygieneId::ROOT)
+        self.ident_hygiene.get(&expr.into()).copied().unwrap_or(HygieneId::ROOT)
     }
 
     pub fn pat_path_hygiene(&self, pat: PatId) -> HygieneId {
-        self.pat_hygiene.get(&pat).copied().unwrap_or(HygieneId::ROOT)
+        self.ident_hygiene.get(&pat.into()).copied().unwrap_or(HygieneId::ROOT)
     }
 
     pub fn expr_or_pat_path_hygiene(&self, id: ExprOrPatId) -> HygieneId {
@@ -673,27 +561,7 @@ impl Body {
     }
 }
 
-impl Default for Body {
-    fn default() -> Self {
-        Self {
-            body_expr: dummy_expr_id(),
-            exprs: Default::default(),
-            pats: Default::default(),
-            bindings: Default::default(),
-            labels: Default::default(),
-            params: Default::default(),
-            block_scopes: Default::default(),
-            binding_owners: Default::default(),
-            self_param: Default::default(),
-            binding_hygiene: Default::default(),
-            expr_hygiene: Default::default(),
-            pat_hygiene: Default::default(),
-            types: Default::default(),
-        }
-    }
-}
-
-impl Index<ExprId> for Body {
+impl Index<ExprId> for ExpressionStore {
     type Output = Expr;
 
     fn index(&self, expr: ExprId) -> &Expr {
@@ -701,7 +569,7 @@ impl Index<ExprId> for Body {
     }
 }
 
-impl Index<PatId> for Body {
+impl Index<PatId> for ExpressionStore {
     type Output = Pat;
 
     fn index(&self, pat: PatId) -> &Pat {
@@ -709,7 +577,7 @@ impl Index<PatId> for Body {
     }
 }
 
-impl Index<LabelId> for Body {
+impl Index<LabelId> for ExpressionStore {
     type Output = Label;
 
     fn index(&self, label: LabelId) -> &Label {
@@ -717,7 +585,7 @@ impl Index<LabelId> for Body {
     }
 }
 
-impl Index<BindingId> for Body {
+impl Index<BindingId> for ExpressionStore {
     type Output = Binding;
 
     fn index(&self, b: BindingId) -> &Binding {
@@ -725,7 +593,7 @@ impl Index<BindingId> for Body {
     }
 }
 
-impl Index<TypeRefId> for Body {
+impl Index<TypeRefId> for ExpressionStore {
     type Output = TypeRef;
 
     fn index(&self, b: TypeRefId) -> &TypeRef {
@@ -735,15 +603,15 @@ impl Index<TypeRefId> for Body {
 
 // FIXME: Change `node_` prefix to something more reasonable.
 // Perhaps `expr_syntax` and `expr_id`?
-impl BodySourceMap {
+impl ExpressionStoreSourceMap {
     pub fn expr_or_pat_syntax(&self, id: ExprOrPatId) -> Result<ExprOrPatSource, SyntheticSyntax> {
         match id {
-            ExprOrPatId::ExprId(id) => self.expr_syntax(id).map(|it| it.map(AstPtr::wrap_left)),
+            ExprOrPatId::ExprId(id) => self.expr_syntax(id),
             ExprOrPatId::PatId(id) => self.pat_syntax(id),
         }
     }
 
-    pub fn expr_syntax(&self, expr: ExprId) -> Result<ExprSource, SyntheticSyntax> {
+    pub fn expr_syntax(&self, expr: ExprId) -> Result<ExprOrPatSource, SyntheticSyntax> {
         self.expr_map_back.get(expr).cloned().ok_or(SyntheticSyntax)
     }
 
@@ -757,9 +625,7 @@ impl BodySourceMap {
         self.expansions.get(&src).cloned()
     }
 
-    pub fn macro_calls(
-        &self,
-    ) -> impl Iterator<Item = (InFile<AstPtr<ast::MacroCall>>, MacroFileId)> + '_ {
+    pub fn macro_calls(&self) -> impl Iterator<Item = (InFile<MacroCallPtr>, MacroFileId)> + '_ {
         self.expansions.iter().map(|(&a, &b)| (a, b))
     }
 
@@ -767,11 +633,7 @@ impl BodySourceMap {
         self.pat_map_back.get(pat).cloned().ok_or(SyntheticSyntax)
     }
 
-    pub fn self_param_syntax(&self) -> Option<InFile<AstPtr<ast::SelfParam>>> {
-        self.self_param
-    }
-
-    pub fn node_pat(&self, node: InFile<&ast::Pat>) -> Option<PatId> {
+    pub fn node_pat(&self, node: InFile<&ast::Pat>) -> Option<ExprOrPatId> {
         self.pat_map.get(&node.map(AstPtr::new)).cloned()
     }
 
@@ -801,9 +663,7 @@ impl BodySourceMap {
         self.expr_map.get(&src).copied()
     }
 
-    pub fn expansions(
-        &self,
-    ) -> impl Iterator<Item = (&InFile<AstPtr<ast::MacroCall>>, &MacroFileId)> {
+    pub fn expansions(&self) -> impl Iterator<Item = (&InFile<MacroCallPtr>, &MacroFileId)> {
         self.expansions.iter()
     }
 
@@ -823,7 +683,7 @@ impl BodySourceMap {
     pub fn format_args_implicit_capture(
         &self,
         capture_expr: ExprId,
-    ) -> Option<InFile<(AstPtr<ast::Expr>, TextRange)>> {
+    ) -> Option<InFile<(ExprPtr, TextRange)>> {
         self.template_map.as_ref()?.implicit_capture_to_source.get(&capture_expr).copied()
     }
 
@@ -837,14 +697,13 @@ impl BodySourceMap {
             .zip(self.template_map.as_ref()?.asm_to_captures.get(&expr).map(std::ops::Deref::deref))
     }
 
-    /// Get a reference to the body source map's diagnostics.
-    pub fn diagnostics(&self) -> &[BodyDiagnostic] {
+    /// Get a reference to the source map's diagnostics.
+    pub fn diagnostics(&self) -> &[ExpressionStoreDiagnostics] {
         &self.diagnostics
     }
 
     fn shrink_to_fit(&mut self) {
         let Self {
-            self_param: _,
             expr_map,
             expr_map_back,
             pat_map,
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/expr_store/body.rs b/src/tools/rust-analyzer/crates/hir-def/src/expr_store/body.rs
new file mode 100644
index 00000000000..a55fec4f8b1
--- /dev/null
+++ b/src/tools/rust-analyzer/crates/hir-def/src/expr_store/body.rs
@@ -0,0 +1,175 @@
+//! Defines `Body`: a lowered representation of functions, statics and
+//! consts.
+use std::ops;
+
+use hir_expand::{InFile, Lookup};
+use la_arena::{Idx, RawIdx};
+use span::Edition;
+use syntax::ast;
+use triomphe::Arc;
+
+use crate::{
+    db::DefDatabase,
+    expander::Expander,
+    expr_store::{lower, pretty, ExpressionStore, ExpressionStoreSourceMap, SelfParamPtr},
+    hir::{BindingId, ExprId, PatId},
+    item_tree::AttrOwner,
+    src::HasSource,
+    DefWithBodyId, HasModule,
+};
+
+/// The body of an item (function, const etc.).
+#[derive(Debug, Eq, PartialEq)]
+pub struct Body {
+    pub store: ExpressionStore,
+    /// The patterns for the function's parameters. While the parameter types are
+    /// part of the function signature, the patterns are not (they don't change
+    /// the external type of the function).
+    ///
+    /// If this `Body` is for the body of a constant, this will just be
+    /// empty.
+    pub params: Box<[PatId]>,
+    pub self_param: Option<BindingId>,
+    /// The `ExprId` of the actual body expression.
+    pub body_expr: ExprId,
+}
+
+impl ops::Deref for Body {
+    type Target = ExpressionStore;
+
+    fn deref(&self) -> &Self::Target {
+        &self.store
+    }
+}
+
+/// An item body together with the mapping from syntax nodes to HIR expression
+/// IDs. This is needed to go from e.g. a position in a file to the HIR
+/// expression containing it; but for type inference etc., we want to operate on
+/// a structure that is agnostic to the actual positions of expressions in the
+/// file, so that we don't recompute types whenever some whitespace is typed.
+///
+/// One complication here is that, due to macro expansion, a single `Body` might
+/// be spread across several files. So, for each ExprId and PatId, we record
+/// both the HirFileId and the position inside the file. However, we only store
+/// AST -> ExprId mapping for non-macro files, as it is not clear how to handle
+/// this properly for macros.
+#[derive(Default, Debug, Eq, PartialEq)]
+pub struct BodySourceMap {
+    pub self_param: Option<InFile<SelfParamPtr>>,
+    pub store: ExpressionStoreSourceMap,
+}
+
+impl ops::Deref for BodySourceMap {
+    type Target = ExpressionStoreSourceMap;
+
+    fn deref(&self) -> &Self::Target {
+        &self.store
+    }
+}
+
+impl Body {
+    pub(crate) fn body_with_source_map_query(
+        db: &dyn DefDatabase,
+        def: DefWithBodyId,
+    ) -> (Arc<Body>, Arc<BodySourceMap>) {
+        let _p = tracing::info_span!("body_with_source_map_query").entered();
+        let mut params = None;
+
+        let mut is_async_fn = false;
+        let InFile { file_id, value: body } = {
+            match def {
+                DefWithBodyId::FunctionId(f) => {
+                    let data = db.function_data(f);
+                    let f = f.lookup(db);
+                    let src = f.source(db);
+                    params = src.value.param_list().map(move |param_list| {
+                        let item_tree = f.id.item_tree(db);
+                        let func = &item_tree[f.id.value];
+                        let krate = f.container.module(db).krate;
+                        let crate_graph = db.crate_graph();
+                        (
+                            param_list,
+                            (0..func.params.len()).map(move |idx| {
+                                item_tree
+                                    .attrs(
+                                        db,
+                                        krate,
+                                        AttrOwner::Param(
+                                            f.id.value,
+                                            Idx::from_raw(RawIdx::from(idx as u32)),
+                                        ),
+                                    )
+                                    .is_cfg_enabled(&crate_graph[krate].cfg_options)
+                            }),
+                        )
+                    });
+                    is_async_fn = data.is_async();
+                    src.map(|it| it.body().map(ast::Expr::from))
+                }
+                DefWithBodyId::ConstId(c) => {
+                    let c = c.lookup(db);
+                    let src = c.source(db);
+                    src.map(|it| it.body())
+                }
+                DefWithBodyId::StaticId(s) => {
+                    let s = s.lookup(db);
+                    let src = s.source(db);
+                    src.map(|it| it.body())
+                }
+                DefWithBodyId::VariantId(v) => {
+                    let s = v.lookup(db);
+                    let src = s.source(db);
+                    src.map(|it| it.expr())
+                }
+                DefWithBodyId::InTypeConstId(c) => c.lookup(db).id.map(|_| c.source(db).expr()),
+            }
+        };
+        let module = def.module(db);
+        let expander = Expander::new(db, file_id, module);
+        let (body, mut source_map) =
+            lower::lower_body(db, def, expander, params, body, module.krate, is_async_fn);
+        source_map.store.shrink_to_fit();
+
+        (Arc::new(body), Arc::new(source_map))
+    }
+
+    pub(crate) fn body_query(db: &dyn DefDatabase, def: DefWithBodyId) -> Arc<Body> {
+        db.body_with_source_map(def).0
+    }
+
+    pub fn pretty_print(
+        &self,
+        db: &dyn DefDatabase,
+        owner: DefWithBodyId,
+        edition: Edition,
+    ) -> String {
+        pretty::print_body_hir(db, self, owner, edition)
+    }
+
+    pub fn pretty_print_expr(
+        &self,
+        db: &dyn DefDatabase,
+        owner: DefWithBodyId,
+        expr: ExprId,
+        edition: Edition,
+    ) -> String {
+        pretty::print_expr_hir(db, self, owner, expr, edition)
+    }
+
+    pub fn pretty_print_pat(
+        &self,
+        db: &dyn DefDatabase,
+        owner: DefWithBodyId,
+        pat: PatId,
+        oneline: bool,
+        edition: Edition,
+    ) -> String {
+        pretty::print_pat_hir(db, self, owner, pat, oneline, edition)
+    }
+}
+
+impl BodySourceMap {
+    pub fn self_param_syntax(&self) -> Option<InFile<SelfParamPtr>> {
+        self.self_param
+    }
+}
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/body/lower.rs b/src/tools/rust-analyzer/crates/hir-def/src/expr_store/lower.rs
index 16c7b5ca00a..6e505a6b112 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/body/lower.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/expr_store/lower.rs
@@ -29,11 +29,14 @@ use triomphe::Arc;
 
 use crate::{
     attr::Attrs,
-    body::{Body, BodyDiagnostic, BodySourceMap, ExprPtr, HygieneId, LabelPtr, PatPtr},
     builtin_type::BuiltinUint,
     data::adt::StructKind,
     db::DefDatabase,
     expander::Expander,
+    expr_store::{
+        Body, BodySourceMap, ExprPtr, ExpressionStore, ExpressionStoreBuilder,
+        ExpressionStoreDiagnostics, ExpressionStoreSourceMap, HygieneId, LabelPtr, PatPtr,
+    },
     hir::{
         format_args::{
             self, FormatAlignment, FormatArgs, FormatArgsPiece, FormatArgument, FormatArgumentKind,
@@ -41,8 +44,8 @@ use crate::{
             FormatPlaceholder, FormatSign, FormatTrait,
         },
         Array, Binding, BindingAnnotation, BindingId, BindingProblems, CaptureBy, ClosureKind,
-        Expr, ExprId, Item, Label, LabelId, Literal, LiteralOrConst, MatchArm, Movability,
-        OffsetOf, Pat, PatId, RecordFieldPat, RecordLitField, Statement,
+        Expr, ExprId, Item, Label, LabelId, Literal, MatchArm, Movability, OffsetOf, Pat, PatId,
+        RecordFieldPat, RecordLitField, Statement,
     },
     item_scope::BuiltinShadowMode,
     lang_item::LangItem,
@@ -55,11 +58,11 @@ use crate::{
 
 type FxIndexSet<K> = indexmap::IndexSet<K, std::hash::BuildHasherDefault<rustc_hash::FxHasher>>;
 
-pub(super) fn lower(
+pub(super) fn lower_body(
     db: &dyn DefDatabase,
     owner: DefWithBodyId,
     expander: Expander,
-    params: Option<(ast::ParamList, impl Iterator<Item = bool>)>,
+    parameters: Option<(ast::ParamList, impl Iterator<Item = bool>)>,
     body: Option<ast::Expr>,
     krate: CrateId,
     is_async_fn: bool,
@@ -75,35 +78,146 @@ pub(super) fn lower(
         };
         Arc::clone(span_map)
     });
-    ExprCollector {
-        db,
-        owner,
-        krate,
-        def_map: expander.module.def_map(db),
-        source_map: BodySourceMap::default(),
-        ast_id_map: db.ast_id_map(expander.current_file_id()),
-        body: Body::default(),
-        expander,
-        current_try_block_label: None,
-        is_lowering_coroutine: false,
-        label_ribs: Vec::new(),
-        current_binding_owner: None,
-        awaitable_context: None,
-        current_span_map: span_map,
-        current_block_legacy_macro_defs_count: FxHashMap::default(),
-    }
-    .collect(params, body, is_async_fn)
+
+    let mut self_param = None;
+    let mut source_map_self_param = None;
+    let mut params = vec![];
+    let mut collector = ExprCollector::new(db, owner, expander, krate, span_map);
+
+    let skip_body = match owner {
+        DefWithBodyId::FunctionId(it) => db.attrs(it.into()),
+        DefWithBodyId::StaticId(it) => db.attrs(it.into()),
+        DefWithBodyId::ConstId(it) => db.attrs(it.into()),
+        DefWithBodyId::InTypeConstId(_) => Attrs::EMPTY,
+        DefWithBodyId::VariantId(it) => db.attrs(it.into()),
+    }
+    .rust_analyzer_tool()
+    .any(|attr| *attr.path() == tool_path![skip]);
+    // If #[rust_analyzer::skip] annotated, only construct enough information for the signature
+    // and skip the body.
+    if skip_body {
+        if let Some((param_list, mut attr_enabled)) = parameters {
+            if let Some(self_param_syn) =
+                param_list.self_param().filter(|_| attr_enabled.next().unwrap_or(false))
+            {
+                let is_mutable =
+                    self_param_syn.mut_token().is_some() && self_param_syn.amp_token().is_none();
+                let binding_id: la_arena::Idx<Binding> = collector.alloc_binding(
+                    Name::new_symbol_root(sym::self_.clone()),
+                    BindingAnnotation::new(is_mutable, false),
+                );
+                self_param = Some(binding_id);
+                source_map_self_param =
+                    Some(collector.expander.in_file(AstPtr::new(&self_param_syn)));
+            }
+            params = param_list
+                .params()
+                .zip(attr_enabled)
+                .filter(|(_, enabled)| *enabled)
+                .map(|_| collector.missing_pat())
+                .collect();
+        };
+        let body_expr = collector.missing_expr();
+        return (
+            Body {
+                store: collector.store.finish(),
+                params: params.into_boxed_slice(),
+                self_param,
+                body_expr,
+            },
+            BodySourceMap { self_param: source_map_self_param, store: collector.source_map },
+        );
+    }
+
+    if let Some((param_list, mut attr_enabled)) = parameters {
+        if let Some(self_param_syn) =
+            param_list.self_param().filter(|_| attr_enabled.next().unwrap_or(false))
+        {
+            let is_mutable =
+                self_param_syn.mut_token().is_some() && self_param_syn.amp_token().is_none();
+            let binding_id: la_arena::Idx<Binding> = collector.alloc_binding(
+                Name::new_symbol_root(sym::self_.clone()),
+                BindingAnnotation::new(is_mutable, false),
+            );
+            let hygiene = self_param_syn
+                .name()
+                .map(|name| collector.hygiene_id_for(name.syntax().text_range().start()))
+                .unwrap_or(HygieneId::ROOT);
+            if !hygiene.is_root() {
+                collector.store.binding_hygiene.insert(binding_id, hygiene);
+            }
+            self_param = Some(binding_id);
+            source_map_self_param = Some(collector.expander.in_file(AstPtr::new(&self_param_syn)));
+        }
+
+        for (param, _) in param_list.params().zip(attr_enabled).filter(|(_, enabled)| *enabled) {
+            let param_pat = collector.collect_pat_top(param.pat());
+            params.push(param_pat);
+        }
+    };
+
+    let body_expr = collector.collect(
+        body,
+        if is_async_fn {
+            Awaitable::Yes
+        } else {
+            match owner {
+                DefWithBodyId::FunctionId(..) => Awaitable::No("non-async function"),
+                DefWithBodyId::StaticId(..) => Awaitable::No("static"),
+                DefWithBodyId::ConstId(..) | DefWithBodyId::InTypeConstId(..) => {
+                    Awaitable::No("constant")
+                }
+                DefWithBodyId::VariantId(..) => Awaitable::No("enum variant"),
+            }
+        },
+    );
+
+    (
+        Body {
+            store: collector.store.finish(),
+            params: params.into_boxed_slice(),
+            self_param,
+            body_expr,
+        },
+        BodySourceMap { self_param: source_map_self_param, store: collector.source_map },
+    )
+}
+
+#[allow(dead_code)]
+pub(super) fn lower(
+    db: &dyn DefDatabase,
+    owner: ExprStoreOwnerId,
+    expander: Expander,
+    body: Option<ast::Expr>,
+    krate: CrateId,
+) -> (ExpressionStore, ExpressionStoreSourceMap) {
+    // We cannot leave the root span map empty and let any identifier from it be treated as root,
+    // because when inside nested macros `SyntaxContextId`s from the outer macro will be interleaved
+    // with the inner macro, and that will cause confusion because they won't be the same as `ROOT`
+    // even though they should be the same. Also, when the body comes from multiple expansions, their
+    // hygiene is different.
+    let span_map = expander.current_file_id().macro_file().map(|_| {
+        let SpanMap::ExpansionSpanMap(span_map) = expander.span_map(db) else {
+            panic!("in a macro file there should be `ExpansionSpanMap`");
+        };
+        Arc::clone(span_map)
+    });
+    let mut expr_collector = ExprCollector::new(db, owner, expander, krate, span_map);
+    expr_collector.collect(body, Awaitable::No("?"));
+    (expr_collector.store.finish(), expr_collector.source_map)
 }
 
+type ExprStoreOwnerId = DefWithBodyId;
+
 struct ExprCollector<'a> {
     db: &'a dyn DefDatabase,
     expander: Expander,
-    owner: DefWithBodyId,
+    owner: ExprStoreOwnerId,
     def_map: Arc<DefMap>,
     ast_id_map: Arc<AstIdMap>,
     krate: CrateId,
-    body: Body,
-    source_map: BodySourceMap,
+    store: ExpressionStoreBuilder,
+    source_map: ExpressionStoreSourceMap,
 
     is_lowering_coroutine: bool,
 
@@ -157,6 +271,7 @@ impl RibKind {
     }
 }
 
+#[derive(PartialEq, Eq, Debug, Copy, Clone)]
 enum Awaitable {
     Yes,
     No(&'static str),
@@ -180,12 +295,12 @@ impl BindingList {
         let id = *self.map.entry((name, hygiene)).or_insert_with_key(|(name, _)| {
             let id = ec.alloc_binding(name.clone(), mode);
             if !hygiene.is_root() {
-                ec.body.binding_hygiene.insert(id, hygiene);
+                ec.store.binding_hygiene.insert(id, hygiene);
             }
             id
         });
-        if ec.body.bindings[id].mode != mode {
-            ec.body.bindings[id].problems = Some(BindingProblems::BoundInconsistently);
+        if ec.store.bindings[id].mode != mode {
+            ec.store.bindings[id].problems = Some(BindingProblems::BoundInconsistently);
         }
         self.check_is_used(ec, id);
         id
@@ -195,11 +310,11 @@ impl BindingList {
         match self.is_used.get(&id) {
             None => {
                 if self.reject_new {
-                    ec.body.bindings[id].problems = Some(BindingProblems::NotBoundAcrossAll);
+                    ec.store.bindings[id].problems = Some(BindingProblems::NotBoundAcrossAll);
                 }
             }
             Some(true) => {
-                ec.body.bindings[id].problems = Some(BindingProblems::BoundMoreThanOnce);
+                ec.store.bindings[id].problems = Some(BindingProblems::BoundMoreThanOnce);
             }
             Some(false) => {}
         }
@@ -208,93 +323,37 @@ impl BindingList {
 }
 
 impl ExprCollector<'_> {
-    fn collect(
-        mut self,
-        param_list: Option<(ast::ParamList, impl Iterator<Item = bool>)>,
-        body: Option<ast::Expr>,
-        is_async_fn: bool,
-    ) -> (Body, BodySourceMap) {
-        let skip_body = match self.owner {
-            DefWithBodyId::FunctionId(it) => self.db.attrs(it.into()),
-            DefWithBodyId::StaticId(it) => self.db.attrs(it.into()),
-            DefWithBodyId::ConstId(it) => self.db.attrs(it.into()),
-            DefWithBodyId::InTypeConstId(_) => Attrs::EMPTY,
-            DefWithBodyId::VariantId(it) => self.db.attrs(it.into()),
+    fn new(
+        db: &dyn DefDatabase,
+        owner: ExprStoreOwnerId,
+        expander: Expander,
+        krate: CrateId,
+        span_map: Option<Arc<ExpansionSpanMap>>,
+    ) -> ExprCollector<'_> {
+        ExprCollector {
+            db,
+            owner,
+            krate,
+            def_map: expander.module.def_map(db),
+            source_map: ExpressionStoreSourceMap::default(),
+            ast_id_map: db.ast_id_map(expander.current_file_id()),
+            store: ExpressionStoreBuilder::default(),
+            expander,
+            current_try_block_label: None,
+            is_lowering_coroutine: false,
+            label_ribs: Vec::new(),
+            current_binding_owner: None,
+            awaitable_context: None,
+            current_span_map: span_map,
+            current_block_legacy_macro_defs_count: FxHashMap::default(),
         }
-        .rust_analyzer_tool()
-        .any(|attr| *attr.path() == tool_path![skip]);
-        // If #[rust_analyzer::skip] annotated, only construct enough information for the signature
-        // and skip the body.
-        if skip_body {
-            self.body.body_expr = self.missing_expr();
-            if let Some((param_list, mut attr_enabled)) = param_list {
-                if let Some(self_param) =
-                    param_list.self_param().filter(|_| attr_enabled.next().unwrap_or(false))
-                {
-                    let is_mutable =
-                        self_param.mut_token().is_some() && self_param.amp_token().is_none();
-                    let binding_id: la_arena::Idx<Binding> = self.alloc_binding(
-                        Name::new_symbol_root(sym::self_.clone()),
-                        BindingAnnotation::new(is_mutable, false),
-                    );
-                    self.body.self_param = Some(binding_id);
-                    self.source_map.self_param =
-                        Some(self.expander.in_file(AstPtr::new(&self_param)));
-                }
-                self.body.params = param_list
-                    .params()
-                    .zip(attr_enabled)
-                    .filter(|(_, enabled)| *enabled)
-                    .map(|_| self.missing_pat())
-                    .collect();
-            };
-            return (self.body, self.source_map);
-        }
-
-        self.awaitable_context.replace(if is_async_fn {
-            Awaitable::Yes
-        } else {
-            match self.owner {
-                DefWithBodyId::FunctionId(..) => Awaitable::No("non-async function"),
-                DefWithBodyId::StaticId(..) => Awaitable::No("static"),
-                DefWithBodyId::ConstId(..) | DefWithBodyId::InTypeConstId(..) => {
-                    Awaitable::No("constant")
-                }
-                DefWithBodyId::VariantId(..) => Awaitable::No("enum variant"),
-            }
-        });
-        if let Some((param_list, mut attr_enabled)) = param_list {
-            let mut params = vec![];
-            if let Some(self_param) =
-                param_list.self_param().filter(|_| attr_enabled.next().unwrap_or(false))
-            {
-                let is_mutable =
-                    self_param.mut_token().is_some() && self_param.amp_token().is_none();
-                let binding_id: la_arena::Idx<Binding> = self.alloc_binding(
-                    Name::new_symbol_root(sym::self_.clone()),
-                    BindingAnnotation::new(is_mutable, false),
-                );
-                let hygiene = self_param
-                    .name()
-                    .map(|name| self.hygiene_id_for(name.syntax().text_range().start()))
-                    .unwrap_or(HygieneId::ROOT);
-                if !hygiene.is_root() {
-                    self.body.binding_hygiene.insert(binding_id, hygiene);
-                }
-                self.body.self_param = Some(binding_id);
-                self.source_map.self_param = Some(self.expander.in_file(AstPtr::new(&self_param)));
-            }
+    }
 
-            for (param, _) in param_list.params().zip(attr_enabled).filter(|(_, enabled)| *enabled)
-            {
-                let param_pat = self.collect_pat_top(param.pat());
-                params.push(param_pat);
-            }
-            self.body.params = params.into_boxed_slice();
-        };
-        self.body.body_expr = self.with_label_rib(RibKind::Closure, |this| {
-            if is_async_fn {
-                match body {
+    fn collect(&mut self, expr: Option<ast::Expr>, awaitable: Awaitable) -> ExprId {
+        self.awaitable_context.replace(awaitable);
+        self.with_label_rib(RibKind::Closure, |this| {
+            if awaitable == Awaitable::Yes {
+                match expr {
                     Some(e) => {
                         let syntax_ptr = AstPtr::new(&e);
                         let expr = this.collect_expr(e);
@@ -306,15 +365,13 @@ impl ExprCollector<'_> {
                     None => this.missing_expr(),
                 }
             } else {
-                this.collect_expr_opt(body)
+                this.collect_expr_opt(expr)
             }
-        });
-
-        (self.body, self.source_map)
+        })
     }
 
     fn ctx(&mut self) -> LowerCtx<'_> {
-        self.expander.ctx(self.db, &mut self.body.types, &mut self.source_map.types)
+        self.expander.ctx(self.db, &mut self.store.types, &mut self.source_map.types)
     }
 
     fn collect_expr(&mut self, expr: ast::Expr) -> ExprId {
@@ -390,7 +447,7 @@ impl ExprCollector<'_> {
                                 parent: this.owner,
                                 root: inner_expr,
                             });
-                            this.body.exprs[result_expr_id] = Expr::Const(it);
+                            this.store.exprs[result_expr_id] = Expr::Const(it);
                             this.current_binding_owner = prev_binding_owner;
                             result_expr_id
                         })
@@ -480,7 +537,7 @@ impl ExprCollector<'_> {
                     .unwrap_or((Expr::Missing, HygieneId::ROOT));
                 let expr_id = self.alloc_expr(path, syntax_ptr);
                 if !hygiene.is_root() {
-                    self.body.expr_hygiene.insert(expr_id, hygiene);
+                    self.store.ident_hygiene.insert(expr_id.into(), hygiene);
                 }
                 expr_id
             }
@@ -562,10 +619,12 @@ impl ExprCollector<'_> {
             ast::Expr::AwaitExpr(e) => {
                 let expr = self.collect_expr_opt(e.expr());
                 if let Awaitable::No(location) = self.is_lowering_awaitable_block() {
-                    self.source_map.diagnostics.push(BodyDiagnostic::AwaitOutsideOfAsync {
-                        node: InFile::new(self.expander.current_file_id(), AstPtr::new(&e)),
-                        location: location.to_string(),
-                    });
+                    self.source_map.diagnostics.push(
+                        ExpressionStoreDiagnostics::AwaitOutsideOfAsync {
+                            node: InFile::new(self.expander.current_file_id(), AstPtr::new(&e)),
+                            location: location.to_string(),
+                        },
+                    );
                 }
                 self.alloc_expr(Expr::Await { expr }, syntax_ptr)
             }
@@ -646,7 +705,7 @@ impl ExprCollector<'_> {
                 this.is_lowering_coroutine = prev_is_lowering_coroutine;
                 this.current_binding_owner = prev_binding_owner;
                 this.current_try_block_label = prev_try_block_label;
-                this.body.exprs[result_expr_id] = Expr::Closure {
+                this.store.exprs[result_expr_id] = Expr::Closure {
                     args: args.into(),
                     arg_types: arg_types.into(),
                     ret_type,
@@ -752,7 +811,7 @@ impl ExprCollector<'_> {
     }
 
     fn parse_path(&mut self, path: ast::Path) -> Option<Path> {
-        self.expander.parse_path(self.db, path, &mut self.body.types, &mut self.source_map.types)
+        self.expander.parse_path(self.db, path, &mut self.store.types, &mut self.source_map.types)
     }
 
     fn collect_expr_path(&mut self, e: ast::PathExpr) -> Option<(Path, HygieneId)> {
@@ -781,7 +840,7 @@ impl ExprCollector<'_> {
             let src = self.expander.in_file(AstPtr::new(&expr).wrap_left());
             let expr = self.collect_expr(expr);
             // Do not use `alloc_pat_from_expr()` here, it will override the entry in `expr_map`.
-            let id = self.body.pats.alloc(Pat::Expr(expr));
+            let id = self.store.pats.alloc(Pat::Expr(expr));
             self.source_map.pat_map_back.insert(id, src);
             id
         })
@@ -835,7 +894,7 @@ impl ExprCollector<'_> {
                     .unwrap_or((Pat::Missing, HygieneId::ROOT));
                 let pat_id = self.alloc_pat_from_expr(path, syntax_ptr);
                 if !hygiene.is_root() {
-                    self.body.pat_hygiene.insert(pat_id, hygiene);
+                    self.store.ident_hygiene.insert(pat_id.into(), hygiene);
                 }
                 pat_id
             }
@@ -967,7 +1026,7 @@ impl ExprCollector<'_> {
     ) -> ExprId {
         let (id, prev_owner) = self.initialize_binding_owner(syntax_ptr);
         let tmp = job(self);
-        self.body.exprs[id] = mem::replace(&mut self.body.exprs[tmp], Expr::Missing);
+        self.store.exprs[id] = mem::replace(&mut self.store.exprs[tmp], Expr::Missing);
         self.current_binding_owner = prev_owner;
         id
     }
@@ -979,8 +1038,9 @@ impl ExprCollector<'_> {
         let Some(try_from_output) = self.lang_path(LangItem::TryTraitFromOutput) else {
             return self.collect_block(e);
         };
-        let label = self
-            .alloc_label_desugared(Label { name: Name::generate_new_name(self.body.labels.len()) });
+        let label = self.alloc_label_desugared(Label {
+            name: Name::generate_new_name(self.store.labels.len()),
+        });
         let old_label = self.current_try_block_label.replace(label);
 
         let ptr = AstPtr::new(&e).upcast();
@@ -1006,7 +1066,7 @@ impl ExprCollector<'_> {
                 )
             }
         };
-        let Expr::Block { tail, .. } = &mut self.body.exprs[expr_id] else {
+        let Expr::Block { tail, .. } = &mut self.store.exprs[expr_id] else {
             unreachable!("block was lowered to non-block");
         };
         *tail = Some(next_tail);
@@ -1112,7 +1172,7 @@ impl ExprCollector<'_> {
                 this.collect_expr_opt(e.loop_body().map(|it| it.into()))
             }),
         };
-        let iter_name = Name::generate_new_name(self.body.exprs.len());
+        let iter_name = Name::generate_new_name(self.store.exprs.len());
         let iter_expr = self.alloc_expr(Expr::Path(Path::from(iter_name.clone())), syntax_ptr);
         let iter_expr_mut = self.alloc_expr(
             Expr::Ref { expr: iter_expr, rawness: Rawness::Ref, mutability: Mutability::Mut },
@@ -1177,7 +1237,7 @@ impl ExprCollector<'_> {
         let try_branch = self.alloc_expr(Expr::Path(try_branch), syntax_ptr);
         let expr = self
             .alloc_expr(Expr::Call { callee: try_branch, args: Box::new([operand]) }, syntax_ptr);
-        let continue_name = Name::generate_new_name(self.body.bindings.len());
+        let continue_name = Name::generate_new_name(self.store.bindings.len());
         let continue_binding =
             self.alloc_binding(continue_name.clone(), BindingAnnotation::Unannotated);
         let continue_bpat =
@@ -1192,7 +1252,7 @@ impl ExprCollector<'_> {
             guard: None,
             expr: self.alloc_expr(Expr::Path(Path::from(continue_name)), syntax_ptr),
         };
-        let break_name = Name::generate_new_name(self.body.bindings.len());
+        let break_name = Name::generate_new_name(self.store.bindings.len());
         let break_binding = self.alloc_binding(break_name.clone(), BindingAnnotation::Unannotated);
         let break_bpat = self.alloc_pat_desugared(Pat::Bind { id: break_binding, subpat: None });
         self.add_definition_to_binding(break_binding, break_bpat);
@@ -1261,17 +1321,19 @@ impl ExprCollector<'_> {
             Ok(res) => res,
             Err(UnresolvedMacro { path }) => {
                 if record_diagnostics {
-                    self.source_map.diagnostics.push(BodyDiagnostic::UnresolvedMacroCall {
-                        node: InFile::new(outer_file, syntax_ptr),
-                        path,
-                    });
+                    self.source_map.diagnostics.push(
+                        ExpressionStoreDiagnostics::UnresolvedMacroCall {
+                            node: InFile::new(outer_file, syntax_ptr),
+                            path,
+                        },
+                    );
                 }
                 return collector(self, None);
             }
         };
         if record_diagnostics {
             if let Some(err) = res.err {
-                self.source_map.diagnostics.push(BodyDiagnostic::MacroError {
+                self.source_map.diagnostics.push(ExpressionStoreDiagnostics::MacroError {
                     node: InFile::new(outer_file, syntax_ptr),
                     err,
                 });
@@ -1464,7 +1526,7 @@ impl ExprCollector<'_> {
         let (module, def_map) =
             match block_id.map(|block_id| (self.db.block_def_map(block_id), block_id)) {
                 Some((def_map, block_id)) => {
-                    self.body.block_scopes.push(block_id);
+                    self.store.block_scopes.push(block_id);
                     (def_map.module_id(DefMap::ROOT), def_map)
                 }
                 None => (self.expander.module, self.def_map.clone()),
@@ -1621,7 +1683,7 @@ impl ExprCollector<'_> {
                     pats.push(self.collect_pat(rest, binding_list));
                     for (&id, &is_used) in binding_list.is_used.iter() {
                         if !is_used {
-                            self.body.bindings[id].problems =
+                            self.store.bindings[id].problems =
                                 Some(BindingProblems::NotBoundAcrossAll);
                         }
                     }
@@ -1722,23 +1784,33 @@ impl ExprCollector<'_> {
                         self.collect_macro_call(call, macro_ptr, true, |this, expanded_pat| {
                             this.collect_pat_opt(expanded_pat, binding_list)
                         });
-                    self.source_map.pat_map.insert(src, pat);
+                    self.source_map.pat_map.insert(src, pat.into());
                     return pat;
                 }
                 None => Pat::Missing,
             },
-            // FIXME: implement in a way that also builds source map and calculates assoc resolutions in type inference.
             ast::Pat::RangePat(p) => {
-                let mut range_part_lower = |p: Option<ast::Pat>| {
-                    p.and_then(|it| match &it {
-                        ast::Pat::LiteralPat(it) => {
-                            Some(Box::new(LiteralOrConst::Literal(pat_literal_to_hir(it)?.0)))
-                        }
-                        pat @ (ast::Pat::IdentPat(_) | ast::Pat::PathPat(_)) => {
-                            let subpat = self.collect_pat(pat.clone(), binding_list);
-                            Some(Box::new(LiteralOrConst::Const(subpat)))
+                let mut range_part_lower = |p: Option<ast::Pat>| -> Option<ExprId> {
+                    p.and_then(|it| {
+                        let ptr = PatPtr::new(&it);
+                        match &it {
+                            ast::Pat::LiteralPat(it) => Some(self.alloc_expr_from_pat(
+                                Expr::Literal(pat_literal_to_hir(it)?.0),
+                                ptr,
+                            )),
+                            ast::Pat::IdentPat(ident) if ident.is_simple_ident() => ident
+                                .name()
+                                .map(|name| name.as_name())
+                                .map(Path::from)
+                                .map(|path| self.alloc_expr_from_pat(Expr::Path(path), ptr)),
+                            ast::Pat::PathPat(p) => p
+                                .path()
+                                .and_then(|path| self.parse_path(path))
+                                .map(|parsed| self.alloc_expr_from_pat(Expr::Path(parsed), ptr)),
+                            // We only need to handle literal, ident (if bare) and path patterns here,
+                            // as any other pattern as a range pattern operand is semantically invalid.
+                            _ => None,
                         }
-                        _ => None,
                     })
                 };
                 let start = range_part_lower(p.start());
@@ -1801,7 +1873,7 @@ impl ExprCollector<'_> {
                             }
                         });
                     if let Some(pat) = pat.left() {
-                        self.source_map.pat_map.insert(src, pat);
+                        self.source_map.pat_map.insert(src, pat.into());
                     }
                     pat
                 }
@@ -1825,7 +1897,7 @@ impl ExprCollector<'_> {
                     return Some(());
                 }
 
-                self.source_map.diagnostics.push(BodyDiagnostic::InactiveCode {
+                self.source_map.diagnostics.push(ExpressionStoreDiagnostics::InactiveCode {
                     node: self.expander.in_file(SyntaxNodePtr::new(owner.syntax())),
                     cfg,
                     opts: self.expander.cfg_options().clone(),
@@ -1853,7 +1925,7 @@ impl ExprCollector<'_> {
     fn resolve_label(
         &self,
         lifetime: Option<ast::Lifetime>,
-    ) -> Result<Option<LabelId>, BodyDiagnostic> {
+    ) -> Result<Option<LabelId>, ExpressionStoreDiagnostics> {
         let Some(lifetime) = lifetime else { return Ok(None) };
         let (mut hygiene_id, mut hygiene_info) = match &self.current_span_map {
             None => (HygieneId::ROOT, None),
@@ -1877,7 +1949,7 @@ impl ExprCollector<'_> {
                         return if self.is_label_valid_from_rib(rib_idx) {
                             Ok(Some(*id))
                         } else {
-                            Err(BodyDiagnostic::UnreachableLabel {
+                            Err(ExpressionStoreDiagnostics::UnreachableLabel {
                                 name,
                                 node: self.expander.in_file(AstPtr::new(&lifetime)),
                             })
@@ -1903,7 +1975,7 @@ impl ExprCollector<'_> {
             }
         }
 
-        Err(BodyDiagnostic::UndeclaredLabel {
+        Err(ExpressionStoreDiagnostics::UndeclaredLabel {
             name,
             node: self.expander.in_file(AstPtr::new(&lifetime)),
         })
@@ -1934,7 +2006,7 @@ impl ExprCollector<'_> {
         f: impl FnOnce(&mut Self) -> T,
     ) -> T {
         self.label_ribs.push(LabelRib::new(RibKind::Normal(
-            self.body[label].name.clone(),
+            self.store.labels[label].name.clone(),
             label,
             hygiene,
         )));
@@ -2023,7 +2095,7 @@ impl ExprCollector<'_> {
                                 );
                         }
                         if !hygiene.is_root() {
-                            self.body.expr_hygiene.insert(expr_id, hygiene);
+                            self.store.ident_hygiene.insert(expr_id.into(), hygiene);
                         }
                         expr_id
                     },
@@ -2171,17 +2243,27 @@ impl ExprCollector<'_> {
         let unsafe_arg_new = self.alloc_expr_desugared(Expr::Path(unsafe_arg_new));
         let unsafe_arg_new =
             self.alloc_expr_desugared(Expr::Call { callee: unsafe_arg_new, args: Box::default() });
-        let unsafe_arg_new = self.alloc_expr_desugared(Expr::Unsafe {
+        let mut unsafe_arg_new = self.alloc_expr_desugared(Expr::Unsafe {
             id: None,
-            // We collect the unused expressions here so that we still infer them instead of
-            // dropping them out of the expression tree
-            statements: fmt
-                .orphans
-                .into_iter()
-                .map(|expr| Statement::Expr { expr, has_semi: true })
-                .collect(),
+            statements: Box::new([]),
             tail: Some(unsafe_arg_new),
         });
+        if !fmt.orphans.is_empty() {
+            unsafe_arg_new = self.alloc_expr_desugared(Expr::Block {
+                id: None,
+                // We collect the unused expressions here so that we still infer them instead of
+                // dropping them out of the expression tree. We cannot store them in the `Unsafe`
+                // block because then unsafe blocks within them will get a false "unused unsafe"
+                // diagnostic (rustc has a notion of builtin unsafe blocks, but we don't).
+                statements: fmt
+                    .orphans
+                    .into_iter()
+                    .map(|expr| Statement::Expr { expr, has_semi: true })
+                    .collect(),
+                tail: Some(unsafe_arg_new),
+                label: None,
+            });
+        }
 
         let idx = self.alloc_expr(
             Expr::Call {
@@ -2417,20 +2499,20 @@ fn pat_literal_to_hir(lit: &ast::LiteralPat) -> Option<(Literal, ast::Literal)>
 impl ExprCollector<'_> {
     fn alloc_expr(&mut self, expr: Expr, ptr: ExprPtr) -> ExprId {
         let src = self.expander.in_file(ptr);
-        let id = self.body.exprs.alloc(expr);
-        self.source_map.expr_map_back.insert(id, src);
+        let id = self.store.exprs.alloc(expr);
+        self.source_map.expr_map_back.insert(id, src.map(AstPtr::wrap_left));
         self.source_map.expr_map.insert(src, id.into());
         id
     }
     // FIXME: desugared exprs don't have ptr, that's wrong and should be fixed.
     // Migrate to alloc_expr_desugared_with_ptr and then rename back
     fn alloc_expr_desugared(&mut self, expr: Expr) -> ExprId {
-        self.body.exprs.alloc(expr)
+        self.store.exprs.alloc(expr)
     }
     fn alloc_expr_desugared_with_ptr(&mut self, expr: Expr, ptr: ExprPtr) -> ExprId {
         let src = self.expander.in_file(ptr);
-        let id = self.body.exprs.alloc(expr);
-        self.source_map.expr_map_back.insert(id, src);
+        let id = self.store.exprs.alloc(expr);
+        self.source_map.expr_map_back.insert(id, src.map(AstPtr::wrap_left));
         // We intentionally don't fill this as it could overwrite a non-desugared entry
         // self.source_map.expr_map.insert(src, id);
         id
@@ -2440,45 +2522,54 @@ impl ExprCollector<'_> {
     }
 
     fn alloc_binding(&mut self, name: Name, mode: BindingAnnotation) -> BindingId {
-        let binding = self.body.bindings.alloc(Binding { name, mode, problems: None });
+        let binding = self.store.bindings.alloc(Binding { name, mode, problems: None });
         if let Some(owner) = self.current_binding_owner {
-            self.body.binding_owners.insert(binding, owner);
+            self.store.binding_owners.insert(binding, owner);
         }
         binding
     }
 
     fn alloc_pat_from_expr(&mut self, pat: Pat, ptr: ExprPtr) -> PatId {
         let src = self.expander.in_file(ptr);
-        let id = self.body.pats.alloc(pat);
+        let id = self.store.pats.alloc(pat);
         self.source_map.expr_map.insert(src, id.into());
         self.source_map.pat_map_back.insert(id, src.map(AstPtr::wrap_left));
         id
     }
+
+    fn alloc_expr_from_pat(&mut self, expr: Expr, ptr: PatPtr) -> ExprId {
+        let src = self.expander.in_file(ptr);
+        let id = self.store.exprs.alloc(expr);
+        self.source_map.pat_map.insert(src, id.into());
+        self.source_map.expr_map_back.insert(id, src.map(AstPtr::wrap_right));
+        id
+    }
+
     fn alloc_pat(&mut self, pat: Pat, ptr: PatPtr) -> PatId {
         let src = self.expander.in_file(ptr);
-        let id = self.body.pats.alloc(pat);
+        let id = self.store.pats.alloc(pat);
         self.source_map.pat_map_back.insert(id, src.map(AstPtr::wrap_right));
-        self.source_map.pat_map.insert(src, id);
+        self.source_map.pat_map.insert(src, id.into());
         id
     }
     // FIXME: desugared pats don't have ptr, that's wrong and should be fixed somehow.
     fn alloc_pat_desugared(&mut self, pat: Pat) -> PatId {
-        self.body.pats.alloc(pat)
+        self.store.pats.alloc(pat)
     }
     fn missing_pat(&mut self) -> PatId {
-        self.body.pats.alloc(Pat::Missing)
+        self.store.pats.alloc(Pat::Missing)
     }
 
     fn alloc_label(&mut self, label: Label, ptr: LabelPtr) -> LabelId {
         let src = self.expander.in_file(ptr);
-        let id = self.body.labels.alloc(label);
+        let id = self.store.labels.alloc(label);
         self.source_map.label_map_back.insert(id, src);
         self.source_map.label_map.insert(src, id);
         id
     }
     // FIXME: desugared labels don't have ptr, that's wrong and should be fixed somehow.
     fn alloc_label_desugared(&mut self, label: Label) -> LabelId {
-        self.body.labels.alloc(label)
+        self.store.labels.alloc(label)
     }
 
     fn is_lowering_awaitable_block(&self) -> &Awaitable {
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/body/lower/asm.rs b/src/tools/rust-analyzer/crates/hir-def/src/expr_store/lower/asm.rs
index 994ba2aa069..032c18688ea 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/body/lower/asm.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/expr_store/lower/asm.rs
@@ -9,7 +9,7 @@ use syntax::{
 use tt::TextRange;
 
 use crate::{
-    body::lower::{ExprCollector, FxIndexSet},
+    expr_store::lower::{ExprCollector, FxIndexSet},
     hir::{AsmOperand, AsmOptions, Expr, ExprId, InlineAsm, InlineAsmRegOrRegClass},
 };
 
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/body/pretty.rs b/src/tools/rust-analyzer/crates/hir-def/src/expr_store/pretty.rs
index 52b91b522a4..82ad756dc2c 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/body/pretty.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/expr_store/pretty.rs
@@ -6,10 +6,7 @@ use itertools::Itertools;
 use span::Edition;
 
 use crate::{
-    hir::{
-        Array, BindingAnnotation, CaptureBy, ClosureKind, Literal, LiteralOrConst, Movability,
-        Statement,
-    },
+    hir::{Array, BindingAnnotation, CaptureBy, ClosureKind, Literal, Movability, Statement},
     pretty::{print_generic_args, print_path, print_type_ref},
 };
 
@@ -60,7 +57,7 @@ pub(super) fn print_body_hir(
 
     let mut p = Printer {
         db,
-        body,
+        store: body,
         buf: header,
         indent_level: 0,
         line_format: LineFormat::Newline,
@@ -103,14 +100,14 @@ pub(super) fn print_body_hir(
 
 pub(super) fn print_expr_hir(
     db: &dyn DefDatabase,
-    body: &Body,
+    store: &ExpressionStore,
     _owner: DefWithBodyId,
     expr: ExprId,
     edition: Edition,
 ) -> String {
     let mut p = Printer {
         db,
-        body,
+        store,
         buf: String::new(),
         indent_level: 0,
         line_format: LineFormat::Newline,
@@ -122,7 +119,7 @@ pub(super) fn print_expr_hir(
 
 pub(super) fn print_pat_hir(
     db: &dyn DefDatabase,
-    body: &Body,
+    store: &ExpressionStore,
     _owner: DefWithBodyId,
     pat: PatId,
     oneline: bool,
@@ -130,7 +127,7 @@ pub(super) fn print_pat_hir(
 ) -> String {
     let mut p = Printer {
         db,
-        body,
+        store,
         buf: String::new(),
         indent_level: 0,
         line_format: if oneline { LineFormat::Oneline } else { LineFormat::Newline },
@@ -157,7 +154,7 @@ macro_rules! wln {
 
 struct Printer<'a> {
     db: &'a dyn DefDatabase,
-    body: &'a Body,
+    store: &'a ExpressionStore,
     buf: String,
     indent_level: usize,
     line_format: LineFormat,
@@ -233,7 +230,7 @@ impl Printer<'_> {
     }
 
     fn print_expr(&mut self, expr: ExprId) {
-        let expr = &self.body[expr];
+        let expr = &self.store[expr];
 
         match expr {
             Expr::Missing => w!(self, "�"),
@@ -241,7 +238,7 @@ impl Printer<'_> {
             Expr::InlineAsm(_) => w!(self, "builtin#asm(_)"),
             Expr::OffsetOf(offset_of) => {
                 w!(self, "builtin#offset_of(");
-                self.print_type_ref(offset_of.container, &self.body.types);
+                self.print_type_ref(offset_of.container, &self.store.types);
                 let edition = self.edition;
                 w!(
                     self,
@@ -271,7 +268,7 @@ impl Printer<'_> {
             }
             Expr::Loop { body, label } => {
                 if let Some(lbl) = label {
-                    w!(self, "{}: ", self.body[*lbl].name.display(self.db.upcast(), self.edition));
+                    w!(self, "{}: ", self.store[*lbl].name.display(self.db.upcast(), self.edition));
                 }
                 w!(self, "loop ");
                 self.print_expr(*body);
@@ -295,7 +292,7 @@ impl Printer<'_> {
                 if let Some(args) = generic_args {
                     w!(self, "::<");
                     let edition = self.edition;
-                    print_generic_args(self.db, args, &self.body.types, self, edition).unwrap();
+                    print_generic_args(self.db, args, &self.store.types, self, edition).unwrap();
                     w!(self, ">");
                 }
                 w!(self, "(");
@@ -330,13 +327,13 @@ impl Printer<'_> {
             Expr::Continue { label } => {
                 w!(self, "continue");
                 if let Some(lbl) = label {
-                    w!(self, " {}", self.body[*lbl].name.display(self.db.upcast(), self.edition));
+                    w!(self, " {}", self.store[*lbl].name.display(self.db.upcast(), self.edition));
                 }
             }
             Expr::Break { expr, label } => {
                 w!(self, "break");
                 if let Some(lbl) = label {
-                    w!(self, " {}", self.body[*lbl].name.display(self.db.upcast(), self.edition));
+                    w!(self, " {}", self.store[*lbl].name.display(self.db.upcast(), self.edition));
                 }
                 if let Some(expr) = expr {
                     self.whitespace();
@@ -404,7 +401,7 @@ impl Printer<'_> {
             Expr::Cast { expr, type_ref } => {
                 self.print_expr(*expr);
                 w!(self, " as ");
-                self.print_type_ref(*type_ref, &self.body.types);
+                self.print_type_ref(*type_ref, &self.store.types);
             }
             Expr::Ref { expr, rawness, mutability } => {
                 w!(self, "&");
@@ -492,13 +489,13 @@ impl Printer<'_> {
                     self.print_pat(*pat);
                     if let Some(ty) = ty {
                         w!(self, ": ");
-                        self.print_type_ref(*ty, &self.body.types);
+                        self.print_type_ref(*ty, &self.store.types);
                     }
                 }
                 w!(self, "|");
                 if let Some(ret_ty) = ret_type {
                     w!(self, " -> ");
-                    self.print_type_ref(*ret_ty, &self.body.types);
+                    self.print_type_ref(*ret_ty, &self.store.types);
                 }
                 self.whitespace();
                 self.print_expr(*body);
@@ -534,7 +531,7 @@ impl Printer<'_> {
             Expr::Literal(lit) => self.print_literal(lit),
             Expr::Block { id: _, statements, tail, label } => {
                 let label = label.map(|lbl| {
-                    format!("{}: ", self.body[lbl].name.display(self.db.upcast(), self.edition))
+                    format!("{}: ", self.store[lbl].name.display(self.db.upcast(), self.edition))
                 });
                 self.print_block(label.as_deref(), statements, tail);
             }
@@ -581,7 +578,7 @@ impl Printer<'_> {
     }
 
     fn print_pat(&mut self, pat: PatId) {
-        let pat = &self.body[pat];
+        let pat = &self.store[pat];
 
         match pat {
             Pat::Missing => w!(self, "�"),
@@ -623,9 +620,9 @@ impl Printer<'_> {
                         let field_name = arg.name.display(self.db.upcast(), edition).to_string();
 
                         let mut same_name = false;
-                        if let Pat::Bind { id, subpat: None } = &self.body[arg.pat] {
+                        if let Pat::Bind { id, subpat: None } = &self.store[arg.pat] {
                             if let Binding { name, mode: BindingAnnotation::Unannotated, .. } =
-                                &self.body.bindings[*id]
+                                &self.store.bindings[*id]
                             {
                                 if name.as_str() == field_name {
                                     same_name = true;
@@ -656,11 +653,11 @@ impl Printer<'_> {
             }
             Pat::Range { start, end } => {
                 if let Some(start) = start {
-                    self.print_literal_or_const(start);
+                    self.print_expr(*start);
                 }
                 w!(self, "..=");
                 if let Some(end) = end {
-                    self.print_literal_or_const(end);
+                    self.print_expr(*end);
                 }
             }
             Pat::Slice { prefix, slice, suffix } => {
@@ -734,7 +731,7 @@ impl Printer<'_> {
                 self.print_pat(*pat);
                 if let Some(ty) = type_ref {
                     w!(self, ": ");
-                    self.print_type_ref(*ty, &self.body.types);
+                    self.print_type_ref(*ty, &self.store.types);
                 }
                 if let Some(init) = initializer {
                     w!(self, " = ");
@@ -757,13 +754,6 @@ impl Printer<'_> {
         }
     }
 
-    fn print_literal_or_const(&mut self, literal_or_const: &LiteralOrConst) {
-        match literal_or_const {
-            LiteralOrConst::Literal(l) => self.print_literal(l),
-            LiteralOrConst::Const(c) => self.print_pat(*c),
-        }
-    }
-
     fn print_literal(&mut self, literal: &Literal) {
         match literal {
             Literal::String(it) => w!(self, "{:?}", it),
@@ -799,11 +789,11 @@ impl Printer<'_> {
 
     fn print_path(&mut self, path: &Path) {
         let edition = self.edition;
-        print_path(self.db, path, &self.body.types, self, edition).unwrap();
+        print_path(self.db, path, &self.store.types, self, edition).unwrap();
     }
 
     fn print_binding(&mut self, id: BindingId) {
-        let Binding { name, mode, .. } = &self.body.bindings[id];
+        let Binding { name, mode, .. } = &self.store.bindings[id];
         let mode = match mode {
             BindingAnnotation::Unannotated => "",
             BindingAnnotation::Mutable => "mut ",
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/body/scope.rs b/src/tools/rust-analyzer/crates/hir-def/src/expr_store/scope.rs
index 08af470b965..859a706177a 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/body/scope.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/expr_store/scope.rs
@@ -4,8 +4,8 @@ use la_arena::{Arena, ArenaMap, Idx, IdxRange, RawIdx};
 use triomphe::Arc;
 
 use crate::{
-    body::{Body, HygieneId},
     db::DefDatabase,
+    expr_store::{Body, ExpressionStore, HygieneId},
     hir::{Binding, BindingId, Expr, ExprId, Item, LabelId, Pat, PatId, Statement},
     BlockId, ConstBlockId, DefWithBodyId,
 };
@@ -53,7 +53,7 @@ pub struct ScopeData {
 impl ExprScopes {
     pub(crate) fn expr_scopes_query(db: &dyn DefDatabase, def: DefWithBodyId) -> Arc<ExprScopes> {
         let body = db.body(def);
-        let mut scopes = ExprScopes::new(&body, |const_block| {
+        let mut scopes = ExprScopes::new_body(&body, |const_block| {
             db.lookup_intern_anonymous_const(const_block).root
         });
         scopes.shrink_to_fit();
@@ -104,7 +104,7 @@ fn empty_entries(idx: usize) -> IdxRange<ScopeEntry> {
 }
 
 impl ExprScopes {
-    fn new(
+    fn new_body(
         body: &Body,
         resolve_const_block: impl (Fn(ConstBlockId) -> ExprId) + Copy,
     ) -> ExprScopes {
@@ -179,28 +179,28 @@ impl ExprScopes {
 
     fn add_bindings(
         &mut self,
-        body: &Body,
+        store: &ExpressionStore,
         scope: ScopeId,
         binding: BindingId,
         hygiene: HygieneId,
     ) {
-        let Binding { name, .. } = &body.bindings[binding];
+        let Binding { name, .. } = &store.bindings[binding];
         let entry = self.scope_entries.alloc(ScopeEntry { name: name.clone(), binding, hygiene });
         self.scopes[scope].entries =
             IdxRange::new_inclusive(self.scopes[scope].entries.start()..=entry);
     }
 
-    fn add_pat_bindings(&mut self, body: &Body, scope: ScopeId, pat: PatId) {
-        let pattern = &body[pat];
+    fn add_pat_bindings(&mut self, store: &ExpressionStore, scope: ScopeId, pat: PatId) {
+        let pattern = &store[pat];
         if let Pat::Bind { id, .. } = *pattern {
-            self.add_bindings(body, scope, id, body.binding_hygiene(id));
+            self.add_bindings(store, scope, id, store.binding_hygiene(id));
         }
 
-        pattern.walk_child_pats(|pat| self.add_pat_bindings(body, scope, pat));
+        pattern.walk_child_pats(|pat| self.add_pat_bindings(store, scope, pat));
     }
 
-    fn add_params_bindings(&mut self, body: &Body, scope: ScopeId, params: &[PatId]) {
-        params.iter().for_each(|pat| self.add_pat_bindings(body, scope, *pat));
+    fn add_params_bindings(&mut self, store: &ExpressionStore, scope: ScopeId, params: &[PatId]) {
+        params.iter().for_each(|pat| self.add_pat_bindings(store, scope, *pat));
     }
 
     fn set_scope(&mut self, node: ExprId, scope: ScopeId) {
@@ -218,7 +218,7 @@ impl ExprScopes {
 fn compute_block_scopes(
     statements: &[Statement],
     tail: Option<ExprId>,
-    body: &Body,
+    store: &ExpressionStore,
     scopes: &mut ExprScopes,
     scope: &mut ScopeId,
     resolve_const_block: impl (Fn(ConstBlockId) -> ExprId) + Copy,
@@ -227,17 +227,17 @@ fn compute_block_scopes(
         match stmt {
             Statement::Let { pat, initializer, else_branch, .. } => {
                 if let Some(expr) = initializer {
-                    compute_expr_scopes(*expr, body, scopes, scope, resolve_const_block);
+                    compute_expr_scopes(*expr, store, scopes, scope, resolve_const_block);
                 }
                 if let Some(expr) = else_branch {
-                    compute_expr_scopes(*expr, body, scopes, scope, resolve_const_block);
+                    compute_expr_scopes(*expr, store, scopes, scope, resolve_const_block);
                 }
 
                 *scope = scopes.new_scope(*scope);
-                scopes.add_pat_bindings(body, *scope, *pat);
+                scopes.add_pat_bindings(store, *scope, *pat);
             }
             Statement::Expr { expr, .. } => {
-                compute_expr_scopes(*expr, body, scopes, scope, resolve_const_block);
+                compute_expr_scopes(*expr, store, scopes, scope, resolve_const_block);
             }
             Statement::Item(Item::MacroDef(macro_id)) => {
                 *scope = scopes.new_macro_def_scope(*scope, macro_id.clone());
@@ -246,32 +246,32 @@ fn compute_block_scopes(
         }
     }
     if let Some(expr) = tail {
-        compute_expr_scopes(expr, body, scopes, scope, resolve_const_block);
+        compute_expr_scopes(expr, store, scopes, scope, resolve_const_block);
     }
 }
 
 fn compute_expr_scopes(
     expr: ExprId,
-    body: &Body,
+    store: &ExpressionStore,
     scopes: &mut ExprScopes,
     scope: &mut ScopeId,
     resolve_const_block: impl (Fn(ConstBlockId) -> ExprId) + Copy,
 ) {
     let make_label =
-        |label: &Option<LabelId>| label.map(|label| (label, body.labels[label].name.clone()));
+        |label: &Option<LabelId>| label.map(|label| (label, store.labels[label].name.clone()));
 
     let compute_expr_scopes = |scopes: &mut ExprScopes, expr: ExprId, scope: &mut ScopeId| {
-        compute_expr_scopes(expr, body, scopes, scope, resolve_const_block)
+        compute_expr_scopes(expr, store, scopes, scope, resolve_const_block)
     };
 
     scopes.set_scope(expr, *scope);
-    match &body[expr] {
+    match &store[expr] {
         Expr::Block { statements, tail, id, label } => {
             let mut scope = scopes.new_block_scope(*scope, *id, make_label(label));
             // Overwrite the old scope for the block expr, so that every block scope can be found
             // via the block itself (important for blocks that only contain items, no expressions).
             scopes.set_scope(expr, scope);
-            compute_block_scopes(statements, *tail, body, scopes, &mut scope, resolve_const_block);
+            compute_block_scopes(statements, *tail, store, scopes, &mut scope, resolve_const_block);
         }
         Expr::Const(id) => {
             let mut scope = scopes.root_scope();
@@ -282,7 +282,7 @@ fn compute_expr_scopes(
             // Overwrite the old scope for the block expr, so that every block scope can be found
             // via the block itself (important for blocks that only contain items, no expressions).
             scopes.set_scope(expr, scope);
-            compute_block_scopes(statements, *tail, body, scopes, &mut scope, resolve_const_block);
+            compute_block_scopes(statements, *tail, store, scopes, &mut scope, resolve_const_block);
         }
         Expr::Loop { body: body_expr, label } => {
             let mut scope = scopes.new_labeled_scope(*scope, make_label(label));
@@ -290,14 +290,14 @@ fn compute_expr_scopes(
         }
         Expr::Closure { args, body: body_expr, .. } => {
             let mut scope = scopes.new_scope(*scope);
-            scopes.add_params_bindings(body, scope, args);
+            scopes.add_params_bindings(store, scope, args);
             compute_expr_scopes(scopes, *body_expr, &mut scope);
         }
         Expr::Match { expr, arms } => {
             compute_expr_scopes(scopes, *expr, scope);
             for arm in arms.iter() {
                 let mut scope = scopes.new_scope(*scope);
-                scopes.add_pat_bindings(body, scope, arm.pat);
+                scopes.add_pat_bindings(store, scope, arm.pat);
                 if let Some(guard) = arm.guard {
                     scope = scopes.new_scope(scope);
                     compute_expr_scopes(scopes, guard, &mut scope);
@@ -316,9 +316,9 @@ fn compute_expr_scopes(
         &Expr::Let { pat, expr } => {
             compute_expr_scopes(scopes, expr, scope);
             *scope = scopes.new_scope(*scope);
-            scopes.add_pat_bindings(body, *scope, pat);
+            scopes.add_pat_bindings(store, *scope, pat);
         }
-        _ => body.walk_child_exprs(expr, |e| compute_expr_scopes(scopes, e, scope)),
+        _ => store.walk_child_exprs(expr, |e| compute_expr_scopes(scopes, e, scope)),
     };
 }
 
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/body/tests.rs b/src/tools/rust-analyzer/crates/hir-def/src/expr_store/tests.rs
index edc7c4c1f21..16bf46d3e3f 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/body/tests.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/expr_store/tests.rs
@@ -1,10 +1,10 @@
 mod block;
 
+use crate::{hir::MatchArm, test_db::TestDB, ModuleDefId};
 use expect_test::{expect, Expect};
+use la_arena::RawIdx;
 use test_fixture::WithFixture;
 
-use crate::{test_db::TestDB, ModuleDefId};
-
 use super::*;
 
 fn lower(#[rust_analyzer::rust_fixture] ra_fixture: &str) -> (TestDB, Arc<Body>, DefWithBodyId) {
@@ -459,3 +459,45 @@ async fn foo(a: (), b: i32) -> u32 {
     expect!["fn foo(�: (), �: i32) -> impl ::core::future::Future::<Output = u32> �"]
         .assert_eq(&printed);
 }
+
+#[test]
+fn range_bounds_are_hir_exprs() {
+    let (_, body, _) = lower(
+        r#"
+pub const L: i32 = 6;
+mod x {
+    pub const R: i32 = 100;
+}
+const fn f(x: i32) -> i32 {
+    match x {
+        -1..=5 => x * 10,
+        L..=x::R => x * 100,
+        _ => x,
+    }
+}"#,
+    );
+
+    let mtch_arms = body
+        .exprs
+        .iter()
+        .find_map(|(_, expr)| {
+            if let Expr::Match { arms, .. } = expr {
+                return Some(arms);
+            }
+
+            None
+        })
+        .unwrap();
+
+    let MatchArm { pat, .. } = mtch_arms[1];
+    match body.pats[pat] {
+        Pat::Range { start, end } => {
+            let hir_start = &body.exprs[start.unwrap()];
+            let hir_end = &body.exprs[end.unwrap()];
+
+            assert!(matches!(hir_start, Expr::Path { .. }));
+            assert!(matches!(hir_end, Expr::Path { .. }));
+        }
+        _ => {}
+    }
+}
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/body/tests/block.rs b/src/tools/rust-analyzer/crates/hir-def/src/expr_store/tests/block.rs
index e136dd18a55..e136dd18a55 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/body/tests/block.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/expr_store/tests/block.rs
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/generics.rs b/src/tools/rust-analyzer/crates/hir-def/src/generics.rs
index 7b3f1d06d21..e2b36da79b2 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/generics.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/generics.rs
@@ -433,7 +433,7 @@ impl GenericParams {
             GenericDefId::TraitAliasId(id) => id_to_generics(db, id, enabled_params),
             GenericDefId::TypeAliasId(id) => id_to_generics(db, id, enabled_params),
             GenericDefId::ImplId(id) => id_to_generics(db, id, enabled_params),
-            GenericDefId::ConstId(_) => (
+            GenericDefId::ConstId(_) | GenericDefId::StaticId(_) => (
                 Arc::new(GenericParams {
                     type_or_consts: Default::default(),
                     lifetimes: Default::default(),
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/hir.rs b/src/tools/rust-analyzer/crates/hir-def/src/hir.rs
index 85963469430..494644d8eff 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/hir.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/hir.rs
@@ -19,7 +19,7 @@ use std::fmt;
 
 use hir_expand::{name::Name, MacroDefId};
 use intern::Symbol;
-use la_arena::{Idx, RawIdx};
+use la_arena::Idx;
 use rustc_apfloat::ieee::{Half as f16, Quad as f128};
 use syntax::ast;
 use type_ref::TypeRefId;
@@ -37,13 +37,10 @@ pub type BindingId = Idx<Binding>;
 
 pub type ExprId = Idx<Expr>;
 
-/// FIXME: this is a hacky function which should be removed
-pub(crate) fn dummy_expr_id() -> ExprId {
-    ExprId::from_raw(RawIdx::from(u32::MAX))
-}
-
 pub type PatId = Idx<Pat>;
 
+// FIXME: Encode this as a single u32, we won't ever reach all 32 bits especially given these counts
+// are local to the body.
 #[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
 pub enum ExprOrPatId {
     ExprId(ExprId),
@@ -58,12 +55,20 @@ impl ExprOrPatId {
         }
     }
 
+    pub fn is_expr(&self) -> bool {
+        matches!(self, Self::ExprId(_))
+    }
+
     pub fn as_pat(self) -> Option<PatId> {
         match self {
             Self::PatId(v) => Some(v),
             _ => None,
         }
     }
+
+    pub fn is_pat(&self) -> bool {
+        matches!(self, Self::PatId(_))
+    }
 }
 stdx::impl_from!(ExprId, PatId for ExprOrPatId);
 
@@ -574,8 +579,8 @@ pub enum Pat {
         ellipsis: bool,
     },
     Range {
-        start: Option<Box<LiteralOrConst>>,
-        end: Option<Box<LiteralOrConst>>,
+        start: Option<ExprId>,
+        end: Option<ExprId>,
     },
     Slice {
         prefix: Box<[PatId]>,
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/import_map.rs b/src/tools/rust-analyzer/crates/hir-def/src/import_map.rs
index 34635997bdf..d43776b8a66 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/import_map.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/import_map.rs
@@ -320,7 +320,7 @@ impl SearchMode {
                     };
                     match m {
                         Some((index, _)) => {
-                            name = &name[index + 1..];
+                            name = name[index..].strip_prefix(|_: char| true).unwrap_or_default();
                             true
                         }
                         None => false,
@@ -519,7 +519,7 @@ mod tests {
                 crate_graph[krate]
                     .display_name
                     .as_ref()
-                    .is_some_and(|it| &**it.crate_name() == crate_name)
+                    .is_some_and(|it| it.crate_name().as_str() == crate_name)
             })
             .expect("could not find crate");
 
@@ -1039,4 +1039,22 @@ pub mod fmt {
             "#]],
         );
     }
+
+    #[test]
+    fn unicode_fn_name() {
+        let ra_fixture = r#"
+            //- /main.rs crate:main deps:dep
+            //- /dep.rs crate:dep
+            pub fn あい() {}
+        "#;
+
+        check_search(
+            ra_fixture,
+            "main",
+            Query::new("あ".to_owned()).fuzzy(),
+            expect![[r#"
+            dep::あい (f)
+        "#]],
+        );
+    }
 }
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/item_scope.rs b/src/tools/rust-analyzer/crates/hir-def/src/item_scope.rs
index 65a39c56561..0ca1eb9bcfe 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/item_scope.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/item_scope.rs
@@ -18,8 +18,8 @@ use crate::{
     db::DefDatabase,
     per_ns::{Item, MacrosItem, PerNs, TypesItem, ValuesItem},
     visibility::{Visibility, VisibilityExplicitness},
-    AdtId, BuiltinType, ConstId, ExternCrateId, FxIndexMap, HasModule, ImplId, LocalModuleId,
-    Lookup, MacroId, ModuleDefId, ModuleId, TraitId, UseId,
+    AdtId, BuiltinType, ConstId, ExternBlockId, ExternCrateId, FxIndexMap, HasModule, ImplId,
+    LocalModuleId, Lookup, MacroId, ModuleDefId, ModuleId, TraitId, UseId,
 };
 
 #[derive(Debug, Default)]
@@ -158,6 +158,8 @@ pub struct ItemScope {
     declarations: Vec<ModuleDefId>,
 
     impls: Vec<ImplId>,
+    #[allow(clippy::box_collection)]
+    extern_blocks: Option<Box<Vec<ExternBlockId>>>,
     unnamed_consts: Vec<ConstId>,
     /// Traits imported via `use Trait as _;`.
     unnamed_trait_imports: FxHashMap<TraitId, Item<()>>,
@@ -319,6 +321,10 @@ impl ItemScope {
         self.extern_crate_decls.iter().copied()
     }
 
+    pub fn extern_blocks(&self) -> impl Iterator<Item = ExternBlockId> + '_ {
+        self.extern_blocks.iter().flat_map(|it| it.iter()).copied()
+    }
+
     pub fn use_decls(&self) -> impl ExactSizeIterator<Item = UseId> + '_ {
         self.use_decls.iter().copied()
     }
@@ -469,6 +475,10 @@ impl ItemScope {
         self.impls.push(imp);
     }
 
+    pub(crate) fn define_extern_block(&mut self, extern_block: ExternBlockId) {
+        self.extern_blocks.get_or_insert_default().push(extern_block);
+    }
+
     pub(crate) fn define_extern_crate_decl(&mut self, extern_crate: ExternCrateId) {
         self.extern_crate_decls.push(extern_crate);
     }
@@ -806,7 +816,11 @@ impl ItemScope {
             use_imports_types,
             use_imports_macros,
             macro_invocations,
+            extern_blocks,
         } = self;
+        if let Some(it) = extern_blocks {
+            it.shrink_to_fit();
+        }
         types.shrink_to_fit();
         values.shrink_to_fit();
         macros.shrink_to_fit();
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/item_tree.rs b/src/tools/rust-analyzer/crates/hir-def/src/item_tree.rs
index b5bf2feb82a..8d5b3eeb28e 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/item_tree.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/item_tree.rs
@@ -937,7 +937,7 @@ pub struct Param {
 
 bitflags::bitflags! {
     #[derive(Debug, Clone, Copy, Eq, PartialEq, Default)]
-    pub(crate) struct FnFlags: u8 {
+    pub(crate) struct FnFlags: u16 {
         const HAS_SELF_PARAM = 1 << 0;
         const HAS_BODY = 1 << 1;
         const HAS_DEFAULT_KW = 1 << 2;
@@ -946,6 +946,12 @@ bitflags::bitflags! {
         const HAS_UNSAFE_KW = 1 << 5;
         const IS_VARARGS = 1 << 6;
         const HAS_SAFE_KW = 1 << 7;
+        /// The `#[target_feature]` attribute is necessary to check safety (with RFC 2396),
+        /// but keeping it for all functions will consume a lot of memory when there are
+        /// only very few functions with it. So we only encode its existence here, and lookup
+        /// it if needed.
+        const HAS_TARGET_FEATURE = 1 << 8;
+        const DEPRECATED_SAFE_2024 = 1 << 9;
     }
 }
 
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/lang_item.rs b/src/tools/rust-analyzer/crates/hir-def/src/lang_item.rs
index 38733577d1c..59f51db9f74 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/lang_item.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/lang_item.rs
@@ -502,4 +502,5 @@ language_item_table! {
 
     String,                  sym::String,              string,                     Target::Struct,         GenericRequirement::None;
     CStr,                    sym::CStr,                c_str,                      Target::Struct,         GenericRequirement::None;
+    Ordering,                sym::Ordering,            ordering,                   Target::Enum,           GenericRequirement::None;
 }
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 c78818c642c..9c947df35e9 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/lib.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/lib.rs
@@ -18,9 +18,15 @@ extern crate ra_ap_rustc_parse_format as rustc_parse_format;
 #[cfg(feature = "in-rust-tree")]
 extern crate rustc_abi;
 
+#[cfg(feature = "in-rust-tree")]
+extern crate rustc_hashes;
+
 #[cfg(not(feature = "in-rust-tree"))]
 extern crate ra_ap_rustc_abi as rustc_abi;
 
+#[cfg(not(feature = "in-rust-tree"))]
+extern crate ra_ap_rustc_hashes as rustc_hashes;
+
 pub mod db;
 
 pub mod attr;
@@ -42,7 +48,7 @@ pub mod lang_item;
 
 pub mod hir;
 pub use self::hir::type_ref;
-pub mod body;
+pub mod expr_store;
 pub mod resolver;
 
 pub mod nameres;
@@ -693,6 +699,7 @@ impl TypeOwnerId {
         Some(match self {
             TypeOwnerId::FunctionId(it) => GenericDefId::FunctionId(it),
             TypeOwnerId::ConstId(it) => GenericDefId::ConstId(it),
+            TypeOwnerId::StaticId(it) => GenericDefId::StaticId(it),
             TypeOwnerId::AdtId(it) => GenericDefId::AdtId(it),
             TypeOwnerId::TraitId(it) => GenericDefId::TraitId(it),
             TypeOwnerId::TraitAliasId(it) => GenericDefId::TraitAliasId(it),
@@ -701,7 +708,7 @@ impl TypeOwnerId {
             TypeOwnerId::EnumVariantId(it) => {
                 GenericDefId::AdtId(AdtId::EnumId(it.lookup(db).parent))
             }
-            TypeOwnerId::InTypeConstId(_) | TypeOwnerId::StaticId(_) => return None,
+            TypeOwnerId::InTypeConstId(_) => return None,
         })
     }
 }
@@ -743,6 +750,7 @@ impl From<GenericDefId> for TypeOwnerId {
             GenericDefId::TypeAliasId(it) => it.into(),
             GenericDefId::ImplId(it) => it.into(),
             GenericDefId::ConstId(it) => it.into(),
+            GenericDefId::StaticId(it) => it.into(),
         }
     }
 }
@@ -851,7 +859,7 @@ impl GeneralConstId {
     pub fn generic_def(self, db: &dyn DefDatabase) -> Option<GenericDefId> {
         match self {
             GeneralConstId::ConstId(it) => Some(it.into()),
-            GeneralConstId::StaticId(_) => None,
+            GeneralConstId::StaticId(it) => Some(it.into()),
             GeneralConstId::ConstBlockId(it) => it.lookup(db).parent.as_generic_def_id(db),
             GeneralConstId::InTypeConstId(it) => it.lookup(db).owner.as_generic_def_id(db),
         }
@@ -897,7 +905,7 @@ impl DefWithBodyId {
     pub fn as_generic_def_id(self, db: &dyn DefDatabase) -> Option<GenericDefId> {
         match self {
             DefWithBodyId::FunctionId(f) => Some(f.into()),
-            DefWithBodyId::StaticId(_) => None,
+            DefWithBodyId::StaticId(s) => Some(s.into()),
             DefWithBodyId::ConstId(c) => Some(c.into()),
             DefWithBodyId::VariantId(c) => Some(c.lookup(db).parent.into()),
             // FIXME: stable rust doesn't allow generics in constants, but we should
@@ -922,23 +930,28 @@ impl_from!(FunctionId, ConstId, TypeAliasId for AssocItemId);
 
 #[derive(Clone, Copy, PartialEq, Eq, Debug, Hash)]
 pub enum GenericDefId {
-    FunctionId(FunctionId),
     AdtId(AdtId),
-    TraitId(TraitId),
-    TraitAliasId(TraitAliasId),
-    TypeAliasId(TypeAliasId),
-    ImplId(ImplId),
     // consts can have type parameters from their parents (i.e. associated consts of traits)
     ConstId(ConstId),
+    FunctionId(FunctionId),
+    ImplId(ImplId),
+    // can't actually have generics currently, but they might in the future
+    // More importantly, this completes the set of items that contain type references
+    // which is to be used by the signature expression store in the future.
+    StaticId(StaticId),
+    TraitAliasId(TraitAliasId),
+    TraitId(TraitId),
+    TypeAliasId(TypeAliasId),
 }
 impl_from!(
-    FunctionId,
     AdtId(StructId, EnumId, UnionId),
-    TraitId,
-    TraitAliasId,
-    TypeAliasId,
+    ConstId,
+    FunctionId,
     ImplId,
-    ConstId
+    StaticId,
+    TraitAliasId,
+    TraitId,
+    TypeAliasId
     for GenericDefId
 );
 
@@ -969,6 +982,7 @@ impl GenericDefId {
             GenericDefId::TraitAliasId(it) => file_id_and_params_of_item_loc(db, it),
             GenericDefId::ImplId(it) => file_id_and_params_of_item_loc(db, it),
             GenericDefId::ConstId(it) => (it.lookup(db).id.file_id(), None),
+            GenericDefId::StaticId(it) => (it.lookup(db).id.file_id(), None),
         }
     }
 
@@ -1350,6 +1364,7 @@ impl HasModule for GenericDefId {
             GenericDefId::TypeAliasId(it) => it.module(db),
             GenericDefId::ImplId(it) => it.module(db),
             GenericDefId::ConstId(it) => it.module(db),
+            GenericDefId::StaticId(it) => it.module(db),
         }
     }
 }
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/proc_macros.rs b/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/proc_macros.rs
index 70e3e1ed4e9..a43c0eb9d70 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/proc_macros.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/proc_macros.rs
@@ -5,7 +5,7 @@
 //! in-memory macros.
 use expect_test::expect;
 
-use crate::macro_expansion_tests::check;
+use crate::macro_expansion_tests::{check, check_errors};
 
 #[test]
 fn attribute_macro_attr_censoring() {
@@ -216,3 +216,21 @@ struct S;
 #[doc = "doc attr"] struct S;"##]],
     );
 }
+
+#[test]
+fn cfg_evaluated_before_attr_macros() {
+    check_errors(
+        r#"
+//- proc_macros: disallow_cfg
+
+use proc_macros::disallow_cfg;
+
+#[disallow_cfg] #[cfg(false)] fn foo() {}
+// True cfg are kept.
+// #[disallow_cfg] #[cfg(true)] fn bar() {}
+#[disallow_cfg] #[cfg_attr(false, inline)] fn baz() {}
+#[disallow_cfg] #[cfg_attr(true, inline)] fn qux() {}
+    "#,
+        expect![[r#""#]],
+    );
+}
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/nameres.rs b/src/tools/rust-analyzer/crates/hir-def/src/nameres.rs
index 39d383f0159..3b6e3c5916e 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/nameres.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/nameres.rs
@@ -337,7 +337,7 @@ impl DefMap {
     pub(crate) fn crate_def_map_query(db: &dyn DefDatabase, crate_id: CrateId) -> Arc<DefMap> {
         let crate_graph = db.crate_graph();
         let krate = &crate_graph[crate_id];
-        let name = krate.display_name.as_deref().unwrap_or_default();
+        let name = krate.display_name.as_deref().map(Symbol::as_str).unwrap_or_default();
         let _p = tracing::info_span!("crate_def_map_query", ?name).entered();
 
         let module_data = ModuleData::new(
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 06276335b71..16f3fd56eb9 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
@@ -19,7 +19,6 @@ use hir_expand::{
 use intern::{sym, Interned};
 use itertools::{izip, Itertools};
 use la_arena::Idx;
-use limit::Limit;
 use rustc_hash::{FxHashMap, FxHashSet};
 use span::{Edition, EditionedFileId, FileAstId, SyntaxContextId};
 use syntax::ast;
@@ -55,8 +54,8 @@ use crate::{
     UnresolvedMacro, UseId, UseLoc,
 };
 
-static GLOB_RECURSION_LIMIT: Limit = Limit::new(100);
-static FIXED_POINT_LIMIT: Limit = Limit::new(8192);
+const GLOB_RECURSION_LIMIT: usize = 100;
+const FIXED_POINT_LIMIT: usize = 8192;
 
 pub(super) fn collect_defs(db: &dyn DefDatabase, def_map: DefMap, tree_id: TreeId) -> DefMap {
     let crate_graph = db.crate_graph();
@@ -393,7 +392,7 @@ impl DefCollector<'_> {
                 }
 
                 i += 1;
-                if FIXED_POINT_LIMIT.check(i).is_err() {
+                if i > FIXED_POINT_LIMIT {
                     tracing::error!("name resolution is stuck");
                     break 'resolve_attr;
                 }
@@ -993,7 +992,7 @@ impl DefCollector<'_> {
         import: Option<ImportOrExternCrate>,
         depth: usize,
     ) {
-        if GLOB_RECURSION_LIMIT.check(depth).is_err() {
+        if depth > GLOB_RECURSION_LIMIT {
             // prevent stack overflows (but this shouldn't be possible)
             panic!("infinite recursion in glob imports!");
         }
@@ -1470,8 +1469,7 @@ impl DefCollector<'_> {
         depth: usize,
         container: ItemContainerId,
     ) {
-        let recursion_limit = Limit::new(self.def_map.recursion_limit() as usize);
-        if recursion_limit.check(depth).is_err() {
+        if depth > self.def_map.recursion_limit() as usize {
             cov_mark::hit!(macro_expansion_overflow);
             tracing::warn!("macro expansion is too deep");
             return;
@@ -1499,7 +1497,6 @@ impl DefCollector<'_> {
 
     fn finish(mut self) -> DefMap {
         // Emit diagnostics for all remaining unexpanded macros.
-
         let _p = tracing::info_span!("DefCollector::finish").entered();
 
         for directive in &self.unresolved_macros {
@@ -1759,16 +1756,20 @@ impl ModCollector<'_, '_> {
                         );
                     }
                 }
-                ModItem::ExternBlock(block) => self.collect(
-                    &self.item_tree[block].children,
-                    ItemContainerId::ExternBlockId(
-                        ExternBlockLoc {
-                            container: module,
-                            id: ItemTreeId::new(self.tree_id, block),
-                        }
-                        .intern(db),
-                    ),
-                ),
+                ModItem::ExternBlock(block) => {
+                    let extern_block_id = ExternBlockLoc {
+                        container: module,
+                        id: ItemTreeId::new(self.tree_id, block),
+                    }
+                    .intern(db);
+                    self.def_collector.def_map.modules[self.module_id]
+                        .scope
+                        .define_extern_block(extern_block_id);
+                    self.collect(
+                        &self.item_tree[block].children,
+                        ItemContainerId::ExternBlockId(extern_block_id),
+                    )
+                }
                 ModItem::MacroCall(mac) => self.collect_macro_call(&self.item_tree[mac], container),
                 ModItem::MacroRules(id) => self.collect_macro_rules(id, module),
                 ModItem::Macro2(id) => self.collect_macro_def(id, module),
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/nameres/mod_resolution.rs b/src/tools/rust-analyzer/crates/hir-def/src/nameres/mod_resolution.rs
index d7e4ca41cd5..afee42ecec0 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/nameres/mod_resolution.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/nameres/mod_resolution.rs
@@ -2,12 +2,11 @@
 use arrayvec::ArrayVec;
 use base_db::AnchoredPath;
 use hir_expand::{name::Name, HirFileIdExt};
-use limit::Limit;
 use span::EditionedFileId;
 
 use crate::{db::DefDatabase, HirFileId};
 
-static MOD_DEPTH_LIMIT: Limit = Limit::new(32);
+const MOD_DEPTH_LIMIT: usize = 32;
 
 #[derive(Clone, Debug)]
 pub(super) struct ModDir {
@@ -50,7 +49,7 @@ impl ModDir {
 
     fn child(&self, dir_path: DirPath, root_non_dir_owner: bool) -> Option<ModDir> {
         let depth = self.depth + 1;
-        if MOD_DEPTH_LIMIT.check(depth as usize).is_err() {
+        if depth as usize > MOD_DEPTH_LIMIT {
             tracing::error!("MOD_DEPTH_LIMIT exceeded");
             cov_mark::hit!(circular_mods);
             return None;
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/path.rs b/src/tools/rust-analyzer/crates/hir-def/src/path.rs
index e59c37104dd..e6c2504d07a 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/path.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/path.rs
@@ -173,10 +173,7 @@ impl Path {
                 segments: path.mod_path().segments(),
                 generic_args: Some(path.generic_args()),
             },
-            Path::LangItem(_, seg) => PathSegments {
-                segments: seg.as_ref().map_or(&[], |seg| std::slice::from_ref(seg)),
-                generic_args: None,
-            },
+            Path::LangItem(_, seg) => PathSegments { segments: seg.as_slice(), generic_args: None },
         }
     }
 
@@ -240,6 +237,11 @@ pub struct PathSegment<'a> {
     pub args_and_bindings: Option<&'a GenericArgs>,
 }
 
+impl PathSegment<'_> {
+    pub const MISSING: PathSegment<'static> =
+        PathSegment { name: &Name::missing(), args_and_bindings: None };
+}
+
 #[derive(Debug, Clone, Copy)]
 pub struct PathSegments<'a> {
     segments: &'a [Name],
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/resolver.rs b/src/tools/rust-analyzer/crates/hir-def/src/resolver.rs
index 8c556d8a8c3..9dfb6e3cc4b 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/resolver.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/resolver.rs
@@ -10,13 +10,13 @@ use smallvec::{smallvec, SmallVec};
 use triomphe::Arc;
 
 use crate::{
-    body::{
-        scope::{ExprScopes, ScopeId},
-        HygieneId,
-    },
     builtin_type::BuiltinType,
     data::ExternCrateDeclData,
     db::DefDatabase,
+    expr_store::{
+        scope::{ExprScopes, ScopeId},
+        HygieneId,
+    },
     generics::{GenericParams, TypeOrConstParamData},
     hir::{BindingId, ExprId, LabelId},
     item_scope::{BuiltinShadowMode, ImportOrExternCrate, ImportOrGlob, BUILTIN_SCOPE},
@@ -327,8 +327,9 @@ impl Resolver {
                     | LangItemTarget::ImplDef(_)
                     | LangItemTarget::Static(_) => return None,
                 };
+                // Remaining segments start from 0 because lang paths have no segments other than the remaining.
                 return Some((
-                    ResolveValueResult::Partial(type_ns, 1, None),
+                    ResolveValueResult::Partial(type_ns, 0, None),
                     ResolvePathResultPrefixInfo::default(),
                 ));
             }
@@ -1264,6 +1265,7 @@ impl HasResolver for GenericDefId {
             GenericDefId::TypeAliasId(inner) => inner.resolver(db),
             GenericDefId::ImplId(inner) => inner.resolver(db),
             GenericDefId::ConstId(inner) => inner.resolver(db),
+            GenericDefId::StaticId(inner) => inner.resolver(db),
         }
     }
 }
diff --git a/src/tools/rust-analyzer/crates/hir-expand/Cargo.toml b/src/tools/rust-analyzer/crates/hir-expand/Cargo.toml
index 03a9d54d2e6..b193a34a01d 100644
--- a/src/tools/rust-analyzer/crates/hir-expand/Cargo.toml
+++ b/src/tools/rust-analyzer/crates/hir-expand/Cargo.toml
@@ -31,7 +31,6 @@ cfg.workspace = true
 syntax.workspace = true
 tt.workspace = true
 mbe.workspace = true
-limit.workspace = true
 span.workspace = true
 parser.workspace = true
 syntax-bridge.workspace = true
diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/builtin/fn_macro.rs b/src/tools/rust-analyzer/crates/hir-expand/src/builtin/fn_macro.rs
index 310ddaaf9e9..55242ab3e57 100644
--- a/src/tools/rust-analyzer/crates/hir-expand/src/builtin/fn_macro.rs
+++ b/src/tools/rust-analyzer/crates/hir-expand/src/builtin/fn_macro.rs
@@ -833,7 +833,7 @@ fn env_expand(
         if key.as_str() == "OUT_DIR" {
             err = Some(ExpandError::other(
                 span,
-                r#"`OUT_DIR` not set, enable "build scripts" to fix"#,
+                r#"`OUT_DIR` not set, build scripts may have failed to run"#,
             ));
         }
 
diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/cfg_process.rs b/src/tools/rust-analyzer/crates/hir-expand/src/cfg_process.rs
index 01a3103af82..626a82ae08e 100644
--- a/src/tools/rust-analyzer/crates/hir-expand/src/cfg_process.rs
+++ b/src/tools/rust-analyzer/crates/hir-expand/src/cfg_process.rs
@@ -201,9 +201,6 @@ pub(crate) fn process_cfg_attrs(
         MacroDefKind::BuiltInAttr(_, expander) => expander.is_derive(),
         _ => false,
     };
-    if !is_derive {
-        return None;
-    }
     let mut remove = FxHashSet::default();
 
     let item = ast::Item::cast(node.clone())?;
@@ -220,28 +217,43 @@ pub(crate) fn process_cfg_attrs(
             }
         }
     }
-    match item {
-        ast::Item::Struct(it) => match it.field_list()? {
-            ast::FieldList::RecordFieldList(fields) => {
-                process_has_attrs_with_possible_comma(db, fields.fields(), loc.krate, &mut remove)?;
+
+    if is_derive {
+        // Only derives get their code cfg-clean, normal attribute macros process only the cfg at their level
+        // (cfg_attr is handled above, cfg is handled in the def map).
+        match item {
+            ast::Item::Struct(it) => match it.field_list()? {
+                ast::FieldList::RecordFieldList(fields) => {
+                    process_has_attrs_with_possible_comma(
+                        db,
+                        fields.fields(),
+                        loc.krate,
+                        &mut remove,
+                    )?;
+                }
+                ast::FieldList::TupleFieldList(fields) => {
+                    process_has_attrs_with_possible_comma(
+                        db,
+                        fields.fields(),
+                        loc.krate,
+                        &mut remove,
+                    )?;
+                }
+            },
+            ast::Item::Enum(it) => {
+                process_enum(db, it.variant_list()?, loc.krate, &mut remove)?;
             }
-            ast::FieldList::TupleFieldList(fields) => {
-                process_has_attrs_with_possible_comma(db, fields.fields(), loc.krate, &mut remove)?;
+            ast::Item::Union(it) => {
+                process_has_attrs_with_possible_comma(
+                    db,
+                    it.record_field_list()?.fields(),
+                    loc.krate,
+                    &mut remove,
+                )?;
             }
-        },
-        ast::Item::Enum(it) => {
-            process_enum(db, it.variant_list()?, loc.krate, &mut remove)?;
-        }
-        ast::Item::Union(it) => {
-            process_has_attrs_with_possible_comma(
-                db,
-                it.record_field_list()?.fields(),
-                loc.krate,
-                &mut remove,
-            )?;
+            // FIXME: Implement for other items if necessary. As we do not support #[cfg_eval] yet, we do not need to implement it for now
+            _ => {}
         }
-        // FIXME: Implement for other items if necessary. As we do not support #[cfg_eval] yet, we do not need to implement it for now
-        _ => {}
     }
     Some(remove)
 }
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 b7804f888ae..8ca8bf1ba4a 100644
--- a/src/tools/rust-analyzer/crates/hir-expand/src/db.rs
+++ b/src/tools/rust-analyzer/crates/hir-expand/src/db.rs
@@ -2,7 +2,6 @@
 
 use base_db::{ra_salsa, CrateId, SourceDatabase};
 use either::Either;
-use limit::Limit;
 use mbe::MatchedArmIndex;
 use rustc_hash::FxHashSet;
 use span::{AstIdMap, Edition, EditionedFileId, Span, SyntaxContextData, SyntaxContextId};
@@ -35,7 +34,7 @@ type MacroArgResult = (Arc<tt::TopSubtree>, SyntaxFixupUndoInfo, Span);
 /// an error will be emitted.
 ///
 /// Actual max for `analysis-stats .` at some point: 30672.
-static TOKEN_LIMIT: Limit = Limit::new(2_097_152);
+const TOKEN_LIMIT: usize = 2_097_152;
 
 #[derive(Debug, Clone, Eq, PartialEq)]
 pub enum TokenExpander {
@@ -740,20 +739,19 @@ pub(crate) fn token_tree_to_syntax_node(
 fn check_tt_count(tt: &tt::TopSubtree) -> Result<(), ExpandResult<()>> {
     let tt = tt.top_subtree();
     let count = tt.count();
-    if TOKEN_LIMIT.check(count).is_err() {
+    if count <= TOKEN_LIMIT {
+        Ok(())
+    } else {
         Err(ExpandResult {
             value: (),
             err: Some(ExpandError::other(
                 tt.delimiter.open,
                 format!(
                     "macro invocation exceeds token limit: produced {} tokens, limit is {}",
-                    count,
-                    TOKEN_LIMIT.inner(),
+                    count, TOKEN_LIMIT,
                 ),
             )),
         })
-    } else {
-        Ok(())
     }
 }
 
diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/name.rs b/src/tools/rust-analyzer/crates/hir-expand/src/name.rs
index 848870c3a38..0758bd4515e 100644
--- a/src/tools/rust-analyzer/crates/hir-expand/src/name.rs
+++ b/src/tools/rust-analyzer/crates/hir-expand/src/name.rs
@@ -142,8 +142,8 @@ impl Name {
     /// Ideally, we want a `gensym` semantics for missing names -- each missing
     /// name is equal only to itself. It's not clear how to implement this in
     /// salsa though, so we punt on that bit for a moment.
-    pub fn missing() -> Name {
-        Name { symbol: sym::MISSING_NAME.clone(), ctx: () }
+    pub const fn missing() -> Name {
+        Name { symbol: sym::consts::MISSING_NAME, ctx: () }
     }
 
     /// Returns true if this is a fake name for things missing in the source code. See
@@ -262,6 +262,6 @@ impl AsName for ast::FieldKind {
 
 impl AsName for base_db::Dependency {
     fn as_name(&self) -> Name {
-        Name::new_root(&self.name)
+        Name::new_symbol_root((*self.name).clone())
     }
 }
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 6ff7831fd81..c744fbce77b 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
@@ -41,9 +41,9 @@ pub fn prettify_macro_expansion(
                 } else if let Some(dep) =
                     target_crate.dependencies.iter().find(|dep| dep.crate_id == macro_def_crate)
                 {
-                    make::tokens::ident(&dep.name)
+                    make::tokens::ident(dep.name.as_str())
                 } else if let Some(crate_name) = &crate_graph[macro_def_crate].display_name {
-                    make::tokens::ident(crate_name.crate_name())
+                    make::tokens::ident(crate_name.crate_name().as_str())
                 } else {
                     return dollar_crate.clone();
                 }
diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/proc_macro.rs b/src/tools/rust-analyzer/crates/hir-expand/src/proc_macro.rs
index 07808fea85b..3dc3dcd760c 100644
--- a/src/tools/rust-analyzer/crates/hir-expand/src/proc_macro.rs
+++ b/src/tools/rust-analyzer/crates/hir-expand/src/proc_macro.rs
@@ -238,6 +238,9 @@ impl CustomProcMacroExpander {
                 let krate_graph = db.crate_graph();
                 // Proc macros have access to the environment variables of the invoking crate.
                 let env = &krate_graph[calling_crate].env;
+                let current_dir =
+                    krate_graph[calling_crate].proc_macro_cwd.as_deref().map(ToString::to_string);
+
                 match proc_macro.expander.expand(
                     tt,
                     attr_arg,
@@ -245,10 +248,7 @@ impl CustomProcMacroExpander {
                     def_site,
                     call_site,
                     mixed_site,
-                    db.crate_workspace_data()[&calling_crate]
-                        .proc_macro_cwd
-                        .as_ref()
-                        .map(ToString::to_string),
+                    current_dir,
                 ) {
                     Ok(t) => ExpandResult::ok(t),
                     Err(err) => match err {
diff --git a/src/tools/rust-analyzer/crates/hir-ty/Cargo.toml b/src/tools/rust-analyzer/crates/hir-ty/Cargo.toml
index 989f0955e1e..4d36de0b383 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/Cargo.toml
+++ b/src/tools/rust-analyzer/crates/hir-ty/Cargo.toml
@@ -36,6 +36,7 @@ indexmap.workspace = true
 rustc_apfloat = "0.2.0"
 
 ra-ap-rustc_abi.workspace = true
+ra-ap-rustc_hashes.workspace = true
 ra-ap-rustc_index.workspace = true
 ra-ap-rustc_pattern_analysis.workspace = true
 
@@ -47,7 +48,6 @@ hir-def.workspace = true
 hir-expand.workspace = true
 base-db.workspace = true
 syntax.workspace = true
-limit.workspace = true
 span.workspace = true
 
 [dev-dependencies]
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/autoderef.rs b/src/tools/rust-analyzer/crates/hir-ty/src/autoderef.rs
index 62feca5f8cb..171ba001c4a 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/autoderef.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/autoderef.rs
@@ -9,7 +9,6 @@ use chalk_ir::cast::Cast;
 use hir_def::lang_item::LangItem;
 use hir_expand::name::Name;
 use intern::sym;
-use limit::Limit;
 use triomphe::Arc;
 
 use crate::{
@@ -17,7 +16,7 @@ use crate::{
     TraitEnvironment, Ty, TyBuilder, TyKind,
 };
 
-static AUTODEREF_RECURSION_LIMIT: Limit = Limit::new(20);
+const AUTODEREF_RECURSION_LIMIT: usize = 20;
 
 #[derive(Debug)]
 pub(crate) enum AutoderefKind {
@@ -140,7 +139,7 @@ impl<T: TrackAutoderefSteps> Iterator for Autoderef<'_, '_, T> {
             return Some((self.ty.clone(), 0));
         }
 
-        if AUTODEREF_RECURSION_LIMIT.check(self.steps.len() + 1).is_err() {
+        if self.steps.len() > AUTODEREF_RECURSION_LIMIT {
             return None;
         }
 
@@ -194,7 +193,11 @@ pub(crate) fn deref_by_trait(
     }
 
     let trait_id = || {
-        if use_receiver_trait {
+        // FIXME: Remove the `false` once `Receiver` needs to be stabilized, doing so will
+        // effectively bump the MSRV of rust-analyzer to 1.84 due to 1.83 and below lacking the
+        // blanked impl on `Deref`.
+        #[expect(clippy::overly_complex_bool_expr)]
+        if use_receiver_trait && false {
             if let Some(receiver) =
                 db.lang_item(table.trait_env.krate, LangItem::Receiver).and_then(|l| l.as_trait())
             {
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 142766c039b..7839589994b 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/consteval.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/consteval.rs
@@ -3,7 +3,7 @@
 use base_db::{ra_salsa::Cycle, CrateId};
 use chalk_ir::{cast::Cast, BoundVar, DebruijnIndex};
 use hir_def::{
-    body::{Body, HygieneId},
+    expr_store::{Body, HygieneId},
     hir::{Expr, ExprId},
     path::Path,
     resolver::{Resolver, ValueNs},
@@ -124,6 +124,7 @@ pub(crate) fn path_to_const<'g>(
             ConstScalar::UnevaluatedConst(c.into(), Substitution::empty(Interner)),
             expected_ty,
         )),
+        // FIXME: With feature(adt_const_params), we also need to consider other things here, e.g. struct constructors.
         _ => None,
     }
 }
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/expr.rs b/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/expr.rs
index 7f9f0c0de19..59aaf85164a 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/expr.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/expr.rs
@@ -36,7 +36,7 @@ use crate::{
 };
 
 pub(crate) use hir_def::{
-    body::Body,
+    expr_store::Body,
     hir::{Expr, ExprId, MatchArm, Pat, PatId, Statement},
     LocalFieldId, VariantId,
 };
@@ -440,7 +440,9 @@ impl ExprValidator {
                             return;
                         };
                         let root = source_ptr.file_syntax(db.upcast());
-                        let ast::Expr::IfExpr(if_expr) = source_ptr.value.to_node(&root) else {
+                        let either::Left(ast::Expr::IfExpr(if_expr)) =
+                            source_ptr.value.to_node(&root)
+                        else {
                             return;
                         };
                         let mut top_if_expr = if_expr;
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/match_check.rs b/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/match_check.rs
index c5d8c956615..b0f9fc53e29 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/match_check.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/match_check.rs
@@ -11,7 +11,8 @@ pub(crate) mod pat_analysis;
 
 use chalk_ir::Mutability;
 use hir_def::{
-    body::Body, data::adt::VariantData, hir::PatId, AdtId, EnumVariantId, LocalFieldId, VariantId,
+    data::adt::VariantData, expr_store::Body, hir::PatId, AdtId, EnumVariantId, LocalFieldId,
+    VariantId,
 };
 use hir_expand::name::Name;
 use span::Edition;
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/match_check/pat_analysis.rs b/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/match_check/pat_analysis.rs
index 2b854310a15..91eb59fb314 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/match_check/pat_analysis.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/match_check/pat_analysis.rs
@@ -95,7 +95,7 @@ impl<'db> MatchCheckCtx<'db> {
 
         let place_validity = PlaceValidity::from_bool(known_valid_scrutinee.unwrap_or(true));
         // Measured to take ~100ms on modern hardware.
-        let complexity_limit = Some(500000);
+        let complexity_limit = 500000;
         compute_match_usefulness(self, arms, scrut_ty, place_validity, complexity_limit)
     }
 
@@ -361,11 +361,11 @@ impl PatCx for MatchCheckCtx<'_> {
         }
     }
 
-    fn ctor_sub_tys<'a>(
-        &'a self,
-        ctor: &'a rustc_pattern_analysis::constructor::Constructor<Self>,
-        ty: &'a Self::Ty,
-    ) -> impl ExactSizeIterator<Item = (Self::Ty, PrivateUninhabitedField)> + Captures<'a> {
+    fn ctor_sub_tys(
+        &self,
+        ctor: &rustc_pattern_analysis::constructor::Constructor<Self>,
+        ty: &Self::Ty,
+    ) -> impl ExactSizeIterator<Item = (Self::Ty, PrivateUninhabitedField)> {
         let single = |ty| smallvec![(ty, PrivateUninhabitedField(false))];
         let tys: SmallVec<[_; 2]> = match ctor {
             Struct | Variant(_) | UnionField => match ty.kind(Interner) {
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/unsafe_check.rs b/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/unsafe_check.rs
index 6bba83fac98..ac849b0762d 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/unsafe_check.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/unsafe_check.rs
@@ -5,28 +5,31 @@ use std::mem;
 
 use either::Either;
 use hir_def::{
-    body::Body,
+    expr_store::Body,
     hir::{Expr, ExprId, ExprOrPatId, Pat, PatId, Statement, UnaryOp},
     path::Path,
     resolver::{HasResolver, ResolveValueResult, Resolver, ValueNs},
     type_ref::Rawness,
-    AdtId, DefWithBodyId, FieldId, VariantId,
+    AdtId, DefWithBodyId, FieldId, FunctionId, VariantId,
 };
+use span::Edition;
 
 use crate::{
-    db::HirDatabase, utils::is_fn_unsafe_to_call, InferenceResult, Interner, TyExt, TyKind,
+    db::HirDatabase, utils::is_fn_unsafe_to_call, InferenceResult, Interner, TargetFeatures, TyExt,
+    TyKind,
 };
 
-/// Returns `(unsafe_exprs, fn_is_unsafe)`.
-///
-/// If `fn_is_unsafe` is false, `unsafe_exprs` are hard errors. If true, they're `unsafe_op_in_unsafe_fn`.
-pub fn missing_unsafe(
-    db: &dyn HirDatabase,
-    def: DefWithBodyId,
-) -> (Vec<(ExprOrPatId, UnsafetyReason)>, bool) {
+#[derive(Debug, Default)]
+pub struct MissingUnsafeResult {
+    pub unsafe_exprs: Vec<(ExprOrPatId, UnsafetyReason)>,
+    /// If `fn_is_unsafe` is false, `unsafe_exprs` are hard errors. If true, they're `unsafe_op_in_unsafe_fn`.
+    pub fn_is_unsafe: bool,
+    pub deprecated_safe_calls: Vec<ExprId>,
+}
+
+pub fn missing_unsafe(db: &dyn HirDatabase, def: DefWithBodyId) -> MissingUnsafeResult {
     let _p = tracing::info_span!("missing_unsafe").entered();
 
-    let mut res = Vec::new();
     let is_unsafe = match def {
         DefWithBodyId::FunctionId(it) => db.function_data(it).is_unsafe(),
         DefWithBodyId::StaticId(_)
@@ -35,11 +38,19 @@ pub fn missing_unsafe(
         | DefWithBodyId::InTypeConstId(_) => false,
     };
 
+    let mut res = MissingUnsafeResult { fn_is_unsafe: is_unsafe, ..MissingUnsafeResult::default() };
     let body = db.body(def);
     let infer = db.infer(def);
-    let mut callback = |node, inside_unsafe_block, reason| {
-        if inside_unsafe_block == InsideUnsafeBlock::No {
-            res.push((node, reason));
+    let mut callback = |diag| match diag {
+        UnsafeDiagnostic::UnsafeOperation { node, inside_unsafe_block, reason } => {
+            if inside_unsafe_block == InsideUnsafeBlock::No {
+                res.unsafe_exprs.push((node, reason));
+            }
+        }
+        UnsafeDiagnostic::DeprecatedSafe2024 { node, inside_unsafe_block } => {
+            if inside_unsafe_block == InsideUnsafeBlock::No {
+                res.deprecated_safe_calls.push(node)
+            }
         }
     };
     let mut visitor = UnsafeVisitor::new(db, &infer, &body, def, &mut callback);
@@ -54,7 +65,7 @@ pub fn missing_unsafe(
         }
     }
 
-    (res, is_unsafe)
+    res
 }
 
 #[derive(Debug, Clone, Copy)]
@@ -73,15 +84,31 @@ pub enum InsideUnsafeBlock {
     Yes,
 }
 
+#[derive(Debug)]
+enum UnsafeDiagnostic {
+    UnsafeOperation {
+        node: ExprOrPatId,
+        inside_unsafe_block: InsideUnsafeBlock,
+        reason: UnsafetyReason,
+    },
+    /// A lint.
+    DeprecatedSafe2024 { node: ExprId, inside_unsafe_block: InsideUnsafeBlock },
+}
+
 pub fn unsafe_expressions(
     db: &dyn HirDatabase,
     infer: &InferenceResult,
     def: DefWithBodyId,
     body: &Body,
     current: ExprId,
-    unsafe_expr_cb: &mut dyn FnMut(ExprOrPatId, InsideUnsafeBlock, UnsafetyReason),
+    callback: &mut dyn FnMut(InsideUnsafeBlock),
 ) {
-    let mut visitor = UnsafeVisitor::new(db, infer, body, def, unsafe_expr_cb);
+    let mut visitor_callback = |diag| {
+        if let UnsafeDiagnostic::UnsafeOperation { inside_unsafe_block, .. } = diag {
+            callback(inside_unsafe_block);
+        }
+    };
+    let mut visitor = UnsafeVisitor::new(db, infer, body, def, &mut visitor_callback);
     _ = visitor.resolver.update_to_inner_scope(db.upcast(), def, current);
     visitor.walk_expr(current);
 }
@@ -95,7 +122,10 @@ struct UnsafeVisitor<'a> {
     inside_unsafe_block: InsideUnsafeBlock,
     inside_assignment: bool,
     inside_union_destructure: bool,
-    unsafe_expr_cb: &'a mut dyn FnMut(ExprOrPatId, InsideUnsafeBlock, UnsafetyReason),
+    callback: &'a mut dyn FnMut(UnsafeDiagnostic),
+    def_target_features: TargetFeatures,
+    // FIXME: This needs to be the edition of the span of each call.
+    edition: Edition,
 }
 
 impl<'a> UnsafeVisitor<'a> {
@@ -104,9 +134,14 @@ impl<'a> UnsafeVisitor<'a> {
         infer: &'a InferenceResult,
         body: &'a Body,
         def: DefWithBodyId,
-        unsafe_expr_cb: &'a mut dyn FnMut(ExprOrPatId, InsideUnsafeBlock, UnsafetyReason),
+        unsafe_expr_cb: &'a mut dyn FnMut(UnsafeDiagnostic),
     ) -> Self {
         let resolver = def.resolver(db.upcast());
+        let def_target_features = match def {
+            DefWithBodyId::FunctionId(func) => TargetFeatures::from_attrs(&db.attrs(func.into())),
+            _ => TargetFeatures::default(),
+        };
+        let edition = db.crate_graph()[resolver.module().krate()].edition;
         Self {
             db,
             infer,
@@ -116,12 +151,34 @@ impl<'a> UnsafeVisitor<'a> {
             inside_unsafe_block: InsideUnsafeBlock::No,
             inside_assignment: false,
             inside_union_destructure: false,
-            unsafe_expr_cb,
+            callback: unsafe_expr_cb,
+            def_target_features,
+            edition,
         }
     }
 
-    fn call_cb(&mut self, node: ExprOrPatId, reason: UnsafetyReason) {
-        (self.unsafe_expr_cb)(node, self.inside_unsafe_block, reason);
+    fn on_unsafe_op(&mut self, node: ExprOrPatId, reason: UnsafetyReason) {
+        (self.callback)(UnsafeDiagnostic::UnsafeOperation {
+            node,
+            inside_unsafe_block: self.inside_unsafe_block,
+            reason,
+        });
+    }
+
+    fn check_call(&mut self, node: ExprId, func: FunctionId) {
+        let unsafety = is_fn_unsafe_to_call(self.db, func, &self.def_target_features, self.edition);
+        match unsafety {
+            crate::utils::Unsafety::Safe => {}
+            crate::utils::Unsafety::Unsafe => {
+                self.on_unsafe_op(node.into(), UnsafetyReason::UnsafeFnCall)
+            }
+            crate::utils::Unsafety::DeprecatedSafe2024 => {
+                (self.callback)(UnsafeDiagnostic::DeprecatedSafe2024 {
+                    node,
+                    inside_unsafe_block: self.inside_unsafe_block,
+                })
+            }
+        }
     }
 
     fn walk_pats_top(&mut self, pats: impl Iterator<Item = PatId>, parent_expr: ExprId) {
@@ -146,7 +203,9 @@ impl<'a> UnsafeVisitor<'a> {
                 | Pat::Ref { .. }
                 | Pat::Box { .. }
                 | Pat::Expr(..)
-                | Pat::ConstBlock(..) => self.call_cb(current.into(), UnsafetyReason::UnionField),
+                | Pat::ConstBlock(..) => {
+                    self.on_unsafe_op(current.into(), UnsafetyReason::UnionField)
+                }
                 // `Or` only wraps other patterns, and `Missing`/`Wild` do not constitute a read.
                 Pat::Missing | Pat::Wild | Pat::Or(_) => {}
             }
@@ -180,9 +239,13 @@ impl<'a> UnsafeVisitor<'a> {
         let inside_assignment = mem::replace(&mut self.inside_assignment, false);
         match expr {
             &Expr::Call { callee, .. } => {
-                if let Some(func) = self.infer[callee].as_fn_def(self.db) {
-                    if is_fn_unsafe_to_call(self.db, func) {
-                        self.call_cb(current.into(), UnsafetyReason::UnsafeFnCall);
+                let callee = &self.infer[callee];
+                if let Some(func) = callee.as_fn_def(self.db) {
+                    self.check_call(current, func);
+                }
+                if let TyKind::Function(fn_ptr) = callee.kind(Interner) {
+                    if fn_ptr.sig.safety == chalk_ir::Safety::Unsafe {
+                        self.on_unsafe_op(current.into(), UnsafetyReason::UnsafeFnCall);
                     }
                 }
             }
@@ -209,18 +272,13 @@ impl<'a> UnsafeVisitor<'a> {
                 }
             }
             Expr::MethodCall { .. } => {
-                if self
-                    .infer
-                    .method_resolution(current)
-                    .map(|(func, _)| is_fn_unsafe_to_call(self.db, func))
-                    .unwrap_or(false)
-                {
-                    self.call_cb(current.into(), UnsafetyReason::UnsafeFnCall);
+                if let Some((func, _)) = self.infer.method_resolution(current) {
+                    self.check_call(current, func);
                 }
             }
             Expr::UnaryOp { expr, op: UnaryOp::Deref } => {
                 if let TyKind::Raw(..) = &self.infer[*expr].kind(Interner) {
-                    self.call_cb(current.into(), UnsafetyReason::RawPtrDeref);
+                    self.on_unsafe_op(current.into(), UnsafetyReason::RawPtrDeref);
                 }
             }
             Expr::Unsafe { .. } => {
@@ -235,7 +293,7 @@ impl<'a> UnsafeVisitor<'a> {
                 self.walk_pats_top(std::iter::once(target), current);
                 self.inside_assignment = old_inside_assignment;
             }
-            Expr::InlineAsm(_) => self.call_cb(current.into(), UnsafetyReason::InlineAsm),
+            Expr::InlineAsm(_) => self.on_unsafe_op(current.into(), UnsafetyReason::InlineAsm),
             // rustc allows union assignment to propagate through field accesses and casts.
             Expr::Cast { .. } => self.inside_assignment = inside_assignment,
             Expr::Field { .. } => {
@@ -244,7 +302,7 @@ impl<'a> UnsafeVisitor<'a> {
                     if let Some(Either::Left(FieldId { parent: VariantId::UnionId(_), .. })) =
                         self.infer.field_resolution(current)
                     {
-                        self.call_cb(current.into(), UnsafetyReason::UnionField);
+                        self.on_unsafe_op(current.into(), UnsafetyReason::UnionField);
                     }
                 }
             }
@@ -279,9 +337,9 @@ impl<'a> UnsafeVisitor<'a> {
         if let Some(ResolveValueResult::ValueNs(ValueNs::StaticId(id), _)) = value_or_partial {
             let static_data = self.db.static_data(id);
             if static_data.mutable {
-                self.call_cb(node, UnsafetyReason::MutableStatic);
+                self.on_unsafe_op(node, UnsafetyReason::MutableStatic);
             } else if static_data.is_extern && !static_data.has_safe_kw {
-                self.call_cb(node, UnsafetyReason::ExternStatic);
+                self.on_unsafe_op(node, UnsafetyReason::ExternStatic);
             }
         }
     }
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/generics.rs b/src/tools/rust-analyzer/crates/hir-ty/src/generics.rs
index abbf2a4f2ef..18cf6e5ce36 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/generics.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/generics.rs
@@ -262,7 +262,8 @@ fn parent_generic_def(db: &dyn DefDatabase, def: GenericDefId) -> Option<Generic
         GenericDefId::FunctionId(it) => it.lookup(db).container,
         GenericDefId::TypeAliasId(it) => it.lookup(db).container,
         GenericDefId::ConstId(it) => it.lookup(db).container,
-        GenericDefId::AdtId(_)
+        GenericDefId::StaticId(_)
+        | GenericDefId::AdtId(_)
         | GenericDefId::TraitId(_)
         | GenericDefId::ImplId(_)
         | GenericDefId::TraitAliasId(_) => return None,
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 25bb3a76de2..0cb7002f446 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs
@@ -16,7 +16,7 @@
 pub(crate) mod cast;
 pub(crate) mod closure;
 mod coerce;
-mod diagnostics;
+pub(crate) mod diagnostics;
 mod expr;
 mod mutability;
 mod pat;
@@ -34,9 +34,9 @@ use chalk_ir::{
 };
 use either::Either;
 use hir_def::{
-    body::{Body, HygieneId},
     builtin_type::{BuiltinInt, BuiltinType, BuiltinUint},
     data::{ConstData, StaticData},
+    expr_store::{Body, HygieneId},
     hir::{BindingAnnotation, BindingId, ExprId, ExprOrPatId, LabelId, PatId},
     lang_item::{LangItem, LangItemTarget},
     layout::Integer,
@@ -236,7 +236,7 @@ pub enum InferenceDiagnostic {
         name: Name,
         /// Contains the type the field resolves to
         field_with_same_name: Option<Ty>,
-        assoc_func_with_same_name: Option<AssocItemId>,
+        assoc_func_with_same_name: Option<FunctionId>,
     },
     UnresolvedAssocItem {
         id: ExprOrPatId,
@@ -466,6 +466,9 @@ pub struct InferenceResult {
     pub type_of_for_iterator: FxHashMap<ExprId, Ty>,
     type_mismatches: FxHashMap<ExprOrPatId, TypeMismatch>,
     /// Whether there are any type-mismatching errors in the result.
+    // FIXME: This isn't as useful as initially thought due to us falling back placeholders to
+    // `TyKind::Error`.
+    // Which will then mark this field.
     pub(crate) has_errors: bool,
     /// Interned common types to return references to.
     // FIXME: Move this into `InferenceContext`
@@ -943,7 +946,7 @@ impl<'a> InferenceContext<'a> {
             let ty = self.insert_type_vars(ty);
             let ty = self.normalize_associated_types_in(ty);
 
-            self.infer_top_pat(*pat, &ty);
+            self.infer_top_pat(*pat, &ty, None);
             if ty
                 .data(Interner)
                 .flags
@@ -1236,7 +1239,29 @@ impl<'a> InferenceContext<'a> {
     }
 
     fn write_expr_adj(&mut self, expr: ExprId, adjustments: Vec<Adjustment>) {
-        self.result.expr_adjustments.insert(expr, adjustments);
+        if adjustments.is_empty() {
+            return;
+        }
+        match self.result.expr_adjustments.entry(expr) {
+            std::collections::hash_map::Entry::Occupied(mut entry) => {
+                match (&mut entry.get_mut()[..], &adjustments[..]) {
+                    (
+                        [Adjustment { kind: Adjust::NeverToAny, target }],
+                        [.., Adjustment { target: new_target, .. }],
+                    ) => {
+                        // NeverToAny coercion can target any type, so instead of adding a new
+                        // adjustment on top we can change the target.
+                        *target = new_target.clone();
+                    }
+                    _ => {
+                        *entry.get_mut() = adjustments;
+                    }
+                }
+            }
+            std::collections::hash_map::Entry::Vacant(entry) => {
+                entry.insert(adjustments);
+            }
+        }
     }
 
     fn write_method_resolution(&mut self, expr: ExprId, func: FunctionId, subst: Substitution) {
@@ -1477,21 +1502,22 @@ impl<'a> InferenceContext<'a> {
             &self.diagnostics,
             InferenceTyDiagnosticSource::Body,
         );
+        let mut path_ctx = ctx.at_path(path, node);
         let (resolution, unresolved) = if value_ns {
-            let Some(res) = ctx.resolve_path_in_value_ns(path, node, HygieneId::ROOT) else {
+            let Some(res) = path_ctx.resolve_path_in_value_ns(HygieneId::ROOT) else {
                 return (self.err_ty(), None);
             };
             match res {
                 ResolveValueResult::ValueNs(value, _) => match value {
                     ValueNs::EnumVariantId(var) => {
-                        let substs = ctx.substs_from_path(path, var.into(), true);
+                        let substs = path_ctx.substs_from_path(var.into(), true);
                         drop(ctx);
                         let ty = self.db.ty(var.lookup(self.db.upcast()).parent.into());
                         let ty = self.insert_type_vars(ty.substitute(Interner, &substs));
                         return (ty, Some(var.into()));
                     }
                     ValueNs::StructId(strukt) => {
-                        let substs = ctx.substs_from_path(path, strukt.into(), true);
+                        let substs = path_ctx.substs_from_path(strukt.into(), true);
                         drop(ctx);
                         let ty = self.db.ty(strukt.into());
                         let ty = self.insert_type_vars(ty.substitute(Interner, &substs));
@@ -1506,7 +1532,7 @@ impl<'a> InferenceContext<'a> {
                 ResolveValueResult::Partial(typens, unresolved, _) => (typens, Some(unresolved)),
             }
         } else {
-            match ctx.resolve_path_in_type_ns(path, node) {
+            match path_ctx.resolve_path_in_type_ns() {
                 Some((it, idx)) => (it, idx),
                 None => return (self.err_ty(), None),
             }
@@ -1517,21 +1543,21 @@ impl<'a> InferenceContext<'a> {
         };
         return match resolution {
             TypeNs::AdtId(AdtId::StructId(strukt)) => {
-                let substs = ctx.substs_from_path(path, strukt.into(), true);
+                let substs = path_ctx.substs_from_path(strukt.into(), true);
                 drop(ctx);
                 let ty = self.db.ty(strukt.into());
                 let ty = self.insert_type_vars(ty.substitute(Interner, &substs));
                 forbid_unresolved_segments((ty, Some(strukt.into())), unresolved)
             }
             TypeNs::AdtId(AdtId::UnionId(u)) => {
-                let substs = ctx.substs_from_path(path, u.into(), true);
+                let substs = path_ctx.substs_from_path(u.into(), true);
                 drop(ctx);
                 let ty = self.db.ty(u.into());
                 let ty = self.insert_type_vars(ty.substitute(Interner, &substs));
                 forbid_unresolved_segments((ty, Some(u.into())), unresolved)
             }
             TypeNs::EnumVariantId(var) => {
-                let substs = ctx.substs_from_path(path, var.into(), true);
+                let substs = path_ctx.substs_from_path(var.into(), true);
                 drop(ctx);
                 let ty = self.db.ty(var.lookup(self.db.upcast()).parent.into());
                 let ty = self.insert_type_vars(ty.substitute(Interner, &substs));
@@ -1542,31 +1568,32 @@ impl<'a> InferenceContext<'a> {
                 let substs = generics.placeholder_subst(self.db);
                 let mut ty = self.db.impl_self_ty(impl_id).substitute(Interner, &substs);
 
-                let Some(mut remaining_idx) = unresolved else {
+                let Some(remaining_idx) = unresolved else {
                     drop(ctx);
                     return self.resolve_variant_on_alias(ty, None, mod_path);
                 };
 
                 let mut remaining_segments = path.segments().skip(remaining_idx);
 
+                if remaining_segments.len() >= 2 {
+                    path_ctx.ignore_last_segment();
+                }
+
                 // We need to try resolving unresolved segments one by one because each may resolve
                 // to a projection, which `TyLoweringContext` cannot handle on its own.
                 let mut tried_resolving_once = false;
-                while !remaining_segments.is_empty() {
-                    let resolved_segment = path.segments().get(remaining_idx - 1).unwrap();
-                    let current_segment = remaining_segments.take(1);
-
+                while let Some(current_segment) = remaining_segments.first() {
                     // If we can resolve to an enum variant, it takes priority over associated type
                     // of the same name.
                     if let Some((AdtId::EnumId(id), _)) = ty.as_adt() {
                         let enum_data = self.db.enum_data(id);
-                        let name = current_segment.first().unwrap().name;
-                        if let Some(variant) = enum_data.variant(name) {
+                        if let Some(variant) = enum_data.variant(current_segment.name) {
                             return if remaining_segments.len() == 1 {
                                 (ty, Some(variant.into()))
                             } else {
                                 // We still have unresolved paths, but enum variants never have
                                 // associated types!
+                                // FIXME: Report an error.
                                 (self.err_ty(), None)
                             };
                         }
@@ -1575,23 +1602,13 @@ impl<'a> InferenceContext<'a> {
                     if tried_resolving_once {
                         // FIXME: with `inherent_associated_types` this is allowed, but our `lower_partly_resolved_path()`
                         // will need to be updated to err at the correct segment.
-                        //
-                        // We need to stop here because otherwise the segment index passed to `lower_partly_resolved_path()`
-                        // will be incorrect, and that can mess up error reporting.
                         break;
                     }
 
                     // `lower_partly_resolved_path()` returns `None` as type namespace unless
                     // `remaining_segments` is empty, which is never the case here. We don't know
                     // which namespace the new `ty` is in until normalized anyway.
-                    (ty, _) = ctx.lower_partly_resolved_path(
-                        node,
-                        resolution,
-                        resolved_segment,
-                        current_segment,
-                        (remaining_idx - 1) as u32,
-                        false,
-                    );
+                    (ty, _) = path_ctx.lower_partly_resolved_path(resolution, false);
                     tried_resolving_once = true;
 
                     ty = self.table.insert_type_vars(ty);
@@ -1601,8 +1618,6 @@ impl<'a> InferenceContext<'a> {
                         return (self.err_ty(), None);
                     }
 
-                    // FIXME(inherent_associated_types): update `resolution` based on `ty` here.
-                    remaining_idx += 1;
                     remaining_segments = remaining_segments.skip(1);
                 }
                 drop(ctx);
@@ -1618,12 +1633,7 @@ impl<'a> InferenceContext<'a> {
                 (ty, variant)
             }
             TypeNs::TypeAliasId(it) => {
-                let resolved_seg = match unresolved {
-                    None => path.segments().last().unwrap(),
-                    Some(n) => path.segments().get(path.segments().len() - n - 1).unwrap(),
-                };
-                let substs =
-                    ctx.substs_from_path_segment(resolved_seg, Some(it.into()), true, None);
+                let substs = path_ctx.substs_from_path_segment(it.into(), true, None);
                 drop(ctx);
                 let ty = self.db.ty(it.into());
                 let ty = self.insert_type_vars(ty.substitute(Interner, &substs));
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 21d0be6ed5f..eb193686e96 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
@@ -374,6 +374,7 @@ enum PointerKind {
 
 fn pointer_kind(ty: &Ty, table: &mut InferenceTable<'_>) -> Result<Option<PointerKind>, ()> {
     let ty = table.resolve_ty_shallow(ty);
+    let ty = table.normalize_associated_types_in(ty);
 
     if table.is_sized(&ty) {
         return Ok(Some(PointerKind::Thin));
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 d40816ba8ce..acd86b1f3ed 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
@@ -163,10 +163,27 @@ 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(), CoerceNever::Yes) {
-            self.final_ty = Some(res);
-        } else if let Ok(res) = ctx.coerce(expr, &self.merged_ty(), &expr_ty, CoerceNever::Yes) {
+        //
+        // - [Comment from rustc](https://github.com/rust-lang/rust/blob/5ff18d0eaefd1bd9ab8ec33dab2404a44e7631ed/compiler/rustc_hir_typeck/src/coercion.rs#L1334-L1335)
+        // First try to coerce the new expression to the type of the previous ones,
+        // but only if the new expression has no coercion already applied to it.
+        if expr.is_none_or(|expr| !ctx.result.expr_adjustments.contains_key(&expr)) {
+            if let Ok(res) = ctx.coerce(expr, &expr_ty, &self.merged_ty(), CoerceNever::Yes) {
+                self.final_ty = Some(res);
+                if let Some(expr) = expr {
+                    self.expressions.push(expr);
+                }
+                return;
+            }
+        }
+
+        if let Ok((adjustments, res)) =
+            ctx.coerce_inner(&self.merged_ty(), &expr_ty, CoerceNever::Yes)
+        {
             self.final_ty = Some(res);
+            for &e in &self.expressions {
+                ctx.write_expr_adj(e, adjustments.clone());
+            }
         } else {
             match cause {
                 CoercionCause::Expr(id) => {
@@ -244,14 +261,23 @@ impl InferenceContext<'_> {
         // 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, coerce_never)?;
+        let (adjustments, ty) = self.coerce_inner(from_ty, to_ty, coerce_never)?;
         if let Some(expr) = expr {
             self.write_expr_adj(expr, adjustments);
         }
         Ok(ty)
     }
+
+    fn coerce_inner(
+        &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);
+        self.table.coerce(&from_ty, &to_ty, coerce_never)
+    }
 }
 
 impl InferenceTable<'_> {
@@ -373,7 +399,7 @@ impl InferenceTable<'_> {
         // Check that the types which they point at are compatible.
         let from_raw = TyKind::Raw(to_mt, from_inner.clone()).intern(Interner);
 
-        // Although references and unsafe ptrs have the same
+        // Although references and raw ptrs have the same
         // representation, we still register an Adjust::DerefRef so that
         // regionck knows that the region for `a` must be valid here.
         if is_ref {
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/diagnostics.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/diagnostics.rs
index 032dc37899d..e4f5b5ed378 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/diagnostics.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/diagnostics.rs
@@ -5,16 +5,14 @@
 use std::cell::RefCell;
 use std::ops::{Deref, DerefMut};
 
-use hir_def::body::HygieneId;
-use hir_def::hir::ExprOrPatId;
-use hir_def::path::{Path, PathSegment, PathSegments};
-use hir_def::resolver::{ResolveValueResult, Resolver, TypeNs};
-use hir_def::type_ref::TypesMap;
-use hir_def::TypeOwnerId;
-
-use crate::db::HirDatabase;
+use either::Either;
+use hir_def::{hir::ExprOrPatId, path::Path, resolver::Resolver, type_ref::TypesMap, TypeOwnerId};
+use la_arena::{Idx, RawIdx};
+
 use crate::{
-    InferenceDiagnostic, InferenceTyDiagnosticSource, Ty, TyLoweringContext, TyLoweringDiagnostic,
+    db::HirDatabase,
+    lower::path::{PathDiagnosticCallback, PathLoweringContext},
+    InferenceDiagnostic, InferenceTyDiagnosticSource, TyLoweringContext, TyLoweringDiagnostic,
 };
 
 // Unfortunately, this struct needs to use interior mutability (but we encapsulate it)
@@ -44,6 +42,11 @@ impl Diagnostics {
     }
 }
 
+pub(crate) struct PathDiagnosticCallbackData<'a> {
+    node: ExprOrPatId,
+    diagnostics: &'a Diagnostics,
+}
+
 pub(super) struct InferenceTyLoweringContext<'a> {
     ctx: TyLoweringContext<'a>,
     diagnostics: &'a Diagnostics,
@@ -51,6 +54,7 @@ pub(super) struct InferenceTyLoweringContext<'a> {
 }
 
 impl<'a> InferenceTyLoweringContext<'a> {
+    #[inline]
     pub(super) fn new(
         db: &'a dyn HirDatabase,
         resolver: &'a Resolver,
@@ -62,65 +66,62 @@ impl<'a> InferenceTyLoweringContext<'a> {
         Self { ctx: TyLoweringContext::new(db, resolver, types_map, owner), diagnostics, source }
     }
 
-    pub(super) fn resolve_path_in_type_ns(
-        &mut self,
-        path: &Path,
+    #[inline]
+    pub(super) fn at_path<'b>(
+        &'b mut self,
+        path: &'b Path,
         node: ExprOrPatId,
-    ) -> Option<(TypeNs, Option<usize>)> {
-        let diagnostics = self.diagnostics;
-        self.ctx.resolve_path_in_type_ns(path, &mut |_, diag| {
-            diagnostics.push(InferenceDiagnostic::PathDiagnostic { node, diag })
-        })
+    ) -> PathLoweringContext<'b, 'a> {
+        let on_diagnostic = PathDiagnosticCallback {
+            data: Either::Right(PathDiagnosticCallbackData { diagnostics: self.diagnostics, node }),
+            callback: |data, _, diag| {
+                let data = data.as_ref().right().unwrap();
+                data.diagnostics
+                    .push(InferenceDiagnostic::PathDiagnostic { node: data.node, diag });
+            },
+        };
+        PathLoweringContext::new(&mut self.ctx, on_diagnostic, path)
     }
 
-    pub(super) fn resolve_path_in_value_ns(
-        &mut self,
-        path: &Path,
-        node: ExprOrPatId,
-        hygiene_id: HygieneId,
-    ) -> Option<ResolveValueResult> {
-        let diagnostics = self.diagnostics;
-        self.ctx.resolve_path_in_value_ns(path, hygiene_id, &mut |_, diag| {
-            diagnostics.push(InferenceDiagnostic::PathDiagnostic { node, diag })
-        })
+    #[inline]
+    pub(super) fn at_path_forget_diagnostics<'b>(
+        &'b mut self,
+        path: &'b Path,
+    ) -> PathLoweringContext<'b, 'a> {
+        let on_diagnostic = PathDiagnosticCallback {
+            data: Either::Right(PathDiagnosticCallbackData {
+                diagnostics: self.diagnostics,
+                node: ExprOrPatId::ExprId(Idx::from_raw(RawIdx::from_u32(0))),
+            }),
+            callback: |_data, _, _diag| {},
+        };
+        PathLoweringContext::new(&mut self.ctx, on_diagnostic, path)
     }
 
-    pub(super) fn lower_partly_resolved_path(
-        &mut self,
-        node: ExprOrPatId,
-        resolution: TypeNs,
-        resolved_segment: PathSegment<'_>,
-        remaining_segments: PathSegments<'_>,
-        resolved_segment_idx: u32,
-        infer_args: bool,
-    ) -> (Ty, Option<TypeNs>) {
-        let diagnostics = self.diagnostics;
-        self.ctx.lower_partly_resolved_path(
-            resolution,
-            resolved_segment,
-            remaining_segments,
-            resolved_segment_idx,
-            infer_args,
-            &mut |_, diag| diagnostics.push(InferenceDiagnostic::PathDiagnostic { node, diag }),
-        )
+    #[inline]
+    pub(super) fn forget_diagnostics(&mut self) {
+        self.ctx.diagnostics.clear();
     }
 }
 
 impl<'a> Deref for InferenceTyLoweringContext<'a> {
     type Target = TyLoweringContext<'a>;
 
+    #[inline]
     fn deref(&self) -> &Self::Target {
         &self.ctx
     }
 }
 
 impl DerefMut for InferenceTyLoweringContext<'_> {
+    #[inline]
     fn deref_mut(&mut self) -> &mut Self::Target {
         &mut self.ctx
     }
 }
 
 impl Drop for InferenceTyLoweringContext<'_> {
+    #[inline]
     fn drop(&mut self) {
         self.diagnostics
             .push_ty_diagnostics(self.source, std::mem::take(&mut self.ctx.diagnostics));
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 b951443897c..80e3ca1fa28 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
@@ -43,9 +43,9 @@ use crate::{
     primitive::{self, UintTy},
     static_lifetime, to_chalk_trait_id,
     traits::FnTrait,
-    Adjust, Adjustment, AdtId, AutoBorrow, Binders, CallableDefId, CallableSig, FnAbi, FnPointer,
-    FnSig, FnSubst, Interner, Rawness, Scalar, Substitution, TraitEnvironment, TraitRef, Ty,
-    TyBuilder, TyExt, TyKind,
+    Adjust, Adjustment, AdtId, AutoBorrow, Binders, CallableDefId, CallableSig, DeclContext,
+    DeclOrigin, FnAbi, FnPointer, FnSig, FnSubst, Interner, Rawness, Scalar, Substitution,
+    TraitEnvironment, TraitRef, Ty, TyBuilder, TyExt, TyKind,
 };
 
 use super::{
@@ -334,7 +334,11 @@ impl InferenceContext<'_> {
                     ExprIsRead::No
                 };
                 let input_ty = self.infer_expr(expr, &Expectation::none(), child_is_read);
-                self.infer_top_pat(pat, &input_ty);
+                self.infer_top_pat(
+                    pat,
+                    &input_ty,
+                    Some(DeclContext { origin: DeclOrigin::LetExpr }),
+                );
                 self.result.standard_types.bool_.clone()
             }
             Expr::Block { statements, tail, label, id } => {
@@ -461,7 +465,7 @@ impl InferenceContext<'_> {
 
                 // Now go through the argument patterns
                 for (arg_pat, arg_ty) in args.iter().zip(&sig_tys) {
-                    self.infer_top_pat(*arg_pat, arg_ty);
+                    self.infer_top_pat(*arg_pat, arg_ty, None);
                 }
 
                 // FIXME: lift these out into a struct
@@ -485,78 +489,7 @@ impl InferenceContext<'_> {
 
                 ty
             }
-            Expr::Call { callee, args, .. } => {
-                let callee_ty = self.infer_expr(*callee, &Expectation::none(), ExprIsRead::Yes);
-                let mut derefs = Autoderef::new(&mut self.table, callee_ty.clone(), false, true);
-                let (res, derefed_callee) = loop {
-                    let Some((callee_deref_ty, _)) = derefs.next() else {
-                        break (None, callee_ty.clone());
-                    };
-                    if let Some(res) = derefs.table.callable_sig(&callee_deref_ty, args.len()) {
-                        break (Some(res), callee_deref_ty);
-                    }
-                };
-                // if the function is unresolved, we use is_varargs=true to
-                // suppress the arg count diagnostic here
-                let is_varargs =
-                    derefed_callee.callable_sig(self.db).is_some_and(|sig| sig.is_varargs)
-                        || res.is_none();
-                let (param_tys, ret_ty) = match res {
-                    Some((func, params, ret_ty)) => {
-                        let mut adjustments = auto_deref_adjust_steps(&derefs);
-                        if let TyKind::Closure(c, _) =
-                            self.table.resolve_completely(callee_ty.clone()).kind(Interner)
-                        {
-                            if let Some(par) = self.current_closure {
-                                self.closure_dependencies.entry(par).or_default().push(*c);
-                            }
-                            self.deferred_closures.entry(*c).or_default().push((
-                                derefed_callee.clone(),
-                                callee_ty.clone(),
-                                params.clone(),
-                                tgt_expr,
-                            ));
-                        }
-                        if let Some(fn_x) = func {
-                            self.write_fn_trait_method_resolution(
-                                fn_x,
-                                &derefed_callee,
-                                &mut adjustments,
-                                &callee_ty,
-                                &params,
-                                tgt_expr,
-                            );
-                        }
-                        self.write_expr_adj(*callee, adjustments);
-                        (params, ret_ty)
-                    }
-                    None => {
-                        self.push_diagnostic(InferenceDiagnostic::ExpectedFunction {
-                            call_expr: tgt_expr,
-                            found: callee_ty.clone(),
-                        });
-                        (Vec::new(), self.err_ty())
-                    }
-                };
-                let indices_to_skip = self.check_legacy_const_generics(derefed_callee, args);
-                self.register_obligations_for_call(&callee_ty);
-
-                let expected_inputs = self.expected_inputs_for_expected_output(
-                    expected,
-                    ret_ty.clone(),
-                    param_tys.clone(),
-                );
-
-                self.check_call_arguments(
-                    tgt_expr,
-                    args,
-                    &expected_inputs,
-                    &param_tys,
-                    &indices_to_skip,
-                    is_varargs,
-                );
-                self.normalize_associated_types_in(ret_ty)
-            }
+            Expr::Call { callee, args, .. } => self.infer_call(tgt_expr, *callee, args, expected),
             Expr::MethodCall { receiver, args, method_name, generic_args } => self
                 .infer_method_call(
                     tgt_expr,
@@ -582,7 +515,7 @@ impl InferenceContext<'_> {
                     let mut all_arms_diverge = Diverges::Always;
                     for arm in arms.iter() {
                         let input_ty = self.resolve_ty_shallow(&input_ty);
-                        self.infer_top_pat(arm.pat, &input_ty);
+                        self.infer_top_pat(arm.pat, &input_ty, None);
                     }
 
                     let expected = expected.adjust_for_branches(&mut self.table);
@@ -927,7 +860,7 @@ impl InferenceContext<'_> {
                     let resolver_guard =
                         self.resolver.update_to_inner_scope(self.db.upcast(), self.owner, tgt_expr);
                     self.inside_assignment = true;
-                    self.infer_top_pat(target, &rhs_ty);
+                    self.infer_top_pat(target, &rhs_ty, None);
                     self.inside_assignment = false;
                     self.resolver.reset_to_guard(resolver_guard);
                 }
@@ -1632,8 +1565,11 @@ impl InferenceContext<'_> {
                                 decl_ty
                             };
 
-                            this.infer_top_pat(*pat, &ty);
+                            let decl = DeclContext {
+                                origin: DeclOrigin::LocalDecl { has_else: else_branch.is_some() },
+                            };
 
+                            this.infer_top_pat(*pat, &ty, Some(decl));
                             if let Some(expr) = else_branch {
                                 let previous_diverges =
                                     mem::replace(&mut this.diverges, Diverges::Maybe);
@@ -1865,6 +1801,107 @@ impl InferenceContext<'_> {
         }
     }
 
+    fn infer_call(
+        &mut self,
+        tgt_expr: ExprId,
+        callee: ExprId,
+        args: &[ExprId],
+        expected: &Expectation,
+    ) -> Ty {
+        let callee_ty = self.infer_expr(callee, &Expectation::none(), ExprIsRead::Yes);
+        let mut derefs = Autoderef::new(&mut self.table, callee_ty.clone(), false, true);
+        let (res, derefed_callee) = loop {
+            let Some((callee_deref_ty, _)) = derefs.next() else {
+                break (None, callee_ty.clone());
+            };
+            if let Some(res) = derefs.table.callable_sig(&callee_deref_ty, args.len()) {
+                break (Some(res), callee_deref_ty);
+            }
+        };
+        // if the function is unresolved, we use is_varargs=true to
+        // suppress the arg count diagnostic here
+        let is_varargs =
+            derefed_callee.callable_sig(self.db).is_some_and(|sig| sig.is_varargs) || res.is_none();
+        let (param_tys, ret_ty) = match res {
+            Some((func, params, ret_ty)) => {
+                let mut adjustments = auto_deref_adjust_steps(&derefs);
+                if let TyKind::Closure(c, _) =
+                    self.table.resolve_completely(callee_ty.clone()).kind(Interner)
+                {
+                    if let Some(par) = self.current_closure {
+                        self.closure_dependencies.entry(par).or_default().push(*c);
+                    }
+                    self.deferred_closures.entry(*c).or_default().push((
+                        derefed_callee.clone(),
+                        callee_ty.clone(),
+                        params.clone(),
+                        tgt_expr,
+                    ));
+                }
+                if let Some(fn_x) = func {
+                    self.write_fn_trait_method_resolution(
+                        fn_x,
+                        &derefed_callee,
+                        &mut adjustments,
+                        &callee_ty,
+                        &params,
+                        tgt_expr,
+                    );
+                }
+                self.write_expr_adj(callee, adjustments);
+                (params, ret_ty)
+            }
+            None => {
+                self.push_diagnostic(InferenceDiagnostic::ExpectedFunction {
+                    call_expr: tgt_expr,
+                    found: callee_ty.clone(),
+                });
+                (Vec::new(), self.err_ty())
+            }
+        };
+        let indices_to_skip = self.check_legacy_const_generics(derefed_callee, args);
+        self.check_call(
+            tgt_expr,
+            args,
+            callee_ty,
+            &param_tys,
+            ret_ty,
+            &indices_to_skip,
+            is_varargs,
+            expected,
+        )
+    }
+
+    fn check_call(
+        &mut self,
+        tgt_expr: ExprId,
+        args: &[ExprId],
+        callee_ty: Ty,
+        param_tys: &[Ty],
+        ret_ty: Ty,
+        indices_to_skip: &[u32],
+        is_varargs: bool,
+        expected: &Expectation,
+    ) -> Ty {
+        self.register_obligations_for_call(&callee_ty);
+
+        let expected_inputs = self.expected_inputs_for_expected_output(
+            expected,
+            ret_ty.clone(),
+            param_tys.to_owned(),
+        );
+
+        self.check_call_arguments(
+            tgt_expr,
+            args,
+            &expected_inputs,
+            param_tys,
+            indices_to_skip,
+            is_varargs,
+        );
+        self.normalize_associated_types_in(ret_ty)
+    }
+
     fn infer_method_call(
         &mut self,
         tgt_expr: ExprId,
@@ -1885,21 +1922,32 @@ impl InferenceContext<'_> {
             VisibleFromModule::Filter(self.resolver.module()),
             method_name,
         );
-        let (receiver_ty, method_ty, substs) = match resolved {
+        match resolved {
             Some((adjust, func, visible)) => {
-                let (ty, adjustments) = adjust.apply(&mut self.table, receiver_ty);
-                let generics = generics(self.db.upcast(), func.into());
-                let substs = self.substs_for_method_call(generics, generic_args);
-                self.write_expr_adj(receiver, adjustments);
-                self.write_method_resolution(tgt_expr, func, substs.clone());
                 if !visible {
                     self.push_diagnostic(InferenceDiagnostic::PrivateAssocItem {
                         id: tgt_expr.into(),
                         item: func.into(),
                     })
                 }
-                (ty, self.db.value_ty(func.into()).unwrap(), substs)
+
+                let (ty, adjustments) = adjust.apply(&mut self.table, receiver_ty);
+                self.write_expr_adj(receiver, adjustments);
+
+                let generics = generics(self.db.upcast(), func.into());
+                let substs = self.substs_for_method_call(generics, generic_args);
+                self.write_method_resolution(tgt_expr, func, substs.clone());
+                self.check_method_call(
+                    tgt_expr,
+                    args,
+                    self.db.value_ty(func.into()).expect("we have a function def"),
+                    substs,
+                    ty,
+                    expected,
+                )
             }
+            // Failed to resolve, report diagnostic and try to resolve as call to field access or
+            // assoc function
             None => {
                 let field_with_same_name_exists = match self.lookup_field(&receiver_ty, method_name)
                 {
@@ -1919,12 +1967,11 @@ impl InferenceContext<'_> {
                     VisibleFromModule::Filter(self.resolver.module()),
                     Some(method_name),
                     method_resolution::LookupMode::Path,
-                    |_ty, item, visible| {
-                        if visible {
-                            Some(item)
-                        } else {
-                            None
+                    |_ty, item, visible| match item {
+                        hir_def::AssocItemId::FunctionId(function_id) if visible => {
+                            Some(function_id)
                         }
+                        _ => None,
                     },
                 );
 
@@ -1932,17 +1979,45 @@ impl InferenceContext<'_> {
                     expr: tgt_expr,
                     receiver: receiver_ty.clone(),
                     name: method_name.clone(),
-                    field_with_same_name: field_with_same_name_exists,
+                    field_with_same_name: field_with_same_name_exists.clone(),
                     assoc_func_with_same_name,
                 });
-                (
-                    receiver_ty,
-                    Binders::empty(Interner, self.err_ty()),
-                    Substitution::empty(Interner),
-                )
+
+                let recovered = match assoc_func_with_same_name {
+                    Some(f) => {
+                        let generics = generics(self.db.upcast(), f.into());
+                        let substs = self.substs_for_method_call(generics, generic_args);
+                        let f = self
+                            .db
+                            .value_ty(f.into())
+                            .expect("we have a function def")
+                            .substitute(Interner, &substs);
+                        let sig = f.callable_sig(self.db).expect("we have a function def");
+                        Some((f, sig, true))
+                    }
+                    None => field_with_same_name_exists.and_then(|field_ty| {
+                        let callable_sig = field_ty.callable_sig(self.db)?;
+                        Some((field_ty, callable_sig, false))
+                    }),
+                };
+                match recovered {
+                    Some((callee_ty, sig, strip_first)) => self.check_call(
+                        tgt_expr,
+                        args,
+                        callee_ty,
+                        sig.params().get(strip_first as usize..).unwrap_or(&[]),
+                        sig.ret().clone(),
+                        &[],
+                        true,
+                        expected,
+                    ),
+                    None => {
+                        self.check_call_arguments(tgt_expr, args, &[], &[], &[], true);
+                        self.err_ty()
+                    }
+                }
             }
-        };
-        self.check_method_call(tgt_expr, args, method_ty, substs, receiver_ty, expected)
+        }
     }
 
     fn check_method_call(
@@ -2012,9 +2087,10 @@ impl InferenceContext<'_> {
         expected_inputs: &[Ty],
         param_tys: &[Ty],
         skip_indices: &[u32],
-        is_varargs: bool,
+        ignore_arg_param_mismatch: bool,
     ) {
-        let arg_count_mismatch = args.len() != param_tys.len() + skip_indices.len() && !is_varargs;
+        let arg_count_mismatch =
+            !ignore_arg_param_mismatch && args.len() != param_tys.len() + skip_indices.len();
         if arg_count_mismatch {
             self.push_diagnostic(InferenceDiagnostic::MismatchedArgCount {
                 call_expr: expr,
@@ -2043,7 +2119,7 @@ impl InferenceContext<'_> {
                     continue;
                 }
 
-                while skip_indices.peek().is_some_and(|i| *i < idx as u32) {
+                while skip_indices.peek().is_some_and(|&i| i < idx as u32) {
                     skip_indices.next();
                 }
                 if skip_indices.peek().copied() == Some(idx as u32) {
@@ -2125,8 +2201,8 @@ impl InferenceContext<'_> {
             for kind_id in def_generics.iter_self_id().take(self_params) {
                 let arg = args.peek();
                 let arg = match (kind_id, arg) {
-                    // Lifetimes can be elided.
-                    // Once we have implemented lifetime elision correctly,
+                    // Lifetimes can be inferred.
+                    // Once we have implemented lifetime inference correctly,
                     // this should be handled in a proper way.
                     (
                         GenericParamId::LifetimeParamId(_),
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 00398f019da..db93116f107 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
@@ -3,23 +3,24 @@
 use std::iter::repeat_with;
 
 use hir_def::{
-    body::Body,
+    expr_store::Body,
     hir::{Binding, BindingAnnotation, BindingId, Expr, ExprId, Literal, Pat, PatId},
     path::Path,
+    HasModule,
 };
 use hir_expand::name::Name;
 use stdx::TupleExt;
 
 use crate::{
-    consteval::{try_const_usize, usize_const},
+    consteval::{self, try_const_usize, usize_const},
     infer::{
         coerce::CoerceNever, expr::ExprIsRead, BindingMode, Expectation, InferenceContext,
         TypeMismatch,
     },
     lower::lower_to_chalk_mutability,
     primitive::UintTy,
-    static_lifetime, InferenceDiagnostic, Interner, Mutability, Scalar, Substitution, Ty,
-    TyBuilder, TyExt, TyKind,
+    static_lifetime, DeclContext, DeclOrigin, InferenceDiagnostic, Interner, Mutability, Scalar,
+    Substitution, Ty, TyBuilder, TyExt, TyKind,
 };
 
 impl InferenceContext<'_> {
@@ -34,6 +35,7 @@ impl InferenceContext<'_> {
         id: PatId,
         ellipsis: Option<u32>,
         subs: &[PatId],
+        decl: Option<DeclContext>,
     ) -> Ty {
         let (ty, def) = self.resolve_variant(id.into(), path, true);
         let var_data = def.map(|it| it.variant_data(self.db.upcast()));
@@ -92,13 +94,13 @@ impl InferenceContext<'_> {
                         }
                     };
 
-                    self.infer_pat(subpat, &expected_ty, default_bm);
+                    self.infer_pat(subpat, &expected_ty, default_bm, decl);
                 }
             }
             None => {
                 let err_ty = self.err_ty();
                 for &inner in subs {
-                    self.infer_pat(inner, &err_ty, default_bm);
+                    self.infer_pat(inner, &err_ty, default_bm, decl);
                 }
             }
         }
@@ -114,6 +116,7 @@ impl InferenceContext<'_> {
         default_bm: BindingMode,
         id: PatId,
         subs: impl ExactSizeIterator<Item = (Name, PatId)>,
+        decl: Option<DeclContext>,
     ) -> Ty {
         let (ty, def) = self.resolve_variant(id.into(), path, false);
         if let Some(variant) = def {
@@ -162,13 +165,13 @@ impl InferenceContext<'_> {
                         }
                     };
 
-                    self.infer_pat(inner, &expected_ty, default_bm);
+                    self.infer_pat(inner, &expected_ty, default_bm, decl);
                 }
             }
             None => {
                 let err_ty = self.err_ty();
                 for (_, inner) in subs {
-                    self.infer_pat(inner, &err_ty, default_bm);
+                    self.infer_pat(inner, &err_ty, default_bm, decl);
                 }
             }
         }
@@ -185,6 +188,7 @@ impl InferenceContext<'_> {
         default_bm: BindingMode,
         ellipsis: Option<u32>,
         subs: &[PatId],
+        decl: Option<DeclContext>,
     ) -> Ty {
         let expected = self.resolve_ty_shallow(expected);
         let expectations = match expected.as_tuple() {
@@ -209,12 +213,12 @@ impl InferenceContext<'_> {
 
         // Process pre
         for (ty, pat) in inner_tys.iter_mut().zip(pre) {
-            *ty = self.infer_pat(*pat, ty, default_bm);
+            *ty = self.infer_pat(*pat, ty, default_bm, decl);
         }
 
         // Process post
         for (ty, pat) in inner_tys.iter_mut().skip(pre.len() + n_uncovered_patterns).zip(post) {
-            *ty = self.infer_pat(*pat, ty, default_bm);
+            *ty = self.infer_pat(*pat, ty, default_bm, decl);
         }
 
         TyKind::Tuple(inner_tys.len(), Substitution::from_iter(Interner, inner_tys))
@@ -223,11 +227,17 @@ impl InferenceContext<'_> {
 
     /// The resolver needs to be updated to the surrounding expression when inside assignment
     /// (because there, `Pat::Path` can refer to a variable).
-    pub(super) fn infer_top_pat(&mut self, pat: PatId, expected: &Ty) {
-        self.infer_pat(pat, expected, BindingMode::default());
+    pub(super) fn infer_top_pat(&mut self, pat: PatId, expected: &Ty, decl: Option<DeclContext>) {
+        self.infer_pat(pat, expected, BindingMode::default(), decl);
     }
 
-    fn infer_pat(&mut self, pat: PatId, expected: &Ty, mut default_bm: BindingMode) -> Ty {
+    fn infer_pat(
+        &mut self,
+        pat: PatId,
+        expected: &Ty,
+        mut default_bm: BindingMode,
+        decl: Option<DeclContext>,
+    ) -> Ty {
         let mut expected = self.resolve_ty_shallow(expected);
 
         if matches!(&self.body[pat], Pat::Ref { .. }) || self.inside_assignment {
@@ -261,11 +271,11 @@ impl InferenceContext<'_> {
 
         let ty = match &self.body[pat] {
             Pat::Tuple { args, ellipsis } => {
-                self.infer_tuple_pat_like(&expected, default_bm, *ellipsis, args)
+                self.infer_tuple_pat_like(&expected, default_bm, *ellipsis, args, decl)
             }
             Pat::Or(pats) => {
                 for pat in pats.iter() {
-                    self.infer_pat(*pat, &expected, default_bm);
+                    self.infer_pat(*pat, &expected, default_bm, decl);
                 }
                 expected.clone()
             }
@@ -274,6 +284,7 @@ impl InferenceContext<'_> {
                 lower_to_chalk_mutability(mutability),
                 &expected,
                 default_bm,
+                decl,
             ),
             Pat::TupleStruct { path: p, args: subpats, ellipsis } => self
                 .infer_tuple_struct_pat_like(
@@ -283,10 +294,11 @@ impl InferenceContext<'_> {
                     pat,
                     *ellipsis,
                     subpats,
+                    decl,
                 ),
             Pat::Record { path: p, args: fields, ellipsis: _ } => {
                 let subs = fields.iter().map(|f| (f.name.clone(), f.pat));
-                self.infer_record_pat_like(p.as_deref(), &expected, default_bm, pat, subs)
+                self.infer_record_pat_like(p.as_deref(), &expected, default_bm, pat, subs, decl)
             }
             Pat::Path(path) => {
                 let ty = self.infer_path(path, pat.into()).unwrap_or_else(|| self.err_ty());
@@ -319,10 +331,10 @@ impl InferenceContext<'_> {
                 }
             }
             Pat::Bind { id, subpat } => {
-                return self.infer_bind_pat(pat, *id, default_bm, *subpat, &expected);
+                return self.infer_bind_pat(pat, *id, default_bm, *subpat, &expected, decl);
             }
             Pat::Slice { prefix, slice, suffix } => {
-                self.infer_slice_pat(&expected, prefix, slice, suffix, default_bm)
+                self.infer_slice_pat(&expected, prefix, slice, suffix, default_bm, decl)
             }
             Pat::Wild => expected.clone(),
             Pat::Range { .. } => {
@@ -345,7 +357,7 @@ impl InferenceContext<'_> {
                         _ => (self.result.standard_types.unknown.clone(), None),
                     };
 
-                    let inner_ty = self.infer_pat(*inner, &inner_ty, default_bm);
+                    let inner_ty = self.infer_pat(*inner, &inner_ty, default_bm, decl);
                     let mut b = TyBuilder::adt(self.db, box_adt).push(inner_ty);
 
                     if let Some(alloc_ty) = alloc_ty {
@@ -420,6 +432,7 @@ impl InferenceContext<'_> {
         mutability: Mutability,
         expected: &Ty,
         default_bm: BindingMode,
+        decl: Option<DeclContext>,
     ) -> Ty {
         let (expectation_type, expectation_lt) = match expected.as_reference() {
             Some((inner_ty, lifetime, _exp_mut)) => (inner_ty.clone(), lifetime.clone()),
@@ -433,7 +446,7 @@ impl InferenceContext<'_> {
                 (inner_ty, inner_lt)
             }
         };
-        let subty = self.infer_pat(inner_pat, &expectation_type, default_bm);
+        let subty = self.infer_pat(inner_pat, &expectation_type, default_bm, decl);
         TyKind::Ref(mutability, expectation_lt, subty).intern(Interner)
     }
 
@@ -444,6 +457,7 @@ impl InferenceContext<'_> {
         default_bm: BindingMode,
         subpat: Option<PatId>,
         expected: &Ty,
+        decl: Option<DeclContext>,
     ) -> Ty {
         let Binding { mode, .. } = self.body.bindings[binding];
         let mode = if mode == BindingAnnotation::Unannotated {
@@ -454,7 +468,7 @@ impl InferenceContext<'_> {
         self.result.binding_modes.insert(pat, mode);
 
         let inner_ty = match subpat {
-            Some(subpat) => self.infer_pat(subpat, expected, default_bm),
+            Some(subpat) => self.infer_pat(subpat, expected, default_bm, decl),
             None => expected.clone(),
         };
         let inner_ty = self.insert_type_vars_shallow(inner_ty);
@@ -478,14 +492,28 @@ impl InferenceContext<'_> {
         slice: &Option<PatId>,
         suffix: &[PatId],
         default_bm: BindingMode,
+        decl: Option<DeclContext>,
     ) -> Ty {
+        let expected = self.resolve_ty_shallow(expected);
+
+        // If `expected` is an infer ty, we try to equate it to an array if the given pattern
+        // allows it. See issue #16609
+        if self.pat_is_irrefutable(decl) && expected.is_ty_var() {
+            if let Some(resolved_array_ty) =
+                self.try_resolve_slice_ty_to_array_ty(prefix, suffix, slice)
+            {
+                self.unify(&expected, &resolved_array_ty);
+            }
+        }
+
+        let expected = self.resolve_ty_shallow(&expected);
         let elem_ty = match expected.kind(Interner) {
             TyKind::Array(st, _) | TyKind::Slice(st) => st.clone(),
             _ => self.err_ty(),
         };
 
         for &pat_id in prefix.iter().chain(suffix.iter()) {
-            self.infer_pat(pat_id, &elem_ty, default_bm);
+            self.infer_pat(pat_id, &elem_ty, default_bm, decl);
         }
 
         if let &Some(slice_pat_id) = slice {
@@ -499,7 +527,7 @@ impl InferenceContext<'_> {
                 _ => TyKind::Slice(elem_ty.clone()),
             }
             .intern(Interner);
-            self.infer_pat(slice_pat_id, &rest_pat_ty, default_bm);
+            self.infer_pat(slice_pat_id, &rest_pat_ty, default_bm, decl);
         }
 
         match expected.kind(Interner) {
@@ -528,7 +556,7 @@ impl InferenceContext<'_> {
         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 {
+    fn is_non_ref_pat(&mut self, body: &hir_def::expr_store::Body, pat: PatId) -> bool {
         match &body[pat] {
             Pat::Tuple { .. }
             | Pat::TupleStruct { .. }
@@ -536,9 +564,10 @@ impl InferenceContext<'_> {
             | Pat::Range { .. }
             | Pat::Slice { .. } => true,
             Pat::Or(pats) => pats.iter().all(|p| self.is_non_ref_pat(body, *p)),
-            Pat::Path(p) => {
-                let v = self.resolve_value_path_inner(p, pat.into());
-                v.is_some_and(|x| !matches!(x.0, hir_def::resolver::ValueNs::ConstId(_)))
+            Pat::Path(path) => {
+                // A const is a reference pattern, but other value ns things aren't (see #16131).
+                let resolved = self.resolve_value_path_inner(path, pat.into(), true);
+                resolved.is_some_and(|it| !matches!(it.0, hir_def::resolver::ValueNs::ConstId(_)))
             }
             Pat::ConstBlock(..) => false,
             Pat::Lit(expr) => !matches!(
@@ -553,6 +582,59 @@ impl InferenceContext<'_> {
             | Pat::Expr(_) => false,
         }
     }
+
+    fn try_resolve_slice_ty_to_array_ty(
+        &mut self,
+        before: &[PatId],
+        suffix: &[PatId],
+        slice: &Option<PatId>,
+    ) -> Option<Ty> {
+        if !slice.is_none() {
+            return None;
+        }
+
+        let len = before.len() + suffix.len();
+        let size =
+            consteval::usize_const(self.db, Some(len as u128), self.owner.krate(self.db.upcast()));
+
+        let elem_ty = self.table.new_type_var();
+        let array_ty = TyKind::Array(elem_ty.clone(), size).intern(Interner);
+        Some(array_ty)
+    }
+
+    /// Used to determine whether we can infer the expected type in the slice pattern to be of type array.
+    /// This is only possible if we're in an irrefutable pattern. If we were to allow this in refutable
+    /// patterns we wouldn't e.g. report ambiguity in the following situation:
+    ///
+    /// ```ignore(rust)
+    ///    struct Zeroes;
+    ///    const ARR: [usize; 2] = [0; 2];
+    ///    const ARR2: [usize; 2] = [2; 2];
+    ///
+    ///    impl Into<&'static [usize; 2]> for Zeroes {
+    ///        fn into(self) -> &'static [usize; 2] {
+    ///            &ARR
+    ///        }
+    ///    }
+    ///
+    ///    impl Into<&'static [usize]> for Zeroes {
+    ///        fn into(self) -> &'static [usize] {
+    ///            &ARR2
+    ///        }
+    ///    }
+    ///
+    ///    fn main() {
+    ///        let &[a, b]: &[usize] = Zeroes.into() else {
+    ///           ..
+    ///        };
+    ///    }
+    /// ```
+    ///
+    /// If we're in an irrefutable pattern we prefer the array impl candidate given that
+    /// the slice impl candidate would be rejected anyway (if no ambiguity existed).
+    fn pat_is_irrefutable(&self, decl_ctxt: Option<DeclContext>) -> bool {
+        matches!(decl_ctxt, Some(DeclContext { origin: DeclOrigin::LocalDecl { has_else: false } }))
+    }
 }
 
 pub(super) fn contains_explicit_ref_binding(body: &Body, pat_id: PatId) -> bool {
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/path.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/path.rs
index 73bcefaf2a9..6254bc12392 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/path.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/path.rs
@@ -7,7 +7,6 @@ use hir_def::{
     AdtId, AssocItemId, GenericDefId, ItemContainerId, Lookup,
 };
 use hir_expand::name::Name;
-use intern::sym;
 use stdx::never;
 
 use crate::{
@@ -41,7 +40,7 @@ impl InferenceContext<'_> {
     }
 
     fn resolve_value_path(&mut self, path: &Path, id: ExprOrPatId) -> Option<ValuePathResolution> {
-        let (value, self_subst) = self.resolve_value_path_inner(path, id)?;
+        let (value, self_subst) = self.resolve_value_path_inner(path, id, false)?;
 
         let value_def: ValueTyDefId = match value {
             ValueNs::FunctionId(it) => it.into(),
@@ -86,16 +85,22 @@ impl InferenceContext<'_> {
             }
         };
 
-        let generic_def_id = value_def.to_generic_def_id(self.db);
-        let Some(generic_def) = generic_def_id else {
-            // `value_def` is the kind of item that can never be generic (i.e. statics, at least
-            // currently). We can just skip the binders to get its type.
+        let generic_def = value_def.to_generic_def_id(self.db);
+        if let GenericDefId::StaticId(_) = generic_def {
+            // `Static` is the kind of item that can never be generic currently. We can just skip the binders to get its type.
             let (ty, binders) = self.db.value_ty(value_def)?.into_value_and_skipped_binders();
             stdx::always!(binders.is_empty(Interner), "non-empty binders for non-generic def",);
             return Some(ValuePathResolution::NonGeneric(ty));
         };
 
-        let substs = self.with_body_ty_lowering(|ctx| ctx.substs_from_path(path, value_def, true));
+        let substs = self.with_body_ty_lowering(|ctx| {
+            let mut path_ctx = ctx.at_path(path, id);
+            let last_segment = path.segments().len().checked_sub(1);
+            if let Some(last_segment) = last_segment {
+                path_ctx.set_current_segment(last_segment)
+            }
+            path_ctx.substs_from_path(value_def, true)
+        });
         let substs = substs.as_slice(Interner);
 
         if let ValueNs::EnumVariantId(_) = value {
@@ -122,7 +127,7 @@ impl InferenceContext<'_> {
         }
 
         let parent_substs = self_subst.or_else(|| {
-            let generics = generics(self.db.upcast(), generic_def_id?);
+            let generics = generics(self.db.upcast(), generic_def);
             let parent_params_len = generics.parent_generics()?.len();
             let parent_args = &substs[substs.len() - parent_params_len..];
             Some(Substitution::from_iter(Interner, parent_args))
@@ -147,6 +152,7 @@ impl InferenceContext<'_> {
         &mut self,
         path: &Path,
         id: ExprOrPatId,
+        no_diagnostics: bool,
     ) -> Option<(ValueNs, Option<chalk_ir::Substitution<Interner>>)> {
         // Don't use `self.make_ty()` here as we need `orig_ns`.
         let mut ctx = TyLoweringContext::new(
@@ -157,33 +163,83 @@ impl InferenceContext<'_> {
             &self.diagnostics,
             InferenceTyDiagnosticSource::Body,
         );
+        let mut path_ctx = if no_diagnostics {
+            ctx.at_path_forget_diagnostics(path)
+        } else {
+            ctx.at_path(path, id)
+        };
         let (value, self_subst) = if let Some(type_ref) = path.type_anchor() {
             let last = path.segments().last()?;
 
-            let (ty, orig_ns) = ctx.lower_ty_ext(type_ref);
+            let (ty, orig_ns) = path_ctx.ty_ctx().lower_ty_ext(type_ref);
             let ty = self.table.insert_type_vars(ty);
             let ty = self.table.normalize_associated_types_in(ty);
 
-            let remaining_segments_for_ty = path.segments().take(path.segments().len() - 1);
-            let (ty, _) = ctx.lower_ty_relative_path(ty, orig_ns, remaining_segments_for_ty);
-            drop(ctx);
+            path_ctx.ignore_last_segment();
+            let (ty, _) = path_ctx.lower_ty_relative_path(ty, orig_ns);
+            drop_ctx(ctx, no_diagnostics);
             let ty = self.table.insert_type_vars(ty);
             let ty = self.table.normalize_associated_types_in(ty);
             self.resolve_ty_assoc_item(ty, last.name, id).map(|(it, substs)| (it, Some(substs)))?
         } else {
             let hygiene = self.body.expr_or_pat_path_hygiene(id);
             // FIXME: report error, unresolved first path segment
-            let value_or_partial = ctx.resolve_path_in_value_ns(path, id, hygiene)?;
-            drop(ctx);
+            let value_or_partial = path_ctx.resolve_path_in_value_ns(hygiene)?;
 
             match value_or_partial {
-                ResolveValueResult::ValueNs(it, _) => (it, None),
-                ResolveValueResult::Partial(def, remaining_index, _) => self
-                    .resolve_assoc_item(id, def, path, remaining_index, id)
-                    .map(|(it, substs)| (it, Some(substs)))?,
+                ResolveValueResult::ValueNs(it, _) => {
+                    drop_ctx(ctx, no_diagnostics);
+                    (it, None)
+                }
+                ResolveValueResult::Partial(def, remaining_index, _) => {
+                    // there may be more intermediate segments between the resolved one and
+                    // the end. Only the last segment needs to be resolved to a value; from
+                    // the segments before that, we need to get either a type or a trait ref.
+
+                    let remaining_segments = path.segments().skip(remaining_index);
+                    let is_before_last = remaining_segments.len() == 1;
+                    let last_segment = remaining_segments
+                        .last()
+                        .expect("there should be at least one segment here");
+
+                    let (resolution, substs) = match (def, is_before_last) {
+                        (TypeNs::TraitId(trait_), true) => {
+                            let self_ty = self.table.new_type_var();
+                            let trait_ref =
+                                path_ctx.lower_trait_ref_from_resolved_path(trait_, self_ty);
+                            drop_ctx(ctx, no_diagnostics);
+                            self.resolve_trait_assoc_item(trait_ref, last_segment, id)
+                        }
+                        (def, _) => {
+                            // Either we already have a type (e.g. `Vec::new`), or we have a
+                            // trait but it's not the last segment, so the next segment
+                            // should resolve to an associated type of that trait (e.g. `<T
+                            // as Iterator>::Item::default`)
+                            path_ctx.ignore_last_segment();
+                            let (ty, _) = path_ctx.lower_partly_resolved_path(def, true);
+                            drop_ctx(ctx, no_diagnostics);
+                            if ty.is_unknown() {
+                                return None;
+                            }
+
+                            let ty = self.insert_type_vars(ty);
+                            let ty = self.normalize_associated_types_in(ty);
+
+                            self.resolve_ty_assoc_item(ty, last_segment.name, id)
+                        }
+                    }?;
+                    (resolution, Some(substs))
+                }
             }
         };
-        Some((value, self_subst))
+        return Some((value, self_subst));
+
+        #[inline]
+        fn drop_ctx(mut ctx: TyLoweringContext<'_>, no_diagnostics: bool) {
+            if no_diagnostics {
+                ctx.forget_diagnostics();
+            }
+        }
     }
 
     fn add_required_obligations_for_value_path(&mut self, def: GenericDefId, subst: &Substitution) {
@@ -213,89 +269,6 @@ impl InferenceContext<'_> {
         }
     }
 
-    fn resolve_assoc_item(
-        &mut self,
-        node: ExprOrPatId,
-        def: TypeNs,
-        path: &Path,
-        remaining_index: usize,
-        id: ExprOrPatId,
-    ) -> Option<(ValueNs, Substitution)> {
-        // there may be more intermediate segments between the resolved one and
-        // the end. Only the last segment needs to be resolved to a value; from
-        // the segments before that, we need to get either a type or a trait ref.
-
-        let _d;
-        let (resolved_segment, remaining_segments) = match path {
-            Path::Normal { .. } | Path::BarePath(_) => {
-                assert!(remaining_index < path.segments().len());
-                (
-                    path.segments().get(remaining_index - 1).unwrap(),
-                    path.segments().skip(remaining_index),
-                )
-            }
-            Path::LangItem(..) => (
-                PathSegment {
-                    name: {
-                        _d = Name::new_symbol_root(sym::Unknown.clone());
-                        &_d
-                    },
-                    args_and_bindings: None,
-                },
-                path.segments(),
-            ),
-        };
-        let is_before_last = remaining_segments.len() == 1;
-
-        match (def, is_before_last) {
-            (TypeNs::TraitId(trait_), true) => {
-                let segment =
-                    remaining_segments.last().expect("there should be at least one segment here");
-                let self_ty = self.table.new_type_var();
-                let trait_ref = self.with_body_ty_lowering(|ctx| {
-                    ctx.lower_trait_ref_from_resolved_path(trait_, resolved_segment, self_ty)
-                });
-                self.resolve_trait_assoc_item(trait_ref, segment, id)
-            }
-            (def, _) => {
-                // Either we already have a type (e.g. `Vec::new`), or we have a
-                // trait but it's not the last segment, so the next segment
-                // should resolve to an associated type of that trait (e.g. `<T
-                // as Iterator>::Item::default`)
-                let remaining_segments_for_ty =
-                    remaining_segments.take(remaining_segments.len() - 1);
-                let mut ctx = TyLoweringContext::new(
-                    self.db,
-                    &self.resolver,
-                    &self.body.types,
-                    self.owner.into(),
-                    &self.diagnostics,
-                    InferenceTyDiagnosticSource::Body,
-                );
-                let (ty, _) = ctx.lower_partly_resolved_path(
-                    node,
-                    def,
-                    resolved_segment,
-                    remaining_segments_for_ty,
-                    (remaining_index - 1) as u32,
-                    true,
-                );
-                drop(ctx);
-                if ty.is_unknown() {
-                    return None;
-                }
-
-                let ty = self.insert_type_vars(ty);
-                let ty = self.normalize_associated_types_in(ty);
-
-                let segment =
-                    remaining_segments.last().expect("there should be at least one segment here");
-
-                self.resolve_ty_assoc_item(ty, segment.name, id)
-            }
-        }
-    }
-
     fn resolve_trait_assoc_item(
         &mut self,
         trait_ref: TraitRef,
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 108171586ea..e2ab336d2e4 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/layout.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/layout.rs
@@ -14,6 +14,7 @@ use hir_def::{
 };
 use la_arena::{Idx, RawIdx};
 use rustc_abi::AddressSpace;
+use rustc_hashes::Hash64;
 use rustc_index::{IndexSlice, IndexVec};
 
 use triomphe::Arc;
@@ -178,7 +179,7 @@ fn layout_of_simd_ty(
         .size
         .checked_mul(e_len, dl)
         .ok_or(LayoutError::BadCalc(LayoutCalculatorError::SizeOverflow))?;
-    let align = dl.vector_align(size);
+    let align = dl.llvmlike_vector_align(size);
     let size = size.align_to(align.abi);
 
     // Compute the placement of the vector fields:
@@ -193,11 +194,12 @@ fn layout_of_simd_ty(
         fields,
         backend_repr: BackendRepr::Vector { element: e_abi, count: e_len },
         largest_niche: e_ly.largest_niche,
+        uninhabited: false,
         size,
         align,
         max_repr_align: None,
         unadjusted_abi_align: align.abi,
-        randomization_seed: 0,
+        randomization_seed: Hash64::ZERO,
     }))
 }
 
@@ -296,25 +298,22 @@ pub fn layout_of_ty_query(
                 .checked_mul(count, dl)
                 .ok_or(LayoutError::BadCalc(LayoutCalculatorError::SizeOverflow))?;
 
-            let backend_repr =
-                if count != 0 && matches!(element.backend_repr, BackendRepr::Uninhabited) {
-                    BackendRepr::Uninhabited
-                } else {
-                    BackendRepr::Memory { sized: true }
-                };
+            let backend_repr = BackendRepr::Memory { sized: true };
 
             let largest_niche = if count != 0 { element.largest_niche } else { None };
+            let uninhabited = if count != 0 { element.uninhabited } else { false };
 
             Layout {
                 variants: Variants::Single { index: struct_variant_idx() },
                 fields: FieldsShape::Array { stride: element.size, count },
                 backend_repr,
                 largest_niche,
+                uninhabited,
                 align: element.align,
                 size,
                 max_repr_align: None,
                 unadjusted_abi_align: element.align.abi,
-                randomization_seed: 0,
+                randomization_seed: Hash64::ZERO,
             }
         }
         TyKind::Slice(element) => {
@@ -324,11 +323,12 @@ pub fn layout_of_ty_query(
                 fields: FieldsShape::Array { stride: element.size, count: 0 },
                 backend_repr: BackendRepr::Memory { sized: false },
                 largest_niche: None,
+                uninhabited: false,
                 align: element.align,
                 size: Size::ZERO,
                 max_repr_align: None,
                 unadjusted_abi_align: element.align.abi,
-                randomization_seed: 0,
+                randomization_seed: Hash64::ZERO,
             }
         }
         TyKind::Str => Layout {
@@ -336,11 +336,12 @@ pub fn layout_of_ty_query(
             fields: FieldsShape::Array { stride: Size::from_bytes(1), count: 0 },
             backend_repr: BackendRepr::Memory { sized: false },
             largest_niche: None,
+            uninhabited: false,
             align: dl.i8_align,
             size: Size::ZERO,
             max_repr_align: None,
             unadjusted_abi_align: dl.i8_align.abi,
-            randomization_seed: 0,
+            randomization_seed: Hash64::ZERO,
         },
         // Potentially-wide pointers.
         TyKind::Ref(_, _, pointee) | TyKind::Raw(_, pointee) => {
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 3c18ea92816..daddcf0b242 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs
@@ -12,6 +12,9 @@ extern crate ra_ap_rustc_index as rustc_index;
 #[cfg(feature = "in-rust-tree")]
 extern crate rustc_abi;
 
+#[cfg(feature = "in-rust-tree")]
+extern crate rustc_hashes;
+
 #[cfg(not(feature = "in-rust-tree"))]
 extern crate ra_ap_rustc_abi as rustc_abi;
 
@@ -21,6 +24,9 @@ extern crate rustc_pattern_analysis;
 #[cfg(not(feature = "in-rust-tree"))]
 extern crate ra_ap_rustc_pattern_analysis as rustc_pattern_analysis;
 
+#[cfg(not(feature = "in-rust-tree"))]
+extern crate ra_ap_rustc_hashes as rustc_hashes;
+
 mod builder;
 mod chalk_db;
 mod chalk_ext;
@@ -100,7 +106,9 @@ pub use mapping::{
 };
 pub use method_resolution::check_orphan_rules;
 pub use traits::TraitEnvironment;
-pub use utils::{all_super_traits, direct_super_traits, is_fn_unsafe_to_call};
+pub use utils::{
+    all_super_traits, direct_super_traits, is_fn_unsafe_to_call, TargetFeatures, Unsafety,
+};
 pub use variance::Variance;
 
 pub use chalk_ir::{
@@ -1047,3 +1055,20 @@ pub fn known_const_to_ast(
     }
     Some(make::expr_const_value(konst.display(db, edition).to_string().as_str()))
 }
+
+#[derive(Debug, Copy, Clone)]
+pub(crate) enum DeclOrigin {
+    LetExpr,
+    /// from `let x = ..`
+    LocalDecl {
+        has_else: bool,
+    },
+}
+
+/// Provides context for checking patterns in declarations. More specifically this
+/// allows us to infer array types if the pattern is irrefutable and allows us to infer
+/// the size of the array. See issue rust-lang/rust#76342.
+#[derive(Debug, Copy, Clone)]
+pub(crate) struct DeclContext {
+    pub(crate) origin: DeclOrigin,
+}
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 432b8f4d94e..af73b5ed9a7 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs
@@ -6,6 +6,7 @@
 //!
 //! This usually involves resolving names, collecting generic arguments etc.
 pub(crate) mod diagnostics;
+pub(crate) mod path;
 
 use std::{
     cell::OnceCell,
@@ -23,7 +24,6 @@ use chalk_ir::{
 
 use either::Either;
 use hir_def::{
-    body::HygieneId,
     builtin_type::BuiltinType,
     data::{adt::StructKind, TraitFlags},
     expander::Expander,
@@ -33,22 +33,20 @@ use hir_def::{
     },
     lang_item::LangItem,
     nameres::MacroSubNs,
-    path::{GenericArg, GenericArgs, ModPath, Path, PathKind, PathSegment, PathSegments},
-    resolver::{HasResolver, LifetimeNs, ResolveValueResult, Resolver, TypeNs, ValueNs},
+    path::{GenericArg, ModPath, Path, PathKind},
+    resolver::{HasResolver, LifetimeNs, Resolver, TypeNs},
     type_ref::{
         ConstRef, LifetimeRef, PathId, TraitBoundModifier, TraitRef as HirTraitRef, TypeBound,
         TypeRef, TypeRefId, TypesMap, TypesSourceMap,
     },
     AdtId, AssocItemId, CallableDefId, ConstId, ConstParamId, DefWithBodyId, EnumId, EnumVariantId,
-    FunctionId, GenericDefId, GenericParamId, HasModule, ImplId, InTypeConstLoc, ItemContainerId,
-    LocalFieldId, Lookup, StaticId, StructId, TraitId, TypeAliasId, TypeOrConstParamId,
-    TypeOwnerId, UnionId, VariantId,
+    FunctionId, GenericDefId, GenericParamId, HasModule, ImplId, InTypeConstLoc, LocalFieldId,
+    Lookup, StaticId, StructId, TypeAliasId, TypeOrConstParamId, TypeOwnerId, UnionId, VariantId,
 };
 use hir_expand::{name::Name, ExpandResult};
 use la_arena::{Arena, ArenaMap};
 use rustc_hash::FxHashSet;
 use rustc_pattern_analysis::Captures;
-use smallvec::SmallVec;
 use stdx::{impl_from, never};
 use syntax::ast;
 use triomphe::{Arc, ThinArc};
@@ -62,18 +60,19 @@ use crate::{
     db::HirDatabase,
     error_lifetime,
     generics::{generics, trait_self_param_idx, Generics},
-    lower::diagnostics::*,
+    lower::{
+        diagnostics::*,
+        path::{PathDiagnosticCallback, PathLoweringContext},
+    },
     make_binders,
     mapping::{from_chalk_trait_id, lt_to_placeholder_idx, ToChalk},
-    static_lifetime, to_assoc_type_id, to_chalk_trait_id, to_placeholder_idx,
-    utils::{
-        all_super_trait_refs, associated_type_by_name_including_super_traits, InTypeConstIdMetadata,
-    },
-    AliasEq, AliasTy, Binders, BoundVar, CallableSig, Const, ConstScalar, DebruijnIndex, DynTy,
-    FnAbi, FnPointer, FnSig, FnSubst, ImplTrait, ImplTraitId, ImplTraits, Interner, Lifetime,
-    LifetimeData, LifetimeOutlives, ParamKind, PolyFnSig, ProgramClause, ProjectionTy,
-    QuantifiedWhereClause, QuantifiedWhereClauses, Substitution, TraitEnvironment, TraitRef,
-    TraitRefExt, Ty, TyBuilder, TyKind, WhereClause,
+    static_lifetime, to_chalk_trait_id, to_placeholder_idx,
+    utils::{all_super_trait_refs, InTypeConstIdMetadata},
+    AliasTy, Binders, BoundVar, CallableSig, Const, ConstScalar, DebruijnIndex, DynTy, FnAbi,
+    FnPointer, FnSig, FnSubst, ImplTrait, ImplTraitId, ImplTraits, Interner, Lifetime,
+    LifetimeData, LifetimeOutlives, ParamKind, PolyFnSig, ProgramClause, QuantifiedWhereClause,
+    QuantifiedWhereClauses, Substitution, TraitEnvironment, TraitRef, TraitRefExt, Ty, TyBuilder,
+    TyKind, WhereClause,
 };
 
 #[derive(Debug, Default)]
@@ -106,6 +105,8 @@ impl ImplTraitLoweringState {
     }
 }
 
+pub(crate) struct PathDiagnosticCallbackData(TypeRefId);
+
 #[derive(Debug)]
 pub struct TyLoweringContext<'a> {
     pub db: &'a dyn HirDatabase,
@@ -527,9 +528,8 @@ impl<'a> TyLoweringContext<'a> {
         if path.segments().len() > 1 {
             return None;
         }
-        let resolution = match self
-            .resolve_path_in_type_ns(path, &mut Self::on_path_diagnostic_callback(type_ref_id))
-        {
+        let mut ctx = self.at_path(PathId::from_type_ref_unchecked(type_ref_id));
+        let resolution = match ctx.resolve_path_in_type_ns() {
             Some((it, None)) => it,
             _ => return None,
         };
@@ -539,409 +539,36 @@ impl<'a> TyLoweringContext<'a> {
         }
     }
 
-    pub(crate) fn lower_ty_relative_path(
-        &mut self,
-        ty: Ty,
-        // We need the original resolution to lower `Self::AssocTy` correctly
-        res: Option<TypeNs>,
-        remaining_segments: PathSegments<'_>,
-    ) -> (Ty, Option<TypeNs>) {
-        match remaining_segments.len() {
-            0 => (ty, res),
-            1 => {
-                // resolve unselected assoc types
-                let segment = remaining_segments.first().unwrap();
-                (self.select_associated_type(res, segment), None)
-            }
-            _ => {
-                // FIXME report error (ambiguous associated type)
-                (TyKind::Error.intern(Interner), None)
-            }
-        }
-    }
-
-    pub(crate) fn lower_partly_resolved_path(
-        &mut self,
-        resolution: TypeNs,
-        resolved_segment: PathSegment<'_>,
-        remaining_segments: PathSegments<'_>,
-        _resolved_segment_idx: u32,
-        infer_args: bool,
-        _on_diagnostic: &mut dyn FnMut(&mut Self, PathLoweringDiagnostic),
-    ) -> (Ty, Option<TypeNs>) {
-        let ty = match resolution {
-            TypeNs::TraitId(trait_) => {
-                let ty = match remaining_segments.len() {
-                    1 => {
-                        let trait_ref = self.lower_trait_ref_from_resolved_path(
-                            trait_,
-                            resolved_segment,
-                            TyKind::Error.intern(Interner),
-                        );
-                        let segment = remaining_segments.first().unwrap();
-                        let found = self
-                            .db
-                            .trait_data(trait_ref.hir_trait_id())
-                            .associated_type_by_name(segment.name);
-
-                        match found {
-                            Some(associated_ty) => {
-                                // FIXME: `substs_from_path_segment()` pushes `TyKind::Error` for every parent
-                                // generic params. It's inefficient to splice the `Substitution`s, so we may want
-                                // that method to optionally take parent `Substitution` as we already know them at
-                                // this point (`trait_ref.substitution`).
-                                let substitution = self.substs_from_path_segment(
-                                    segment,
-                                    Some(associated_ty.into()),
-                                    false,
-                                    None,
-                                );
-                                let len_self =
-                                    generics(self.db.upcast(), associated_ty.into()).len_self();
-                                let substitution = Substitution::from_iter(
-                                    Interner,
-                                    substitution
-                                        .iter(Interner)
-                                        .take(len_self)
-                                        .chain(trait_ref.substitution.iter(Interner)),
-                                );
-                                TyKind::Alias(AliasTy::Projection(ProjectionTy {
-                                    associated_ty_id: to_assoc_type_id(associated_ty),
-                                    substitution,
-                                }))
-                                .intern(Interner)
-                            }
-                            None => {
-                                // FIXME: report error (associated type not found)
-                                TyKind::Error.intern(Interner)
-                            }
-                        }
-                    }
-                    0 => {
-                        // Trait object type without dyn; this should be handled in upstream. See
-                        // `lower_path()`.
-                        stdx::never!("unexpected fully resolved trait path");
-                        TyKind::Error.intern(Interner)
-                    }
-                    _ => {
-                        // FIXME report error (ambiguous associated type)
-                        TyKind::Error.intern(Interner)
-                    }
-                };
-                return (ty, None);
-            }
-            TypeNs::TraitAliasId(_) => {
-                // FIXME(trait_alias): Implement trait alias.
-                return (TyKind::Error.intern(Interner), None);
-            }
-            TypeNs::GenericParam(param_id) => match self.type_param_mode {
-                ParamLoweringMode::Placeholder => {
-                    TyKind::Placeholder(to_placeholder_idx(self.db, param_id.into()))
-                }
-                ParamLoweringMode::Variable => {
-                    let idx = match self
-                        .generics()
-                        .expect("generics in scope")
-                        .type_or_const_param_idx(param_id.into())
-                    {
-                        None => {
-                            never!("no matching generics");
-                            return (TyKind::Error.intern(Interner), None);
-                        }
-                        Some(idx) => idx,
-                    };
-
-                    TyKind::BoundVar(BoundVar::new(self.in_binders, idx))
-                }
-            }
-            .intern(Interner),
-            TypeNs::SelfType(impl_id) => {
-                let generics = self.generics().expect("impl should have generic param scope");
-
-                match self.type_param_mode {
-                    ParamLoweringMode::Placeholder => {
-                        // `def` can be either impl itself or item within, and we need impl itself
-                        // now.
-                        let generics = generics.parent_or_self();
-                        let subst = generics.placeholder_subst(self.db);
-                        self.db.impl_self_ty(impl_id).substitute(Interner, &subst)
-                    }
-                    ParamLoweringMode::Variable => {
-                        let starting_from = match generics.def() {
-                            GenericDefId::ImplId(_) => 0,
-                            // `def` is an item within impl. We need to substitute `BoundVar`s but
-                            // remember that they are for parent (i.e. impl) generic params so they
-                            // come after our own params.
-                            _ => generics.len_self(),
-                        };
-                        TyBuilder::impl_self_ty(self.db, impl_id)
-                            .fill_with_bound_vars(self.in_binders, starting_from)
-                            .build()
-                    }
-                }
-            }
-            TypeNs::AdtSelfType(adt) => {
-                let generics = generics(self.db.upcast(), adt.into());
-                let substs = match self.type_param_mode {
-                    ParamLoweringMode::Placeholder => generics.placeholder_subst(self.db),
-                    ParamLoweringMode::Variable => {
-                        generics.bound_vars_subst(self.db, self.in_binders)
-                    }
-                };
-                self.db.ty(adt.into()).substitute(Interner, &substs)
-            }
-
-            TypeNs::AdtId(it) => self.lower_path_inner(resolved_segment, it.into(), infer_args),
-            TypeNs::BuiltinType(it) => {
-                self.lower_path_inner(resolved_segment, it.into(), infer_args)
-            }
-            TypeNs::TypeAliasId(it) => {
-                self.lower_path_inner(resolved_segment, it.into(), infer_args)
-            }
-            // FIXME: report error
-            TypeNs::EnumVariantId(_) => return (TyKind::Error.intern(Interner), None),
-        };
-        self.lower_ty_relative_path(ty, Some(resolution), remaining_segments)
-    }
-
-    fn handle_type_ns_resolution(
-        &mut self,
-        resolution: &TypeNs,
-        resolved_segment: PathSegment<'_>,
-        resolved_segment_idx: usize,
-        on_diagnostic: &mut dyn FnMut(&mut Self, PathLoweringDiagnostic),
-    ) {
-        let mut prohibit_generics_on_resolved = |reason| {
-            if resolved_segment.args_and_bindings.is_some() {
-                on_diagnostic(
-                    self,
-                    PathLoweringDiagnostic::GenericArgsProhibited {
-                        segment: resolved_segment_idx as u32,
-                        reason,
-                    },
-                );
-            }
-        };
-
-        match resolution {
-            TypeNs::SelfType(_) => {
-                prohibit_generics_on_resolved(GenericArgsProhibitedReason::SelfTy)
-            }
-            TypeNs::GenericParam(_) => {
-                prohibit_generics_on_resolved(GenericArgsProhibitedReason::TyParam)
-            }
-            TypeNs::AdtSelfType(_) => {
-                prohibit_generics_on_resolved(GenericArgsProhibitedReason::SelfTy)
-            }
-            TypeNs::BuiltinType(_) => {
-                prohibit_generics_on_resolved(GenericArgsProhibitedReason::PrimitiveTy)
-            }
-            TypeNs::AdtId(_)
-            | TypeNs::EnumVariantId(_)
-            | TypeNs::TypeAliasId(_)
-            | TypeNs::TraitId(_)
-            | TypeNs::TraitAliasId(_) => {}
-        }
-    }
-
-    pub(crate) fn resolve_path_in_type_ns_fully(
-        &mut self,
-        path: &Path,
-        on_diagnostic: &mut dyn FnMut(&mut Self, PathLoweringDiagnostic),
-    ) -> Option<TypeNs> {
-        let (res, unresolved) = self.resolve_path_in_type_ns(path, on_diagnostic)?;
-        if unresolved.is_some() {
-            return None;
-        }
-        Some(res)
-    }
-
-    pub(crate) fn resolve_path_in_type_ns(
-        &mut self,
-        path: &Path,
-        on_diagnostic: &mut dyn FnMut(&mut Self, PathLoweringDiagnostic),
-    ) -> Option<(TypeNs, Option<usize>)> {
-        let (resolution, remaining_index, _, prefix_info) =
-            self.resolver.resolve_path_in_type_ns_with_prefix_info(self.db.upcast(), path)?;
-        let segments = path.segments();
-
-        match path {
-            // `segments.is_empty()` can occur with `self`.
-            Path::Normal(..) if !segments.is_empty() => (),
-            _ => return Some((resolution, remaining_index)),
-        };
-
-        let (module_segments, resolved_segment_idx, enum_segment) = match remaining_index {
-            None if prefix_info.enum_variant => {
-                (segments.strip_last_two(), segments.len() - 1, Some(segments.len() - 2))
-            }
-            None => (segments.strip_last(), segments.len() - 1, None),
-            Some(i) => (segments.take(i - 1), i - 1, None),
-        };
-
-        for (i, mod_segment) in module_segments.iter().enumerate() {
-            if mod_segment.args_and_bindings.is_some() {
-                on_diagnostic(
-                    self,
-                    PathLoweringDiagnostic::GenericArgsProhibited {
-                        segment: i as u32,
-                        reason: GenericArgsProhibitedReason::Module,
-                    },
-                );
-            }
-        }
-
-        if let Some(enum_segment) = enum_segment {
-            if segments.get(enum_segment).is_some_and(|it| it.args_and_bindings.is_some())
-                && segments.get(enum_segment + 1).is_some_and(|it| it.args_and_bindings.is_some())
-            {
-                on_diagnostic(
-                    self,
-                    PathLoweringDiagnostic::GenericArgsProhibited {
-                        segment: (enum_segment + 1) as u32,
-                        reason: GenericArgsProhibitedReason::EnumVariant,
-                    },
-                );
-            }
-        }
-
-        self.handle_type_ns_resolution(
-            &resolution,
-            segments.get(resolved_segment_idx).expect("should have resolved segment"),
-            resolved_segment_idx,
-            on_diagnostic,
-        );
-
-        Some((resolution, remaining_index))
-    }
-
-    pub(crate) fn resolve_path_in_value_ns(
-        &mut self,
-        path: &Path,
-        hygiene_id: HygieneId,
-        on_diagnostic: &mut dyn FnMut(&mut Self, PathLoweringDiagnostic),
-    ) -> Option<ResolveValueResult> {
-        let (res, prefix_info) = self.resolver.resolve_path_in_value_ns_with_prefix_info(
-            self.db.upcast(),
-            path,
-            hygiene_id,
-        )?;
-
-        let segments = path.segments();
-        match path {
-            // `segments.is_empty()` can occur with `self`.
-            Path::Normal(..) if !segments.is_empty() => (),
-            _ => return Some(res),
-        };
-
-        let (mod_segments, enum_segment) = match res {
-            ResolveValueResult::Partial(_, unresolved_segment, _) => {
-                (segments.take(unresolved_segment - 1), None)
-            }
-            ResolveValueResult::ValueNs(ValueNs::EnumVariantId(_), _)
-                if prefix_info.enum_variant =>
-            {
-                (segments.strip_last_two(), segments.len().checked_sub(2))
-            }
-            ResolveValueResult::ValueNs(..) => (segments.strip_last(), None),
-        };
-        for (i, mod_segment) in mod_segments.iter().enumerate() {
-            if mod_segment.args_and_bindings.is_some() {
-                on_diagnostic(
-                    self,
-                    PathLoweringDiagnostic::GenericArgsProhibited {
-                        segment: i as u32,
-                        reason: GenericArgsProhibitedReason::Module,
-                    },
-                );
-            }
-        }
-
-        if let Some(enum_segment) = enum_segment {
-            if segments.get(enum_segment).is_some_and(|it| it.args_and_bindings.is_some())
-                && segments.get(enum_segment + 1).is_some_and(|it| it.args_and_bindings.is_some())
-            {
-                on_diagnostic(
-                    self,
-                    PathLoweringDiagnostic::GenericArgsProhibited {
-                        segment: (enum_segment + 1) as u32,
-                        reason: GenericArgsProhibitedReason::EnumVariant,
-                    },
-                );
-            }
+    #[inline]
+    fn on_path_diagnostic_callback(type_ref: TypeRefId) -> PathDiagnosticCallback<'static> {
+        PathDiagnosticCallback {
+            data: Either::Left(PathDiagnosticCallbackData(type_ref)),
+            callback: |data, this, diag| {
+                let type_ref = data.as_ref().left().unwrap().0;
+                this.push_diagnostic(type_ref, TyLoweringDiagnosticKind::PathDiagnostic(diag))
+            },
         }
-
-        match &res {
-            ResolveValueResult::ValueNs(resolution, _) => {
-                let resolved_segment_idx =
-                    segments.len().checked_sub(1).unwrap_or_else(|| panic!("{path:?}"));
-                let resolved_segment = segments.last().unwrap();
-
-                let mut prohibit_generics_on_resolved = |reason| {
-                    if resolved_segment.args_and_bindings.is_some() {
-                        on_diagnostic(
-                            self,
-                            PathLoweringDiagnostic::GenericArgsProhibited {
-                                segment: resolved_segment_idx as u32,
-                                reason,
-                            },
-                        );
-                    }
-                };
-
-                match resolution {
-                    ValueNs::ImplSelf(_) => {
-                        prohibit_generics_on_resolved(GenericArgsProhibitedReason::SelfTy)
-                    }
-                    // FIXME: rustc generates E0107 (incorrect number of generic arguments) and not
-                    // E0109 (generic arguments provided for a type that doesn't accept them) for
-                    // consts and statics, presumably as a defense against future in which consts
-                    // and statics can be generic, or just because it was easier for rustc implementors.
-                    // That means we'll show the wrong error code. Because of us it's easier to do it
-                    // this way :)
-                    ValueNs::GenericParam(_) | ValueNs::ConstId(_) => {
-                        prohibit_generics_on_resolved(GenericArgsProhibitedReason::Const)
-                    }
-                    ValueNs::StaticId(_) => {
-                        prohibit_generics_on_resolved(GenericArgsProhibitedReason::Static)
-                    }
-                    ValueNs::FunctionId(_) | ValueNs::StructId(_) | ValueNs::EnumVariantId(_) => {}
-                    ValueNs::LocalBinding(_) => {}
-                }
-            }
-            ResolveValueResult::Partial(resolution, unresolved_idx, _) => {
-                let resolved_segment_idx = unresolved_idx - 1;
-                let resolved_segment = segments.get(resolved_segment_idx).unwrap();
-                self.handle_type_ns_resolution(
-                    resolution,
-                    resolved_segment,
-                    resolved_segment_idx,
-                    on_diagnostic,
-                );
-            }
-        };
-        Some(res)
     }
 
-    fn on_path_diagnostic_callback(
-        type_ref: TypeRefId,
-    ) -> impl FnMut(&mut Self, PathLoweringDiagnostic) {
-        move |this, diag| {
-            this.push_diagnostic(type_ref, TyLoweringDiagnosticKind::PathDiagnostic(diag))
-        }
+    #[inline]
+    fn at_path(&mut self, path_id: PathId) -> PathLoweringContext<'_, 'a> {
+        PathLoweringContext::new(
+            self,
+            Self::on_path_diagnostic_callback(path_id.type_ref()),
+            &self.types_map[path_id],
+        )
     }
 
     pub(crate) fn lower_path(&mut self, path: &Path, path_id: PathId) -> (Ty, Option<TypeNs>) {
         // Resolve the path (in type namespace)
         if let Some(type_ref) = path.type_anchor() {
             let (ty, res) = self.lower_ty_ext(type_ref);
-            return self.lower_ty_relative_path(ty, res, path.segments());
+            let mut ctx = self.at_path(path_id);
+            return ctx.lower_ty_relative_path(ty, res);
         }
 
-        let (resolution, remaining_index) = match self.resolve_path_in_type_ns(
-            path,
-            &mut Self::on_path_diagnostic_callback(path_id.type_ref()),
-        ) {
+        let mut ctx = self.at_path(path_id);
+        let (resolution, remaining_index) = match ctx.resolve_path_in_type_ns() {
             Some(it) => it,
             None => return (TyKind::Error.intern(Interner), None),
         };
@@ -953,354 +580,21 @@ impl<'a> TyLoweringContext<'a> {
             return (ty, None);
         }
 
-        let (resolved_segment_idx, resolved_segment, remaining_segments) = match remaining_index {
-            None => (
-                path.segments().len() - 1,
-                path.segments().last().expect("resolved path has at least one element"),
-                PathSegments::EMPTY,
-            ),
-            Some(i) => (i - 1, path.segments().get(i - 1).unwrap(), path.segments().skip(i)),
-        };
-
-        self.lower_partly_resolved_path(
-            resolution,
-            resolved_segment,
-            remaining_segments,
-            resolved_segment_idx as u32,
-            false,
-            &mut Self::on_path_diagnostic_callback(path_id.type_ref()),
-        )
-    }
-
-    fn select_associated_type(&mut self, res: Option<TypeNs>, segment: PathSegment<'_>) -> Ty {
-        let Some((generics, res)) = self.generics().zip(res) else {
-            return TyKind::Error.intern(Interner);
-        };
-        let ty = named_associated_type_shorthand_candidates(
-            self.db,
-            generics.def(),
-            res,
-            Some(segment.name.clone()),
-            move |name, t, associated_ty| {
-                let generics = self.generics().unwrap();
-
-                if name != segment.name {
-                    return None;
-                }
-
-                let parent_subst = t.substitution.clone();
-                let parent_subst = match self.type_param_mode {
-                    ParamLoweringMode::Placeholder => {
-                        // if we're lowering to placeholders, we have to put them in now.
-                        let s = generics.placeholder_subst(self.db);
-                        s.apply(parent_subst, Interner)
-                    }
-                    ParamLoweringMode::Variable => {
-                        // We need to shift in the bound vars, since
-                        // `named_associated_type_shorthand_candidates` does not do that.
-                        parent_subst.shifted_in_from(Interner, self.in_binders)
-                    }
-                };
-
-                // FIXME: `substs_from_path_segment()` pushes `TyKind::Error` for every parent
-                // generic params. It's inefficient to splice the `Substitution`s, so we may want
-                // that method to optionally take parent `Substitution` as we already know them at
-                // this point (`t.substitution`).
-                let substs =
-                    self.substs_from_path_segment(segment, Some(associated_ty.into()), false, None);
-
-                let len_self =
-                    crate::generics::generics(self.db.upcast(), associated_ty.into()).len_self();
-
-                let substs = Substitution::from_iter(
-                    Interner,
-                    substs.iter(Interner).take(len_self).chain(parent_subst.iter(Interner)),
-                );
-
-                Some(
-                    TyKind::Alias(AliasTy::Projection(ProjectionTy {
-                        associated_ty_id: to_assoc_type_id(associated_ty),
-                        substitution: substs,
-                    }))
-                    .intern(Interner),
-                )
-            },
-        );
-
-        ty.unwrap_or_else(|| TyKind::Error.intern(Interner))
-    }
-
-    fn lower_path_inner(
-        &mut self,
-        segment: PathSegment<'_>,
-        typeable: TyDefId,
-        infer_args: bool,
-    ) -> Ty {
-        let generic_def = match typeable {
-            TyDefId::BuiltinType(_) => None,
-            TyDefId::AdtId(it) => Some(it.into()),
-            TyDefId::TypeAliasId(it) => Some(it.into()),
-        };
-        let substs = self.substs_from_path_segment(segment, generic_def, infer_args, None);
-        self.db.ty(typeable).substitute(Interner, &substs)
-    }
-
-    /// Collect generic arguments from a path into a `Substs`. See also
-    /// `create_substs_for_ast_path` and `def_to_ty` in rustc.
-    pub(super) fn substs_from_path(
-        &mut self,
-        path: &Path,
-        // Note that we don't call `db.value_type(resolved)` here,
-        // `ValueTyDefId` is just a convenient way to pass generics and
-        // special-case enum variants
-        resolved: ValueTyDefId,
-        infer_args: bool,
-    ) -> Substitution {
-        let last = path.segments().last();
-        let (segment, generic_def) = match resolved {
-            ValueTyDefId::FunctionId(it) => (last, Some(it.into())),
-            ValueTyDefId::StructId(it) => (last, Some(it.into())),
-            ValueTyDefId::UnionId(it) => (last, Some(it.into())),
-            ValueTyDefId::ConstId(it) => (last, Some(it.into())),
-            ValueTyDefId::StaticId(_) => (last, None),
-            ValueTyDefId::EnumVariantId(var) => {
-                // the generic args for an enum variant may be either specified
-                // on the segment referring to the enum, or on the segment
-                // referring to the variant. So `Option::<T>::None` and
-                // `Option::None::<T>` are both allowed (though the former is
-                // preferred). See also `def_ids_for_path_segments` in rustc.
-                let len = path.segments().len();
-                let penultimate = len.checked_sub(2).and_then(|idx| path.segments().get(idx));
-                let segment = match penultimate {
-                    Some(segment) if segment.args_and_bindings.is_some() => Some(segment),
-                    _ => last,
-                };
-                (segment, Some(var.lookup(self.db.upcast()).parent.into()))
-            }
-        };
-        if let Some(segment) = segment {
-            self.substs_from_path_segment(segment, generic_def, infer_args, None)
-        } else if let Some(generic_def) = generic_def {
-            // lang item
-            self.substs_from_args_and_bindings(None, Some(generic_def), infer_args, None)
-        } else {
-            Substitution::empty(Interner)
-        }
-    }
-
-    pub(super) fn substs_from_path_segment(
-        &mut self,
-        segment: PathSegment<'_>,
-        def: Option<GenericDefId>,
-        infer_args: bool,
-        explicit_self_ty: Option<Ty>,
-    ) -> Substitution {
-        self.substs_from_args_and_bindings(
-            segment.args_and_bindings,
-            def,
-            infer_args,
-            explicit_self_ty,
-        )
-    }
-
-    fn substs_from_args_and_bindings(
-        &mut self,
-        args_and_bindings: Option<&GenericArgs>,
-        def: Option<GenericDefId>,
-        infer_args: bool,
-        explicit_self_ty: Option<Ty>,
-    ) -> Substitution {
-        let Some(def) = def else { return Substitution::empty(Interner) };
-
-        // Order is
-        // - Optional Self parameter
-        // - Lifetime parameters
-        // - Type or Const parameters
-        // - Parent parameters
-        let def_generics = generics(self.db.upcast(), def);
-        let (
-            parent_params,
-            self_param,
-            type_params,
-            const_params,
-            impl_trait_params,
-            lifetime_params,
-        ) = def_generics.provenance_split();
-        let item_len =
-            self_param as usize + type_params + const_params + impl_trait_params + lifetime_params;
-        let total_len = parent_params + item_len;
-
-        let mut substs = Vec::new();
-
-        // we need to iterate the lifetime and type/const params separately as our order of them
-        // differs from the supplied syntax
-
-        let ty_error = || TyKind::Error.intern(Interner).cast(Interner);
-        let mut def_toc_iter = def_generics.iter_self_type_or_consts_id();
-        let fill_self_param = || {
-            if self_param {
-                let self_ty = explicit_self_ty.map(|x| x.cast(Interner)).unwrap_or_else(ty_error);
-
-                if let Some(id) = def_toc_iter.next() {
-                    assert!(matches!(id, GenericParamId::TypeParamId(_)));
-                    substs.push(self_ty);
-                }
-            }
-        };
-        let mut had_explicit_args = false;
-
-        if let Some(&GenericArgs { ref args, has_self_type, .. }) = args_and_bindings {
-            // Fill in the self param first
-            if has_self_type && self_param {
-                had_explicit_args = true;
-                if let Some(id) = def_toc_iter.next() {
-                    assert!(matches!(id, GenericParamId::TypeParamId(_)));
-                    had_explicit_args = true;
-                    if let GenericArg::Type(ty) = &args[0] {
-                        substs.push(self.lower_ty(*ty).cast(Interner));
-                    }
-                }
-            } else {
-                fill_self_param()
-            };
-
-            // Then fill in the supplied lifetime args, or error lifetimes if there are too few
-            // (default lifetimes aren't a thing)
-            for arg in args
-                .iter()
-                .filter_map(|arg| match arg {
-                    GenericArg::Lifetime(arg) => Some(self.lower_lifetime(arg)),
-                    _ => None,
-                })
-                .chain(iter::repeat(error_lifetime()))
-                .take(lifetime_params)
-            {
-                substs.push(arg.cast(Interner));
-            }
-
-            let skip = if has_self_type { 1 } else { 0 };
-            // Fill in supplied type and const args
-            // Note if non-lifetime args are provided, it should be all of them, but we can't rely on that
-            for (arg, id) in args
-                .iter()
-                .filter(|arg| !matches!(arg, GenericArg::Lifetime(_)))
-                .skip(skip)
-                .take(type_params + const_params)
-                .zip(def_toc_iter)
-            {
-                had_explicit_args = true;
-                let arg = generic_arg_to_chalk(
-                    self.db,
-                    id,
-                    arg,
-                    self,
-                    self.types_map,
-                    |this, type_ref| this.lower_ty(type_ref),
-                    |this, const_ref, ty| this.lower_const(const_ref, ty),
-                    |this, lifetime_ref| this.lower_lifetime(lifetime_ref),
-                );
-                substs.push(arg);
-            }
-        } else {
-            fill_self_param();
-        }
-
-        let param_to_err = |id| match id {
-            GenericParamId::ConstParamId(x) => unknown_const_as_generic(self.db.const_param_ty(x)),
-            GenericParamId::TypeParamId(_) => ty_error(),
-            GenericParamId::LifetimeParamId(_) => error_lifetime().cast(Interner),
-        };
-        // handle defaults. In expression or pattern path segments without
-        // explicitly specified type arguments, missing type arguments are inferred
-        // (i.e. defaults aren't used).
-        // Generic parameters for associated types are not supposed to have defaults, so we just
-        // ignore them.
-        let is_assoc_ty = || match def {
-            GenericDefId::TypeAliasId(id) => {
-                matches!(id.lookup(self.db.upcast()).container, ItemContainerId::TraitId(_))
-            }
-            _ => false,
-        };
-        let fill_defaults = (!infer_args || had_explicit_args) && !is_assoc_ty();
-        if fill_defaults {
-            let defaults = &*self.db.generic_defaults(def);
-            let (item, _parent) = defaults.split_at(item_len);
-            let parent_from = item_len - substs.len();
-
-            let mut rem =
-                def_generics.iter_id().skip(substs.len()).map(param_to_err).collect::<Vec<_>>();
-            // Fill in defaults for type/const params
-            for (idx, default_ty) in item[substs.len()..].iter().enumerate() {
-                // each default can depend on the previous parameters
-                let substs_so_far = Substitution::from_iter(
-                    Interner,
-                    substs.iter().cloned().chain(rem[idx..].iter().cloned()),
-                );
-                substs.push(default_ty.clone().substitute(Interner, &substs_so_far));
-            }
-            // Fill in remaining parent params
-            substs.extend(rem.drain(parent_from..));
-        } else {
-            // Fill in remaining def params and parent params
-            substs.extend(def_generics.iter_id().skip(substs.len()).map(param_to_err));
-        }
-
-        assert_eq!(substs.len(), total_len, "expected {} substs, got {}", total_len, substs.len());
-        Substitution::from_iter(Interner, substs)
-    }
-
-    pub(crate) fn lower_trait_ref_from_resolved_path(
-        &mut self,
-        resolved: TraitId,
-        segment: PathSegment<'_>,
-        explicit_self_ty: Ty,
-    ) -> TraitRef {
-        let substs = self.trait_ref_substs_from_path(segment, resolved, explicit_self_ty);
-        TraitRef { trait_id: to_chalk_trait_id(resolved), substitution: substs }
-    }
-
-    fn prohibit_generics(
-        &mut self,
-        path_id: PathId,
-        idx: u32,
-        segments: PathSegments<'_>,
-        reason: GenericArgsProhibitedReason,
-    ) {
-        segments.iter().zip(idx..).for_each(|(segment, idx)| {
-            if segment.args_and_bindings.is_some() {
-                self.push_diagnostic(
-                    path_id.type_ref(),
-                    TyLoweringDiagnosticKind::PathDiagnostic(
-                        PathLoweringDiagnostic::GenericArgsProhibited { segment: idx, reason },
-                    ),
-                );
-            }
-        });
+        ctx.lower_partly_resolved_path(resolution, false)
     }
 
     fn lower_trait_ref_from_path(
         &mut self,
         path_id: PathId,
         explicit_self_ty: Ty,
-    ) -> Option<TraitRef> {
-        let path = &self.types_map[path_id];
-        let resolved = match self.resolve_path_in_type_ns_fully(
-            path,
-            &mut Self::on_path_diagnostic_callback(path_id.type_ref()),
-        )? {
+    ) -> Option<(TraitRef, PathLoweringContext<'_, 'a>)> {
+        let mut ctx = self.at_path(path_id);
+        let resolved = match ctx.resolve_path_in_type_ns_fully()? {
             // FIXME(trait_alias): We need to handle trait alias here.
             TypeNs::TraitId(tr) => tr,
             _ => return None,
         };
-        // Do this after we verify it's indeed a trait to not confuse the user if they're not modules.
-        self.prohibit_generics(
-            path_id,
-            0,
-            path.segments().strip_last(),
-            GenericArgsProhibitedReason::Module,
-        );
-        let segment = path.segments().last().expect("path should have at least one segment");
-        Some(self.lower_trait_ref_from_resolved_path(resolved, segment, explicit_self_ty))
+        Some((ctx.lower_trait_ref_from_resolved_path(resolved, explicit_self_ty), ctx))
     }
 
     fn lower_trait_ref(
@@ -1308,16 +602,7 @@ impl<'a> TyLoweringContext<'a> {
         trait_ref: &HirTraitRef,
         explicit_self_ty: Ty,
     ) -> Option<TraitRef> {
-        self.lower_trait_ref_from_path(trait_ref.path, explicit_self_ty)
-    }
-
-    fn trait_ref_substs_from_path(
-        &mut self,
-        segment: PathSegment<'_>,
-        resolved: TraitId,
-        explicit_self_ty: Ty,
-    ) -> Substitution {
-        self.substs_from_path_segment(segment, Some(resolved.into()), false, Some(explicit_self_ty))
+        self.lower_trait_ref_from_path(trait_ref.path, explicit_self_ty).map(|it| it.0)
     }
 
     pub(crate) fn lower_where_predicate<'b>(
@@ -1365,11 +650,18 @@ impl<'a> TyLoweringContext<'a> {
         self_ty: Ty,
         ignore_bindings: bool,
     ) -> impl Iterator<Item = QuantifiedWhereClause> + use<'b, 'a> {
-        let mut trait_ref = None;
-        let clause = match bound {
-            &TypeBound::Path(path, TraitBoundModifier::None) => {
-                trait_ref = self.lower_trait_ref_from_path(path, self_ty);
-                trait_ref.clone().map(WhereClause::Implemented).map(crate::wrap_empty_binders)
+        let mut assoc_bounds = None;
+        let mut clause = None;
+        match bound {
+            &TypeBound::Path(path, TraitBoundModifier::None) | &TypeBound::ForLifetime(_, path) => {
+                // FIXME Don't silently drop the hrtb lifetimes here
+                if let Some((trait_ref, ctx)) = self.lower_trait_ref_from_path(path, self_ty) {
+                    if !ignore_bindings {
+                        assoc_bounds =
+                            ctx.assoc_type_bindings_from_type_bound(bound, trait_ref.clone());
+                    }
+                    clause = Some(crate::wrap_empty_binders(WhereClause::Implemented(trait_ref)));
+                }
             }
             &TypeBound::Path(path, TraitBoundModifier::Maybe) => {
                 let sized_trait = self
@@ -1381,170 +673,21 @@ impl<'a> TyLoweringContext<'a> {
                 // If we got another trait here ignore the bound completely.
                 let trait_id = self
                     .lower_trait_ref_from_path(path, self_ty.clone())
-                    .map(|trait_ref| trait_ref.hir_trait_id());
+                    .map(|(trait_ref, _)| trait_ref.hir_trait_id());
                 if trait_id == sized_trait {
                     self.unsized_types.insert(self_ty);
                 }
-                None
-            }
-            &TypeBound::ForLifetime(_, path) => {
-                // FIXME Don't silently drop the hrtb lifetimes here
-                trait_ref = self.lower_trait_ref_from_path(path, self_ty);
-                trait_ref.clone().map(WhereClause::Implemented).map(crate::wrap_empty_binders)
             }
             TypeBound::Lifetime(l) => {
                 let lifetime = self.lower_lifetime(l);
-                Some(crate::wrap_empty_binders(WhereClause::TypeOutlives(TypeOutlives {
+                clause = Some(crate::wrap_empty_binders(WhereClause::TypeOutlives(TypeOutlives {
                     ty: self_ty,
                     lifetime,
-                })))
+                })));
             }
-            TypeBound::Use(_) | TypeBound::Error => None,
-        };
-        clause.into_iter().chain(
-            trait_ref
-                .filter(move |_| !ignore_bindings)
-                .map(move |tr| self.assoc_type_bindings_from_type_bound(bound, tr))
-                .into_iter()
-                .flatten(),
-        )
-    }
-
-    fn assoc_type_bindings_from_type_bound<'b>(
-        &'b mut self,
-        bound: &'b TypeBound,
-        trait_ref: TraitRef,
-    ) -> impl Iterator<Item = QuantifiedWhereClause> + use<'b, 'a> {
-        let last_segment = match bound {
-            &TypeBound::Path(path, TraitBoundModifier::None) | &TypeBound::ForLifetime(_, path) => {
-                self.types_map[path].segments().last()
-            }
-            TypeBound::Path(_, TraitBoundModifier::Maybe)
-            | TypeBound::Use(_)
-            | TypeBound::Error
-            | TypeBound::Lifetime(_) => None,
-        };
-        last_segment
-            .into_iter()
-            .filter_map(|segment| segment.args_and_bindings)
-            .flat_map(|args_and_bindings| args_and_bindings.bindings.iter())
-            .flat_map(move |binding| {
-                let found = associated_type_by_name_including_super_traits(
-                    self.db,
-                    trait_ref.clone(),
-                    &binding.name,
-                );
-                let (super_trait_ref, associated_ty) = match found {
-                    None => return SmallVec::new(),
-                    Some(t) => t,
-                };
-                // FIXME: `substs_from_path_segment()` pushes `TyKind::Error` for every parent
-                // generic params. It's inefficient to splice the `Substitution`s, so we may want
-                // that method to optionally take parent `Substitution` as we already know them at
-                // this point (`super_trait_ref.substitution`).
-                let substitution = self.substs_from_path_segment(
-                    // FIXME: This is hack. We shouldn't really build `PathSegment` directly.
-                    PathSegment { name: &binding.name, args_and_bindings: binding.args.as_ref() },
-                    Some(associated_ty.into()),
-                    false, // this is not relevant
-                    Some(super_trait_ref.self_type_parameter(Interner)),
-                );
-                let self_params = generics(self.db.upcast(), associated_ty.into()).len_self();
-                let substitution = Substitution::from_iter(
-                    Interner,
-                    substitution
-                        .iter(Interner)
-                        .take(self_params)
-                        .chain(super_trait_ref.substitution.iter(Interner)),
-                );
-                let projection_ty = ProjectionTy {
-                    associated_ty_id: to_assoc_type_id(associated_ty),
-                    substitution,
-                };
-                let mut predicates: SmallVec<[_; 1]> = SmallVec::with_capacity(
-                    binding.type_ref.as_ref().map_or(0, |_| 1) + binding.bounds.len(),
-                );
-                if let Some(type_ref) = binding.type_ref {
-                    match (&self.types_map[type_ref], self.impl_trait_mode.mode) {
-                        (TypeRef::ImplTrait(_), ImplTraitLoweringMode::Disallowed) => (),
-                        (_, ImplTraitLoweringMode::Disallowed | ImplTraitLoweringMode::Opaque) => {
-                            let ty = self.lower_ty(type_ref);
-                            let alias_eq =
-                                AliasEq { alias: AliasTy::Projection(projection_ty.clone()), ty };
-                            predicates
-                                .push(crate::wrap_empty_binders(WhereClause::AliasEq(alias_eq)));
-                        }
-                        (_, ImplTraitLoweringMode::Param | ImplTraitLoweringMode::Variable) => {
-                            // Find the generic index for the target of our `bound`
-                            let target_param_idx = self
-                                .resolver
-                                .where_predicates_in_scope()
-                                .find_map(|(p, _)| match p {
-                                    WherePredicate::TypeBound {
-                                        target: WherePredicateTypeTarget::TypeOrConstParam(idx),
-                                        bound: b,
-                                    } if b == bound => Some(idx),
-                                    _ => None,
-                                });
-                            let ty = if let Some(target_param_idx) = target_param_idx {
-                                let mut counter = 0;
-                                let generics = self.generics().expect("generics in scope");
-                                for (idx, data) in generics.iter_self_type_or_consts() {
-                                    // Count the number of `impl Trait` things that appear before
-                                    // the target of our `bound`.
-                                    // Our counter within `impl_trait_mode` should be that number
-                                    // to properly lower each types within `type_ref`
-                                    if data.type_param().is_some_and(|p| {
-                                        p.provenance == TypeParamProvenance::ArgumentImplTrait
-                                    }) {
-                                        counter += 1;
-                                    }
-                                    if idx == *target_param_idx {
-                                        break;
-                                    }
-                                }
-                                let mut ext = TyLoweringContext::new_maybe_unowned(
-                                    self.db,
-                                    self.resolver,
-                                    self.types_map,
-                                    self.types_source_map,
-                                    self.owner,
-                                )
-                                .with_type_param_mode(self.type_param_mode);
-                                match self.impl_trait_mode.mode {
-                                    ImplTraitLoweringMode::Param => {
-                                        ext.impl_trait_mode =
-                                            ImplTraitLoweringState::param(counter);
-                                    }
-                                    ImplTraitLoweringMode::Variable => {
-                                        ext.impl_trait_mode =
-                                            ImplTraitLoweringState::variable(counter);
-                                    }
-                                    _ => unreachable!(),
-                                }
-                                let ty = ext.lower_ty(type_ref);
-                                self.diagnostics.extend(ext.diagnostics);
-                                ty
-                            } else {
-                                self.lower_ty(type_ref)
-                            };
-
-                            let alias_eq =
-                                AliasEq { alias: AliasTy::Projection(projection_ty.clone()), ty };
-                            predicates
-                                .push(crate::wrap_empty_binders(WhereClause::AliasEq(alias_eq)));
-                        }
-                    }
-                }
-                for bound in binding.bounds.iter() {
-                    predicates.extend(self.lower_type_bound(
-                        bound,
-                        TyKind::Alias(AliasTy::Projection(projection_ty.clone())).intern(Interner),
-                        false,
-                    ));
-                }
-                predicates
-            })
+            TypeBound::Use(_) | TypeBound::Error => {}
+        }
+        clause.into_iter().chain(assoc_bounds.into_iter().flatten())
     }
 
     fn lower_dyn_trait(&mut self, bounds: &[TypeBound]) -> Ty {
@@ -2471,14 +1614,14 @@ pub enum ValueTyDefId {
 impl_from!(FunctionId, StructId, UnionId, EnumVariantId, ConstId, StaticId for ValueTyDefId);
 
 impl ValueTyDefId {
-    pub(crate) fn to_generic_def_id(self, db: &dyn HirDatabase) -> Option<GenericDefId> {
+    pub(crate) fn to_generic_def_id(self, db: &dyn HirDatabase) -> GenericDefId {
         match self {
-            Self::FunctionId(id) => Some(id.into()),
-            Self::StructId(id) => Some(id.into()),
-            Self::UnionId(id) => Some(id.into()),
-            Self::EnumVariantId(var) => Some(var.lookup(db.upcast()).parent.into()),
-            Self::ConstId(id) => Some(id.into()),
-            Self::StaticId(_) => None,
+            Self::FunctionId(id) => id.into(),
+            Self::StructId(id) => id.into(),
+            Self::UnionId(id) => id.into(),
+            Self::EnumVariantId(var) => var.lookup(db.upcast()).parent.into(),
+            Self::ConstId(id) => id.into(),
+            Self::StaticId(id) => id.into(),
         }
     }
 }
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/lower/diagnostics.rs b/src/tools/rust-analyzer/crates/hir-ty/src/lower/diagnostics.rs
index 7fe196cdbb5..5c77bcd0736 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/lower/diagnostics.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/lower/diagnostics.rs
@@ -26,11 +26,11 @@ pub enum GenericArgsProhibitedReason {
     Static,
     /// When there is a generic enum, within the expression `Enum::Variant`,
     /// either `Enum` or `Variant` are allowed to have generic arguments, but not both.
-    // FIXME: This is not used now but it should be.
     EnumVariant,
 }
 
 #[derive(Debug, PartialEq, Eq, Clone)]
 pub enum PathLoweringDiagnostic {
     GenericArgsProhibited { segment: u32, reason: GenericArgsProhibitedReason },
+    ParenthesizedGenericArgsWithoutFnTrait { segment: u32 },
 }
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/lower/path.rs b/src/tools/rust-analyzer/crates/hir-ty/src/lower/path.rs
new file mode 100644
index 00000000000..a165932ddcc
--- /dev/null
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/lower/path.rs
@@ -0,0 +1,917 @@
+//! A wrapper around [`TyLoweringContext`] specifically for lowering paths.
+
+use std::iter;
+
+use chalk_ir::{cast::Cast, fold::Shift, BoundVar};
+use either::Either;
+use hir_def::{
+    data::TraitFlags,
+    expr_store::HygieneId,
+    generics::{TypeParamProvenance, WherePredicate, WherePredicateTypeTarget},
+    path::{GenericArg, GenericArgs, Path, PathSegment, PathSegments},
+    resolver::{ResolveValueResult, TypeNs, ValueNs},
+    type_ref::{TypeBound, TypeRef, TypesMap},
+    GenericDefId, GenericParamId, ItemContainerId, Lookup, TraitId,
+};
+use smallvec::SmallVec;
+use stdx::never;
+
+use crate::{
+    consteval::unknown_const_as_generic,
+    error_lifetime,
+    generics::generics,
+    lower::{
+        generic_arg_to_chalk, named_associated_type_shorthand_candidates, ImplTraitLoweringState,
+    },
+    to_assoc_type_id, to_chalk_trait_id, to_placeholder_idx,
+    utils::associated_type_by_name_including_super_traits,
+    AliasEq, AliasTy, GenericArgsProhibitedReason, ImplTraitLoweringMode, Interner,
+    ParamLoweringMode, PathLoweringDiagnostic, ProjectionTy, QuantifiedWhereClause, Substitution,
+    TraitRef, Ty, TyBuilder, TyDefId, TyKind, TyLoweringContext, ValueTyDefId, WhereClause,
+};
+
+type CallbackData<'a> = Either<
+    super::PathDiagnosticCallbackData,
+    crate::infer::diagnostics::PathDiagnosticCallbackData<'a>,
+>;
+
+// We cannot use `&mut dyn FnMut()` because of lifetime issues, and we don't want to use `Box<dyn FnMut()>`
+// because of the allocation, so we create a lifetime-less callback, tailored for our needs.
+pub(crate) struct PathDiagnosticCallback<'a> {
+    pub(crate) data: CallbackData<'a>,
+    pub(crate) callback: fn(&CallbackData<'_>, &mut TyLoweringContext<'_>, PathLoweringDiagnostic),
+}
+
+pub(crate) struct PathLoweringContext<'a, 'b> {
+    ctx: &'a mut TyLoweringContext<'b>,
+    on_diagnostic: PathDiagnosticCallback<'a>,
+    path: &'a Path,
+    segments: PathSegments<'a>,
+    current_segment_idx: usize,
+    /// Contains the previous segment if `current_segment_idx == segments.len()`
+    current_or_prev_segment: PathSegment<'a>,
+}
+
+impl<'a, 'b> PathLoweringContext<'a, 'b> {
+    #[inline]
+    pub(crate) fn new(
+        ctx: &'a mut TyLoweringContext<'b>,
+        on_diagnostic: PathDiagnosticCallback<'a>,
+        path: &'a Path,
+    ) -> Self {
+        let segments = path.segments();
+        let first_segment = segments.first().unwrap_or(PathSegment::MISSING);
+        Self {
+            ctx,
+            on_diagnostic,
+            path,
+            segments,
+            current_segment_idx: 0,
+            current_or_prev_segment: first_segment,
+        }
+    }
+
+    #[inline]
+    #[cold]
+    fn on_diagnostic(&mut self, diag: PathLoweringDiagnostic) {
+        (self.on_diagnostic.callback)(&self.on_diagnostic.data, self.ctx, diag);
+    }
+
+    #[inline]
+    pub(crate) fn ty_ctx(&mut self) -> &mut TyLoweringContext<'b> {
+        self.ctx
+    }
+
+    #[inline]
+    fn current_segment_u32(&self) -> u32 {
+        self.current_segment_idx as u32
+    }
+
+    #[inline]
+    fn skip_resolved_segment(&mut self) {
+        if !matches!(self.path, Path::LangItem(..)) {
+            // In lang items, the resolved "segment" is not one of the segments. Perhaps we should've put it
+            // point at -1, but I don't feel this is clearer.
+            self.current_segment_idx += 1;
+        }
+        self.update_current_segment();
+    }
+
+    #[inline]
+    fn update_current_segment(&mut self) {
+        self.current_or_prev_segment =
+            self.segments.get(self.current_segment_idx).unwrap_or(self.current_or_prev_segment);
+    }
+
+    #[inline]
+    pub(crate) fn ignore_last_segment(&mut self) {
+        self.segments = self.segments.strip_last();
+    }
+
+    #[inline]
+    pub(crate) fn set_current_segment(&mut self, segment: usize) {
+        self.current_segment_idx = segment;
+        self.current_or_prev_segment = self
+            .segments
+            .get(segment)
+            .expect("invalid segment passed to PathLoweringContext::set_current_segment()");
+    }
+
+    pub(crate) fn lower_ty_relative_path(
+        &mut self,
+        ty: Ty,
+        // We need the original resolution to lower `Self::AssocTy` correctly
+        res: Option<TypeNs>,
+    ) -> (Ty, Option<TypeNs>) {
+        match self.segments.len() - self.current_segment_idx {
+            0 => (ty, res),
+            1 => {
+                // resolve unselected assoc types
+                (self.select_associated_type(res), None)
+            }
+            _ => {
+                // FIXME report error (ambiguous associated type)
+                (TyKind::Error.intern(Interner), None)
+            }
+        }
+    }
+
+    fn prohibit_parenthesized_generic_args(&mut self) -> bool {
+        if let Some(generic_args) = self.current_or_prev_segment.args_and_bindings {
+            if generic_args.desugared_from_fn {
+                let segment = self.current_segment_u32();
+                self.on_diagnostic(
+                    PathLoweringDiagnostic::ParenthesizedGenericArgsWithoutFnTrait { segment },
+                );
+                return true;
+            }
+        }
+        false
+    }
+
+    // When calling this, the current segment is the resolved segment (we don't advance it yet).
+    pub(crate) fn lower_partly_resolved_path(
+        &mut self,
+        resolution: TypeNs,
+        infer_args: bool,
+    ) -> (Ty, Option<TypeNs>) {
+        let remaining_segments = self.segments.skip(self.current_segment_idx + 1);
+
+        let ty = match resolution {
+            TypeNs::TraitId(trait_) => {
+                let ty = match remaining_segments.len() {
+                    1 => {
+                        let trait_ref = self.lower_trait_ref_from_resolved_path(
+                            trait_,
+                            TyKind::Error.intern(Interner),
+                        );
+
+                        self.skip_resolved_segment();
+                        let segment = self.current_or_prev_segment;
+                        let found =
+                            self.ctx.db.trait_data(trait_).associated_type_by_name(segment.name);
+
+                        match found {
+                            Some(associated_ty) => {
+                                // FIXME: `substs_from_path_segment()` pushes `TyKind::Error` for every parent
+                                // generic params. It's inefficient to splice the `Substitution`s, so we may want
+                                // that method to optionally take parent `Substitution` as we already know them at
+                                // this point (`trait_ref.substitution`).
+                                let substitution = self.substs_from_path_segment(
+                                    associated_ty.into(),
+                                    false,
+                                    None,
+                                );
+                                let len_self =
+                                    generics(self.ctx.db.upcast(), associated_ty.into()).len_self();
+                                let substitution = Substitution::from_iter(
+                                    Interner,
+                                    substitution
+                                        .iter(Interner)
+                                        .take(len_self)
+                                        .chain(trait_ref.substitution.iter(Interner)),
+                                );
+                                TyKind::Alias(AliasTy::Projection(ProjectionTy {
+                                    associated_ty_id: to_assoc_type_id(associated_ty),
+                                    substitution,
+                                }))
+                                .intern(Interner)
+                            }
+                            None => {
+                                // FIXME: report error (associated type not found)
+                                TyKind::Error.intern(Interner)
+                            }
+                        }
+                    }
+                    0 => {
+                        // Trait object type without dyn; this should be handled in upstream. See
+                        // `lower_path()`.
+                        stdx::never!("unexpected fully resolved trait path");
+                        TyKind::Error.intern(Interner)
+                    }
+                    _ => {
+                        // FIXME report error (ambiguous associated type)
+                        TyKind::Error.intern(Interner)
+                    }
+                };
+                return (ty, None);
+            }
+            TypeNs::TraitAliasId(_) => {
+                // FIXME(trait_alias): Implement trait alias.
+                return (TyKind::Error.intern(Interner), None);
+            }
+            TypeNs::GenericParam(param_id) => match self.ctx.type_param_mode {
+                ParamLoweringMode::Placeholder => {
+                    TyKind::Placeholder(to_placeholder_idx(self.ctx.db, param_id.into()))
+                }
+                ParamLoweringMode::Variable => {
+                    let idx = match self
+                        .ctx
+                        .generics()
+                        .expect("generics in scope")
+                        .type_or_const_param_idx(param_id.into())
+                    {
+                        None => {
+                            never!("no matching generics");
+                            return (TyKind::Error.intern(Interner), None);
+                        }
+                        Some(idx) => idx,
+                    };
+
+                    TyKind::BoundVar(BoundVar::new(self.ctx.in_binders, idx))
+                }
+            }
+            .intern(Interner),
+            TypeNs::SelfType(impl_id) => {
+                let generics = self.ctx.generics().expect("impl should have generic param scope");
+
+                match self.ctx.type_param_mode {
+                    ParamLoweringMode::Placeholder => {
+                        // `def` can be either impl itself or item within, and we need impl itself
+                        // now.
+                        let generics = generics.parent_or_self();
+                        let subst = generics.placeholder_subst(self.ctx.db);
+                        self.ctx.db.impl_self_ty(impl_id).substitute(Interner, &subst)
+                    }
+                    ParamLoweringMode::Variable => {
+                        let starting_from = match generics.def() {
+                            GenericDefId::ImplId(_) => 0,
+                            // `def` is an item within impl. We need to substitute `BoundVar`s but
+                            // remember that they are for parent (i.e. impl) generic params so they
+                            // come after our own params.
+                            _ => generics.len_self(),
+                        };
+                        TyBuilder::impl_self_ty(self.ctx.db, impl_id)
+                            .fill_with_bound_vars(self.ctx.in_binders, starting_from)
+                            .build()
+                    }
+                }
+            }
+            TypeNs::AdtSelfType(adt) => {
+                let generics = generics(self.ctx.db.upcast(), adt.into());
+                let substs = match self.ctx.type_param_mode {
+                    ParamLoweringMode::Placeholder => generics.placeholder_subst(self.ctx.db),
+                    ParamLoweringMode::Variable => {
+                        generics.bound_vars_subst(self.ctx.db, self.ctx.in_binders)
+                    }
+                };
+                self.ctx.db.ty(adt.into()).substitute(Interner, &substs)
+            }
+
+            TypeNs::AdtId(it) => self.lower_path_inner(it.into(), infer_args),
+            TypeNs::BuiltinType(it) => self.lower_path_inner(it.into(), infer_args),
+            TypeNs::TypeAliasId(it) => self.lower_path_inner(it.into(), infer_args),
+            // FIXME: report error
+            TypeNs::EnumVariantId(_) => return (TyKind::Error.intern(Interner), None),
+        };
+
+        self.skip_resolved_segment();
+        self.lower_ty_relative_path(ty, Some(resolution))
+    }
+
+    fn handle_type_ns_resolution(&mut self, resolution: &TypeNs) {
+        let mut prohibit_generics_on_resolved = |reason| {
+            if self.current_or_prev_segment.args_and_bindings.is_some() {
+                let segment = self.current_segment_u32();
+                self.on_diagnostic(PathLoweringDiagnostic::GenericArgsProhibited {
+                    segment,
+                    reason,
+                });
+            }
+        };
+
+        match resolution {
+            TypeNs::SelfType(_) => {
+                prohibit_generics_on_resolved(GenericArgsProhibitedReason::SelfTy)
+            }
+            TypeNs::GenericParam(_) => {
+                prohibit_generics_on_resolved(GenericArgsProhibitedReason::TyParam)
+            }
+            TypeNs::AdtSelfType(_) => {
+                prohibit_generics_on_resolved(GenericArgsProhibitedReason::SelfTy)
+            }
+            TypeNs::BuiltinType(_) => {
+                prohibit_generics_on_resolved(GenericArgsProhibitedReason::PrimitiveTy)
+            }
+            TypeNs::AdtId(_)
+            | TypeNs::EnumVariantId(_)
+            | TypeNs::TypeAliasId(_)
+            | TypeNs::TraitId(_)
+            | TypeNs::TraitAliasId(_) => {}
+        }
+    }
+
+    pub(crate) fn resolve_path_in_type_ns_fully(&mut self) -> Option<TypeNs> {
+        let (res, unresolved) = self.resolve_path_in_type_ns()?;
+        if unresolved.is_some() {
+            return None;
+        }
+        Some(res)
+    }
+
+    pub(crate) fn resolve_path_in_type_ns(&mut self) -> Option<(TypeNs, Option<usize>)> {
+        let (resolution, remaining_index, _, prefix_info) = self
+            .ctx
+            .resolver
+            .resolve_path_in_type_ns_with_prefix_info(self.ctx.db.upcast(), self.path)?;
+
+        let segments = self.segments;
+        if segments.is_empty() || matches!(self.path, Path::LangItem(..)) {
+            // `segments.is_empty()` can occur with `self`.
+            return Some((resolution, remaining_index));
+        }
+
+        let (module_segments, resolved_segment_idx, enum_segment) = match remaining_index {
+            None if prefix_info.enum_variant => {
+                (segments.strip_last_two(), segments.len() - 1, Some(segments.len() - 2))
+            }
+            None => (segments.strip_last(), segments.len() - 1, None),
+            Some(i) => (segments.take(i - 1), i - 1, None),
+        };
+
+        self.current_segment_idx = resolved_segment_idx;
+        self.current_or_prev_segment =
+            segments.get(resolved_segment_idx).expect("should have resolved segment");
+
+        if matches!(self.path, Path::BarePath(..)) {
+            // Bare paths cannot have generics, so skip them as an optimization.
+            return Some((resolution, remaining_index));
+        }
+
+        for (i, mod_segment) in module_segments.iter().enumerate() {
+            if mod_segment.args_and_bindings.is_some() {
+                self.on_diagnostic(PathLoweringDiagnostic::GenericArgsProhibited {
+                    segment: i as u32,
+                    reason: GenericArgsProhibitedReason::Module,
+                });
+            }
+        }
+
+        if let Some(enum_segment) = enum_segment {
+            if segments.get(enum_segment).is_some_and(|it| it.args_and_bindings.is_some())
+                && segments.get(enum_segment + 1).is_some_and(|it| it.args_and_bindings.is_some())
+            {
+                self.on_diagnostic(PathLoweringDiagnostic::GenericArgsProhibited {
+                    segment: (enum_segment + 1) as u32,
+                    reason: GenericArgsProhibitedReason::EnumVariant,
+                });
+            }
+        }
+
+        self.handle_type_ns_resolution(&resolution);
+
+        Some((resolution, remaining_index))
+    }
+
+    pub(crate) fn resolve_path_in_value_ns(
+        &mut self,
+        hygiene_id: HygieneId,
+    ) -> Option<ResolveValueResult> {
+        let (res, prefix_info) = self.ctx.resolver.resolve_path_in_value_ns_with_prefix_info(
+            self.ctx.db.upcast(),
+            self.path,
+            hygiene_id,
+        )?;
+
+        let segments = self.segments;
+        if segments.is_empty() || matches!(self.path, Path::LangItem(..)) {
+            // `segments.is_empty()` can occur with `self`.
+            return Some(res);
+        }
+
+        let (mod_segments, enum_segment, resolved_segment_idx) = match res {
+            ResolveValueResult::Partial(_, unresolved_segment, _) => {
+                (segments.take(unresolved_segment - 1), None, unresolved_segment - 1)
+            }
+            ResolveValueResult::ValueNs(ValueNs::EnumVariantId(_), _)
+                if prefix_info.enum_variant =>
+            {
+                (segments.strip_last_two(), segments.len().checked_sub(2), segments.len() - 1)
+            }
+            ResolveValueResult::ValueNs(..) => (segments.strip_last(), None, segments.len() - 1),
+        };
+
+        self.current_segment_idx = resolved_segment_idx;
+        self.current_or_prev_segment =
+            segments.get(resolved_segment_idx).expect("should have resolved segment");
+
+        for (i, mod_segment) in mod_segments.iter().enumerate() {
+            if mod_segment.args_and_bindings.is_some() {
+                self.on_diagnostic(PathLoweringDiagnostic::GenericArgsProhibited {
+                    segment: i as u32,
+                    reason: GenericArgsProhibitedReason::Module,
+                });
+            }
+        }
+
+        if let Some(enum_segment) = enum_segment {
+            if segments.get(enum_segment).is_some_and(|it| it.args_and_bindings.is_some())
+                && segments.get(enum_segment + 1).is_some_and(|it| it.args_and_bindings.is_some())
+            {
+                self.on_diagnostic(PathLoweringDiagnostic::GenericArgsProhibited {
+                    segment: (enum_segment + 1) as u32,
+                    reason: GenericArgsProhibitedReason::EnumVariant,
+                });
+            }
+        }
+
+        match &res {
+            ResolveValueResult::ValueNs(resolution, _) => {
+                let resolved_segment_idx = self.current_segment_u32();
+                let resolved_segment = self.current_or_prev_segment;
+
+                let mut prohibit_generics_on_resolved = |reason| {
+                    if resolved_segment.args_and_bindings.is_some() {
+                        self.on_diagnostic(PathLoweringDiagnostic::GenericArgsProhibited {
+                            segment: resolved_segment_idx,
+                            reason,
+                        });
+                    }
+                };
+
+                match resolution {
+                    ValueNs::ImplSelf(_) => {
+                        prohibit_generics_on_resolved(GenericArgsProhibitedReason::SelfTy)
+                    }
+                    // FIXME: rustc generates E0107 (incorrect number of generic arguments) and not
+                    // E0109 (generic arguments provided for a type that doesn't accept them) for
+                    // consts and statics, presumably as a defense against future in which consts
+                    // and statics can be generic, or just because it was easier for rustc implementors.
+                    // That means we'll show the wrong error code. Because of us it's easier to do it
+                    // this way :)
+                    ValueNs::GenericParam(_) | ValueNs::ConstId(_) => {
+                        prohibit_generics_on_resolved(GenericArgsProhibitedReason::Const)
+                    }
+                    ValueNs::StaticId(_) => {
+                        prohibit_generics_on_resolved(GenericArgsProhibitedReason::Static)
+                    }
+                    ValueNs::FunctionId(_) | ValueNs::StructId(_) | ValueNs::EnumVariantId(_) => {}
+                    ValueNs::LocalBinding(_) => {}
+                }
+            }
+            ResolveValueResult::Partial(resolution, _, _) => {
+                self.handle_type_ns_resolution(resolution);
+            }
+        };
+        Some(res)
+    }
+
+    fn select_associated_type(&mut self, res: Option<TypeNs>) -> Ty {
+        let Some((generics, res)) = self.ctx.generics().zip(res) else {
+            return TyKind::Error.intern(Interner);
+        };
+        let segment = self.current_or_prev_segment;
+        let ty = named_associated_type_shorthand_candidates(
+            self.ctx.db,
+            generics.def(),
+            res,
+            Some(segment.name.clone()),
+            move |name, t, associated_ty| {
+                let generics = self.ctx.generics().unwrap();
+
+                if name != segment.name {
+                    return None;
+                }
+
+                let parent_subst = t.substitution.clone();
+                let parent_subst = match self.ctx.type_param_mode {
+                    ParamLoweringMode::Placeholder => {
+                        // if we're lowering to placeholders, we have to put them in now.
+                        let s = generics.placeholder_subst(self.ctx.db);
+                        s.apply(parent_subst, Interner)
+                    }
+                    ParamLoweringMode::Variable => {
+                        // We need to shift in the bound vars, since
+                        // `named_associated_type_shorthand_candidates` does not do that.
+                        parent_subst.shifted_in_from(Interner, self.ctx.in_binders)
+                    }
+                };
+
+                // FIXME: `substs_from_path_segment()` pushes `TyKind::Error` for every parent
+                // generic params. It's inefficient to splice the `Substitution`s, so we may want
+                // that method to optionally take parent `Substitution` as we already know them at
+                // this point (`t.substitution`).
+                let substs = self.substs_from_path_segment(associated_ty.into(), false, None);
+
+                let len_self =
+                    crate::generics::generics(self.ctx.db.upcast(), associated_ty.into())
+                        .len_self();
+
+                let substs = Substitution::from_iter(
+                    Interner,
+                    substs.iter(Interner).take(len_self).chain(parent_subst.iter(Interner)),
+                );
+
+                Some(
+                    TyKind::Alias(AliasTy::Projection(ProjectionTy {
+                        associated_ty_id: to_assoc_type_id(associated_ty),
+                        substitution: substs,
+                    }))
+                    .intern(Interner),
+                )
+            },
+        );
+
+        ty.unwrap_or_else(|| TyKind::Error.intern(Interner))
+    }
+
+    fn lower_path_inner(&mut self, typeable: TyDefId, infer_args: bool) -> Ty {
+        let generic_def = match typeable {
+            TyDefId::BuiltinType(builtin) => return TyBuilder::builtin(builtin),
+            TyDefId::AdtId(it) => it.into(),
+            TyDefId::TypeAliasId(it) => it.into(),
+        };
+        let substs = self.substs_from_path_segment(generic_def, infer_args, None);
+        self.ctx.db.ty(typeable).substitute(Interner, &substs)
+    }
+
+    /// Collect generic arguments from a path into a `Substs`. See also
+    /// `create_substs_for_ast_path` and `def_to_ty` in rustc.
+    pub(crate) fn substs_from_path(
+        &mut self,
+        // Note that we don't call `db.value_type(resolved)` here,
+        // `ValueTyDefId` is just a convenient way to pass generics and
+        // special-case enum variants
+        resolved: ValueTyDefId,
+        infer_args: bool,
+    ) -> Substitution {
+        let prev_current_segment_idx = self.current_segment_idx;
+        let prev_current_segment = self.current_or_prev_segment;
+
+        let generic_def = match resolved {
+            ValueTyDefId::FunctionId(it) => it.into(),
+            ValueTyDefId::StructId(it) => it.into(),
+            ValueTyDefId::UnionId(it) => it.into(),
+            ValueTyDefId::ConstId(it) => it.into(),
+            ValueTyDefId::StaticId(_) => return Substitution::empty(Interner),
+            ValueTyDefId::EnumVariantId(var) => {
+                // the generic args for an enum variant may be either specified
+                // on the segment referring to the enum, or on the segment
+                // referring to the variant. So `Option::<T>::None` and
+                // `Option::None::<T>` are both allowed (though the former is
+                // FIXME: This isn't strictly correct, enum variants may be used not through the enum
+                // (via `use Enum::Variant`). The resolver returns whether they were, but we don't have its result
+                // available here. The worst that can happen is that we will show some confusing diagnostics to the user,
+                // if generics exist on the module and they don't match with the variant.
+                // preferred). See also `def_ids_for_path_segments` in rustc.
+                //
+                // `wrapping_sub(1)` will return a number which `get` will return None for if current_segment_idx<2.
+                // This simplifies the code a bit.
+                let penultimate_idx = self.current_segment_idx.wrapping_sub(1);
+                let penultimate = self.segments.get(penultimate_idx);
+                if let Some(penultimate) = penultimate {
+                    if self.current_or_prev_segment.args_and_bindings.is_none()
+                        && penultimate.args_and_bindings.is_some()
+                    {
+                        self.current_segment_idx = penultimate_idx;
+                        self.current_or_prev_segment = penultimate;
+                    }
+                }
+                var.lookup(self.ctx.db.upcast()).parent.into()
+            }
+        };
+        let result = self.substs_from_path_segment(generic_def, infer_args, None);
+        self.current_segment_idx = prev_current_segment_idx;
+        self.current_or_prev_segment = prev_current_segment;
+        result
+    }
+
+    pub(crate) fn substs_from_path_segment(
+        &mut self,
+        def: GenericDefId,
+        infer_args: bool,
+        explicit_self_ty: Option<Ty>,
+    ) -> Substitution {
+        let prohibit_parens = match def {
+            GenericDefId::TraitId(trait_) => {
+                let trait_data = self.ctx.db.trait_data(trait_);
+                !trait_data.flags.contains(TraitFlags::RUSTC_PAREN_SUGAR)
+            }
+            _ => true,
+        };
+        if prohibit_parens && self.prohibit_parenthesized_generic_args() {
+            return TyBuilder::unknown_subst(self.ctx.db, def);
+        }
+
+        self.substs_from_args_and_bindings(
+            self.current_or_prev_segment.args_and_bindings,
+            def,
+            infer_args,
+            explicit_self_ty,
+        )
+    }
+
+    pub(super) fn substs_from_args_and_bindings(
+        &mut self,
+        args_and_bindings: Option<&GenericArgs>,
+        def: GenericDefId,
+        infer_args: bool,
+        explicit_self_ty: Option<Ty>,
+    ) -> Substitution {
+        // Order is
+        // - Optional Self parameter
+        // - Lifetime parameters
+        // - Type or Const parameters
+        // - Parent parameters
+        let def_generics = generics(self.ctx.db.upcast(), def);
+        let (
+            parent_params,
+            self_param,
+            type_params,
+            const_params,
+            impl_trait_params,
+            lifetime_params,
+        ) = def_generics.provenance_split();
+        let item_len =
+            self_param as usize + type_params + const_params + impl_trait_params + lifetime_params;
+        let total_len = parent_params + item_len;
+
+        let mut substs = Vec::new();
+
+        // we need to iterate the lifetime and type/const params separately as our order of them
+        // differs from the supplied syntax
+
+        let ty_error = || TyKind::Error.intern(Interner).cast(Interner);
+        let mut def_toc_iter = def_generics.iter_self_type_or_consts_id();
+        let fill_self_param = || {
+            if self_param {
+                let self_ty = explicit_self_ty.map(|x| x.cast(Interner)).unwrap_or_else(ty_error);
+
+                if let Some(id) = def_toc_iter.next() {
+                    assert!(matches!(id, GenericParamId::TypeParamId(_)));
+                    substs.push(self_ty);
+                }
+            }
+        };
+        let mut had_explicit_args = false;
+
+        if let Some(&GenericArgs { ref args, has_self_type, .. }) = args_and_bindings {
+            // Fill in the self param first
+            if has_self_type && self_param {
+                had_explicit_args = true;
+                if let Some(id) = def_toc_iter.next() {
+                    assert!(matches!(id, GenericParamId::TypeParamId(_)));
+                    had_explicit_args = true;
+                    if let GenericArg::Type(ty) = &args[0] {
+                        substs.push(self.ctx.lower_ty(*ty).cast(Interner));
+                    }
+                }
+            } else {
+                fill_self_param()
+            };
+
+            // Then fill in the supplied lifetime args, or error lifetimes if there are too few
+            // (default lifetimes aren't a thing)
+            for arg in args
+                .iter()
+                .filter_map(|arg| match arg {
+                    GenericArg::Lifetime(arg) => Some(self.ctx.lower_lifetime(arg)),
+                    _ => None,
+                })
+                .chain(iter::repeat(error_lifetime()))
+                .take(lifetime_params)
+            {
+                substs.push(arg.cast(Interner));
+            }
+
+            let skip = if has_self_type { 1 } else { 0 };
+            // Fill in supplied type and const args
+            // Note if non-lifetime args are provided, it should be all of them, but we can't rely on that
+            for (arg, id) in args
+                .iter()
+                .filter(|arg| !matches!(arg, GenericArg::Lifetime(_)))
+                .skip(skip)
+                .take(type_params + const_params)
+                .zip(def_toc_iter)
+            {
+                had_explicit_args = true;
+                let arg = generic_arg_to_chalk(
+                    self.ctx.db,
+                    id,
+                    arg,
+                    self.ctx,
+                    self.ctx.types_map,
+                    |ctx, type_ref| ctx.lower_ty(type_ref),
+                    |ctx, const_ref, ty| ctx.lower_const(const_ref, ty),
+                    |ctx, lifetime_ref| ctx.lower_lifetime(lifetime_ref),
+                );
+                substs.push(arg);
+            }
+        } else {
+            fill_self_param();
+        }
+
+        let param_to_err = |id| match id {
+            GenericParamId::ConstParamId(x) => {
+                unknown_const_as_generic(self.ctx.db.const_param_ty(x))
+            }
+            GenericParamId::TypeParamId(_) => ty_error(),
+            GenericParamId::LifetimeParamId(_) => error_lifetime().cast(Interner),
+        };
+        // handle defaults. In expression or pattern path segments without
+        // explicitly specified type arguments, missing type arguments are inferred
+        // (i.e. defaults aren't used).
+        // Generic parameters for associated types are not supposed to have defaults, so we just
+        // ignore them.
+        let is_assoc_ty = || match def {
+            GenericDefId::TypeAliasId(id) => {
+                matches!(id.lookup(self.ctx.db.upcast()).container, ItemContainerId::TraitId(_))
+            }
+            _ => false,
+        };
+        let fill_defaults = (!infer_args || had_explicit_args) && !is_assoc_ty();
+        if fill_defaults {
+            let defaults = &*self.ctx.db.generic_defaults(def);
+            let (item, _parent) = defaults.split_at(item_len);
+            let parent_from = item_len - substs.len();
+
+            let mut rem =
+                def_generics.iter_id().skip(substs.len()).map(param_to_err).collect::<Vec<_>>();
+            // Fill in defaults for type/const params
+            for (idx, default_ty) in item[substs.len()..].iter().enumerate() {
+                // each default can depend on the previous parameters
+                let substs_so_far = Substitution::from_iter(
+                    Interner,
+                    substs.iter().cloned().chain(rem[idx..].iter().cloned()),
+                );
+                substs.push(default_ty.clone().substitute(Interner, &substs_so_far));
+            }
+            // Fill in remaining parent params
+            substs.extend(rem.drain(parent_from..));
+        } else {
+            // Fill in remaining def params and parent params
+            substs.extend(def_generics.iter_id().skip(substs.len()).map(param_to_err));
+        }
+
+        assert_eq!(substs.len(), total_len, "expected {} substs, got {}", total_len, substs.len());
+        Substitution::from_iter(Interner, substs)
+    }
+
+    pub(crate) fn lower_trait_ref_from_resolved_path(
+        &mut self,
+        resolved: TraitId,
+        explicit_self_ty: Ty,
+    ) -> TraitRef {
+        let substs = self.trait_ref_substs_from_path(resolved, explicit_self_ty);
+        TraitRef { trait_id: to_chalk_trait_id(resolved), substitution: substs }
+    }
+
+    fn trait_ref_substs_from_path(
+        &mut self,
+        resolved: TraitId,
+        explicit_self_ty: Ty,
+    ) -> Substitution {
+        self.substs_from_path_segment(resolved.into(), false, Some(explicit_self_ty))
+    }
+
+    pub(super) fn assoc_type_bindings_from_type_bound<'c>(
+        mut self,
+        bound: &'c TypeBound,
+        trait_ref: TraitRef,
+    ) -> Option<impl Iterator<Item = QuantifiedWhereClause> + use<'a, 'b, 'c>> {
+        self.current_or_prev_segment.args_and_bindings.map(|args_and_bindings| {
+            args_and_bindings.bindings.iter().flat_map(move |binding| {
+                let found = associated_type_by_name_including_super_traits(
+                    self.ctx.db,
+                    trait_ref.clone(),
+                    &binding.name,
+                );
+                let (super_trait_ref, associated_ty) = match found {
+                    None => return SmallVec::new(),
+                    Some(t) => t,
+                };
+                // FIXME: `substs_from_path_segment()` pushes `TyKind::Error` for every parent
+                // generic params. It's inefficient to splice the `Substitution`s, so we may want
+                // that method to optionally take parent `Substitution` as we already know them at
+                // this point (`super_trait_ref.substitution`).
+                let substitution = self.substs_from_args_and_bindings(
+                    binding.args.as_ref(),
+                    associated_ty.into(),
+                    false, // this is not relevant
+                    Some(super_trait_ref.self_type_parameter(Interner)),
+                );
+                let self_params = generics(self.ctx.db.upcast(), associated_ty.into()).len_self();
+                let substitution = Substitution::from_iter(
+                    Interner,
+                    substitution
+                        .iter(Interner)
+                        .take(self_params)
+                        .chain(super_trait_ref.substitution.iter(Interner)),
+                );
+                let projection_ty = ProjectionTy {
+                    associated_ty_id: to_assoc_type_id(associated_ty),
+                    substitution,
+                };
+                let mut predicates: SmallVec<[_; 1]> = SmallVec::with_capacity(
+                    binding.type_ref.as_ref().map_or(0, |_| 1) + binding.bounds.len(),
+                );
+                if let Some(type_ref) = binding.type_ref {
+                    match (&self.ctx.types_map[type_ref], self.ctx.impl_trait_mode.mode) {
+                        (TypeRef::ImplTrait(_), ImplTraitLoweringMode::Disallowed) => (),
+                        (_, ImplTraitLoweringMode::Disallowed | ImplTraitLoweringMode::Opaque) => {
+                            let ty = self.ctx.lower_ty(type_ref);
+                            let alias_eq =
+                                AliasEq { alias: AliasTy::Projection(projection_ty.clone()), ty };
+                            predicates
+                                .push(crate::wrap_empty_binders(WhereClause::AliasEq(alias_eq)));
+                        }
+                        (_, ImplTraitLoweringMode::Param | ImplTraitLoweringMode::Variable) => {
+                            // Find the generic index for the target of our `bound`
+                            let target_param_idx =
+                                self.ctx.resolver.where_predicates_in_scope().find_map(
+                                    |(p, (_, types_map))| match p {
+                                        WherePredicate::TypeBound {
+                                            target: WherePredicateTypeTarget::TypeOrConstParam(idx),
+                                            bound: b,
+                                        } if std::ptr::eq::<TypesMap>(
+                                            self.ctx.types_map,
+                                            types_map,
+                                        ) && bound == b =>
+                                        {
+                                            Some(idx)
+                                        }
+                                        _ => None,
+                                    },
+                                );
+                            let ty = if let Some(target_param_idx) = target_param_idx {
+                                let mut counter = 0;
+                                let generics = self.ctx.generics().expect("generics in scope");
+                                for (idx, data) in generics.iter_self_type_or_consts() {
+                                    // Count the number of `impl Trait` things that appear before
+                                    // the target of our `bound`.
+                                    // Our counter within `impl_trait_mode` should be that number
+                                    // to properly lower each types within `type_ref`
+                                    if data.type_param().is_some_and(|p| {
+                                        p.provenance == TypeParamProvenance::ArgumentImplTrait
+                                    }) {
+                                        counter += 1;
+                                    }
+                                    if idx == *target_param_idx {
+                                        break;
+                                    }
+                                }
+                                let mut ext = TyLoweringContext::new_maybe_unowned(
+                                    self.ctx.db,
+                                    self.ctx.resolver,
+                                    self.ctx.types_map,
+                                    self.ctx.types_source_map,
+                                    self.ctx.owner,
+                                )
+                                .with_type_param_mode(self.ctx.type_param_mode);
+                                match self.ctx.impl_trait_mode.mode {
+                                    ImplTraitLoweringMode::Param => {
+                                        ext.impl_trait_mode =
+                                            ImplTraitLoweringState::param(counter);
+                                    }
+                                    ImplTraitLoweringMode::Variable => {
+                                        ext.impl_trait_mode =
+                                            ImplTraitLoweringState::variable(counter);
+                                    }
+                                    _ => unreachable!(),
+                                }
+                                let ty = ext.lower_ty(type_ref);
+                                self.ctx.diagnostics.extend(ext.diagnostics);
+                                ty
+                            } else {
+                                self.ctx.lower_ty(type_ref)
+                            };
+
+                            let alias_eq =
+                                AliasEq { alias: AliasTy::Projection(projection_ty.clone()), ty };
+                            predicates
+                                .push(crate::wrap_empty_binders(WhereClause::AliasEq(alias_eq)));
+                        }
+                    }
+                }
+                for bound in binding.bounds.iter() {
+                    predicates.extend(self.ctx.lower_type_bound(
+                        bound,
+                        TyKind::Alias(AliasTy::Projection(projection_ty.clone())).intern(Interner),
+                        false,
+                    ));
+                }
+                predicates
+            })
+        })
+    }
+}
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution.rs b/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution.rs
index 1cea67ee964..db94351dcc9 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution.rs
@@ -4,6 +4,7 @@
 //! and the corresponding code mostly in rustc_hir_analysis/check/method/probe.rs.
 use std::ops::ControlFlow;
 
+use arrayvec::ArrayVec;
 use base_db::CrateId;
 use chalk_ir::{cast::Cast, UniverseIndex, WithKind};
 use hir_def::{
@@ -732,15 +733,27 @@ fn lookup_impl_assoc_item_for_trait_ref(
     let self_ty = trait_ref.self_type_parameter(Interner);
     let self_ty_fp = TyFingerprint::for_trait_impl(&self_ty)?;
     let impls = db.trait_impls_in_deps(env.krate);
-    let self_impls = match self_ty.kind(Interner) {
-        TyKind::Adt(id, _) => {
-            id.0.module(db.upcast()).containing_block().and_then(|it| db.trait_impls_in_block(it))
+
+    let trait_module = hir_trait_id.module(db.upcast());
+    let type_module = match self_ty_fp {
+        TyFingerprint::Adt(adt_id) => Some(adt_id.module(db.upcast())),
+        TyFingerprint::ForeignType(type_id) => {
+            Some(from_foreign_def_id(type_id).module(db.upcast()))
         }
+        TyFingerprint::Dyn(trait_id) => Some(trait_id.module(db.upcast())),
         _ => None,
     };
+
+    let def_blocks: ArrayVec<_, 2> =
+        [trait_module.containing_block(), type_module.and_then(|it| it.containing_block())]
+            .into_iter()
+            .flatten()
+            .filter_map(|block_id| db.trait_impls_in_block(block_id))
+            .collect();
+
     let impls = impls
         .iter()
-        .chain(self_impls.as_ref())
+        .chain(&def_blocks)
         .flat_map(|impls| impls.for_trait_and_self_ty(hir_trait_id, self_ty_fp));
 
     let table = InferenceTable::new(db, env);
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir.rs
index 59c583afb2a..41304bbd8a9 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/mir.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir.rs
@@ -10,13 +10,13 @@ use crate::{
     lang_items::is_box,
     mapping::ToChalk,
     CallableDefId, ClosureId, Const, ConstScalar, InferenceResult, Interner, MemoryMap,
-    Substitution, TraitEnvironment, Ty, TyKind,
+    Substitution, TraitEnvironment, Ty, TyExt, TyKind,
 };
 use base_db::CrateId;
 use chalk_ir::Mutability;
 use either::Either;
 use hir_def::{
-    body::Body,
+    expr_store::Body,
     hir::{BindingAnnotation, BindingId, Expr, ExprId, Ordering, PatId},
     DefWithBodyId, FieldId, StaticId, TupleFieldId, UnionId, VariantId,
 };
@@ -144,6 +144,13 @@ impl<V, T> ProjectionElem<V, T> {
         closure_field: impl FnOnce(ClosureId, &Substitution, usize) -> Ty,
         krate: CrateId,
     ) -> Ty {
+        // we only bail on mir building when there are type mismatches
+        // but error types may pop up resulting in us still attempting to build the mir
+        // so just propagate the error type
+        if base.is_unknown() {
+            return TyKind::Error.intern(Interner);
+        }
+
         if matches!(base.kind(Interner), TyKind::Alias(_) | TyKind::AssociatedType(..)) {
             base = normalize(
                 db,
@@ -166,7 +173,7 @@ impl<V, T> ProjectionElem<V, T> {
                     TyKind::Error.intern(Interner)
                 }
             },
-            ProjectionElem::Field(Either::Left(f)) => match &base.kind(Interner) {
+            ProjectionElem::Field(Either::Left(f)) => match base.kind(Interner) {
                 TyKind::Adt(_, subst) => {
                     db.field_types(f.parent)[f.local_id].clone().substitute(Interner, subst)
                 }
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval.rs
index dcae6877ba8..6b20522cf34 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval.rs
@@ -6,9 +6,9 @@ use base_db::CrateId;
 use chalk_ir::{cast::Cast, Mutability};
 use either::Either;
 use hir_def::{
-    body::HygieneId,
     builtin_type::BuiltinType,
     data::adt::{StructFlags, VariantData},
+    expr_store::HygieneId,
     lang_item::LangItem,
     layout::{TagEncoding, Variants},
     resolver::{HasResolver, TypeNs, ValueNs},
@@ -1644,14 +1644,15 @@ impl Evaluator<'_> {
             Variants::Multiple { tag, tag_encoding, variants, .. } => {
                 let size = tag.size(&*self.target_data_layout).bytes_usize();
                 let offset = layout.fields.offset(0).bytes_usize(); // The only field on enum variants is the tag field
+                let is_signed = tag.is_signed();
                 match tag_encoding {
                     TagEncoding::Direct => {
                         let tag = &bytes[offset..offset + size];
-                        Ok(i128::from_le_bytes(pad16(tag, false)))
+                        Ok(i128::from_le_bytes(pad16(tag, is_signed)))
                     }
                     TagEncoding::Niche { untagged_variant, niche_start, .. } => {
                         let tag = &bytes[offset..offset + size];
-                        let candidate_tag = i128::from_le_bytes(pad16(tag, false))
+                        let candidate_tag = i128::from_le_bytes(pad16(tag, is_signed))
                             .wrapping_sub(*niche_start as i128)
                             as usize;
                         let idx = variants
@@ -2943,10 +2944,10 @@ pub fn render_const_using_debug_impl(
     // a3 = ::core::fmt::Arguments::new_v1(a1, a2)
     // FIXME: similarly, we should call function here, not directly working with memory.
     let a3 = evaluator.heap_allocate(evaluator.ptr_size() * 6, evaluator.ptr_size())?;
-    evaluator.write_memory(a3.offset(2 * evaluator.ptr_size()), &a1.to_bytes())?;
+    evaluator.write_memory(a3, &a1.to_bytes())?;
+    evaluator.write_memory(a3.offset(evaluator.ptr_size()), &[1])?;
+    evaluator.write_memory(a3.offset(2 * evaluator.ptr_size()), &a2.to_bytes())?;
     evaluator.write_memory(a3.offset(3 * evaluator.ptr_size()), &[1])?;
-    evaluator.write_memory(a3.offset(4 * evaluator.ptr_size()), &a2.to_bytes())?;
-    evaluator.write_memory(a3.offset(5 * evaluator.ptr_size()), &[1])?;
     let Some(ValueNs::FunctionId(format_fn)) = resolver.resolve_path_in_value_ns_fully(
         db.upcast(),
         &hir_def::path::Path::from_known_path_with_no_generic(path![std::fmt::format]),
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval/shim.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval/shim.rs
index 0a78f4a5b24..38b189a517f 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval/shim.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval/shim.rs
@@ -1,11 +1,12 @@
 //! Interpret intrinsics, lang items and `extern "C"` wellknown functions which their implementation
 //! is not available.
 //!
-use std::cmp;
+use std::cmp::{self, Ordering};
 
 use chalk_ir::TyKind;
 use hir_def::{
     builtin_type::{BuiltinInt, BuiltinUint},
+    lang_item::LangItemTarget,
     resolver::HasResolver,
 };
 use hir_expand::name::Name;
@@ -1317,6 +1318,82 @@ impl Evaluator<'_> {
                 self.write_memory_using_ref(dst, size)?.fill(val);
                 Ok(())
             }
+            "ptr_metadata" => {
+                let [ptr] = args else {
+                    return Err(MirEvalError::InternalError(
+                        "ptr_metadata args are not provided".into(),
+                    ));
+                };
+                let arg = ptr.interval.get(self)?.to_owned();
+                let metadata = &arg[self.ptr_size()..];
+                destination.write_from_bytes(self, metadata)?;
+                Ok(())
+            }
+            "three_way_compare" => {
+                let [lhs, rhs] = args else {
+                    return Err(MirEvalError::InternalError(
+                        "three_way_compare args are not provided".into(),
+                    ));
+                };
+                let Some(ty) =
+                    generic_args.as_slice(Interner).first().and_then(|it| it.ty(Interner))
+                else {
+                    return Err(MirEvalError::InternalError(
+                        "three_way_compare generic arg is not provided".into(),
+                    ));
+                };
+                let signed = match ty.as_builtin().unwrap() {
+                    BuiltinType::Int(_) => true,
+                    BuiltinType::Uint(_) => false,
+                    _ => {
+                        return Err(MirEvalError::InternalError(
+                            "three_way_compare expects an integral type".into(),
+                        ))
+                    }
+                };
+                let rhs = rhs.get(self)?;
+                let lhs = lhs.get(self)?;
+                let mut result = Ordering::Equal;
+                for (l, r) in lhs.iter().zip(rhs).rev() {
+                    let it = l.cmp(r);
+                    if it != Ordering::Equal {
+                        result = it;
+                        break;
+                    }
+                }
+                if signed {
+                    if let Some((&l, &r)) = lhs.iter().zip(rhs).next_back() {
+                        if l != r {
+                            result = (l as i8).cmp(&(r as i8));
+                        }
+                    }
+                }
+                if let Some(LangItemTarget::EnumId(e)) =
+                    self.db.lang_item(self.crate_id, LangItem::Ordering)
+                {
+                    let ty = self.db.ty(e.into());
+                    let r = self
+                        .compute_discriminant(ty.skip_binders().clone(), &[result as i8 as u8])?;
+                    destination.write_from_bytes(self, &r.to_le_bytes()[0..destination.size])?;
+                    Ok(())
+                } else {
+                    Err(MirEvalError::InternalError("Ordering enum not found".into()))
+                }
+            }
+            "aggregate_raw_ptr" => {
+                let [data, meta] = args else {
+                    return Err(MirEvalError::InternalError(
+                        "aggregate_raw_ptr args are not provided".into(),
+                    ));
+                };
+                destination.write_from_interval(self, data.interval)?;
+                Interval {
+                    addr: destination.addr.offset(data.interval.size),
+                    size: destination.size - data.interval.size,
+                }
+                .write_from_interval(self, meta.interval)?;
+                Ok(())
+            }
             _ if needs_override => not_supported!("intrinsic {name} is not implemented"),
             _ => return Ok(false),
         }
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval/tests.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval/tests.rs
index f1e86daea23..2b5486fc5fa 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval/tests.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval/tests.rs
@@ -3,7 +3,7 @@ use span::{Edition, EditionedFileId};
 use syntax::{TextRange, TextSize};
 use test_fixture::WithFixture;
 
-use crate::{db::HirDatabase, test_db::TestDB, Interner, Substitution};
+use crate::{db::HirDatabase, mir::MirLowerError, test_db::TestDB, Interner, Substitution};
 
 use super::{interpret_mir, MirEvalError};
 
@@ -84,6 +84,16 @@ fn check_panic(#[rust_analyzer::rust_fixture] ra_fixture: &str, expected_panic:
     assert_eq!(e.is_panic().unwrap_or_else(|| panic!("unexpected error: {e:?}")), expected_panic);
 }
 
+fn check_error_with(
+    #[rust_analyzer::rust_fixture] ra_fixture: &str,
+    expect_err: impl FnOnce(MirEvalError) -> bool,
+) {
+    let (db, file_ids) = TestDB::with_many_files(ra_fixture);
+    let file_id = *file_ids.last().unwrap();
+    let e = eval_main(&db, file_id).unwrap_err();
+    assert!(expect_err(e));
+}
+
 #[test]
 fn function_with_extern_c_abi() {
     check_pass(
@@ -912,3 +922,60 @@ fn main() {
         "",
     );
 }
+
+#[test]
+fn regression_19021() {
+    check_pass(
+        r#"
+//- minicore: deref
+use core::ops::Deref;
+
+#[lang = "owned_box"]
+struct Box<T>(T);
+
+impl<T> Deref for Box<T> {
+    type Target = T;
+
+    fn deref(&self) -> &Self::Target {
+        &self.0
+    }
+}
+
+struct Foo;
+
+fn main() {
+    let x = Box(Foo);
+    let y = &Foo;
+
+    || match x {
+        ref x => x,
+        _ => y,
+    };
+}
+"#,
+    );
+}
+
+#[test]
+fn regression_19177() {
+    check_error_with(
+        r#"
+//- minicore: copy
+trait Foo {}
+trait Bar {}
+trait Baz {}
+trait Qux {
+    type Assoc;
+}
+
+fn main<'a, T: Foo + Bar + Baz>(
+    x: &T,
+    y: (),
+    z: &'a dyn Qux<Assoc = T>,
+    w: impl Foo + Bar,
+) {
+}
+"#,
+        |e| matches!(e, MirEvalError::MirLowerError(_, MirLowerError::GenericArgNotProvided(..))),
+    );
+}
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 1d1044df6e9..f88696692e6 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
@@ -5,11 +5,11 @@ use std::{fmt::Write, iter, mem};
 use base_db::ra_salsa::Cycle;
 use chalk_ir::{BoundVar, ConstData, DebruijnIndex, TyKind};
 use hir_def::{
-    body::{Body, HygieneId},
     data::adt::{StructKind, VariantData},
+    expr_store::{Body, HygieneId},
     hir::{
-        ArithOp, Array, BinaryOp, BindingAnnotation, BindingId, ExprId, LabelId, Literal,
-        LiteralOrConst, MatchArm, Pat, PatId, RecordFieldPat, RecordLitField,
+        ArithOp, Array, BinaryOp, BindingAnnotation, BindingId, ExprId, LabelId, Literal, MatchArm,
+        Pat, PatId, RecordFieldPat, RecordLitField,
     },
     lang_item::{LangItem, LangItemTarget},
     path::Path,
@@ -1358,20 +1358,10 @@ impl<'ctx> MirLowerCtx<'ctx> {
         Ok(())
     }
 
-    fn lower_literal_or_const_to_operand(
-        &mut self,
-        ty: Ty,
-        loc: &LiteralOrConst,
-    ) -> Result<Operand> {
-        match loc {
-            LiteralOrConst::Literal(l) => self.lower_literal_to_operand(ty, l),
-            LiteralOrConst::Const(c) => {
-                let c = match &self.body.pats[*c] {
-                    Pat::Path(p) => p,
-                    _ => not_supported!(
-                        "only `char` and numeric types are allowed in range patterns"
-                    ),
-                };
+    fn lower_literal_or_const_to_operand(&mut self, ty: Ty, loc: &ExprId) -> Result<Operand> {
+        match &self.body.exprs[*loc] {
+            Expr::Literal(l) => self.lower_literal_to_operand(ty, l),
+            Expr::Path(c) => {
                 let edition = self.edition();
                 let unresolved_name =
                     || MirLowerError::unresolved_path(self.db, c, edition, &self.body.types);
@@ -1392,6 +1382,9 @@ impl<'ctx> MirLowerCtx<'ctx> {
                     }
                 }
             }
+            _ => {
+                not_supported!("only `char` and numeric types are allowed in range patterns");
+            }
         }
     }
 
@@ -2156,7 +2149,7 @@ pub fn lower_to_mir(
     // need to take this input explicitly.
     root_expr: ExprId,
 ) -> Result<MirBody> {
-    if infer.has_errors {
+    if infer.type_mismatches().next().is_some() {
         return Err(MirLowerError::HasErrors);
     }
     let mut ctx = MirLowerCtx::new(db, owner, body, infer);
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower/pattern_matching.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower/pattern_matching.rs
index 2ffea34c85a..289175feefb 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower/pattern_matching.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower/pattern_matching.rs
@@ -1,6 +1,6 @@
 //! MIR lowering for patterns
 
-use hir_def::{hir::LiteralOrConst, AssocItemId};
+use hir_def::{hir::ExprId, AssocItemId};
 
 use crate::{
     mir::{
@@ -207,7 +207,7 @@ impl MirLowerCtx<'_> {
                 )?
             }
             Pat::Range { start, end } => {
-                let mut add_check = |l: &LiteralOrConst, binop| -> Result<()> {
+                let mut add_check = |l: &ExprId, binop| -> Result<()> {
                     let lv =
                         self.lower_literal_or_const_to_operand(self.infer[pattern].clone(), l)?;
                     let else_target = *current_else.get_or_insert_with(|| self.new_basic_block());
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir/pretty.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/pretty.rs
index 06765a104cb..2a26101ac43 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/mir/pretty.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir/pretty.rs
@@ -6,7 +6,7 @@ use std::{
 };
 
 use either::Either;
-use hir_def::{body::Body, hir::BindingId};
+use hir_def::{expr_store::Body, hir::BindingId};
 use hir_expand::{name::Name, Lookup};
 use la_arena::ArenaMap;
 use span::Edition;
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests.rs
index 00da9b25176..f5a4d4ff35c 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/tests.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests.rs
@@ -18,8 +18,8 @@ use std::sync::LazyLock;
 use base_db::SourceDatabaseFileInputExt as _;
 use expect_test::Expect;
 use hir_def::{
-    body::{Body, BodySourceMap},
     db::DefDatabase,
+    expr_store::{Body, BodySourceMap},
     hir::{ExprId, Pat, PatId},
     item_scope::ItemScope,
     nameres::DefMap,
@@ -117,7 +117,7 @@ fn check_impl(
                     expected.trim_start_matches("adjustments:").trim().to_owned(),
                 );
             } else {
-                panic!("unexpected annotation: {expected}");
+                panic!("unexpected annotation: {expected} @ {range:?}");
             }
             had_annotations = true;
         }
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/coercion.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/coercion.rs
index 7992f1feeeb..7e7c1f835c7 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/coercion.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/coercion.rs
@@ -185,11 +185,10 @@ fn test() {
     let t = &mut 1;
     let x = match 1 {
         1 => t as *mut i32,
+           //^^^^^^^^^^^^^ adjustments: Pointer(MutToConstPointer)
         2 => t as &i32,
            //^^^^^^^^^ expected *mut i32, got &'? i32
         _ => t as *const i32,
-          // ^^^^^^^^^^^^^^^ adjustments: Pointer(MutToConstPointer)
-
     };
     x;
   //^ type: *const i32
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/diagnostics.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/diagnostics.rs
index def06f2d59d..855034117c0 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/diagnostics.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/diagnostics.rs
@@ -153,3 +153,53 @@ fn consume() -> Option<()> {
 "#,
     );
 }
+
+#[test]
+fn method_call_on_field() {
+    check(
+        r#"
+struct S {
+    field: fn(f32) -> u32,
+    field2: u32
+}
+
+fn main() {
+    let s = S { field: |_| 0, field2: 0 };
+    s.field(0);
+         // ^ expected f32, got i32
+ // ^^^^^^^^^^ type: u32
+    s.field2(0);
+          // ^ type: i32
+ // ^^^^^^^^^^^ type: {unknown}
+    s.not_a_field(0);
+               // ^ type: i32
+ // ^^^^^^^^^^^^^^^^ type: {unknown}
+}
+"#,
+    );
+}
+
+#[test]
+fn method_call_on_assoc() {
+    check(
+        r#"
+struct S;
+
+impl S {
+    fn not_a_method() -> f32 { 0.0 }
+    fn not_a_method2(this: Self, param: f32) -> Self { this }
+    fn not_a_method3(param: f32) -> Self { S }
+}
+
+fn main() {
+    S.not_a_method(0);
+ // ^^^^^^^^^^^^^^^^^ type: f32
+    S.not_a_method2(0);
+                 // ^ expected f32, got i32
+ // ^^^^^^^^^^^^^^^^^^ type: S
+    S.not_a_method3(0);
+ // ^^^^^^^^^^^^^^^^^^ type: S
+}
+"#,
+    );
+}
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/method_resolution.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/method_resolution.rs
index 8866de22dfb..3a258ecad10 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/method_resolution.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/method_resolution.rs
@@ -1210,7 +1210,7 @@ impl<T> Slice<T> {
 fn main() {
     let foo: Slice<u32>;
     foo.into_vec(); // we shouldn't crash on this at least
-} //^^^^^^^^^^^^^^ {unknown}
+} //^^^^^^^^^^^^^^ ()
 "#,
     );
 }
@@ -2163,9 +2163,9 @@ impl Receiver for Bar {
 fn main() {
     let bar = Bar;
     let _v1 = bar.foo1();
-      //^^^ type: i32
+      //^^^ type: {unknown}
     let _v2 = bar.foo2();
-      //^^^ type: bool
+      //^^^ type: {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 15636604570..50a1ecd006d 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
@@ -3814,3 +3814,50 @@ async fn foo(a: (), b: i32) -> u32 {
         "#,
     );
 }
+
+#[test]
+fn irrefutable_slices() {
+    check_infer(
+        r#"
+//- minicore: from
+struct A;
+
+impl From<A> for [u8; 2] {
+    fn from(a: A) -> Self {
+        [0; 2]
+    }
+}
+impl From<A> for [u8; 3] {
+    fn from(a: A) -> Self {
+        [0; 3]
+    }
+}
+
+
+fn main() {
+    let a = A;
+    let [b, c] = a.into();
+}
+"#,
+        expect![[r#"
+            50..51 'a': A
+            64..86 '{     ...     }': [u8; 2]
+            74..80 '[0; 2]': [u8; 2]
+            75..76 '0': u8
+            78..79 '2': usize
+            128..129 'a': A
+            142..164 '{     ...     }': [u8; 3]
+            152..158 '[0; 3]': [u8; 3]
+            153..154 '0': u8
+            156..157 '3': usize
+            179..224 '{     ...o(); }': ()
+            189..190 'a': A
+            193..194 'A': A
+            204..210 '[b, c]': [u8; 2]
+            205..206 'b': u8
+            208..209 'c': u8
+            213..214 'a': A
+            213..221 'a.into()': [u8; 2]
+        "#]],
+    );
+}
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/utils.rs b/src/tools/rust-analyzer/crates/hir-ty/src/utils.rs
index bf7892f69bd..c131e97bc4c 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/utils.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/utils.rs
@@ -9,19 +9,22 @@ use chalk_ir::{
     DebruijnIndex,
 };
 use hir_def::{
+    attr::Attrs,
     db::DefDatabase,
     generics::{WherePredicate, WherePredicateTypeTarget},
     lang_item::LangItem,
     resolver::{HasResolver, TypeNs},
+    tt,
     type_ref::{TraitBoundModifier, TypeRef},
     EnumId, EnumVariantId, FunctionId, Lookup, OpaqueInternableThing, TraitId, TypeAliasId,
     TypeOrConstParamId,
 };
 use hir_expand::name::Name;
-use intern::sym;
+use intern::{sym, Symbol};
 use rustc_abi::TargetDataLayout;
 use rustc_hash::FxHashSet;
 use smallvec::{smallvec, SmallVec};
+use span::Edition;
 use stdx::never;
 
 use crate::{
@@ -264,10 +267,65 @@ impl<'a> ClosureSubst<'a> {
     }
 }
 
-pub fn is_fn_unsafe_to_call(db: &dyn HirDatabase, func: FunctionId) -> bool {
+#[derive(Debug, Default)]
+pub struct TargetFeatures {
+    enabled: FxHashSet<Symbol>,
+}
+
+impl TargetFeatures {
+    pub fn from_attrs(attrs: &Attrs) -> Self {
+        let enabled = attrs
+            .by_key(&sym::target_feature)
+            .tt_values()
+            .filter_map(|tt| {
+                match tt.token_trees().flat_tokens() {
+                    [
+                        tt::TokenTree::Leaf(tt::Leaf::Ident(enable_ident)),
+                        tt::TokenTree::Leaf(tt::Leaf::Punct(tt::Punct { char: '=', .. })),
+                        tt::TokenTree::Leaf(tt::Leaf::Literal(tt::Literal { kind: tt::LitKind::Str, symbol: features, .. })),
+                    ] if enable_ident.sym == sym::enable => Some(features),
+                    _ => None,
+                }
+            })
+            .flat_map(|features| features.as_str().split(',').map(Symbol::intern))
+            .collect();
+        Self { enabled }
+    }
+}
+
+#[derive(Debug, Clone, Copy, PartialEq, Eq)]
+pub enum Unsafety {
+    Safe,
+    Unsafe,
+    /// A lint.
+    DeprecatedSafe2024,
+}
+
+pub fn is_fn_unsafe_to_call(
+    db: &dyn HirDatabase,
+    func: FunctionId,
+    caller_target_features: &TargetFeatures,
+    call_edition: Edition,
+) -> Unsafety {
     let data = db.function_data(func);
     if data.is_unsafe() {
-        return true;
+        return Unsafety::Unsafe;
+    }
+
+    if data.has_target_feature() {
+        // RFC 2396 <https://rust-lang.github.io/rfcs/2396-target-feature-1.1.html>.
+        let callee_target_features = TargetFeatures::from_attrs(&db.attrs(func.into()));
+        if !caller_target_features.enabled.is_superset(&callee_target_features.enabled) {
+            return Unsafety::Unsafe;
+        }
+    }
+
+    if data.is_deprecated_safe_2024() {
+        if call_edition.at_least_2024() {
+            return Unsafety::Unsafe;
+        } else {
+            return Unsafety::DeprecatedSafe2024;
+        }
     }
 
     let loc = func.lookup(db.upcast());
@@ -279,14 +337,22 @@ pub fn is_fn_unsafe_to_call(db: &dyn HirDatabase, func: FunctionId) -> bool {
             if is_intrinsic_block {
                 // legacy intrinsics
                 // extern "rust-intrinsic" intrinsics are unsafe unless they have the rustc_safe_intrinsic attribute
-                !db.attrs(func.into()).by_key(&sym::rustc_safe_intrinsic).exists()
+                if db.attrs(func.into()).by_key(&sym::rustc_safe_intrinsic).exists() {
+                    Unsafety::Safe
+                } else {
+                    Unsafety::Unsafe
+                }
             } else {
                 // Function in an `extern` block are always unsafe to call, except when
                 // it is marked as `safe`.
-                !data.is_safe()
+                if data.is_safe() {
+                    Unsafety::Safe
+                } else {
+                    Unsafety::Unsafe
+                }
             }
         }
-        _ => false,
+        _ => Unsafety::Safe,
     }
 }
 
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/variance.rs b/src/tools/rust-analyzer/crates/hir-ty/src/variance.rs
index afd163fbd96..3a22158ce6f 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/variance.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/variance.rs
@@ -1028,6 +1028,7 @@ struct FixedPoint<T, U, V>(&'static FixedPoint<(), T, U>, V);
                         }
                         GenericDefId::ImplId(_) => return None,
                         GenericDefId::ConstId(_) => return None,
+                        GenericDefId::StaticId(_) => return None,
                     },
                 ))
             })
diff --git a/src/tools/rust-analyzer/crates/hir/src/diagnostics.rs b/src/tools/rust-analyzer/crates/hir/src/diagnostics.rs
index fc77d1889c8..1ed0daa3756 100644
--- a/src/tools/rust-analyzer/crates/hir/src/diagnostics.rs
+++ b/src/tools/rust-analyzer/crates/hir/src/diagnostics.rs
@@ -6,10 +6,11 @@
 use cfg::{CfgExpr, CfgOptions};
 use either::Either;
 use hir_def::{
+    expr_store::ExprOrPatPtr,
     hir::ExprOrPatId,
     path::{hir_segment_to_ast_segment, ModPath},
     type_ref::TypesSourceMap,
-    AssocItemId, DefWithBodyId, SyntheticSyntax,
+    DefWithBodyId, SyntheticSyntax,
 };
 use hir_expand::{name::Name, HirFileId, InFile};
 use hir_ty::{
@@ -24,7 +25,7 @@ use syntax::{
 };
 use triomphe::Arc;
 
-use crate::{AssocItem, Field, Local, Trait, Type};
+use crate::{AssocItem, Field, Function, Local, Trait, Type};
 
 pub use hir_def::VariantId;
 pub use hir_ty::{
@@ -111,18 +112,19 @@ diagnostics![
     UnusedMut,
     UnusedVariable,
     GenericArgsProhibited,
+    ParenthesizedGenericArgsWithoutFnTrait,
 ];
 
 #[derive(Debug)]
 pub struct BreakOutsideOfLoop {
-    pub expr: InFile<AstPtr<ast::Expr>>,
+    pub expr: InFile<ExprOrPatPtr>,
     pub is_break: bool,
     pub bad_value_break: bool,
 }
 
 #[derive(Debug)]
 pub struct TypedHole {
-    pub expr: InFile<AstPtr<ast::Expr>>,
+    pub expr: InFile<ExprOrPatPtr>,
     pub expected: Type,
 }
 
@@ -221,26 +223,26 @@ pub struct NoSuchField {
 
 #[derive(Debug)]
 pub struct PrivateAssocItem {
-    pub expr_or_pat: InFile<AstPtr<Either<ast::Expr, ast::Pat>>>,
+    pub expr_or_pat: InFile<ExprOrPatPtr>,
     pub item: AssocItem,
 }
 
 #[derive(Debug)]
 pub struct MismatchedTupleStructPatArgCount {
-    pub expr_or_pat: InFile<AstPtr<Either<ast::Expr, ast::Pat>>>,
+    pub expr_or_pat: InFile<ExprOrPatPtr>,
     pub expected: usize,
     pub found: usize,
 }
 
 #[derive(Debug)]
 pub struct ExpectedFunction {
-    pub call: InFile<AstPtr<ast::Expr>>,
+    pub call: InFile<ExprOrPatPtr>,
     pub found: Type,
 }
 
 #[derive(Debug)]
 pub struct UnresolvedField {
-    pub expr: InFile<AstPtr<ast::Expr>>,
+    pub expr: InFile<ExprOrPatPtr>,
     pub receiver: Type,
     pub name: Name,
     pub method_with_same_name_exists: bool,
@@ -248,34 +250,40 @@ pub struct UnresolvedField {
 
 #[derive(Debug)]
 pub struct UnresolvedMethodCall {
-    pub expr: InFile<AstPtr<ast::Expr>>,
+    pub expr: InFile<ExprOrPatPtr>,
     pub receiver: Type,
     pub name: Name,
     pub field_with_same_name: Option<Type>,
-    pub assoc_func_with_same_name: Option<AssocItemId>,
+    pub assoc_func_with_same_name: Option<Function>,
 }
 
 #[derive(Debug)]
 pub struct UnresolvedAssocItem {
-    pub expr_or_pat: InFile<AstPtr<Either<ast::Expr, ast::Pat>>>,
+    pub expr_or_pat: InFile<ExprOrPatPtr>,
 }
 
 #[derive(Debug)]
 pub struct UnresolvedIdent {
-    pub node: InFile<(AstPtr<Either<ast::Expr, ast::Pat>>, Option<TextRange>)>,
+    pub node: InFile<(ExprOrPatPtr, Option<TextRange>)>,
 }
 
 #[derive(Debug)]
 pub struct PrivateField {
-    pub expr: InFile<AstPtr<ast::Expr>>,
+    pub expr: InFile<ExprOrPatPtr>,
     pub field: Field,
 }
 
+#[derive(Debug, Clone, Copy, PartialEq, Eq)]
+pub enum UnsafeLint {
+    HardError,
+    UnsafeOpInUnsafeFn,
+    DeprecatedSafe2024,
+}
+
 #[derive(Debug)]
 pub struct MissingUnsafe {
-    pub node: InFile<AstPtr<Either<ast::Expr, ast::Pat>>>,
-    /// If true, the diagnostics is an `unsafe_op_in_unsafe_fn` lint instead of a hard error.
-    pub only_lint: bool,
+    pub node: InFile<ExprOrPatPtr>,
+    pub lint: UnsafeLint,
     pub reason: UnsafetyReason,
 }
 
@@ -296,7 +304,7 @@ pub struct ReplaceFilterMapNextWithFindMap {
 
 #[derive(Debug)]
 pub struct MismatchedArgCount {
-    pub call_expr: InFile<AstPtr<ast::Expr>>,
+    pub call_expr: InFile<ExprOrPatPtr>,
     pub expected: usize,
     pub found: usize,
 }
@@ -315,7 +323,7 @@ pub struct NonExhaustiveLet {
 
 #[derive(Debug)]
 pub struct TypeMismatch {
-    pub expr_or_pat: InFile<AstPtr<Either<ast::Expr, ast::Pat>>>,
+    pub expr_or_pat: InFile<ExprOrPatPtr>,
     pub expected: Type,
     pub actual: Type,
 }
@@ -389,13 +397,13 @@ pub struct RemoveUnnecessaryElse {
 
 #[derive(Debug)]
 pub struct CastToUnsized {
-    pub expr: InFile<AstPtr<ast::Expr>>,
+    pub expr: InFile<ExprOrPatPtr>,
     pub cast_ty: Type,
 }
 
 #[derive(Debug)]
 pub struct InvalidCast {
-    pub expr: InFile<AstPtr<ast::Expr>>,
+    pub expr: InFile<ExprOrPatPtr>,
     pub error: CastError,
     pub expr_ty: Type,
     pub cast_ty: Type,
@@ -407,11 +415,16 @@ pub struct GenericArgsProhibited {
     pub reason: GenericArgsProhibitedReason,
 }
 
+#[derive(Debug)]
+pub struct ParenthesizedGenericArgsWithoutFnTrait {
+    pub args: InFile<AstPtr<ast::ParenthesizedArgList>>,
+}
+
 impl AnyDiagnostic {
     pub(crate) fn body_validation_diagnostic(
         db: &dyn HirDatabase,
         diagnostic: BodyValidationDiagnostic,
-        source_map: &hir_def::body::BodySourceMap,
+        source_map: &hir_def::expr_store::BodySourceMap,
     ) -> Option<AnyDiagnostic> {
         match diagnostic {
             BodyValidationDiagnostic::RecordMissingFields { record, variant, missed_fields } => {
@@ -422,9 +435,7 @@ impl AnyDiagnostic {
                     .collect();
 
                 let record = match record {
-                    Either::Left(record_expr) => {
-                        source_map.expr_syntax(record_expr).ok()?.map(AstPtr::wrap_left)
-                    }
+                    Either::Left(record_expr) => source_map.expr_syntax(record_expr).ok()?,
                     Either::Right(record_pat) => source_map.pat_syntax(record_pat).ok()?,
                 };
                 let file = record.file_id;
@@ -468,7 +479,7 @@ impl AnyDiagnostic {
                     return Some(
                         ReplaceFilterMapNextWithFindMap {
                             file: next_source_ptr.file_id,
-                            next_expr: next_source_ptr.value,
+                            next_expr: next_source_ptr.value.cast()?,
                         }
                         .into(),
                     );
@@ -478,7 +489,9 @@ impl AnyDiagnostic {
                 match source_map.expr_syntax(match_expr) {
                     Ok(source_ptr) => {
                         let root = source_ptr.file_syntax(db.upcast());
-                        if let ast::Expr::MatchExpr(match_expr) = &source_ptr.value.to_node(&root) {
+                        if let Either::Left(ast::Expr::MatchExpr(match_expr)) =
+                            &source_ptr.value.to_node(&root)
+                        {
                             match match_expr.expr() {
                                 Some(scrut_expr) if match_expr.match_arm_list().is_some() => {
                                     return Some(
@@ -547,7 +560,7 @@ impl AnyDiagnostic {
         def: DefWithBodyId,
         d: &InferenceDiagnostic,
         outer_types_source_map: &TypesSourceMap,
-        source_map: &hir_def::body::BodySourceMap,
+        source_map: &hir_def::expr_store::BodySourceMap,
     ) -> Option<AnyDiagnostic> {
         let expr_syntax = |expr| {
             source_map.expr_syntax(expr).inspect_err(|_| stdx::never!("synthetic syntax")).ok()
@@ -555,7 +568,7 @@ impl AnyDiagnostic {
         let pat_syntax =
             |pat| source_map.pat_syntax(pat).inspect_err(|_| stdx::never!("synthetic syntax")).ok();
         let expr_or_pat_syntax = |id| match id {
-            ExprOrPatId::ExprId(expr) => expr_syntax(expr).map(|it| it.map(AstPtr::wrap_left)),
+            ExprOrPatId::ExprId(expr) => expr_syntax(expr),
             ExprOrPatId::PatId(pat) => pat_syntax(pat),
         };
         Some(match d {
@@ -616,7 +629,7 @@ impl AnyDiagnostic {
                     field_with_same_name: field_with_same_name
                         .clone()
                         .map(|ty| Type::new(db, def, ty)),
-                    assoc_func_with_same_name: *assoc_func_with_same_name,
+                    assoc_func_with_same_name: assoc_func_with_same_name.map(Into::into),
                 }
                 .into()
             }
@@ -627,7 +640,7 @@ impl AnyDiagnostic {
             &InferenceDiagnostic::UnresolvedIdent { id } => {
                 let node = match id {
                     ExprOrPatId::ExprId(id) => match source_map.expr_syntax(id) {
-                        Ok(syntax) => syntax.map(|it| (it.wrap_left(), None)),
+                        Ok(syntax) => syntax.map(|it| (it, None)),
                         Err(SyntheticSyntax) => source_map
                             .format_args_implicit_capture(id)?
                             .map(|(node, range)| (node.wrap_left(), Some(range))),
@@ -646,7 +659,7 @@ impl AnyDiagnostic {
             }
             &InferenceDiagnostic::MismatchedTupleStructPatArgCount { pat, expected, found } => {
                 let expr_or_pat = match pat {
-                    ExprOrPatId::ExprId(expr) => expr_syntax(expr)?.map(AstPtr::wrap_left),
+                    ExprOrPatId::ExprId(expr) => expr_syntax(expr)?,
                     ExprOrPatId::PatId(pat) => {
                         let InFile { file_id, value } = pat_syntax(pat)?;
 
@@ -696,8 +709,8 @@ impl AnyDiagnostic {
         diag: &PathLoweringDiagnostic,
         path: InFile<ast::Path>,
     ) -> Option<AnyDiagnostic> {
-        Some(match diag {
-            &PathLoweringDiagnostic::GenericArgsProhibited { segment, reason } => {
+        Some(match *diag {
+            PathLoweringDiagnostic::GenericArgsProhibited { segment, reason } => {
                 let segment = hir_segment_to_ast_segment(&path.value, segment)?;
                 let args = if let Some(generics) = segment.generic_arg_list() {
                     AstPtr::new(&generics).wrap_left()
@@ -707,6 +720,12 @@ impl AnyDiagnostic {
                 let args = path.with_value(args);
                 GenericArgsProhibited { args, reason }.into()
             }
+            PathLoweringDiagnostic::ParenthesizedGenericArgsWithoutFnTrait { segment } => {
+                let segment = hir_segment_to_ast_segment(&path.value, segment)?;
+                let args = AstPtr::new(&segment.parenthesized_arg_list()?);
+                let args = path.with_value(args);
+                ParenthesizedGenericArgsWithoutFnTrait { args }.into()
+            }
         })
     }
 
diff --git a/src/tools/rust-analyzer/crates/hir/src/display.rs b/src/tools/rust-analyzer/crates/hir/src/display.rs
index b29c91694d3..6f4168ab086 100644
--- a/src/tools/rust-analyzer/crates/hir/src/display.rs
+++ b/src/tools/rust-analyzer/crates/hir/src/display.rs
@@ -80,7 +80,9 @@ impl HirDisplay for Function {
         if data.is_async() {
             f.write_str("async ")?;
         }
-        if self.is_unsafe_to_call(db) {
+        // FIXME: This will show `unsafe` for functions that are `#[target_feature]` but not unsafe
+        // (they are conditionally unsafe to call). We probably should show something else.
+        if self.is_unsafe_to_call(db, None, f.edition()) {
             f.write_str("unsafe ")?;
         }
         if let Some(abi) = &data.abi {
diff --git a/src/tools/rust-analyzer/crates/hir/src/from_id.rs b/src/tools/rust-analyzer/crates/hir/src/from_id.rs
index 2ad39817b2f..72df07ef8c0 100644
--- a/src/tools/rust-analyzer/crates/hir/src/from_id.rs
+++ b/src/tools/rust-analyzer/crates/hir/src/from_id.rs
@@ -49,6 +49,7 @@ from_id![
     (hir_def::LifetimeParamId, crate::LifetimeParam),
     (hir_def::MacroId, crate::Macro),
     (hir_def::ExternCrateId, crate::ExternCrateDecl),
+    (hir_def::ExternBlockId, crate::ExternBlock),
 ];
 
 impl From<AdtId> for Adt {
@@ -183,6 +184,7 @@ impl From<GenericDef> for GenericDefId {
             GenericDef::TypeAlias(it) => GenericDefId::TypeAliasId(it.id),
             GenericDef::Impl(it) => GenericDefId::ImplId(it.id),
             GenericDef::Const(it) => GenericDefId::ConstId(it.id),
+            GenericDef::Static(it) => GenericDefId::StaticId(it.id),
         }
     }
 }
@@ -197,6 +199,7 @@ impl From<GenericDefId> for GenericDef {
             GenericDefId::TypeAliasId(it) => GenericDef::TypeAlias(it.into()),
             GenericDefId::ImplId(it) => GenericDef::Impl(it.into()),
             GenericDefId::ConstId(it) => GenericDef::Const(it.into()),
+            GenericDefId::StaticId(it) => GenericDef::Static(it.into()),
         }
     }
 }
diff --git a/src/tools/rust-analyzer/crates/hir/src/has_source.rs b/src/tools/rust-analyzer/crates/hir/src/has_source.rs
index 82c90ac3010..a34b4980832 100644
--- a/src/tools/rust-analyzer/crates/hir/src/has_source.rs
+++ b/src/tools/rust-analyzer/crates/hir/src/has_source.rs
@@ -248,7 +248,7 @@ impl HasSource for Param {
                 let ast @ InFile { file_id, value } = source_map.expr_syntax(expr_id).ok()?;
                 let root = db.parse_or_expand(file_id);
                 match value.to_node(&root) {
-                    ast::Expr::ClosureExpr(it) => it
+                    Either::Left(ast::Expr::ClosureExpr(it)) => it
                         .param_list()?
                         .params()
                         .nth(self.idx)
@@ -301,7 +301,7 @@ impl HasSource for InlineAsmOperand {
             let root = src.file_syntax(db.upcast());
             return src
                 .map(|ast| match ast.to_node(&root) {
-                    ast::Expr::AsmExpr(asm) => asm
+                    Either::Left(ast::Expr::AsmExpr(asm)) => asm
                         .asm_pieces()
                         .filter_map(|it| match it {
                             ast::AsmPiece::AsmOperandNamed(it) => Some(it),
diff --git a/src/tools/rust-analyzer/crates/hir/src/lib.rs b/src/tools/rust-analyzer/crates/hir/src/lib.rs
index 0cbc75726bf..5923a1bc30e 100644
--- a/src/tools/rust-analyzer/crates/hir/src/lib.rs
+++ b/src/tools/rust-analyzer/crates/hir/src/lib.rs
@@ -42,8 +42,8 @@ use arrayvec::ArrayVec;
 use base_db::{CrateDisplayName, CrateId, CrateOrigin};
 use either::Either;
 use hir_def::{
-    body::BodyDiagnostic,
     data::{adt::VariantData, TraitFlags},
+    expr_store::ExpressionStoreDiagnostics,
     generics::{LifetimeParamData, TypeOrConstParamData, TypeParamProvenance},
     hir::{BindingAnnotation, BindingId, Expr, ExprId, ExprOrPatId, LabelId, Pat},
     item_tree::{AttrOwner, FieldParent, ItemTreeFieldId, ItemTreeNode},
@@ -55,8 +55,8 @@ use hir_def::{
     resolver::{HasResolver, Resolver},
     type_ref::TypesSourceMap,
     AdtId, AssocItemId, AssocItemLoc, AttrDefId, CallableDefId, ConstId, ConstParamId,
-    CrateRootModuleId, DefWithBodyId, EnumId, EnumVariantId, ExternCrateId, FunctionId,
-    GenericDefId, GenericParamId, HasModule, ImplId, InTypeConstId, ItemContainerId,
+    CrateRootModuleId, DefWithBodyId, EnumId, EnumVariantId, ExternBlockId, ExternCrateId,
+    FunctionId, GenericDefId, GenericParamId, HasModule, ImplId, InTypeConstId, ItemContainerId,
     LifetimeParamId, LocalFieldId, Lookup, MacroExpander, MacroId, ModuleId, StaticId, StructId,
     SyntheticSyntax, TraitAliasId, TupleId, TypeAliasId, TypeOrConstParamId, TypeParamId, UnionId,
 };
@@ -1892,10 +1892,10 @@ impl DefWithBody {
 
         for diag in source_map.diagnostics() {
             acc.push(match diag {
-                BodyDiagnostic::InactiveCode { node, cfg, opts } => {
+                ExpressionStoreDiagnostics::InactiveCode { node, cfg, opts } => {
                     InactiveCode { node: *node, cfg: cfg.clone(), opts: opts.clone() }.into()
                 }
-                BodyDiagnostic::MacroError { node, err } => {
+                ExpressionStoreDiagnostics::MacroError { node, err } => {
                     let RenderedExpandError { message, error, kind } =
                         err.render_to_string(db.upcast());
 
@@ -1919,20 +1919,22 @@ impl DefWithBody {
                     }
                     .into()
                 }
-                BodyDiagnostic::UnresolvedMacroCall { node, path } => UnresolvedMacroCall {
-                    macro_call: (*node).map(|ast_ptr| ast_ptr.into()),
-                    precise_location: None,
-                    path: path.clone(),
-                    is_bang: true,
+                ExpressionStoreDiagnostics::UnresolvedMacroCall { node, path } => {
+                    UnresolvedMacroCall {
+                        macro_call: (*node).map(|ast_ptr| ast_ptr.into()),
+                        precise_location: None,
+                        path: path.clone(),
+                        is_bang: true,
+                    }
+                    .into()
                 }
-                .into(),
-                BodyDiagnostic::AwaitOutsideOfAsync { node, location } => {
+                ExpressionStoreDiagnostics::AwaitOutsideOfAsync { node, location } => {
                     AwaitOutsideOfAsync { node: *node, location: location.clone() }.into()
                 }
-                BodyDiagnostic::UnreachableLabel { node, name } => {
+                ExpressionStoreDiagnostics::UnreachableLabel { node, name } => {
                     UnreachableLabel { node: *node, name: name.clone() }.into()
                 }
-                BodyDiagnostic::UndeclaredLabel { node, name } => {
+                ExpressionStoreDiagnostics::UndeclaredLabel { node, name } => {
                     UndeclaredLabel { node: *node, name: name.clone() }.into()
                 }
             });
@@ -1955,7 +1957,7 @@ impl DefWithBody {
                 ExprOrPatId::PatId(pat) => source_map.pat_syntax(pat).map(Either::Right),
             };
             let expr_or_pat = match expr_or_pat {
-                Ok(Either::Left(expr)) => expr.map(AstPtr::wrap_left),
+                Ok(Either::Left(expr)) => expr,
                 Ok(Either::Right(InFile { file_id, value: pat })) => {
                     // cast from Either<Pat, SelfParam> -> Either<_, Pat>
                     let Some(ptr) = AstPtr::try_from_raw(pat.syntax_node_ptr()) else {
@@ -1976,16 +1978,40 @@ impl DefWithBody {
             );
         }
 
-        let (unsafe_exprs, only_lint) = hir_ty::diagnostics::missing_unsafe(db, self.into());
-        for (node, reason) in unsafe_exprs {
+        let missing_unsafe = hir_ty::diagnostics::missing_unsafe(db, self.into());
+        for (node, reason) in missing_unsafe.unsafe_exprs {
             match source_map.expr_or_pat_syntax(node) {
-                Ok(node) => acc.push(MissingUnsafe { node, only_lint, reason }.into()),
+                Ok(node) => acc.push(
+                    MissingUnsafe {
+                        node,
+                        lint: if missing_unsafe.fn_is_unsafe {
+                            UnsafeLint::UnsafeOpInUnsafeFn
+                        } else {
+                            UnsafeLint::HardError
+                        },
+                        reason,
+                    }
+                    .into(),
+                ),
                 Err(SyntheticSyntax) => {
                     // FIXME: Here and elsewhere in this file, the `expr` was
                     // desugared, report or assert that this doesn't happen.
                 }
             }
         }
+        for node in missing_unsafe.deprecated_safe_calls {
+            match source_map.expr_syntax(node) {
+                Ok(node) => acc.push(
+                    MissingUnsafe {
+                        node,
+                        lint: UnsafeLint::DeprecatedSafe2024,
+                        reason: UnsafetyReason::UnsafeFnCall,
+                    }
+                    .into(),
+                ),
+                Err(SyntheticSyntax) => never!("synthetic DeprecatedSafe2024"),
+            }
+        }
 
         if let Ok(borrowck_results) = db.borrowck(self.into()) {
             for borrowck_result in borrowck_results.iter() {
@@ -2301,6 +2327,13 @@ impl Function {
         db.function_data(self.id).is_async()
     }
 
+    pub fn extern_block(self, db: &dyn HirDatabase) -> Option<ExternBlock> {
+        match self.id.lookup(db.upcast()).container {
+            ItemContainerId::ExternBlockId(id) => Some(ExternBlock { id }),
+            _ => None,
+        }
+    }
+
     pub fn returns_impl_future(self, db: &dyn HirDatabase) -> bool {
         if self.is_async(db) {
             return true;
@@ -2361,8 +2394,19 @@ impl Function {
         db.attrs(self.id.into()).is_unstable()
     }
 
-    pub fn is_unsafe_to_call(self, db: &dyn HirDatabase) -> bool {
-        hir_ty::is_fn_unsafe_to_call(db, self.id)
+    pub fn is_unsafe_to_call(
+        self,
+        db: &dyn HirDatabase,
+        caller: Option<Function>,
+        call_edition: Edition,
+    ) -> bool {
+        let target_features = caller
+            .map(|caller| hir_ty::TargetFeatures::from_attrs(&db.attrs(caller.id.into())))
+            .unwrap_or_default();
+        matches!(
+            hir_ty::is_fn_unsafe_to_call(db, self.id, &target_features, call_edition),
+            hir_ty::Unsafety::Unsafe
+        )
     }
 
     /// Whether this function declaration has a definition.
@@ -2724,6 +2768,13 @@ impl Static {
         Type::from_value_def(db, self.id)
     }
 
+    pub fn extern_block(self, db: &dyn HirDatabase) -> Option<ExternBlock> {
+        match self.id.lookup(db.upcast()).container {
+            ItemContainerId::ExternBlockId(id) => Some(ExternBlock { id }),
+            _ => None,
+        }
+    }
+
     /// Evaluate the static initializer.
     pub fn eval(self, db: &dyn HirDatabase) -> Result<EvaluatedConst, ConstEvalError> {
         db.const_eval(self.id.into(), Substitution::empty(Interner), None)
@@ -2892,6 +2943,17 @@ impl HasVisibility for TypeAlias {
 }
 
 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
+pub struct ExternBlock {
+    pub(crate) id: ExternBlockId,
+}
+
+impl ExternBlock {
+    pub fn module(self, db: &dyn HirDatabase) -> Module {
+        Module { id: self.id.module(db.upcast()) }
+    }
+}
+
+#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
 pub struct StaticLifetime;
 
 impl StaticLifetime {
@@ -3453,6 +3515,7 @@ pub enum GenericDef {
     Impl(Impl),
     // consts can have type parameters from their parents (i.e. associated consts of traits)
     Const(Const),
+    Static(Static),
 }
 impl_from!(
     Function,
@@ -3461,7 +3524,8 @@ impl_from!(
     TraitAlias,
     TypeAlias,
     Impl,
-    Const
+    Const,
+    Static
     for GenericDef
 );
 
@@ -3511,6 +3575,7 @@ impl GenericDef {
             GenericDef::TypeAlias(it) => it.id.into(),
             GenericDef::Impl(it) => it.id.into(),
             GenericDef::Const(it) => it.id.into(),
+            GenericDef::Static(it) => it.id.into(),
         }
     }
 
@@ -3568,6 +3633,7 @@ impl GenericDef {
                     item_tree_source_maps.impl_(id.value).generics()
                 }
                 GenericDefId::ConstId(_) => return,
+                GenericDefId::StaticId(_) => return,
             },
         };
 
@@ -4551,10 +4617,7 @@ impl CaptureUsages {
             match span {
                 mir::MirSpan::ExprId(expr) => {
                     if let Ok(expr) = source_map.expr_syntax(expr) {
-                        result.push(CaptureUsageSource {
-                            is_ref,
-                            source: expr.map(AstPtr::wrap_left),
-                        })
+                        result.push(CaptureUsageSource { is_ref, source: expr })
                     }
                 }
                 mir::MirSpan::PatId(pat) => {
@@ -4624,17 +4687,6 @@ impl Type {
         Type { env: TraitEnvironment::empty(krate), ty }
     }
 
-    pub fn reference(inner: &Type, m: Mutability) -> Type {
-        inner.derived(
-            TyKind::Ref(
-                if m.is_mut() { hir_ty::Mutability::Mut } else { hir_ty::Mutability::Not },
-                hir_ty::error_lifetime(),
-                inner.ty.clone(),
-            )
-            .intern(Interner),
-        )
-    }
-
     fn new(db: &dyn HirDatabase, lexical_env: impl HasResolver, ty: Ty) -> Type {
         let resolver = lexical_env.resolver(db.upcast());
         let environment = resolver
@@ -4866,6 +4918,17 @@ impl Type {
         self.normalize_trait_assoc_type(db, &[], iterator_item.into())
     }
 
+    pub fn impls_iterator(self, db: &dyn HirDatabase) -> bool {
+        let Some(iterator_trait) =
+            db.lang_item(self.env.krate, LangItem::Iterator).and_then(|it| it.as_trait())
+        else {
+            return false;
+        };
+        let canonical_ty =
+            Canonical { value: self.ty.clone(), binders: CanonicalVarKinds::empty(Interner) };
+        method_resolution::implements_trait_unique(&canonical_ty, db, &self.env, iterator_trait)
+    }
+
     /// Resolves the projection `<Self as IntoIterator>::IntoIter` and returns the resulting type
     pub fn into_iterator_iter(self, db: &dyn HirDatabase) -> Option<Type> {
         let trait_ = db.lang_item(self.env.krate, LangItem::IntoIterIntoIter).and_then(|it| {
@@ -6139,9 +6202,15 @@ impl HasContainer for TraitAlias {
     }
 }
 
+impl HasContainer for ExternBlock {
+    fn container(&self, db: &dyn HirDatabase) -> ItemContainer {
+        ItemContainer::Module(Module { id: self.id.lookup(db.upcast()).container })
+    }
+}
+
 fn container_id_to_hir(c: ItemContainerId) -> ItemContainer {
     match c {
-        ItemContainerId::ExternBlockId(_id) => ItemContainer::ExternBlock(),
+        ItemContainerId::ExternBlockId(id) => ItemContainer::ExternBlock(ExternBlock { id }),
         ItemContainerId::ModuleId(id) => ItemContainer::Module(Module { id }),
         ItemContainerId::ImplId(id) => ItemContainer::Impl(Impl { id }),
         ItemContainerId::TraitId(id) => ItemContainer::Trait(Trait { id }),
@@ -6153,7 +6222,7 @@ pub enum ItemContainer {
     Trait(Trait),
     Impl(Impl),
     Module(Module),
-    ExternBlock(),
+    ExternBlock(ExternBlock),
     Crate(CrateId),
 }
 
diff --git a/src/tools/rust-analyzer/crates/hir/src/semantics.rs b/src/tools/rust-analyzer/crates/hir/src/semantics.rs
index 09470bed9cf..c9145f7d212 100644
--- a/src/tools/rust-analyzer/crates/hir/src/semantics.rs
+++ b/src/tools/rust-analyzer/crates/hir/src/semantics.rs
@@ -1998,6 +1998,7 @@ to_def_impls![
     (crate::Adt, ast::Adt, adt_to_def),
     (crate::ExternCrateDecl, ast::ExternCrate, extern_crate_to_def),
     (crate::InlineAsmOperand, ast::AsmOperandNamed, asm_operand_to_def),
+    (crate::ExternBlock, ast::ExternBlock, extern_block_to_def),
     (MacroCallId, ast::MacroCall, macro_call_to_macro_call),
 ];
 
@@ -2040,6 +2041,13 @@ impl SemanticsScope<'_> {
         Crate { id: self.resolver.krate() }
     }
 
+    pub fn containing_function(&self) -> Option<Function> {
+        self.resolver.body_owner().and_then(|owner| match owner {
+            DefWithBodyId::FunctionId(id) => Some(id.into()),
+            _ => None,
+        })
+    }
+
     pub(crate) fn resolver(&self) -> &Resolver {
         &self.resolver
     }
diff --git a/src/tools/rust-analyzer/crates/hir/src/semantics/child_by_source.rs b/src/tools/rust-analyzer/crates/hir/src/semantics/child_by_source.rs
index d5dfb985718..d0fdf5cbdf7 100644
--- a/src/tools/rust-analyzer/crates/hir/src/semantics/child_by_source.rs
+++ b/src/tools/rust-analyzer/crates/hir/src/semantics/child_by_source.rs
@@ -74,6 +74,9 @@ impl ChildBySource for ItemScope {
     fn child_by_source_to(&self, db: &dyn DefDatabase, res: &mut DynMap, file_id: HirFileId) {
         self.declarations().for_each(|item| add_module_def(db, res, file_id, item));
         self.impls().for_each(|imp| insert_item_loc(db, res, file_id, imp, keys::IMPL));
+        self.extern_blocks().for_each(|extern_block| {
+            insert_item_loc(db, res, file_id, extern_block, keys::EXTERN_BLOCK)
+        });
         self.extern_crate_decls()
             .for_each(|ext| insert_item_loc(db, res, file_id, ext, keys::EXTERN_CRATE));
         self.use_decls().for_each(|ext| insert_item_loc(db, res, file_id, ext, keys::USE));
diff --git a/src/tools/rust-analyzer/crates/hir/src/semantics/source_to_def.rs b/src/tools/rust-analyzer/crates/hir/src/semantics/source_to_def.rs
index 3c9e7065c41..4481b8855fd 100644
--- a/src/tools/rust-analyzer/crates/hir/src/semantics/source_to_def.rs
+++ b/src/tools/rust-analyzer/crates/hir/src/semantics/source_to_def.rs
@@ -92,10 +92,10 @@ use hir_def::{
         DynMap,
     },
     hir::{BindingId, Expr, LabelId},
-    AdtId, BlockId, ConstId, ConstParamId, DefWithBodyId, EnumId, EnumVariantId, ExternCrateId,
-    FieldId, FunctionId, GenericDefId, GenericParamId, ImplId, LifetimeParamId, Lookup, MacroId,
-    ModuleId, StaticId, StructId, TraitAliasId, TraitId, TypeAliasId, TypeParamId, UnionId, UseId,
-    VariantId,
+    AdtId, BlockId, ConstId, ConstParamId, DefWithBodyId, EnumId, EnumVariantId, ExternBlockId,
+    ExternCrateId, FieldId, FunctionId, GenericDefId, GenericParamId, ImplId, LifetimeParamId,
+    Lookup, MacroId, ModuleId, StaticId, StructId, TraitAliasId, TraitId, TypeAliasId, TypeParamId,
+    UnionId, UseId, VariantId,
 };
 use hir_expand::{
     attrs::AttrId, name::AsName, ExpansionInfo, HirFileId, HirFileIdExt, InMacroFile, MacroCallId,
@@ -308,6 +308,12 @@ impl SourceToDefCtx<'_, '_> {
     ) -> Option<ExternCrateId> {
         self.to_def(src, keys::EXTERN_CRATE)
     }
+    pub(super) fn extern_block_to_def(
+        &mut self,
+        src: InFile<&ast::ExternBlock>,
+    ) -> Option<ExternBlockId> {
+        self.to_def(src, keys::EXTERN_BLOCK)
+    }
     #[allow(dead_code)]
     pub(super) fn use_to_def(&mut self, src: InFile<&ast::Use>) -> Option<UseId> {
         self.to_def(src, keys::USE)
@@ -352,7 +358,7 @@ impl SourceToDefCtx<'_, '_> {
         let src = src.cloned().map(ast::Pat::from);
         let pat_id = source_map.node_pat(src.as_ref())?;
         // the pattern could resolve to a constant, verify that this is not the case
-        if let crate::Pat::Bind { id, .. } = body[pat_id] {
+        if let crate::Pat::Bind { id, .. } = body[pat_id.as_pat()?] {
             Some((container, id))
         } else {
             None
diff --git a/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs b/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs
index b699ccde412..9019863f7fd 100644
--- a/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs
+++ b/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs
@@ -14,11 +14,11 @@ use crate::{
 };
 use either::Either;
 use hir_def::{
-    body::{
+    expr_store::{
         scope::{ExprScopes, ScopeId},
         Body, BodySourceMap, HygieneId,
     },
-    hir::{BindingId, Expr, ExprId, ExprOrPatId, Pat, PatId},
+    hir::{BindingId, Expr, ExprId, ExprOrPatId, Pat},
     lang_item::LangItem,
     lower::LowerCtx,
     nameres::MacroSubNs,
@@ -139,7 +139,7 @@ impl SourceAnalyzer {
         sm.node_expr(src.as_ref())
     }
 
-    fn pat_id(&self, pat: &ast::Pat) -> Option<PatId> {
+    fn pat_id(&self, pat: &ast::Pat) -> Option<ExprOrPatId> {
         // FIXME: macros, see `expr_id`
         let src = InFile { file_id: self.file_id, value: pat };
         self.body_source_map()?.node_pat(src)
@@ -147,7 +147,7 @@ impl SourceAnalyzer {
 
     fn binding_id_of_pat(&self, pat: &ast::IdentPat) -> Option<BindingId> {
         let pat_id = self.pat_id(&pat.clone().into())?;
-        if let Pat::Bind { id, .. } = self.body()?.pats[pat_id] {
+        if let Pat::Bind { id, .. } = self.body()?.pats[pat_id.as_pat()?] {
             Some(id)
         } else {
             None
@@ -210,11 +210,20 @@ impl SourceAnalyzer {
         db: &dyn HirDatabase,
         pat: &ast::Pat,
     ) -> Option<(Type, Option<Type>)> {
-        let pat_id = self.pat_id(pat)?;
+        let expr_or_pat_id = self.pat_id(pat)?;
         let infer = self.infer.as_ref()?;
-        let coerced =
-            infer.pat_adjustments.get(&pat_id).and_then(|adjusts| adjusts.last().cloned());
-        let ty = infer[pat_id].clone();
+        let coerced = match expr_or_pat_id {
+            ExprOrPatId::ExprId(idx) => infer
+                .expr_adjustments
+                .get(&idx)
+                .and_then(|adjusts| adjusts.last().cloned())
+                .map(|adjust| adjust.target),
+            ExprOrPatId::PatId(idx) => {
+                infer.pat_adjustments.get(&idx).and_then(|adjusts| adjusts.last().cloned())
+            }
+        };
+
+        let ty = infer[expr_or_pat_id].clone();
         let mk_ty = |ty| Type::new_with_resolver(db, &self.resolver, ty);
         Some((mk_ty(ty), coerced.map(mk_ty)))
     }
@@ -248,7 +257,7 @@ impl SourceAnalyzer {
     ) -> Option<BindingMode> {
         let id = self.pat_id(&pat.clone().into())?;
         let infer = self.infer.as_ref()?;
-        infer.binding_modes.get(id).map(|bm| match bm {
+        infer.binding_modes.get(id.as_pat()?).map(|bm| match bm {
             hir_ty::BindingMode::Move => BindingMode::Move,
             hir_ty::BindingMode::Ref(hir_ty::Mutability::Mut) => BindingMode::Ref(Mutability::Mut),
             hir_ty::BindingMode::Ref(hir_ty::Mutability::Not) => {
@@ -266,7 +275,7 @@ impl SourceAnalyzer {
         Some(
             infer
                 .pat_adjustments
-                .get(&pat_id)?
+                .get(&pat_id.as_pat()?)?
                 .iter()
                 .map(|ty| Type::new_with_resolver(db, &self.resolver, ty.clone()))
                 .collect(),
@@ -649,10 +658,10 @@ impl SourceAnalyzer {
         let field_name = field.field_name()?.as_name();
         let record_pat = ast::RecordPat::cast(field.syntax().parent().and_then(|p| p.parent())?)?;
         let pat_id = self.pat_id(&record_pat.into())?;
-        let variant = self.infer.as_ref()?.variant_resolution_for_pat(pat_id)?;
+        let variant = self.infer.as_ref()?.variant_resolution_for_pat(pat_id.as_pat()?)?;
         let variant_data = variant.variant_data(db.upcast());
         let field = FieldId { parent: variant, local_id: variant_data.field(&field_name)? };
-        let (adt, subst) = self.infer.as_ref()?.type_of_pat.get(pat_id)?.as_adt()?;
+        let (adt, subst) = self.infer.as_ref()?.type_of_pat.get(pat_id.as_pat()?)?.as_adt()?;
         let field_ty =
             db.field_types(variant).get(field.local_id)?.clone().substitute(Interner, subst);
         Some((
@@ -682,12 +691,20 @@ impl SourceAnalyzer {
         db: &dyn HirDatabase,
         pat: &ast::IdentPat,
     ) -> Option<ModuleDef> {
-        let pat_id = self.pat_id(&pat.clone().into())?;
+        let expr_or_pat_id = self.pat_id(&pat.clone().into())?;
         let body = self.body()?;
-        let path = match &body[pat_id] {
-            Pat::Path(path) => path,
-            _ => return None,
+
+        let path = match expr_or_pat_id {
+            ExprOrPatId::ExprId(idx) => match &body[idx] {
+                Expr::Path(path) => path,
+                _ => return None,
+            },
+            ExprOrPatId::PatId(idx) => match &body[idx] {
+                Pat::Path(path) => path,
+                _ => return None,
+            },
         };
+
         let res = resolve_hir_path(db, &self.resolver, path, HygieneId::ROOT, TypesMap::EMPTY)?;
         match res {
             PathResolution::Def(def) => Some(def),
@@ -782,8 +799,9 @@ impl SourceAnalyzer {
                 }
                 prefer_value_ns = true;
             } else if let Some(path_pat) = parent().and_then(ast::PathPat::cast) {
-                let pat_id = self.pat_id(&path_pat.into())?;
-                if let Some((assoc, subs)) = infer.assoc_resolutions_for_pat(pat_id) {
+                let expr_or_pat_id = self.pat_id(&path_pat.into())?;
+                if let Some((assoc, subs)) = infer.assoc_resolutions_for_expr_or_pat(expr_or_pat_id)
+                {
                     let (assoc, subst) = match assoc {
                         AssocItemId::ConstId(const_id) => {
                             let (konst, subst) =
@@ -807,7 +825,7 @@ impl SourceAnalyzer {
                     return Some((PathResolution::Def(AssocItem::from(assoc).into()), Some(subst)));
                 }
                 if let Some(VariantId::EnumVariantId(variant)) =
-                    infer.variant_resolution_for_pat(pat_id)
+                    infer.variant_resolution_for_expr_or_pat(expr_or_pat_id)
                 {
                     return Some((PathResolution::Def(ModuleDef::Variant(variant.into())), None));
                 }
@@ -824,7 +842,7 @@ impl SourceAnalyzer {
                     || parent().and_then(ast::TupleStructPat::cast).map(ast::Pat::from);
                 if let Some(pat) = record_pat.or_else(tuple_struct_pat) {
                     let pat_id = self.pat_id(&pat)?;
-                    let variant_res_for_pat = infer.variant_resolution_for_pat(pat_id);
+                    let variant_res_for_pat = infer.variant_resolution_for_pat(pat_id.as_pat()?);
                     if let Some(VariantId::EnumVariantId(variant)) = variant_res_for_pat {
                         return Some((
                             PathResolution::Def(ModuleDef::Variant(variant.into())),
@@ -866,7 +884,8 @@ impl SourceAnalyzer {
 
         // Case where path is a qualifier of another path, e.g. foo::bar::Baz where we are
         // trying to resolve foo::bar.
-        if path.parent_path().is_some() {
+        if let Some(parent_path) = path.parent_path() {
+            let parent_hir_path = Path::from_src(&mut ctx, parent_path);
             return match resolve_hir_path_qualifier(db, &self.resolver, &hir_path, &types_map) {
                 None if meta_path.is_some() => path
                     .first_segment()
@@ -876,6 +895,42 @@ impl SourceAnalyzer {
                             .map(PathResolution::ToolModule)
                     })
                     .map(|it| (it, None)),
+                // Case the type name conflict with use module,
+                // e.g.
+                // ```
+                // use std::str;
+                // fn main() {
+                //     str::from_utf8();  // as module std::str
+                //     str::len();        // as primitive type str
+                //     str::no_exist_item(); // as primitive type str
+                // }
+                // ```
+                Some(it) if matches!(it, PathResolution::Def(ModuleDef::BuiltinType(_))) => {
+                    if let (Some(mod_path), Some(parent_hir_path)) =
+                        (hir_path.mod_path(), parent_hir_path)
+                    {
+                        if let Some(ModuleDefId::ModuleId(id)) = self
+                            .resolver
+                            .resolve_module_path_in_items(db.upcast(), mod_path)
+                            .take_types()
+                        {
+                            let parent_hir_name =
+                                parent_hir_path.segments().get(1).map(|it| it.name);
+                            let module = crate::Module { id };
+                            if module
+                                .scope(db, None)
+                                .into_iter()
+                                .any(|(name, _)| Some(&name) == parent_hir_name)
+                            {
+                                return Some((
+                                    PathResolution::Def(ModuleDef::Module(module)),
+                                    None,
+                                ));
+                            };
+                        }
+                    }
+                    Some((it, None))
+                }
                 // FIXME: We do not show substitutions for parts of path, because this is really complex
                 // due to the interactions with associated items of `impl`s and associated items of associated
                 // types.
@@ -1043,7 +1098,7 @@ impl SourceAnalyzer {
         let body = self.body()?;
         let infer = self.infer.as_ref()?;
 
-        let pat_id = self.pat_id(&pattern.clone().into())?;
+        let pat_id = self.pat_id(&pattern.clone().into())?.as_pat()?;
         let substs = infer.type_of_pat[pat_id].as_adt()?.1;
 
         let (variant, missing_fields, _exhaustive) =
@@ -1105,16 +1160,9 @@ impl SourceAnalyzer {
             if let Some(expanded_expr) = sm.macro_expansion_expr(macro_expr) {
                 let mut is_unsafe = false;
                 let mut walk_expr = |expr_id| {
-                    unsafe_expressions(
-                        db,
-                        infer,
-                        *def,
-                        body,
-                        expr_id,
-                        &mut |_, inside_unsafe_block, _| {
-                            is_unsafe |= inside_unsafe_block == InsideUnsafeBlock::No
-                        },
-                    )
+                    unsafe_expressions(db, infer, *def, body, expr_id, &mut |inside_unsafe_block| {
+                        is_unsafe |= inside_unsafe_block == InsideUnsafeBlock::No
+                    })
                 };
                 match expanded_expr {
                     ExprOrPatId::ExprId(expanded_expr) => walk_expr(expanded_expr),
@@ -1259,7 +1307,11 @@ fn scope_for(
     node: InFile<&SyntaxNode>,
 ) -> Option<ScopeId> {
     node.ancestors_with_macros(db.upcast())
-        .take_while(|it| !ast::Item::can_cast(it.kind()) || ast::MacroCall::can_cast(it.kind()))
+        .take_while(|it| {
+            !ast::Item::can_cast(it.kind())
+                || ast::MacroCall::can_cast(it.kind())
+                || ast::Use::can_cast(it.kind())
+        })
         .filter_map(|it| it.map(ast::Expr::cast).transpose())
         .filter_map(|it| source_map.node_expr(it.as_ref())?.as_expr())
         .find_map(|it| scopes.scope_for(it))
diff --git a/src/tools/rust-analyzer/crates/hir/src/term_search.rs b/src/tools/rust-analyzer/crates/hir/src/term_search.rs
index 6f845137084..af72179305c 100644
--- a/src/tools/rust-analyzer/crates/hir/src/term_search.rs
+++ b/src/tools/rust-analyzer/crates/hir/src/term_search.rs
@@ -145,7 +145,7 @@ impl LookupTable {
                 self.data
                     .iter()
                     .find(|(t, _)| {
-                        Type::reference(t, Mutability::Shared).could_unify_with_deeply(db, ty)
+                        t.add_reference(Mutability::Shared).could_unify_with_deeply(db, ty)
                     })
                     .map(|(t, it)| {
                         it.exprs(t)
diff --git a/src/tools/rust-analyzer/crates/hir/src/term_search/tactics.rs b/src/tools/rust-analyzer/crates/hir/src/term_search/tactics.rs
index 1b0e6f8bd5b..847304d503a 100644
--- a/src/tools/rust-analyzer/crates/hir/src/term_search/tactics.rs
+++ b/src/tools/rust-analyzer/crates/hir/src/term_search/tactics.rs
@@ -15,6 +15,7 @@ use hir_ty::mir::BorrowKind;
 use hir_ty::TyBuilder;
 use itertools::Itertools;
 use rustc_hash::FxHashSet;
+use span::Edition;
 
 use crate::{
     Adt, AssocItem, GenericDef, GenericParam, HasAttrs, HasVisibility, Impl, ModuleDef, ScopeDef,
@@ -365,7 +366,7 @@ pub(super) fn free_function<'a, DB: HirDatabase>(
                         let ret_ty = it.ret_type_with_args(db, generics.iter().cloned());
                         // Filter out private and unsafe functions
                         if !it.is_visible_from(db, module)
-                            || it.is_unsafe_to_call(db)
+                            || it.is_unsafe_to_call(db, None, Edition::CURRENT_FIXME)
                             || it.is_unstable(db)
                             || ctx.config.enable_borrowcheck && ret_ty.contains_reference(db)
                             || ret_ty.is_raw_ptr()
@@ -470,7 +471,10 @@ pub(super) fn impl_method<'a, DB: HirDatabase>(
             }
 
             // Filter out private and unsafe functions
-            if !it.is_visible_from(db, module) || it.is_unsafe_to_call(db) || it.is_unstable(db) {
+            if !it.is_visible_from(db, module)
+                || it.is_unsafe_to_call(db, None, Edition::CURRENT_FIXME)
+                || it.is_unstable(db)
+            {
                 return None;
             }
 
@@ -658,7 +662,10 @@ pub(super) fn impl_static_method<'a, DB: HirDatabase>(
             }
 
             // Filter out private and unsafe functions
-            if !it.is_visible_from(db, module) || it.is_unsafe_to_call(db) || it.is_unstable(db) {
+            if !it.is_visible_from(db, module)
+                || it.is_unsafe_to_call(db, None, Edition::CURRENT_FIXME)
+                || it.is_unstable(db)
+            {
                 return None;
             }
 
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/assist_config.rs b/src/tools/rust-analyzer/crates/ide-assists/src/assist_config.rs
index fb533077d96..05105c8c92c 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/assist_config.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/assist_config.rs
@@ -20,6 +20,7 @@ pub struct AssistConfig {
     pub assist_emit_must_use: bool,
     pub term_search_fuel: u64,
     pub term_search_borrowck: bool,
+    pub code_action_grouping: bool,
 }
 
 impl AssistConfig {
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_missing_match_arms.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_missing_match_arms.rs
index 5899ec5a005..4a9e2256e9b 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_missing_match_arms.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_missing_match_arms.rs
@@ -6,7 +6,9 @@ use ide_db::syntax_helpers::suggest_name;
 use ide_db::RootDatabase;
 use ide_db::{famous_defs::FamousDefs, helpers::mod_path_to_ast};
 use itertools::Itertools;
-use syntax::ast::edit_in_place::Removable;
+use syntax::ast::edit::IndentLevel;
+use syntax::ast::edit_in_place::Indent;
+use syntax::ast::syntax_factory::SyntaxFactory;
 use syntax::ast::{self, make, AstNode, MatchArmList, MatchExpr, Pat};
 
 use crate::{utils, AssistContext, AssistId, AssistKind, Assists};
@@ -200,8 +202,8 @@ pub(crate) fn add_missing_match_arms(acc: &mut Assists, ctx: &AssistContext<'_>)
         AssistId("add_missing_match_arms", AssistKind::QuickFix),
         "Fill match arms",
         ctx.sema.original_range(match_expr.syntax()).range,
-        |edit| {
-            let new_match_arm_list = match_arm_list.clone_for_update();
+        |builder| {
+            let make = SyntaxFactory::new();
 
             // having any hidden variants means that we need a catch-all arm
             needs_catch_all_arm |= has_hidden_variants;
@@ -211,89 +213,85 @@ pub(crate) fn add_missing_match_arms(acc: &mut Assists, ctx: &AssistContext<'_>)
                     // filter out hidden patterns because they're handled by the catch-all arm
                     !hidden
                 })
-                .map(|(pat, _)| {
-                    make::match_arm(pat, None, make::ext::expr_todo()).clone_for_update()
-                });
+                .map(|(pat, _)| make.match_arm(pat, None, make::ext::expr_todo()));
 
-            let catch_all_arm = new_match_arm_list
+            let mut arms: Vec<_> = match_arm_list
                 .arms()
-                .find(|arm| matches!(arm.pat(), Some(ast::Pat::WildcardPat(_))));
-            if let Some(arm) = catch_all_arm {
-                let is_empty_expr = arm.expr().is_none_or(|e| match e {
-                    ast::Expr::BlockExpr(b) => {
-                        b.statements().next().is_none() && b.tail_expr().is_none()
+                .filter(|arm| {
+                    if matches!(arm.pat(), Some(ast::Pat::WildcardPat(_))) {
+                        let is_empty_expr = arm.expr().is_none_or(|e| match e {
+                            ast::Expr::BlockExpr(b) => {
+                                b.statements().next().is_none() && b.tail_expr().is_none()
+                            }
+                            ast::Expr::TupleExpr(t) => t.fields().next().is_none(),
+                            _ => false,
+                        });
+                        if is_empty_expr {
+                            false
+                        } else {
+                            cov_mark::hit!(add_missing_match_arms_empty_expr);
+                            true
+                        }
+                    } else {
+                        true
                     }
-                    ast::Expr::TupleExpr(t) => t.fields().next().is_none(),
-                    _ => false,
-                });
-                if is_empty_expr {
-                    arm.remove();
-                } else {
-                    cov_mark::hit!(add_missing_match_arms_empty_expr);
-                }
-            }
+                })
+                .collect();
 
-            let mut added_arms = Vec::new();
-            let mut todo_placeholders = Vec::new();
-            for arm in missing_arms {
-                todo_placeholders.push(arm.expr().unwrap());
-                added_arms.push(arm);
-            }
+            let first_new_arm_idx = arms.len();
+            arms.extend(missing_arms);
 
             if needs_catch_all_arm && !has_catch_all_arm {
                 cov_mark::hit!(added_wildcard_pattern);
-                let arm =
-                    make::match_arm(make::wildcard_pat().into(), None, make::ext::expr_todo())
-                        .clone_for_update();
-                todo_placeholders.push(arm.expr().unwrap());
-                added_arms.push(arm);
-            }
-
-            let first_new_arm = added_arms.first().cloned();
-            let last_new_arm = added_arms.last().cloned();
-
-            for arm in added_arms {
-                new_match_arm_list.add_arm(arm);
+                let arm = make.match_arm(make::wildcard_pat().into(), None, make::ext::expr_todo());
+                arms.push(arm);
             }
 
-            if let Some(cap) = ctx.config.snippet_cap {
-                if let Some(it) = first_new_arm
-                    .and_then(|arm| arm.syntax().descendants().find_map(ast::WildcardPat::cast))
-                {
-                    edit.add_placeholder_snippet(cap, it);
-                }
-
-                for placeholder in todo_placeholders {
-                    edit.add_placeholder_snippet(cap, placeholder);
-                }
-
-                if let Some(arm) = last_new_arm {
-                    edit.add_tabstop_after(cap, arm);
-                }
-            }
+            let new_match_arm_list = make.match_arm_list(arms);
 
-            // FIXME: Hack for mutable syntax trees not having great support for macros
+            // FIXME: Hack for syntax trees not having great support for macros
             // Just replace the element that the original range came from
             let old_place = {
                 // Find the original element
                 let file = ctx.sema.parse(arm_list_range.file_id);
                 let old_place = file.syntax().covering_element(arm_list_range.range);
 
-                // Make `old_place` mut
                 match old_place {
-                    syntax::SyntaxElement::Node(it) => {
-                        syntax::SyntaxElement::from(edit.make_syntax_mut(it))
-                    }
+                    syntax::SyntaxElement::Node(it) => it,
                     syntax::SyntaxElement::Token(it) => {
                         // If a token is found, it is '{' or '}'
                         // The parent is `{ ... }`
-                        let parent = it.parent().expect("Token must have a parent.");
-                        syntax::SyntaxElement::from(edit.make_syntax_mut(parent))
+                        it.parent().expect("Token must have a parent.")
                     }
                 }
             };
 
-            syntax::ted::replace(old_place, new_match_arm_list.syntax());
+            let mut editor = builder.make_editor(&old_place);
+            new_match_arm_list.indent(IndentLevel::from_node(&old_place));
+            editor.replace(old_place, new_match_arm_list.syntax());
+
+            if let Some(cap) = ctx.config.snippet_cap {
+                if let Some(it) = new_match_arm_list
+                    .arms()
+                    .nth(first_new_arm_idx)
+                    .and_then(|arm| arm.syntax().descendants().find_map(ast::WildcardPat::cast))
+                {
+                    editor.add_annotation(it.syntax(), builder.make_placeholder_snippet(cap));
+                }
+
+                for arm in new_match_arm_list.arms().skip(first_new_arm_idx) {
+                    if let Some(expr) = arm.expr() {
+                        editor.add_annotation(expr.syntax(), builder.make_placeholder_snippet(cap));
+                    }
+                }
+
+                if let Some(arm) = new_match_arm_list.arms().skip(first_new_arm_idx).last() {
+                    editor.add_annotation(arm.syntax(), builder.make_tabstop_after(cap));
+                }
+            }
+
+            editor.add_mappings(make.finish_with_mappings());
+            builder.add_file_edits(ctx.file_id(), editor);
         },
     )
 }
@@ -1377,6 +1375,9 @@ fn main() {
         );
     }
 
+    // FIXME: Preserving comments is quite hard in the current transitional syntax editing model.
+    // Once we migrate to new trivia model addressed in #6854, remove the ignore attribute.
+    #[ignore]
     #[test]
     fn add_missing_match_arms_preserves_comments() {
         check_assist(
@@ -1405,6 +1406,9 @@ fn foo(a: A) {
         );
     }
 
+    // FIXME: Preserving comments is quite hard in the current transitional syntax editing model.
+    // Once we migrate to new trivia model addressed in #6854, remove the ignore attribute.
+    #[ignore]
     #[test]
     fn add_missing_match_arms_preserves_comments_empty() {
         check_assist(
@@ -1502,10 +1506,10 @@ enum Test {
 
 fn foo(t: Test) {
     m!(match t {
-    Test::A => ${1:todo!()},
-    Test::B => ${2:todo!()},
-    Test::C => ${3:todo!()},$0
-});
+        Test::A => ${1:todo!()},
+        Test::B => ${2:todo!()},
+        Test::C => ${3:todo!()},$0
+    });
 }"#,
         );
     }
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/apply_demorgan.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/apply_demorgan.rs
index 70fb5680052..491727a30a8 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/apply_demorgan.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/apply_demorgan.rs
@@ -15,7 +15,7 @@ use crate::{utils::invert_boolean_expression, AssistContext, AssistId, AssistKin
 
 // Assist: apply_demorgan
 //
-// Apply https://en.wikipedia.org/wiki/De_Morgan%27s_laws[De Morgan's law].
+// Apply [De Morgan's law](https://en.wikipedia.org/wiki/De_Morgan%27s_laws).
 // This transforms expressions of the form `!l || !r` into `!(l && r)`.
 // This also works with `&&`. This assist can only be applied with the cursor
 // on either `||` or `&&`.
@@ -131,7 +131,7 @@ pub(crate) fn apply_demorgan(acc: &mut Assists, ctx: &AssistContext<'_>) -> Opti
 
 // Assist: apply_demorgan_iterator
 //
-// Apply https://en.wikipedia.org/wiki/De_Morgan%27s_laws[De Morgan's law] to
+// Apply [De Morgan's law](https://en.wikipedia.org/wiki/De_Morgan%27s_laws) to
 // `Iterator::all` and `Iterator::any`.
 //
 // This transforms expressions of the form `!iter.any(|x| predicate(x))` into
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/auto_import.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/auto_import.rs
index d86948818b1..a92a000c3fb 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/auto_import.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/auto_import.rs
@@ -38,7 +38,7 @@ use crate::{AssistContext, AssistId, AssistKind, Assists, GroupLabel};
 // use super::AssistContext;
 // ```
 //
-// .Import Granularity
+// #### Import Granularity
 //
 // It is possible to configure how use-trees are merged with the `imports.granularity.group` setting.
 // It has the following configurations:
@@ -54,7 +54,7 @@ use crate::{AssistContext, AssistId, AssistKind, Assists, GroupLabel};
 //
 // In `VS Code` the configuration for this is `rust-analyzer.imports.granularity.group`.
 //
-// .Import Prefix
+// #### Import Prefix
 //
 // The style of imports in the same crate is configurable through the `imports.prefix` setting.
 // It has the following configurations:
@@ -68,7 +68,7 @@ use crate::{AssistContext, AssistId, AssistKind, Assists, GroupLabel};
 //
 // In `VS Code` the configuration for this is `rust-analyzer.imports.prefix`.
 //
-// image::https://user-images.githubusercontent.com/48062697/113020673-b85be580-917a-11eb-9022-59585f35d4f8.gif[]
+// ![Auto Import](https://user-images.githubusercontent.com/48062697/113020673-b85be580-917a-11eb-9022-59585f35d4f8.gif)
 
 // Assist: auto_import
 //
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_tuple_struct_to_named_struct.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_tuple_struct_to_named_struct.rs
index 3c84f83906a..f6e516db888 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_tuple_struct_to_named_struct.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_tuple_struct_to_named_struct.rs
@@ -140,8 +140,10 @@ fn edit_struct_references(
         match_ast! {
             match node {
                 ast::TupleStructPat(tuple_struct_pat) => {
+                    let file_range = ctx.sema.original_range_opt(&node)?;
+                    edit.edit_file(file_range.file_id);
                     edit.replace(
-                        tuple_struct_pat.syntax().text_range(),
+                        file_range.range,
                         ast::make::record_pat_with_fields(
                             tuple_struct_pat.path()?,
                             ast::make::record_pat_field_list(tuple_struct_pat.fields().zip(names).map(
@@ -924,4 +926,102 @@ pub struct Foo { #[my_custom_attr] field1: u32 }
 "#,
         );
     }
+
+    #[test]
+    fn convert_in_macro_pattern_args() {
+        check_assist(
+            convert_tuple_struct_to_named_struct,
+            r#"
+macro_rules! foo {
+    ($expression:expr, $pattern:pat) => {
+        match $expression {
+            $pattern => true,
+            _ => false
+        }
+    };
+}
+enum Expr {
+    A$0(usize),
+}
+fn main() {
+    let e = Expr::A(0);
+    foo!(e, Expr::A(0));
+}
+"#,
+            r#"
+macro_rules! foo {
+    ($expression:expr, $pattern:pat) => {
+        match $expression {
+            $pattern => true,
+            _ => false
+        }
+    };
+}
+enum Expr {
+    A { field1: usize },
+}
+fn main() {
+    let e = Expr::A { field1: 0 };
+    foo!(e, Expr::A { field1: 0 });
+}
+"#,
+        );
+    }
+
+    #[test]
+    fn convert_in_multi_file_macro_pattern_args() {
+        check_assist(
+            convert_tuple_struct_to_named_struct,
+            r#"
+//- /main.rs
+mod foo;
+
+enum Test {
+    A$0(i32)
+}
+
+//- /foo.rs
+use crate::Test;
+
+macro_rules! foo {
+    ($expression:expr, $pattern:pat) => {
+        match $expression {
+            $pattern => true,
+            _ => false
+        }
+    };
+}
+
+fn foo() {
+    let a = Test::A(0);
+    foo!(a, Test::A(0));
+}
+"#,
+            r#"
+//- /main.rs
+mod foo;
+
+enum Test {
+    A { field1: i32 }
+}
+
+//- /foo.rs
+use crate::Test;
+
+macro_rules! foo {
+    ($expression:expr, $pattern:pat) => {
+        match $expression {
+            $pattern => true,
+            _ => false
+        }
+    };
+}
+
+fn foo() {
+    let a = Test::A { field1: 0 };
+    foo!(a, Test::A { field1: 0 });
+}
+"#,
+        );
+    }
 }
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/expand_glob_import.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/expand_glob_import.rs
index 094fdc46eb7..0b95d6177f9 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/expand_glob_import.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/expand_glob_import.rs
@@ -3,10 +3,11 @@ use hir::{AssocItem, Enum, HasVisibility, Module, ModuleDef, Name, PathResolutio
 use ide_db::{
     defs::{Definition, NameRefClass},
     search::SearchScope,
+    source_change::SourceChangeBuilder,
 };
 use stdx::never;
 use syntax::{
-    ast::{self, make},
+    ast::{self, make, Use, UseTree, VisibilityKind},
     ted, AstNode, Direction, SyntaxNode, SyntaxToken, T,
 };
 
@@ -43,6 +44,7 @@ use crate::{
 pub(crate) fn expand_glob_import(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
     let star = ctx.find_token_syntax_at_offset(T![*])?;
     let use_tree = star.parent().and_then(ast::UseTree::cast)?;
+    let use_item = star.parent_ancestors().find_map(ast::Use::cast)?;
     let (parent, mod_path) = find_parent_and_path(&star)?;
     let target_module = match ctx.sema.resolve_path(&mod_path)? {
         PathResolution::Def(ModuleDef::Module(it)) => Expandable::Module(it),
@@ -53,8 +55,9 @@ pub(crate) fn expand_glob_import(acc: &mut Assists, ctx: &AssistContext<'_>) ->
     let current_scope = ctx.sema.scope(&star.parent()?)?;
     let current_module = current_scope.module();
 
-    let refs_in_target = find_refs_in_mod(ctx, target_module, current_module)?;
-    let imported_defs = find_imported_defs(ctx, star)?;
+    if !is_visible_from(ctx, &target_module, current_module) {
+        return None;
+    }
 
     let target = parent.either(|n| n.syntax().clone(), |n| n.syntax().clone());
     acc.add(
@@ -62,37 +65,149 @@ pub(crate) fn expand_glob_import(acc: &mut Assists, ctx: &AssistContext<'_>) ->
         "Expand glob import",
         target.text_range(),
         |builder| {
-            let use_tree = builder.make_mut(use_tree);
-
-            let names_to_import = find_names_to_import(ctx, refs_in_target, imported_defs);
-            let expanded = make::use_tree_list(names_to_import.iter().map(|n| {
-                let path = make::ext::ident_path(
-                    &n.display(ctx.db(), current_module.krate().edition(ctx.db())).to_string(),
-                );
-                make::use_tree(path, None, None, false)
-            }))
-            .clone_for_update();
-
-            match use_tree.star_token() {
-                Some(star) => {
-                    let needs_braces = use_tree.path().is_some() && names_to_import.len() != 1;
-                    if needs_braces {
-                        ted::replace(star, expanded.syntax())
-                    } else {
-                        let without_braces = expanded
-                            .syntax()
-                            .children_with_tokens()
-                            .filter(|child| !matches!(child.kind(), T!['{'] | T!['}']))
-                            .collect();
-                        ted::replace_with_many(star, without_braces)
-                    }
-                }
-                None => never!(),
-            }
+            build_expanded_import(
+                ctx,
+                builder,
+                use_tree,
+                use_item,
+                target_module,
+                current_module,
+                false,
+            )
+        },
+    )
+}
+
+// Assist: expand_glob_reexport
+//
+// Expands non-private glob imports.
+//
+// ```
+// mod foo {
+//     pub struct Bar;
+//     pub struct Baz;
+// }
+//
+// pub use foo::*$0;
+// ```
+// ->
+// ```
+// mod foo {
+//     pub struct Bar;
+//     pub struct Baz;
+// }
+//
+// pub use foo::{Bar, Baz};
+// ```
+pub(crate) fn expand_glob_reexport(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
+    let star = ctx.find_token_syntax_at_offset(T![*])?;
+    let use_tree = star.parent().and_then(ast::UseTree::cast)?;
+    let use_item = star.parent_ancestors().find_map(ast::Use::cast)?;
+    let (parent, mod_path) = find_parent_and_path(&star)?;
+    let target_module = match ctx.sema.resolve_path(&mod_path)? {
+        PathResolution::Def(ModuleDef::Module(it)) => Expandable::Module(it),
+        PathResolution::Def(ModuleDef::Adt(hir::Adt::Enum(e))) => Expandable::Enum(e),
+        _ => return None,
+    };
+
+    let current_scope = ctx.sema.scope(&star.parent()?)?;
+    let current_module = current_scope.module();
+
+    if let VisibilityKind::PubSelf = get_export_visibility_kind(&use_item) {
+        return None;
+    }
+    if !is_visible_from(ctx, &target_module, current_module) {
+        return None;
+    }
+
+    let target = parent.either(|n| n.syntax().clone(), |n| n.syntax().clone());
+    acc.add(
+        AssistId("expand_glob_reexport", AssistKind::RefactorRewrite),
+        "Expand glob reexport",
+        target.text_range(),
+        |builder| {
+            build_expanded_import(
+                ctx,
+                builder,
+                use_tree,
+                use_item,
+                target_module,
+                current_module,
+                true,
+            )
         },
     )
 }
 
+fn build_expanded_import(
+    ctx: &AssistContext<'_>,
+    builder: &mut SourceChangeBuilder,
+    use_tree: UseTree,
+    use_item: Use,
+    target_module: Expandable,
+    current_module: Module,
+    reexport_public_items: bool,
+) {
+    let (must_be_pub, visible_from) = if !reexport_public_items {
+        (false, current_module)
+    } else {
+        match get_export_visibility_kind(&use_item) {
+            VisibilityKind::Pub => (true, current_module.krate().root_module()),
+            VisibilityKind::PubCrate => (false, current_module.krate().root_module()),
+            _ => (false, current_module),
+        }
+    };
+
+    let refs_in_target = find_refs_in_mod(ctx, target_module, visible_from, must_be_pub);
+    let imported_defs = find_imported_defs(ctx, use_item);
+
+    let filtered_defs =
+        if reexport_public_items { refs_in_target } else { refs_in_target.used_refs(ctx) };
+
+    let use_tree = builder.make_mut(use_tree);
+
+    let names_to_import = find_names_to_import(filtered_defs, imported_defs);
+    let expanded = make::use_tree_list(names_to_import.iter().map(|n| {
+        let path = make::ext::ident_path(
+            &n.display(ctx.db(), current_module.krate().edition(ctx.db())).to_string(),
+        );
+        make::use_tree(path, None, None, false)
+    }))
+    .clone_for_update();
+
+    match use_tree.star_token() {
+        Some(star) => {
+            let needs_braces = use_tree.path().is_some() && names_to_import.len() != 1;
+            if needs_braces {
+                ted::replace(star, expanded.syntax())
+            } else {
+                let without_braces = expanded
+                    .syntax()
+                    .children_with_tokens()
+                    .filter(|child| !matches!(child.kind(), T!['{'] | T!['}']))
+                    .collect();
+                ted::replace_with_many(star, without_braces)
+            }
+        }
+        None => never!(),
+    }
+}
+
+fn get_export_visibility_kind(use_item: &Use) -> VisibilityKind {
+    use syntax::ast::HasVisibility as _;
+    match use_item.visibility() {
+        Some(vis) => match vis.kind() {
+            VisibilityKind::PubCrate => VisibilityKind::PubCrate,
+            VisibilityKind::Pub => VisibilityKind::Pub,
+            VisibilityKind::PubSelf => VisibilityKind::PubSelf,
+            // We don't handle pub(in ...) and pub(super) yet
+            VisibilityKind::In(_) => VisibilityKind::PubSelf,
+            VisibilityKind::PubSuper => VisibilityKind::PubSelf,
+        },
+        None => VisibilityKind::PubSelf,
+    }
+}
+
 enum Expandable {
     Module(Module),
     Enum(Enum),
@@ -130,14 +245,17 @@ struct Ref {
     // could be alias
     visible_name: Name,
     def: Definition,
+    is_pub: bool,
 }
 
 impl Ref {
-    fn from_scope_def(name: Name, scope_def: ScopeDef) -> Option<Self> {
+    fn from_scope_def(ctx: &AssistContext<'_>, name: Name, scope_def: ScopeDef) -> Option<Self> {
         match scope_def {
-            ScopeDef::ModuleDef(def) => {
-                Some(Ref { visible_name: name, def: Definition::from(def) })
-            }
+            ScopeDef::ModuleDef(def) => Some(Ref {
+                visible_name: name,
+                def: Definition::from(def),
+                is_pub: matches!(def.visibility(ctx.db()), hir::Visibility::Public),
+            }),
             _ => None,
         }
     }
@@ -180,32 +298,32 @@ fn find_refs_in_mod(
     ctx: &AssistContext<'_>,
     expandable: Expandable,
     visible_from: Module,
-) -> Option<Refs> {
-    if !is_expandable_visible_from(ctx, &expandable, visible_from) {
-        return None;
-    }
-
+    must_be_pub: bool,
+) -> Refs {
     match expandable {
         Expandable::Module(module) => {
             let module_scope = module.scope(ctx.db(), Some(visible_from));
-            let refs =
-                module_scope.into_iter().filter_map(|(n, d)| Ref::from_scope_def(n, d)).collect();
-            Some(Refs(refs))
+            let refs = module_scope
+                .into_iter()
+                .filter_map(|(n, d)| Ref::from_scope_def(ctx, n, d))
+                .filter(|r| !must_be_pub || r.is_pub)
+                .collect();
+            Refs(refs)
         }
-        Expandable::Enum(enm) => Some(Refs(
+        Expandable::Enum(enm) => Refs(
             enm.variants(ctx.db())
                 .into_iter()
-                .map(|v| Ref { visible_name: v.name(ctx.db()), def: Definition::Variant(v) })
+                .map(|v| Ref {
+                    visible_name: v.name(ctx.db()),
+                    def: Definition::Variant(v),
+                    is_pub: true,
+                })
                 .collect(),
-        )),
+        ),
     }
 }
 
-fn is_expandable_visible_from(
-    ctx: &AssistContext<'_>,
-    expandable: &Expandable,
-    from: Module,
-) -> bool {
+fn is_visible_from(ctx: &AssistContext<'_>, expandable: &Expandable, from: Module) -> bool {
     fn is_mod_visible_from(ctx: &AssistContext<'_>, module: Module, from: Module) -> bool {
         match module.parent(ctx.db()) {
             Some(parent) => {
@@ -246,50 +364,34 @@ fn is_expandable_visible_from(
 // use foo::*$0;
 // use baz::Baz;
 // ↑ ---------------
-fn find_imported_defs(ctx: &AssistContext<'_>, star: SyntaxToken) -> Option<Vec<Definition>> {
-    let parent_use_item_syntax = star.parent_ancestors().find_map(|n| {
-        if ast::Use::can_cast(n.kind()) {
-            Some(n)
-        } else {
-            None
-        }
-    })?;
-
-    Some(
-        [Direction::Prev, Direction::Next]
-            .into_iter()
-            .flat_map(|dir| {
-                parent_use_item_syntax
-                    .siblings(dir.to_owned())
-                    .filter(|n| ast::Use::can_cast(n.kind()))
-            })
-            .flat_map(|n| n.descendants().filter_map(ast::NameRef::cast))
-            .filter_map(|r| match NameRefClass::classify(&ctx.sema, &r)? {
-                NameRefClass::Definition(
-                    def @ (Definition::Macro(_)
-                    | Definition::Module(_)
-                    | Definition::Function(_)
-                    | Definition::Adt(_)
-                    | Definition::Variant(_)
-                    | Definition::Const(_)
-                    | Definition::Static(_)
-                    | Definition::Trait(_)
-                    | Definition::TypeAlias(_)),
-                    _,
-                ) => Some(def),
-                _ => None,
-            })
-            .collect(),
-    )
+fn find_imported_defs(ctx: &AssistContext<'_>, use_item: Use) -> Vec<Definition> {
+    [Direction::Prev, Direction::Next]
+        .into_iter()
+        .flat_map(|dir| {
+            use_item.syntax().siblings(dir.to_owned()).filter(|n| ast::Use::can_cast(n.kind()))
+        })
+        .flat_map(|n| n.descendants().filter_map(ast::NameRef::cast))
+        .filter_map(|r| match NameRefClass::classify(&ctx.sema, &r)? {
+            NameRefClass::Definition(
+                def @ (Definition::Macro(_)
+                | Definition::Module(_)
+                | Definition::Function(_)
+                | Definition::Adt(_)
+                | Definition::Variant(_)
+                | Definition::Const(_)
+                | Definition::Static(_)
+                | Definition::Trait(_)
+                | Definition::TypeAlias(_)),
+                _,
+            ) => Some(def),
+            _ => None,
+        })
+        .collect()
 }
 
-fn find_names_to_import(
-    ctx: &AssistContext<'_>,
-    refs_in_target: Refs,
-    imported_defs: Vec<Definition>,
-) -> Vec<Name> {
-    let used_refs = refs_in_target.used_refs(ctx).filter_out_by_defs(imported_defs);
-    used_refs.0.iter().map(|r| r.visible_name.clone()).collect()
+fn find_names_to_import(refs_in_target: Refs, imported_defs: Vec<Definition>) -> Vec<Name> {
+    let final_refs = refs_in_target.filter_out_by_defs(imported_defs);
+    final_refs.0.iter().map(|r| r.visible_name.clone()).collect()
 }
 
 #[cfg(test)]
@@ -1036,4 +1138,83 @@ mod abc {
 }"#,
         )
     }
+
+    #[test]
+    fn expanding_glob_reexport() {
+        check_assist(
+            expand_glob_reexport,
+            r"
+mod foo {
+    pub struct Bar;
+    pub struct Baz;
+    struct Qux;
+
+    pub fn f() {}
+
+    pub(crate) fn g() {}
+    pub(self) fn h() {}
+}
+
+pub use foo::*$0;
+",
+            r"
+mod foo {
+    pub struct Bar;
+    pub struct Baz;
+    struct Qux;
+
+    pub fn f() {}
+
+    pub(crate) fn g() {}
+    pub(self) fn h() {}
+}
+
+pub use foo::{Bar, Baz, f};
+",
+        )
+    }
+
+    #[test]
+    fn expanding_recursive_glob_reexport() {
+        check_assist(
+            expand_glob_reexport,
+            r"
+mod foo {
+    pub use bar::*;
+    mod bar {
+        pub struct Bar;
+        pub struct Baz;
+    }
+}
+
+pub use foo::*$0;
+",
+            r"
+mod foo {
+    pub use bar::*;
+    mod bar {
+        pub struct Bar;
+        pub struct Baz;
+    }
+}
+
+pub use foo::{Bar, Baz};
+",
+        )
+    }
+
+    #[test]
+    fn expanding_reexport_is_not_applicable_for_private_import() {
+        check_assist_not_applicable(
+            expand_glob_reexport,
+            r"
+mod foo {
+    pub struct Bar;
+    pub struct Baz;
+}
+
+use foo::*$0;
+",
+        );
+    }
 }
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_delegate_methods.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_delegate_methods.rs
index 081e36b4ff6..220259451e8 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_delegate_methods.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_delegate_methods.rs
@@ -48,6 +48,10 @@ use crate::{
 // }
 // ```
 pub(crate) fn generate_delegate_methods(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
+    if !ctx.config.code_action_grouping {
+        return None;
+    }
+
     let strukt = ctx.find_node_at_offset::<ast::Struct>()?;
     let strukt_name = strukt.name()?;
     let current_module = ctx.sema.scope(strukt.syntax())?.module();
@@ -213,7 +217,9 @@ pub(crate) fn generate_delegate_methods(acc: &mut Assists, ctx: &AssistContext<'
 
 #[cfg(test)]
 mod tests {
-    use crate::tests::{check_assist, check_assist_not_applicable};
+    use crate::tests::{
+        check_assist, check_assist_not_applicable, check_assist_not_applicable_no_grouping,
+    };
 
     use super::*;
 
@@ -717,4 +723,21 @@ impl Person {
 "#,
         );
     }
+
+    #[test]
+    fn delegate_method_skipped_when_no_grouping() {
+        check_assist_not_applicable_no_grouping(
+            generate_delegate_methods,
+            r#"
+struct Age(u8);
+impl Age {
+    fn age(&self) -> u8 {
+        self.0
+    }
+}
+struct Person {
+    ag$0e: Age,
+}"#,
+        );
+    }
 }
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_delegate_trait.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_delegate_trait.rs
index 66bf9b01868..55b860d0ff5 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_delegate_trait.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_delegate_trait.rs
@@ -88,6 +88,10 @@ use syntax::{
 // }
 // ```
 pub(crate) fn generate_delegate_trait(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
+    if !ctx.config.code_action_grouping {
+        return None;
+    }
+
     let strukt = Struct::new(ctx.find_node_at_offset::<ast::Struct>()?)?;
 
     let field: Field = match ctx.find_node_at_offset::<ast::RecordField>() {
@@ -788,7 +792,9 @@ fn qualified_path(qual_path_ty: ast::Path, path_expr_seg: ast::Path) -> ast::Pat
 mod test {
 
     use super::*;
-    use crate::tests::{check_assist, check_assist_not_applicable};
+    use crate::tests::{
+        check_assist, check_assist_not_applicable, check_assist_not_applicable_no_grouping,
+    };
 
     #[test]
     fn test_tuple_struct_basic() {
@@ -1836,4 +1842,33 @@ impl<D, T: C<A>> C<D> for B<T> {
 "#,
         )
     }
+
+    #[test]
+    fn delegate_trait_skipped_when_no_grouping() {
+        check_assist_not_applicable_no_grouping(
+            generate_delegate_trait,
+            r#"
+trait SomeTrait {
+    type T;
+    fn fn_(arg: u32) -> u32;
+    fn method_(&mut self) -> bool;
+}
+struct A;
+impl SomeTrait for A {
+    type T = u32;
+
+    fn fn_(arg: u32) -> u32 {
+        42
+    }
+
+    fn method_(&mut self) -> bool {
+        false
+    }
+}
+struct B {
+    a$0 : A,
+}
+"#,
+        );
+    }
 }
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/lib.rs b/src/tools/rust-analyzer/crates/ide-assists/src/lib.rs
index 5c95b25f28d..179742f91b4 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/lib.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/lib.rs
@@ -270,6 +270,7 @@ mod handlers {
             destructure_tuple_binding::destructure_tuple_binding,
             destructure_struct_binding::destructure_struct_binding,
             expand_glob_import::expand_glob_import,
+            expand_glob_import::expand_glob_reexport,
             explicit_enum_discriminant::explicit_enum_discriminant,
             extract_expressions_from_format_string::extract_expressions_from_format_string,
             extract_struct_from_enum_variant::extract_struct_from_enum_variant,
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/tests.rs b/src/tools/rust-analyzer/crates/ide-assists/src/tests.rs
index 48d2af6d3ff..11aeb21c77e 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/tests.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/tests.rs
@@ -34,6 +34,26 @@ pub(crate) const TEST_CONFIG: AssistConfig = AssistConfig {
     assist_emit_must_use: false,
     term_search_fuel: 400,
     term_search_borrowck: true,
+    code_action_grouping: true,
+};
+
+pub(crate) const TEST_CONFIG_NO_GROUPING: AssistConfig = AssistConfig {
+    snippet_cap: SnippetCap::new(true),
+    allowed: None,
+    insert_use: InsertUseConfig {
+        granularity: ImportGranularity::Crate,
+        prefix_kind: hir::PrefixKind::Plain,
+        enforce_granularity: true,
+        group: true,
+        skip_glob_imports: true,
+    },
+    prefer_no_std: false,
+    prefer_prelude: true,
+    prefer_absolute: false,
+    assist_emit_must_use: false,
+    term_search_fuel: 400,
+    term_search_borrowck: true,
+    code_action_grouping: false,
 };
 
 pub(crate) const TEST_CONFIG_NO_SNIPPET_CAP: AssistConfig = AssistConfig {
@@ -52,6 +72,7 @@ pub(crate) const TEST_CONFIG_NO_SNIPPET_CAP: AssistConfig = AssistConfig {
     assist_emit_must_use: false,
     term_search_fuel: 400,
     term_search_borrowck: true,
+    code_action_grouping: true,
 };
 
 pub(crate) const TEST_CONFIG_IMPORT_ONE: AssistConfig = AssistConfig {
@@ -70,6 +91,7 @@ pub(crate) const TEST_CONFIG_IMPORT_ONE: AssistConfig = AssistConfig {
     assist_emit_must_use: false,
     term_search_fuel: 400,
     term_search_borrowck: true,
+    code_action_grouping: true,
 };
 
 pub(crate) fn with_single_file(text: &str) -> (RootDatabase, EditionedFileId) {
@@ -173,6 +195,20 @@ pub(crate) fn check_assist_not_applicable_for_import_one(
     );
 }
 
+#[track_caller]
+pub(crate) fn check_assist_not_applicable_no_grouping(
+    assist: Handler,
+    #[rust_analyzer::rust_fixture] ra_fixture: &str,
+) {
+    check_with_config(
+        TEST_CONFIG_NO_GROUPING,
+        assist,
+        ra_fixture,
+        ExpectedResult::NotApplicable,
+        None,
+    );
+}
+
 /// Check assist in unresolved state. Useful to check assists for lazy computation.
 #[track_caller]
 pub(crate) fn check_assist_unresolved(
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/tests/generated.rs b/src/tools/rust-analyzer/crates/ide-assists/src/tests/generated.rs
index 54e42f126bc..0662527a387 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/tests/generated.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/tests/generated.rs
@@ -1,4 +1,4 @@
-//! Generated by `cargo codegen assists-doc-tests`, do not edit by hand.
+//! Generated by `cargo xtask codegen assists-doc-tests`, do not edit by hand.
 
 use super::check_doc_test;
 
@@ -910,6 +910,29 @@ fn qux(bar: Bar, baz: Baz) {}
 }
 
 #[test]
+fn doctest_expand_glob_reexport() {
+    check_doc_test(
+        "expand_glob_reexport",
+        r#####"
+mod foo {
+    pub struct Bar;
+    pub struct Baz;
+}
+
+pub use foo::*$0;
+"#####,
+        r#####"
+mod foo {
+    pub struct Bar;
+    pub struct Baz;
+}
+
+pub use foo::{Bar, Baz};
+"#####,
+    )
+}
+
+#[test]
 fn doctest_explicit_enum_discriminant() {
     check_doc_test(
         "explicit_enum_discriminant",
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/utils.rs b/src/tools/rust-analyzer/crates/ide-assists/src/utils.rs
index 78ff4417913..c1332d99bff 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/utils.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/utils.rs
@@ -793,8 +793,8 @@ pub(crate) fn convert_reference_type(
 }
 
 fn could_deref_to_target(ty: &hir::Type, target: &hir::Type, db: &dyn HirDatabase) -> bool {
-    let ty_ref = hir::Type::reference(ty, hir::Mutability::Shared);
-    let target_ref = hir::Type::reference(target, hir::Mutability::Shared);
+    let ty_ref = ty.add_reference(hir::Mutability::Shared);
+    let target_ref = target.add_reference(hir::Mutability::Shared);
     ty_ref.could_coerce_to(db, &target_ref)
 }
 
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/dot.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/dot.rs
index d12654665ce..b38b9ac1f53 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/dot.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/dot.rs
@@ -89,7 +89,7 @@ pub(crate) fn complete_dot(
         acc.add_method(ctx, dot_access, func, None, None)
     });
 
-    if ctx.config.enable_auto_iter {
+    if ctx.config.enable_auto_iter && !receiver_ty.strip_references().impls_iterator(ctx.db) {
         // FIXME:
         // Checking for the existence of `iter()` is complicated in our setup, because we need to substitute
         // its return type, so we instead check for `<&Self as IntoIterator>::IntoIter`.
@@ -1500,9 +1500,31 @@ fn main() {
     bar.$0
 }
 "#,
+            expect![[r#""#]],
+        );
+    }
+
+    #[test]
+    fn no_iter_suggestion_on_iterator() {
+        check_no_kw(
+            r#"
+//- minicore: iterator
+struct MyIter;
+impl Iterator for MyIter {
+    type Item = ();
+    fn next(&mut self) -> Option<Self::Item> { None }
+}
+
+fn main() {
+    MyIter.$0
+}
+"#,
             expect![[r#"
-    me foo() fn(self: Bar)
-"#]],
+                me by_ref() (as Iterator)                             fn(&mut self) -> &mut Self
+                me into_iter() (as IntoIterator)    fn(self) -> <Self as IntoIterator>::IntoIter
+                me next() (as Iterator)        fn(&mut self) -> Option<<Self as Iterator>::Item>
+                me nth(…) (as Iterator) fn(&mut self, usize) -> Option<<Self as Iterator>::Item>
+            "#]],
         );
     }
 }
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/flyimport.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/flyimport.rs
index 24243f57b46..b5555e66102 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/flyimport.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/flyimport.rs
@@ -83,19 +83,19 @@ use crate::{
 // NOTE: currently, if an assoc item comes from a trait that's not currently imported, and it also has an unresolved and/or partially-qualified path,
 // no imports will be proposed.
 //
-// .Fuzzy search details
+// #### Fuzzy search details
 //
 // To avoid an excessive amount of the results returned, completion input is checked for inclusion in the names only
 // (i.e. in `HashMap` in the `std::collections::HashMap` path).
 // For the same reasons, avoids searching for any path imports for inputs with their length less than 2 symbols
 // (but shows all associated items for any input length).
 //
-// .Import configuration
+// #### Import configuration
 //
 // It is possible to configure how use-trees are merged with the `imports.granularity.group` setting.
 // Mimics the corresponding behavior of the `Auto Import` feature.
 //
-// .LSP and performance implications
+// #### LSP and performance implications
 //
 // The feature is enabled only if the LSP client supports LSP protocol version 3.16+ and reports the `additionalTextEdits`
 // (case-sensitive) resolve client capability in its client capabilities.
@@ -103,7 +103,7 @@ use crate::{
 // For clients with no such support, all edits have to be calculated on the completion request, including the fuzzy search completion ones,
 // which might be slow ergo the feature is automatically disabled.
 //
-// .Feature toggle
+// #### Feature toggle
 //
 // The feature can be forcefully turned off in the settings with the `rust-analyzer.completion.autoimport.enable` flag.
 // Note that having this flag set to `true` does not guarantee that the feature is enabled: your client needs to have the corresponding
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/postfix.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/postfix.rs
index 2c39a8fdfed..28e2853096e 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/postfix.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/postfix.rs
@@ -2,17 +2,18 @@
 
 mod format_like;
 
-use hir::ItemInNs;
-use ide_db::text_edit::TextEdit;
+use base_db::SourceDatabase;
+use hir::{ItemInNs, Semantics};
 use ide_db::{
     documentation::{Documentation, HasDocs},
     imports::insert_use::ImportScope,
+    text_edit::TextEdit,
     ty_filter::TryEnum,
-    SnippetCap,
+    RootDatabase, SnippetCap,
 };
 use stdx::never;
 use syntax::{
-    ast::{self, make, AstNode, AstToken},
+    ast::{self, AstNode, AstToken},
     SyntaxKind::{BLOCK_EXPR, EXPR_STMT, FOR_EXPR, IF_EXPR, LOOP_EXPR, STMT_LIST, WHILE_EXPR},
     TextRange, TextSize,
 };
@@ -48,7 +49,8 @@ pub(crate) fn complete_postfix(
     };
     let expr_ctx = &dot_access.ctx;
 
-    let receiver_text = get_receiver_text(dot_receiver, receiver_is_ambiguous_float_literal);
+    let receiver_text =
+        get_receiver_text(&ctx.sema, dot_receiver, receiver_is_ambiguous_float_literal);
 
     let cap = match ctx.config.snippet_cap {
         Some(it) => it,
@@ -172,13 +174,15 @@ pub(crate) fn complete_postfix(
     // The rest of the postfix completions create an expression that moves an argument,
     // so it's better to consider references now to avoid breaking the compilation
 
-    let (dot_receiver, node_to_replace_with) = include_references(dot_receiver);
-    let receiver_text =
-        get_receiver_text(&node_to_replace_with, receiver_is_ambiguous_float_literal);
-    let postfix_snippet = match build_postfix_snippet_builder(ctx, cap, &dot_receiver) {
-        Some(it) => it,
-        None => return,
-    };
+    let (dot_receiver_including_refs, prefix) = include_references(dot_receiver);
+    let mut receiver_text =
+        get_receiver_text(&ctx.sema, dot_receiver, receiver_is_ambiguous_float_literal);
+    receiver_text.insert_str(0, &prefix);
+    let postfix_snippet =
+        match build_postfix_snippet_builder(ctx, cap, &dot_receiver_including_refs) {
+            Some(it) => it,
+            None => return,
+        };
 
     if !ctx.config.snippets.is_empty() {
         add_custom_postfix_completions(acc, ctx, &postfix_snippet, &receiver_text);
@@ -222,7 +226,7 @@ pub(crate) fn complete_postfix(
     postfix_snippet("call", "function(expr)", &format!("${{1}}({receiver_text})"))
         .add_to(acc, ctx.db);
 
-    if let Some(parent) = dot_receiver.syntax().parent().and_then(|p| p.parent()) {
+    if let Some(parent) = dot_receiver_including_refs.syntax().parent().and_then(|p| p.parent()) {
         if matches!(parent.kind(), STMT_LIST | EXPR_STMT) {
             postfix_snippet("let", "let", &format!("let $0 = {receiver_text};"))
                 .add_to(acc, ctx.db);
@@ -231,9 +235,9 @@ pub(crate) fn complete_postfix(
         }
     }
 
-    if let ast::Expr::Literal(literal) = dot_receiver.clone() {
+    if let ast::Expr::Literal(literal) = dot_receiver_including_refs.clone() {
         if let Some(literal_text) = ast::String::cast(literal.token()) {
-            add_format_like_completions(acc, ctx, &dot_receiver, cap, &literal_text);
+            add_format_like_completions(acc, ctx, &dot_receiver_including_refs, cap, &literal_text);
         }
     }
 
@@ -260,14 +264,20 @@ pub(crate) fn complete_postfix(
     }
 }
 
-fn get_receiver_text(receiver: &ast::Expr, receiver_is_ambiguous_float_literal: bool) -> String {
-    let mut text = if receiver_is_ambiguous_float_literal {
-        let text = receiver.syntax().text();
-        let without_dot = ..text.len() - TextSize::of('.');
-        text.slice(without_dot).to_string()
-    } else {
-        receiver.to_string()
+fn get_receiver_text(
+    sema: &Semantics<'_, RootDatabase>,
+    receiver: &ast::Expr,
+    receiver_is_ambiguous_float_literal: bool,
+) -> String {
+    // Do not just call `receiver.to_string()`, as that will mess up whitespaces inside macros.
+    let Some(mut range) = sema.original_range_opt(receiver.syntax()) else {
+        return receiver.to_string();
     };
+    if receiver_is_ambiguous_float_literal {
+        range.range = TextRange::at(range.range.start(), range.range.len() - TextSize::of('.'))
+    }
+    let file_text = sema.db.file_text(range.file_id.file_id());
+    let mut text = file_text[range.range].to_owned();
 
     // The receiver texts should be interpreted as-is, as they are expected to be
     // normal Rust expressions.
@@ -284,7 +294,7 @@ fn escape_snippet_bits(text: &mut String) {
     stdx::replace(text, '$', "\\$");
 }
 
-fn include_references(initial_element: &ast::Expr) -> (ast::Expr, ast::Expr) {
+fn include_references(initial_element: &ast::Expr) -> (ast::Expr, String) {
     let mut resulting_element = initial_element.clone();
 
     while let Some(field_expr) = resulting_element.syntax().parent().and_then(ast::FieldExpr::cast)
@@ -292,7 +302,7 @@ fn include_references(initial_element: &ast::Expr) -> (ast::Expr, ast::Expr) {
         resulting_element = ast::Expr::from(field_expr);
     }
 
-    let mut new_element_opt = initial_element.clone();
+    let mut prefix = String::new();
 
     while let Some(parent_deref_element) =
         resulting_element.syntax().parent().and_then(ast::PrefixExpr::cast)
@@ -303,7 +313,7 @@ fn include_references(initial_element: &ast::Expr) -> (ast::Expr, ast::Expr) {
 
         resulting_element = ast::Expr::from(parent_deref_element);
 
-        new_element_opt = make::expr_prefix(syntax::T![*], new_element_opt).into();
+        prefix.insert(0, '*');
     }
 
     if let Some(first_ref_expr) = resulting_element.syntax().parent().and_then(ast::RefExpr::cast) {
@@ -317,7 +327,7 @@ fn include_references(initial_element: &ast::Expr) -> (ast::Expr, ast::Expr) {
             let exclusive = parent_ref_element.mut_token().is_some();
             resulting_element = ast::Expr::from(parent_ref_element);
 
-            new_element_opt = make::expr_ref(new_element_opt, exclusive);
+            prefix.insert_str(0, if exclusive { "&mut " } else { "&" });
         }
     } else {
         // If we do not find any ref expressions, restore
@@ -325,7 +335,7 @@ fn include_references(initial_element: &ast::Expr) -> (ast::Expr, ast::Expr) {
         resulting_element = initial_element.clone();
     }
 
-    (resulting_element, new_element_opt)
+    (resulting_element, prefix)
 }
 
 fn build_postfix_snippet_builder<'ctx>(
@@ -901,4 +911,31 @@ fn main() {
 "#,
         );
     }
+
+    #[test]
+    fn inside_macro() {
+        check_edit(
+            "box",
+            r#"
+macro_rules! assert {
+    ( $it:expr $(,)? ) => { $it };
+}
+
+fn foo() {
+    let a = true;
+    assert!(if a == false { true } else { false }.$0);
+}
+        "#,
+            r#"
+macro_rules! assert {
+    ( $it:expr $(,)? ) => { $it };
+}
+
+fn foo() {
+    let a = true;
+    assert!(Box::new(if a == false { true } else { false }));
+}
+        "#,
+        );
+    }
 }
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/postfix/format_like.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/postfix/format_like.rs
index 2755329bb31..c612170eb54 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/postfix/format_like.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/postfix/format_like.rs
@@ -14,7 +14,7 @@
 // ** `logw` -> `log::warn!(...)`
 // ** `loge` -> `log::error!(...)`
 //
-// image::https://user-images.githubusercontent.com/48062697/113020656-b560f500-917a-11eb-87de-02991f61beb8.gif[]
+// ![Format String Completion](https://user-images.githubusercontent.com/48062697/113020656-b560f500-917a-11eb-87de-02991f61beb8.gif)
 
 use ide_db::{
     syntax_helpers::format_string_exprs::{parse_format_exprs, with_placeholders, Arg},
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/context.rs b/src/tools/rust-analyzer/crates/ide-completion/src/context.rs
index 2f1860cbb59..7862b258789 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/context.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/context.rs
@@ -442,6 +442,8 @@ pub(crate) struct CompletionContext<'a> {
     pub(crate) krate: hir::Crate,
     /// The module of the `scope`.
     pub(crate) module: hir::Module,
+    /// The function where we're completing, if inside a function.
+    pub(crate) containing_function: Option<hir::Function>,
     /// Whether nightly toolchain is used. Cached since this is looked up a lot.
     pub(crate) is_nightly: bool,
     /// The edition of the current crate
@@ -760,6 +762,7 @@ impl<'a> CompletionContext<'a> {
 
         let krate = scope.krate();
         let module = scope.module();
+        let containing_function = scope.containing_function();
         let edition = krate.edition(db);
 
         let toolchain = db.toolchain_channel(krate.into());
@@ -874,6 +877,7 @@ impl<'a> CompletionContext<'a> {
             token,
             krate,
             module,
+            containing_function,
             is_nightly,
             edition,
             expected_name,
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/context/analysis.rs b/src/tools/rust-analyzer/crates/ide-completion/src/context/analysis.rs
index f5a50ae8190..eecd412bc43 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/context/analysis.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/context/analysis.rs
@@ -59,7 +59,7 @@ pub(super) fn expand_and_analyze(
     // make the offset point to the start of the original token, as that is what the
     // intermediate offsets calculated in expansion always points to
     let offset = offset - relative_offset;
-    let expansion = expand(
+    let expansion = expand_maybe_stop(
         sema,
         original_file.clone(),
         speculative_file.clone(),
@@ -118,7 +118,7 @@ fn token_at_offset_ignore_whitespace(file: &SyntaxNode, offset: TextSize) -> Opt
 /// that we check, we subtract `COMPLETION_MARKER.len()`. This may not be accurate because proc macros
 /// can insert the text of the completion marker in other places while removing the span, but this is
 /// the best we can do.
-fn expand(
+fn expand_maybe_stop(
     sema: &Semantics<'_, RootDatabase>,
     original_file: SyntaxNode,
     speculative_file: SyntaxNode,
@@ -126,23 +126,48 @@ fn expand(
     fake_ident_token: SyntaxToken,
     relative_offset: TextSize,
 ) -> Option<ExpansionResult> {
-    let _p = tracing::info_span!("CompletionContext::expand").entered();
+    if let result @ Some(_) = expand(
+        sema,
+        original_file.clone(),
+        speculative_file.clone(),
+        original_offset,
+        fake_ident_token.clone(),
+        relative_offset,
+    ) {
+        return result;
+    }
 
+    // This needs to come after the recursive call, because our "inside macro" detection is subtly wrong
+    // with regard to attribute macros named `test` that are not std's test. So hopefully we will expand
+    // them successfully above and be able to analyze.
     // Left biased since there may already be an identifier token there, and we appended to it.
     if !sema.might_be_inside_macro_call(&fake_ident_token)
         && token_at_offset_ignore_whitespace(&original_file, original_offset + relative_offset)
             .is_some_and(|original_token| !sema.might_be_inside_macro_call(&original_token))
     {
         // Recursion base case.
-        return Some(ExpansionResult {
+        Some(ExpansionResult {
             original_file,
             speculative_file,
             original_offset,
             speculative_offset: fake_ident_token.text_range().start(),
             fake_ident_token,
             derive_ctx: None,
-        });
+        })
+    } else {
+        None
     }
+}
+
+fn expand(
+    sema: &Semantics<'_, RootDatabase>,
+    original_file: SyntaxNode,
+    speculative_file: SyntaxNode,
+    original_offset: TextSize,
+    fake_ident_token: SyntaxToken,
+    relative_offset: TextSize,
+) -> Option<ExpansionResult> {
+    let _p = tracing::info_span!("CompletionContext::expand").entered();
 
     let parent_item =
         |item: &ast::Item| item.syntax().ancestors().skip(1).find_map(ast::Item::cast);
@@ -197,7 +222,7 @@ fn expand(
                             // stop here to prevent problems from happening
                             return None;
                         }
-                        let result = expand(
+                        let result = expand_maybe_stop(
                             sema,
                             actual_expansion.clone(),
                             fake_expansion.clone(),
@@ -317,7 +342,7 @@ fn expand(
                                     // stop here to prevent problems from happening
                                     return None;
                                 }
-                                let result = expand(
+                                let result = expand_maybe_stop(
                                     sema,
                                     actual_expansion.clone(),
                                     fake_expansion.clone(),
@@ -386,7 +411,7 @@ fn expand(
                         // stop here to prevent problems from happening
                         return None;
                     }
-                    let result = expand(
+                    let result = expand_maybe_stop(
                         sema,
                         actual_expansion.clone(),
                         fake_expansion.clone(),
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/lib.rs b/src/tools/rust-analyzer/crates/ide-completion/src/lib.rs
index 8051d48ca5f..a1f2eaeb1b6 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/lib.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/lib.rs
@@ -106,11 +106,13 @@ impl CompletionFieldsToResolve {
 //
 // There also snippet completions:
 //
-// .Expressions
+// #### Expressions
+//
 // - `pd` -> `eprintln!(" = {:?}", );`
 // - `ppd` -> `eprintln!(" = {:#?}", );`
 //
-// .Items
+// #### Items
+//
 // - `tfn` -> `#[test] fn feature(){}`
 // - `tmod` ->
 // ```rust
@@ -127,7 +129,7 @@ impl CompletionFieldsToResolve {
 // Those are the additional completion options with automatic `use` import and options from all project importable items,
 // fuzzy matched against the completion input.
 //
-// image::https://user-images.githubusercontent.com/48062697/113020667-b72ab880-917a-11eb-8778-716cf26a0eb3.gif[]
+// ![Magic Completions](https://user-images.githubusercontent.com/48062697/113020667-b72ab880-917a-11eb-8778-716cf26a0eb3.gif)
 
 /// Main entry point for completion. We run completion as a two-phase process.
 ///
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/render/function.rs b/src/tools/rust-analyzer/crates/ide-completion/src/render/function.rs
index c3354902c3b..fd90613964a 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/render/function.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/render/function.rs
@@ -144,7 +144,7 @@ fn render(
     let detail = if ctx.completion.config.full_function_signatures {
         detail_full(db, func, ctx.completion.edition)
     } else {
-        detail(db, func, ctx.completion.edition)
+        detail(ctx.completion, func, ctx.completion.edition)
     };
     item.set_documentation(ctx.docs(func))
         .set_deprecated(ctx.is_deprecated(func) || ctx.is_deprecated_assoc_item(func))
@@ -307,26 +307,26 @@ fn ref_of_param(ctx: &CompletionContext<'_>, arg: &str, ty: &hir::Type) -> &'sta
     ""
 }
 
-fn detail(db: &dyn HirDatabase, func: hir::Function, edition: Edition) -> String {
-    let mut ret_ty = func.ret_type(db);
+fn detail(ctx: &CompletionContext<'_>, func: hir::Function, edition: Edition) -> String {
+    let mut ret_ty = func.ret_type(ctx.db);
     let mut detail = String::new();
 
-    if func.is_const(db) {
+    if func.is_const(ctx.db) {
         format_to!(detail, "const ");
     }
-    if func.is_async(db) {
+    if func.is_async(ctx.db) {
         format_to!(detail, "async ");
-        if let Some(async_ret) = func.async_ret_type(db) {
+        if let Some(async_ret) = func.async_ret_type(ctx.db) {
             ret_ty = async_ret;
         }
     }
-    if func.is_unsafe_to_call(db) {
+    if func.is_unsafe_to_call(ctx.db, ctx.containing_function, ctx.edition) {
         format_to!(detail, "unsafe ");
     }
 
-    format_to!(detail, "fn({})", params_display(db, func, edition));
+    format_to!(detail, "fn({})", params_display(ctx.db, func, edition));
     if !ret_ty.is_unit() {
-        format_to!(detail, " -> {}", ret_ty.display(db, edition));
+        format_to!(detail, " -> {}", ret_ty.display(ctx.db, edition));
     }
     detail
 }
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/snippet.rs b/src/tools/rust-analyzer/crates/ide-completion/src/snippet.rs
index 866b83a6146..07f33a826e4 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/snippet.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/snippet.rs
@@ -8,8 +8,7 @@
 //
 // A custom snippet can be defined by adding it to the `rust-analyzer.completion.snippets.custom` object respectively.
 //
-// [source,json]
-// ----
+// ```json
 // {
 //   "rust-analyzer.completion.snippets.custom": {
 //     "thread spawn": {
@@ -25,7 +24,7 @@
 //     }
 //   }
 // }
-// ----
+// ```
 //
 // In the example above:
 //
@@ -39,6 +38,7 @@
 // * `description` is an optional description of the snippet, if unset the snippet name will be used.
 //
 // * `requires` is an optional list of item paths that have to be resolvable in the current crate where the completion is rendered.
+
 // On failure of resolution the snippet won't be applicable, otherwise the snippet will insert an import for the items on insertion if
 // the items aren't yet in scope.
 //
@@ -55,8 +55,8 @@
 //
 // For the VSCode editor, rust-analyzer also ships with a small set of defaults which can be removed
 // by overwriting the settings object mentioned above, the defaults are:
-// [source,json]
-// ----
+//
+// ```json
 // {
 //     "Arc::new": {
 //         "postfix": "arc",
@@ -98,7 +98,7 @@
 //         "scope": "expr"
 //     }
 // }
-// ----
+// ````
 
 use hir::{ModPath, Name, Symbol};
 use ide_db::imports::import_assets::LocatedImport;
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/tests/expression.rs b/src/tools/rust-analyzer/crates/ide-completion/src/tests/expression.rs
index 663a038580d..37557512837 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/tests/expression.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/tests/expression.rs
@@ -1986,3 +1986,53 @@ fn foo() {
         "#]],
     );
 }
+
+#[test]
+fn non_std_test_attr_macro() {
+    check(
+        r#"
+//- proc_macros: identity
+use proc_macros::identity as test;
+
+#[test]
+fn foo() {
+    $0
+}
+    "#,
+        expect![[r#"
+            fn foo()  fn()
+            md proc_macros
+            bt u32     u32
+            kw async
+            kw const
+            kw crate::
+            kw enum
+            kw extern
+            kw false
+            kw fn
+            kw for
+            kw if
+            kw if let
+            kw impl
+            kw let
+            kw loop
+            kw match
+            kw mod
+            kw return
+            kw self::
+            kw static
+            kw struct
+            kw trait
+            kw true
+            kw type
+            kw union
+            kw unsafe
+            kw use
+            kw while
+            kw while let
+            sn macro_rules
+            sn pd
+            sn ppd
+        "#]],
+    );
+}
diff --git a/src/tools/rust-analyzer/crates/ide-db/Cargo.toml b/src/tools/rust-analyzer/crates/ide-db/Cargo.toml
index 17f0e69bde4..c8a8a2d1698 100644
--- a/src/tools/rust-analyzer/crates/ide-db/Cargo.toml
+++ b/src/tools/rust-analyzer/crates/ide-db/Cargo.toml
@@ -30,7 +30,6 @@ bitflags.workspace = true
 
 # local deps
 base-db.workspace = true
-limit.workspace = true
 parser.workspace = true
 profile.workspace = true
 stdx.workspace = true
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 35e3a8d9bf7..46ff4fbf9e9 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
@@ -44,12 +44,11 @@ impl RootDatabase {
     //
     // Clears rust-analyzer's internal database and prints memory usage statistics.
     //
-    // |===
-    // | Editor  | Action Name
-    //
+    // | Editor  | Action Name |
+    // |---------|-------------|
     // | VS Code | **rust-analyzer: Memory Usage (Clears Database)**
-    // |===
-    // image::https://user-images.githubusercontent.com/48062697/113065592-08559f00-91b1-11eb-8c96-64b88068ec02.gif[]
+
+    // ![Memory Usage](https://user-images.githubusercontent.com/48062697/113065592-08559f00-91b1-11eb-8c96-64b88068ec02.gif)
     pub fn per_query_memory_usage(&mut self) -> Vec<(String, Bytes, usize)> {
         let mut acc: Vec<(String, Bytes, usize)> = vec![];
 
diff --git a/src/tools/rust-analyzer/crates/ide-db/src/defs.rs b/src/tools/rust-analyzer/crates/ide-db/src/defs.rs
index d12bda0816f..6f71c3d9bd7 100644
--- a/src/tools/rust-analyzer/crates/ide-db/src/defs.rs
+++ b/src/tools/rust-analyzer/crates/ide-db/src/defs.rs
@@ -108,7 +108,7 @@ impl Definition {
                 ItemContainer::Trait(it) => Some(it.into()),
                 ItemContainer::Impl(it) => Some(it.into()),
                 ItemContainer::Module(it) => Some(it.into()),
-                ItemContainer::ExternBlock() | ItemContainer::Crate(_) => None,
+                ItemContainer::ExternBlock(_) | ItemContainer::Crate(_) => None,
             }
         }
         match self {
@@ -986,6 +986,7 @@ impl From<GenericDef> for Definition {
             GenericDef::TypeAlias(it) => it.into(),
             GenericDef::Impl(it) => it.into(),
             GenericDef::Const(it) => it.into(),
+            GenericDef::Static(it) => it.into(),
         }
     }
 }
diff --git a/src/tools/rust-analyzer/crates/ide-db/src/generated/lints.rs b/src/tools/rust-analyzer/crates/ide-db/src/generated/lints.rs
index 14af22c3193..ed9d6c67501 100644
--- a/src/tools/rust-analyzer/crates/ide-db/src/generated/lints.rs
+++ b/src/tools/rust-analyzer/crates/ide-db/src/generated/lints.rs
@@ -9107,8 +9107,8 @@ The tracking issue for this feature is: [#27721]
         deny_since: None,
     },
     Lint {
-        label: "pattern_complexity",
-        description: r##"# `pattern_complexity`
+        label: "pattern_complexity_limit",
+        description: r##"# `pattern_complexity_limit`
 
 This feature has no tracking issue, and is therefore likely internal to the compiler, not being intended for general use.
 
diff --git a/src/tools/rust-analyzer/crates/ide-db/src/imports/import_assets.rs b/src/tools/rust-analyzer/crates/ide-db/src/imports/import_assets.rs
index ad86d855b55..77fc59b4ecc 100644
--- a/src/tools/rust-analyzer/crates/ide-db/src/imports/import_assets.rs
+++ b/src/tools/rust-analyzer/crates/ide-db/src/imports/import_assets.rs
@@ -357,7 +357,7 @@ fn path_applicable_imports(
                 let mod_path = mod_path(item)?;
                 Some(LocatedImport::new(mod_path, item, item))
             })
-            .take(DEFAULT_QUERY_SEARCH_LIMIT.inner())
+            .take(DEFAULT_QUERY_SEARCH_LIMIT)
             .collect()
         }
         // we have some unresolved qualifier that we search an import for
@@ -383,7 +383,7 @@ fn path_applicable_imports(
                 qualifier_rest,
             )
         })
-        .take(DEFAULT_QUERY_SEARCH_LIMIT.inner())
+        .take(DEFAULT_QUERY_SEARCH_LIMIT)
         .collect(),
     }
 }
diff --git a/src/tools/rust-analyzer/crates/ide-db/src/items_locator.rs b/src/tools/rust-analyzer/crates/ide-db/src/items_locator.rs
index a2062f36d3f..4d9c051354a 100644
--- a/src/tools/rust-analyzer/crates/ide-db/src/items_locator.rs
+++ b/src/tools/rust-analyzer/crates/ide-db/src/items_locator.rs
@@ -6,7 +6,6 @@ use std::ops::ControlFlow;
 
 use either::Either;
 use hir::{import_map, Crate, ItemInNs, Module, Semantics};
-use limit::Limit;
 
 use crate::{
     imports::import_assets::NameToImport,
@@ -15,7 +14,7 @@ use crate::{
 };
 
 /// A value to use, when uncertain which limit to pick.
-pub static DEFAULT_QUERY_SEARCH_LIMIT: Limit = Limit::new(100);
+pub const DEFAULT_QUERY_SEARCH_LIMIT: usize = 100;
 
 pub use import_map::AssocSearchMode;
 
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 0002fda0ba7..22dc3d9e29d 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
@@ -6,12 +6,13 @@ mod topologic_sort;
 
 use std::time::Duration;
 
-use hir::db::DefDatabase;
+use hir::{db::DefDatabase, Symbol};
+use itertools::Itertools;
 
 use crate::{
     base_db::{
         ra_salsa::{Database, ParallelDatabase, Snapshot},
-        Cancelled, CrateId, SourceDatabase, SourceRootDatabase,
+        Cancelled, CrateId, SourceDatabase,
     },
     symbol_index::SymbolsDatabase,
     FxIndexMap, RootDatabase,
@@ -21,11 +22,12 @@ use crate::{
 #[derive(Debug)]
 pub struct ParallelPrimeCachesProgress {
     /// the crates that we are currently priming.
-    pub crates_currently_indexing: Vec<String>,
+    pub crates_currently_indexing: Vec<Symbol>,
     /// the total number of crates we want to prime.
     pub crates_total: usize,
     /// the total number of crates that have finished priming
     pub crates_done: usize,
+    pub work_type: &'static str,
 }
 
 pub fn parallel_prime_caches(
@@ -47,41 +49,32 @@ pub fn parallel_prime_caches(
     };
 
     enum ParallelPrimeCacheWorkerProgress {
-        BeginCrate { crate_id: CrateId, crate_name: String },
+        BeginCrate { crate_id: CrateId, crate_name: Symbol },
         EndCrate { crate_id: CrateId },
     }
 
+    // We split off def map computation from other work,
+    // as the def map is the relevant one. Once the defmaps are computed
+    // the project is ready to go, the other indices are just nice to have for some IDE features.
+    #[derive(PartialOrd, Ord, PartialEq, Eq, Copy, Clone)]
+    enum PrimingPhase {
+        DefMap,
+        ImportMap,
+        CrateSymbols,
+    }
+
     let (work_sender, progress_receiver) = {
         let (progress_sender, progress_receiver) = crossbeam_channel::unbounded();
         let (work_sender, work_receiver) = crossbeam_channel::unbounded();
-        let graph = graph.clone();
-        let local_roots = db.local_roots();
         let prime_caches_worker = move |db: Snapshot<RootDatabase>| {
-            while let Ok((crate_id, crate_name)) = work_receiver.recv() {
+            while let Ok((crate_id, crate_name, kind)) = work_receiver.recv() {
                 progress_sender
                     .send(ParallelPrimeCacheWorkerProgress::BeginCrate { crate_id, crate_name })?;
 
-                // Compute the DefMap and possibly ImportMap
-                let file_id = graph[crate_id].root_file_id;
-                let root_id = db.file_source_root(file_id);
-                if db.source_root(root_id).is_library {
-                    db.crate_def_map(crate_id);
-                } else {
-                    // This also computes the DefMap
-                    db.import_map(crate_id);
-                }
-
-                // Compute the symbol search index.
-                // This primes the cache for `ide_db::symbol_index::world_symbols()`.
-                //
-                // We do this for workspace crates only (members of local_roots), because doing it
-                // for all dependencies could be *very* unnecessarily slow in a large project.
-                //
-                // FIXME: We should do it unconditionally if the configuration is set to default to
-                // searching dependencies (rust-analyzer.workspace.symbol.search.scope), but we
-                // would need to pipe that configuration information down here.
-                if local_roots.contains(&root_id) {
-                    db.crate_symbols(crate_id.into());
+                match kind {
+                    PrimingPhase::DefMap => _ = db.crate_def_map(crate_id),
+                    PrimingPhase::ImportMap => _ = db.import_map(crate_id),
+                    PrimingPhase::CrateSymbols => _ = db.crate_symbols(crate_id.into()),
                 }
 
                 progress_sender.send(ParallelPrimeCacheWorkerProgress::EndCrate { crate_id })?;
@@ -112,16 +105,34 @@ pub fn parallel_prime_caches(
     let mut crates_currently_indexing =
         FxIndexMap::with_capacity_and_hasher(num_worker_threads, Default::default());
 
+    let mut additional_phases = vec![];
+
     while crates_done < crates_total {
         db.unwind_if_cancelled();
 
         for crate_id in &mut crates_to_prime {
-            work_sender
-                .send((
-                    crate_id,
-                    graph[crate_id].display_name.as_deref().unwrap_or_default().to_owned(),
-                ))
-                .ok();
+            let krate = &graph[crate_id];
+            let name = krate
+                .display_name
+                .as_deref()
+                .cloned()
+                .unwrap_or_else(|| Symbol::integer(crate_id.into_raw().into_u32() as usize));
+            if krate.origin.is_lang() {
+                additional_phases.push((crate_id, name.clone(), PrimingPhase::ImportMap));
+            } else if krate.origin.is_local() {
+                // Compute the symbol search index.
+                // This primes the cache for `ide_db::symbol_index::world_symbols()`.
+                //
+                // We do this for workspace crates only (members of local_roots), because doing it
+                // for all dependencies could be *very* unnecessarily slow in a large project.
+                //
+                // FIXME: We should do it unconditionally if the configuration is set to default to
+                // searching dependencies (rust-analyzer.workspace.symbol.search.scope), but we
+                // would need to pipe that configuration information down here.
+                additional_phases.push((crate_id, name.clone(), PrimingPhase::CrateSymbols));
+            }
+
+            work_sender.send((crate_id, name, PrimingPhase::DefMap)).ok();
         }
 
         // recv_timeout is somewhat a hack, we need a way to from this thread check to see if the current salsa revision
@@ -153,6 +164,50 @@ pub fn parallel_prime_caches(
             crates_currently_indexing: crates_currently_indexing.values().cloned().collect(),
             crates_done,
             crates_total,
+            work_type: "Indexing",
+        };
+
+        cb(progress);
+    }
+
+    let mut crates_done = 0;
+    let crates_total = additional_phases.len();
+    for w in additional_phases.into_iter().sorted_by_key(|&(_, _, phase)| phase) {
+        work_sender.send(w).ok();
+    }
+
+    while crates_done < crates_total {
+        db.unwind_if_cancelled();
+
+        // recv_timeout is somewhat a hack, we need a way to from this thread check to see if the current salsa revision
+        // is cancelled on a regular basis. workers will only exit if they are processing a task that is cancelled, or
+        // if this thread exits, and closes the work channel.
+        let worker_progress = match progress_receiver.recv_timeout(Duration::from_millis(10)) {
+            Ok(p) => p,
+            Err(crossbeam_channel::RecvTimeoutError::Timeout) => {
+                continue;
+            }
+            Err(crossbeam_channel::RecvTimeoutError::Disconnected) => {
+                // our workers may have died from a cancelled task, so we'll check and re-raise here.
+                db.unwind_if_cancelled();
+                break;
+            }
+        };
+        match worker_progress {
+            ParallelPrimeCacheWorkerProgress::BeginCrate { crate_id, crate_name } => {
+                crates_currently_indexing.insert(crate_id, crate_name);
+            }
+            ParallelPrimeCacheWorkerProgress::EndCrate { crate_id } => {
+                crates_currently_indexing.swap_remove(&crate_id);
+                crates_done += 1;
+            }
+        };
+
+        let progress = ParallelPrimeCachesProgress {
+            crates_currently_indexing: crates_currently_indexing.values().cloned().collect(),
+            crates_done,
+            crates_total,
+            work_type: "Populating symbols",
         };
 
         cb(progress);
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 7fc563a4241..7963e8ae4f7 100644
--- a/src/tools/rust-analyzer/crates/ide-db/src/search.rs
+++ b/src/tools/rust-analyzer/crates/ide-db/src/search.rs
@@ -354,6 +354,7 @@ impl Definition {
                 hir::GenericDef::TypeAlias(it) => it.source(db).map(|src| src.syntax().cloned()),
                 hir::GenericDef::Impl(it) => it.source(db).map(|src| src.syntax().cloned()),
                 hir::GenericDef::Const(it) => it.source(db).map(|src| src.syntax().cloned()),
+                hir::GenericDef::Static(it) => it.source(db).map(|src| src.syntax().cloned()),
             };
             return match def {
                 Some(def) => SearchScope::file_range(
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 e5ce10a771e..bb4c289c908 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
@@ -193,11 +193,9 @@ impl<DB> std::ops::Deref for Snap<DB> {
 // `rust-analyzer.workspace.symbol.search.kind` settings. Symbols prefixed
 // with `__` are hidden from the search results unless configured otherwise.
 //
-// |===
-// | Editor  | Shortcut
-//
-// | VS Code | kbd:[Ctrl+T]
-// |===
+// | Editor  | Shortcut |
+// |---------|-----------|
+// | VS Code | <kbd>Ctrl+T</kbd>
 pub fn world_symbols(db: &RootDatabase, query: Query) -> Vec<FileSymbol> {
     let _p = tracing::info_span!("world_symbols", query = ?query.query).entered();
 
diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/incorrect_case.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/incorrect_case.rs
index bbdeb7cf085..246330e6efa 100644
--- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/incorrect_case.rs
+++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/incorrect_case.rs
@@ -12,7 +12,7 @@ use crate::{
 
 // Diagnostic: incorrect-ident-case
 //
-// This diagnostic is triggered if an item name doesn't follow https://doc.rust-lang.org/1.0.0/style/style/naming/README.html[Rust naming convention].
+// This diagnostic is triggered if an item name doesn't follow [Rust naming convention](https://doc.rust-lang.org/1.0.0/style/style/naming/README.html).
 pub(crate) fn incorrect_case(ctx: &DiagnosticsContext<'_>, d: &hir::IncorrectCase) -> Diagnostic {
     let code = match d.expected_case {
         CaseType::LowerSnakeCase => DiagnosticCode::RustcLint("non_snake_case"),
@@ -936,6 +936,7 @@ fn func() {
     fn override_lint_level() {
         check_diagnostics(
             r#"
+#![allow(unused_variables)]
 #[warn(nonstandard_style)]
 fn foo() {
     let BAR;
@@ -992,6 +993,7 @@ struct QUX;
 const foo: i32 = 0;
 fn BAR() {
     let BAZ;
+    _ = BAZ;
 }
         "#,
         );
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 c7cdcf49820..5730508436d 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
@@ -1129,4 +1129,39 @@ fn main() {
         "#,
         );
     }
+
+    #[test]
+    fn regression_18682() {
+        check_diagnostics(
+            r#"
+//- minicore: coerce_unsized
+struct Flexible {
+    body: [u8],
+}
+
+trait Field {
+    type Type: ?Sized;
+}
+
+impl Field for Flexible {
+    type Type = [u8];
+}
+
+trait KnownLayout {
+    type MaybeUninit: ?Sized;
+}
+
+
+impl<T> KnownLayout for [T] {
+    type MaybeUninit = [T];
+}
+
+struct ZerocopyKnownLayoutMaybeUninit(<<Flexible as Field>::Type as KnownLayout>::MaybeUninit);
+
+fn test(ptr: *mut [u8]) -> *mut ZerocopyKnownLayoutMaybeUninit {
+    ptr as *mut _
+}
+"#,
+        );
+    }
 }
diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/macro_error.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/macro_error.rs
index 99894fefef3..2f132985895 100644
--- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/macro_error.rs
+++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/macro_error.rs
@@ -133,7 +133,7 @@ macro_rules! env { () => {} }
 macro_rules! concat { () => {} }
 
   include!(concat!(env!("OUT_DIR"), "/out.rs"));
-                      //^^^^^^^^^ error: `OUT_DIR` not set, enable "build scripts" to fix
+                      //^^^^^^^^^ error: `OUT_DIR` not set, build scripts may have failed to run
 "#,
         );
     }
@@ -186,7 +186,7 @@ fn main() {
        //^^^^^^^ error: expected string literal
 
     env!("OUT_DIR");
-       //^^^^^^^^^ error: `OUT_DIR` not set, enable "build scripts" to fix
+       //^^^^^^^^^ error: `OUT_DIR` not set, build scripts may have failed to run
 
     compile_error!("compile_error works");
   //^^^^^^^^^^^^^ error: compile_error works
diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/mismatched_arg_count.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/mismatched_arg_count.rs
index 7126617cdee..0520bb3fe9b 100644
--- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/mismatched_arg_count.rs
+++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/mismatched_arg_count.rs
@@ -40,7 +40,7 @@ pub(crate) fn mismatched_arg_count(
     Diagnostic::new(
         DiagnosticCode::RustcHardError("E0107"),
         message,
-        invalid_args_range(ctx, d.call_expr.map(AstPtr::wrap_left), d.expected, d.found),
+        invalid_args_range(ctx, d.call_expr, d.expected, d.found),
     )
 }
 
diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_unsafe.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_unsafe.rs
index 8117401a534..323a5723d4a 100644
--- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_unsafe.rs
+++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_unsafe.rs
@@ -1,5 +1,5 @@
 use hir::db::ExpandDatabase;
-use hir::{HirFileIdExt, UnsafetyReason};
+use hir::{HirFileIdExt, UnsafeLint, UnsafetyReason};
 use ide_db::text_edit::TextEdit;
 use ide_db::{assists::Assist, source_change::SourceChange};
 use syntax::{ast, SyntaxNode};
@@ -11,10 +11,10 @@ use crate::{fix, Diagnostic, DiagnosticCode, DiagnosticsContext};
 //
 // This diagnostic is triggered if an operation marked as `unsafe` is used outside of an `unsafe` function or block.
 pub(crate) fn missing_unsafe(ctx: &DiagnosticsContext<'_>, d: &hir::MissingUnsafe) -> Diagnostic {
-    let code = if d.only_lint {
-        DiagnosticCode::RustcLint("unsafe_op_in_unsafe_fn")
-    } else {
-        DiagnosticCode::RustcHardError("E0133")
+    let code = match d.lint {
+        UnsafeLint::HardError => DiagnosticCode::RustcHardError("E0133"),
+        UnsafeLint::UnsafeOpInUnsafeFn => DiagnosticCode::RustcLint("unsafe_op_in_unsafe_fn"),
+        UnsafeLint::DeprecatedSafe2024 => DiagnosticCode::RustcLint("deprecated_safe_2024"),
     };
     let operation = display_unsafety_reason(d.reason);
     Diagnostic::new_with_syntax_node_ptr(
@@ -585,25 +585,59 @@ fn main() {
             r#"
 //- /ed2021.rs crate:ed2021 edition:2021
 #[rustc_deprecated_safe_2024]
-unsafe fn safe() -> u8 {
+unsafe fn deprecated_safe() -> u8 {
     0
 }
+
 //- /ed2024.rs crate:ed2024 edition:2024
 #[rustc_deprecated_safe_2024]
-unsafe fn not_safe() -> u8 {
+unsafe fn deprecated_safe() -> u8 {
     0
 }
-//- /main.rs crate:main deps:ed2021,ed2024
+
+//- /dep1.rs crate:dep1 deps:ed2021,ed2024 edition:2021
+fn main() {
+    ed2021::deprecated_safe();
+    ed2024::deprecated_safe();
+}
+
+//- /dep2.rs crate:dep2 deps:ed2021,ed2024 edition:2024
+fn main() {
+    ed2021::deprecated_safe();
+ // ^^^^^^^^^^^^^^^^^^^^^^^^^💡 error: call to unsafe function is unsafe and requires an unsafe function or block
+    ed2024::deprecated_safe();
+ // ^^^^^^^^^^^^^^^^^^^^^^^^^💡 error: call to unsafe function is unsafe and requires an unsafe function or block
+}
+
+//- /dep3.rs crate:dep3 deps:ed2021,ed2024 edition:2021
+#![warn(deprecated_safe)]
+
 fn main() {
-    ed2021::safe();
-    ed2024::not_safe();
-  //^^^^^^^^^^^^^^^^^^💡 error: call to unsafe function is unsafe and requires an unsafe function or block
+    ed2021::deprecated_safe();
+ // ^^^^^^^^^^^^^^^^^^^^^^^^^💡 warn: call to unsafe function is unsafe and requires an unsafe function or block
+    ed2024::deprecated_safe();
+ // ^^^^^^^^^^^^^^^^^^^^^^^^^💡 warn: call to unsafe function is unsafe and requires an unsafe function or block
 }
             "#,
         )
     }
 
     #[test]
+    fn orphan_unsafe_format_args() {
+        // Checks that we don't place orphan arguments for formatting under an unsafe block.
+        check_diagnostics(
+            r#"
+//- minicore: fmt
+fn foo() {
+    let p = 0xDEADBEEF as *const i32;
+    format_args!("", *p);
+                  // ^^ error: dereference of raw pointer is unsafe and requires an unsafe function or block
+}
+        "#,
+        );
+    }
+
+    #[test]
     fn unsafe_op_in_unsafe_fn_allowed_by_default_in_edition_2021() {
         check_diagnostics(
             r#"
@@ -812,4 +846,36 @@ fn main() {
 "#,
         )
     }
+
+    #[test]
+    fn target_feature() {
+        check_diagnostics(
+            r#"
+#[target_feature(enable = "avx")]
+fn foo() {}
+
+#[target_feature(enable = "avx,avx2")]
+fn bar() {
+    foo();
+}
+
+fn baz() {
+    foo();
+ // ^^^^^ 💡 error: call to unsafe function is unsafe and requires an unsafe function or block
+}
+        "#,
+        );
+    }
+
+    #[test]
+    fn unsafe_fn_ptr_call() {
+        check_diagnostics(
+            r#"
+fn f(it: unsafe fn()){
+    it();
+ // ^^^^ 💡 error: call to unsafe function is unsafe and requires an unsafe function or block
+}
+        "#,
+        );
+    }
 }
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 13979791444..0e3c4c7aa36 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
@@ -831,13 +831,14 @@ fn f() {
 
     #[test]
     fn or_pattern() {
-        // FIXME: `None` is inferred as unknown here for some reason
         check_diagnostics(
             r#"
 //- minicore: option
 fn f(_: i32) {}
 fn main() {
     let ((Some(mut x), None) | (_, Some(mut x))) = (None, Some(7)) else { return };
+             //^^^^^ 💡 warn: variable does not need to be mutable
+
     f(x);
 }
 "#,
diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/parenthesized_generic_args_without_fn_trait.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/parenthesized_generic_args_without_fn_trait.rs
new file mode 100644
index 00000000000..ccf51723418
--- /dev/null
+++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/parenthesized_generic_args_without_fn_trait.rs
@@ -0,0 +1,59 @@
+use crate::{Diagnostic, DiagnosticCode, DiagnosticsContext};
+
+// Diagnostic: parenthesized-generic-args-without-fn-trait
+//
+// This diagnostic is shown when a `Fn`-trait-style generic parameters (`Trait(A, B) -> C`)
+// was used on non-`Fn` trait/type.
+pub(crate) fn parenthesized_generic_args_without_fn_trait(
+    ctx: &DiagnosticsContext<'_>,
+    d: &hir::ParenthesizedGenericArgsWithoutFnTrait,
+) -> Diagnostic {
+    Diagnostic::new_with_syntax_node_ptr(
+        ctx,
+        DiagnosticCode::RustcHardError("E0214"),
+        "parenthesized type parameters may only be used with a `Fn` trait",
+        d.args.map(Into::into),
+    )
+}
+
+#[cfg(test)]
+mod tests {
+    use crate::tests::check_diagnostics;
+
+    #[test]
+    fn fn_traits_work() {
+        check_diagnostics(
+            r#"
+//- minicore: async_fn, fn
+fn foo<
+    A: Fn(),
+    B: FnMut() -> i32,
+    C: FnOnce(&str, bool),
+    D: AsyncFn::(u32) -> u32,
+    E: AsyncFnMut(),
+    F: AsyncFnOnce() -> bool,
+>() {}
+        "#,
+        );
+    }
+
+    #[test]
+    fn non_fn_trait() {
+        check_diagnostics(
+            r#"
+struct Struct<T>(T);
+enum Enum<T> { EnumVariant(T) }
+type TypeAlias<T> = bool;
+
+type Foo = TypeAlias() -> bool;
+                 // ^^ error: parenthesized type parameters may only be used with a `Fn` trait
+
+fn foo(_a: Struct(i32)) {
+              // ^^^^^ error: parenthesized type parameters may only be used with a `Fn` trait
+    let _ = <Enum::(u32)>::EnumVariant(0);
+              // ^^^^^^^ error: parenthesized type parameters may only be used with a `Fn` trait
+}
+        "#,
+        );
+    }
+}
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 56afb38cc81..7cf8282d052 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
@@ -1,5 +1,5 @@
 use either::Either;
-use hir::{db::ExpandDatabase, CallableKind, ClosureStyle, HirDisplay, HirFileIdExt, InFile, Type};
+use hir::{db::ExpandDatabase, CallableKind, ClosureStyle, HirDisplay, HirFileIdExt, InFile};
 use ide_db::{
     famous_defs::FamousDefs,
     source_change::{SourceChange, SourceChangeBuilder},
@@ -88,7 +88,7 @@ fn add_reference(
     let range = ctx.sema.diagnostics_display_range((*expr_ptr).map(|it| it.into()));
 
     let (_, mutability) = d.expected.as_reference()?;
-    let actual_with_ref = Type::reference(&d.actual, mutability);
+    let actual_with_ref = d.actual.add_reference(mutability);
     if !actual_with_ref.could_coerce_to(ctx.sema.db, &d.expected) {
         return None;
     }
@@ -1235,4 +1235,25 @@ fn f() {
 "#,
         );
     }
+
+    #[test]
+    fn complex_enum_variant_non_ref_pat() {
+        check_diagnostics(
+            r#"
+enum Enum { Variant }
+
+trait Trait {
+    type Assoc;
+}
+impl Trait for () {
+    type Assoc = Enum;
+}
+
+fn foo(v: &Enum) {
+    let <Enum>::Variant = v;
+    let <() as Trait>::Assoc::Variant = v;
+}
+    "#,
+        );
+    }
 }
diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unresolved_field.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unresolved_field.rs
index 4accd181ca4..dfb03eee732 100644
--- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unresolved_field.rs
+++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unresolved_field.rs
@@ -1,5 +1,6 @@
 use std::iter;
 
+use either::Either;
 use hir::{db::ExpandDatabase, Adt, FileRange, HasSource, HirDisplay, InFile, Struct, Union};
 use ide_db::text_edit::TextEdit;
 use ide_db::{
@@ -41,7 +42,7 @@ pub(crate) fn unresolved_field(
         ),
         adjusted_display_range(ctx, d.expr, &|expr| {
             Some(
-                match expr {
+                match expr.left()? {
                     ast::Expr::MethodCallExpr(it) => it.name_ref(),
                     ast::Expr::FieldExpr(it) => it.name_ref(),
                     _ => None,
@@ -72,7 +73,7 @@ fn fixes(ctx: &DiagnosticsContext<'_>, d: &hir::UnresolvedField) -> Option<Vec<A
 fn field_fix(ctx: &DiagnosticsContext<'_>, d: &hir::UnresolvedField) -> Option<Assist> {
     // Get the FileRange of the invalid field access
     let root = ctx.sema.db.parse_or_expand(d.expr.file_id);
-    let expr = d.expr.value.to_node(&root);
+    let expr = d.expr.value.to_node(&root).left()?;
 
     let error_range = ctx.sema.original_range_opt(expr.syntax())?;
     let field_name = d.name.as_str();
@@ -263,7 +264,7 @@ fn record_field_layout(
 // FIXME: We should fill out the call here, move the cursor and trigger signature help
 fn method_fix(
     ctx: &DiagnosticsContext<'_>,
-    expr_ptr: &InFile<AstPtr<ast::Expr>>,
+    expr_ptr: &InFile<AstPtr<Either<ast::Expr, ast::Pat>>>,
 ) -> Option<Assist> {
     let root = ctx.sema.db.parse_or_expand(expr_ptr.file_id);
     let expr = expr_ptr.value.to_node(&root);
diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unresolved_method.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unresolved_method.rs
index 4ab649cc162..e4de107249b 100644
--- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unresolved_method.rs
+++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unresolved_method.rs
@@ -1,4 +1,4 @@
-use hir::{db::ExpandDatabase, AssocItem, FileRange, HirDisplay, InFile};
+use hir::{db::ExpandDatabase, FileRange, HirDisplay, InFile};
 use ide_db::text_edit::TextEdit;
 use ide_db::{
     assists::{Assist, AssistId, AssistKind},
@@ -35,7 +35,7 @@ pub(crate) fn unresolved_method(
         ),
         adjusted_display_range(ctx, d.expr, &|expr| {
             Some(
-                match expr {
+                match expr.left()? {
                     ast::Expr::MethodCallExpr(it) => it.name_ref(),
                     ast::Expr::FieldExpr(it) => it.name_ref(),
                     _ => None,
@@ -85,7 +85,7 @@ fn field_fix(
     let expr_ptr = &d.expr;
     let root = ctx.sema.db.parse_or_expand(expr_ptr.file_id);
     let expr = expr_ptr.value.to_node(&root);
-    let (file_id, range) = match expr {
+    let (file_id, range) = match expr.left()? {
         ast::Expr::MethodCallExpr(mcall) => {
             let FileRange { range, file_id } =
                 ctx.sema.original_range_opt(mcall.receiver()?.syntax())?;
@@ -112,12 +112,12 @@ fn field_fix(
 }
 
 fn assoc_func_fix(ctx: &DiagnosticsContext<'_>, d: &hir::UnresolvedMethodCall) -> Option<Assist> {
-    if let Some(assoc_item_id) = d.assoc_func_with_same_name {
+    if let Some(f) = d.assoc_func_with_same_name {
         let db = ctx.sema.db;
 
         let expr_ptr = &d.expr;
         let root = db.parse_or_expand(expr_ptr.file_id);
-        let expr: ast::Expr = expr_ptr.value.to_node(&root);
+        let expr: ast::Expr = expr_ptr.value.to_node(&root).left()?;
 
         let call = ast::MethodCallExpr::cast(expr.syntax().clone())?;
         let range = InFile::new(expr_ptr.file_id, call.syntax().text_range())
@@ -127,30 +127,25 @@ fn assoc_func_fix(ctx: &DiagnosticsContext<'_>, d: &hir::UnresolvedMethodCall) -
         let receiver = call.receiver()?;
         let receiver_type = &ctx.sema.type_of_expr(&receiver)?.original;
 
-        let need_to_take_receiver_as_first_arg = match hir::AssocItem::from(assoc_item_id) {
-            AssocItem::Function(f) => {
-                let assoc_fn_params = f.assoc_fn_params(db);
-                if assoc_fn_params.is_empty() {
-                    false
-                } else {
-                    assoc_fn_params
-                        .first()
-                        .map(|first_arg| {
-                            // For generic type, say `Box`, take `Box::into_raw(b: Self)` as example,
-                            // type of `b` is `Self`, which is `Box<T, A>`, containing unspecified generics.
-                            // However, type of `receiver` is specified, it could be `Box<i32, Global>` or something like that,
-                            // so `first_arg.ty() == receiver_type` evaluate to `false` here.
-                            // Here add `first_arg.ty().as_adt() == receiver_type.as_adt()` as guard,
-                            // apply `.as_adt()` over `Box<T, A>` or `Box<i32, Global>` gets `Box`, so we get `true` here.
-
-                            // FIXME: it fails when type of `b` is `Box` with other generic param different from `receiver`
-                            first_arg.ty() == receiver_type
-                                || first_arg.ty().as_adt() == receiver_type.as_adt()
-                        })
-                        .unwrap_or(false)
-                }
-            }
-            _ => false,
+        let assoc_fn_params = f.assoc_fn_params(db);
+        let need_to_take_receiver_as_first_arg = if assoc_fn_params.is_empty() {
+            false
+        } else {
+            assoc_fn_params
+                .first()
+                .map(|first_arg| {
+                    // For generic type, say `Box`, take `Box::into_raw(b: Self)` as example,
+                    // type of `b` is `Self`, which is `Box<T, A>`, containing unspecified generics.
+                    // However, type of `receiver` is specified, it could be `Box<i32, Global>` or something like that,
+                    // so `first_arg.ty() == receiver_type` evaluate to `false` here.
+                    // Here add `first_arg.ty().as_adt() == receiver_type.as_adt()` as guard,
+                    // apply `.as_adt()` over `Box<T, A>` or `Box<i32, Global>` gets `Box`, so we get `true` here.
+
+                    // FIXME: it fails when type of `b` is `Box` with other generic param different from `receiver`
+                    first_arg.ty() == receiver_type
+                        || first_arg.ty().as_adt() == receiver_type.as_adt()
+                })
+                .unwrap_or(false)
         };
 
         let mut receiver_type_adt_name =
diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unused_variables.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unused_variables.rs
index 67ece566941..d5caf4de336 100644
--- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unused_variables.rs
+++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unused_variables.rs
@@ -263,4 +263,17 @@ fn main() {
 "#,
         );
     }
+
+    // regression test as we used to panic in this scenario
+    #[test]
+    fn unknown_struct_pattern_param_type() {
+        check_diagnostics(
+            r#"
+struct S { field : u32 }
+fn f(S { field }: error) {
+      // ^^^^^ 💡 warn: unused variable
+}
+"#,
+        );
+    }
 }
diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/lib.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/lib.rs
index 50c91a69602..3ea41aa7e85 100644
--- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/lib.rs
+++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/lib.rs
@@ -43,6 +43,7 @@ mod handlers {
     pub(crate) mod mutability_errors;
     pub(crate) mod no_such_field;
     pub(crate) mod non_exhaustive_let;
+    pub(crate) mod parenthesized_generic_args_without_fn_trait;
     pub(crate) mod private_assoc_item;
     pub(crate) mod private_field;
     pub(crate) mod remove_trailing_return;
@@ -466,7 +467,12 @@ pub fn semantic_diagnostics(
                 Some(it) => it,
                 None => continue,
             },
-            AnyDiagnostic::GenericArgsProhibited(d) => handlers::generic_args_prohibited::generic_args_prohibited(&ctx, &d)
+            AnyDiagnostic::GenericArgsProhibited(d) => {
+                handlers::generic_args_prohibited::generic_args_prohibited(&ctx, &d)
+            }
+            AnyDiagnostic::ParenthesizedGenericArgsWithoutFnTrait(d) => {
+                handlers::parenthesized_generic_args_without_fn_trait::parenthesized_generic_args_without_fn_trait(&ctx, &d)
+            }
         };
         res.push(d)
     }
diff --git a/src/tools/rust-analyzer/crates/ide-ssr/src/lib.rs b/src/tools/rust-analyzer/crates/ide-ssr/src/lib.rs
index 6b654f89345..889258c94c5 100644
--- a/src/tools/rust-analyzer/crates/ide-ssr/src/lib.rs
+++ b/src/tools/rust-analyzer/crates/ide-ssr/src/lib.rs
@@ -33,12 +33,10 @@
 //
 // Supported constraints:
 //
-// |===
-// | Constraint    | Restricts placeholder
-//
-// | kind(literal) | Is a literal (e.g. `42` or `"forty two"`)
-// | not(a)        | Negates the constraint `a`
-// |===
+// | Constraint    | Restricts placeholder |
+// |---------------|------------------------|
+// | kind(literal) | Is a literal (e.g. `42` or `"forty two"`) |
+// | not(a)        | Negates the constraint `a` |
 //
 // Available via the command `rust-analyzer.ssr`.
 //
@@ -52,11 +50,9 @@
 // String::from((y + 5).foo(z))
 // ```
 //
-// |===
-// | Editor  | Action Name
-//
-// | VS Code | **rust-analyzer: Structural Search Replace**
-// |===
+// | Editor  | Action Name |
+// |---------|--------------|
+// | VS Code | **rust-analyzer: Structural Search Replace** |
 //
 // Also available as an assist, by writing a comment containing the structural
 // search and replace rule. You will only see the assist if the comment can
diff --git a/src/tools/rust-analyzer/crates/ide/src/annotations.rs b/src/tools/rust-analyzer/crates/ide/src/annotations.rs
index 18f866eb9fc..e47891bbdfe 100644
--- a/src/tools/rust-analyzer/crates/ide/src/annotations.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/annotations.rs
@@ -1,6 +1,6 @@
 use hir::{HasSource, InFile, InRealFile, Semantics};
 use ide_db::{
-    defs::Definition, helpers::visit_file_defs, FileId, FilePosition, FileRange, FxHashSet,
+    defs::Definition, helpers::visit_file_defs, FileId, FilePosition, FileRange, FxIndexSet,
     RootDatabase,
 };
 use itertools::Itertools;
@@ -21,7 +21,7 @@ mod fn_references;
 // Provides user with annotations above items for looking up references or impl blocks
 // and running/debugging binaries.
 //
-// image::https://user-images.githubusercontent.com/48062697/113020672-b7c34f00-917a-11eb-8f6e-858735660a0e.png[]
+// ![Annotations](https://user-images.githubusercontent.com/48062697/113020672-b7c34f00-917a-11eb-8f6e-858735660a0e.png)
 #[derive(Debug, Hash, PartialEq, Eq)]
 pub struct Annotation {
     pub range: TextRange,
@@ -55,7 +55,7 @@ pub(crate) fn annotations(
     config: &AnnotationConfig,
     file_id: FileId,
 ) -> Vec<Annotation> {
-    let mut annotations = FxHashSet::default();
+    let mut annotations = FxIndexSet::default();
 
     if config.annotate_runnables {
         for runnable in runnables(db, file_id) {
@@ -170,7 +170,12 @@ pub(crate) fn annotations(
         }));
     }
 
-    annotations.into_iter().sorted_by_key(|a| (a.range.start(), a.range.end())).collect()
+    annotations
+        .into_iter()
+        .sorted_by_key(|a| {
+            (a.range.start(), a.range.end(), matches!(a.kind, AnnotationKind::Runnable(..)))
+        })
+        .collect()
 }
 
 pub(crate) fn resolve_annotation(db: &RootDatabase, mut annotation: Annotation) -> Annotation {
@@ -537,6 +542,20 @@ fn main() {
                     },
                     Annotation {
                         range: 69..73,
+                        kind: HasReferences {
+                            pos: FilePositionWrapper {
+                                file_id: FileId(
+                                    0,
+                                ),
+                                offset: 69,
+                            },
+                            data: Some(
+                                [],
+                            ),
+                        },
+                    },
+                    Annotation {
+                        range: 69..73,
                         kind: Runnable(
                             Runnable {
                                 use_name_in_title: false,
@@ -559,20 +578,6 @@ fn main() {
                             },
                         ),
                     },
-                    Annotation {
-                        range: 69..73,
-                        kind: HasReferences {
-                            pos: FilePositionWrapper {
-                                file_id: FileId(
-                                    0,
-                                ),
-                                offset: 69,
-                            },
-                            data: Some(
-                                [],
-                            ),
-                        },
-                    },
                 ]
             "#]],
         );
@@ -719,6 +724,20 @@ fn main() {
                     },
                     Annotation {
                         range: 61..65,
+                        kind: HasReferences {
+                            pos: FilePositionWrapper {
+                                file_id: FileId(
+                                    0,
+                                ),
+                                offset: 61,
+                            },
+                            data: Some(
+                                [],
+                            ),
+                        },
+                    },
+                    Annotation {
+                        range: 61..65,
                         kind: Runnable(
                             Runnable {
                                 use_name_in_title: false,
@@ -741,20 +760,6 @@ fn main() {
                             },
                         ),
                     },
-                    Annotation {
-                        range: 61..65,
-                        kind: HasReferences {
-                            pos: FilePositionWrapper {
-                                file_id: FileId(
-                                    0,
-                                ),
-                                offset: 61,
-                            },
-                            data: Some(
-                                [],
-                            ),
-                        },
-                    },
                 ]
             "#]],
         );
diff --git a/src/tools/rust-analyzer/crates/ide/src/doc_links.rs b/src/tools/rust-analyzer/crates/ide/src/doc_links.rs
index cfd8919730a..e35e47e7471 100644
--- a/src/tools/rust-analyzer/crates/ide/src/doc_links.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/doc_links.rs
@@ -122,11 +122,9 @@ pub(crate) fn remove_links(markdown: &str) -> String {
 // The simplest way to use this feature is via the context menu. Right-click on
 // the selected item. The context menu opens. Select **Open Docs**.
 //
-// |===
-// | Editor  | Action Name
-//
-// | VS Code | **rust-analyzer: Open Docs**
-// |===
+// | Editor  | Action Name |
+// |---------|-------------|
+// | VS Code | **rust-analyzer: Open Docs** |
 pub(crate) fn external_docs(
     db: &RootDatabase,
     FilePosition { file_id, offset }: FilePosition,
diff --git a/src/tools/rust-analyzer/crates/ide/src/expand_macro.rs b/src/tools/rust-analyzer/crates/ide/src/expand_macro.rs
index 0ad894427b2..ad4308e06a1 100644
--- a/src/tools/rust-analyzer/crates/ide/src/expand_macro.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/expand_macro.rs
@@ -19,13 +19,11 @@ pub struct ExpandedMacro {
 //
 // Shows the full macro expansion of the macro at the current caret position.
 //
-// |===
-// | Editor  | Action Name
+// | Editor  | Action Name |
+// |---------|-------------|
+// | VS Code | **rust-analyzer: Expand macro recursively at caret** |
 //
-// | VS Code | **rust-analyzer: Expand macro recursively at caret**
-// |===
-//
-// image::https://user-images.githubusercontent.com/48062697/113020648-b3973180-917a-11eb-84a9-ecb921293dc5.gif[]
+// ![Expand Macro Recursively](https://user-images.githubusercontent.com/48062697/113020648-b3973180-917a-11eb-84a9-ecb921293dc5.gif)
 pub(crate) fn expand_macro(db: &RootDatabase, position: FilePosition) -> Option<ExpandedMacro> {
     let sema = Semantics::new(db);
     let file = sema.parse_guess_edition(position.file_id);
diff --git a/src/tools/rust-analyzer/crates/ide/src/extend_selection.rs b/src/tools/rust-analyzer/crates/ide/src/extend_selection.rs
index 3d49082f285..76414854e91 100644
--- a/src/tools/rust-analyzer/crates/ide/src/extend_selection.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/extend_selection.rs
@@ -17,13 +17,11 @@ use crate::FileRange;
 // Extends or shrinks the current selection to the encompassing syntactic construct
 // (expression, statement, item, module, etc). It works with multiple cursors.
 //
-// |===
-// | Editor  | Shortcut
+// | Editor  | Shortcut |
+// |---------|----------|
+// | VS Code | <kbd>Alt+Shift+→</kbd>, <kbd>Alt+Shift+←</kbd> |
 //
-// | VS Code | kbd:[Alt+Shift+→], kbd:[Alt+Shift+←]
-// |===
-//
-// image::https://user-images.githubusercontent.com/48062697/113020651-b42fc800-917a-11eb-8a4f-cf1a07859fac.gif[]
+// ![Expand and Shrink Selection](https://user-images.githubusercontent.com/48062697/113020651-b42fc800-917a-11eb-8a4f-cf1a07859fac.gif)
 pub(crate) fn extend_selection(db: &RootDatabase, frange: FileRange) -> TextRange {
     let sema = Semantics::new(db);
     let src = sema.parse_guess_edition(frange.file_id);
diff --git a/src/tools/rust-analyzer/crates/ide/src/fetch_crates.rs b/src/tools/rust-analyzer/crates/ide/src/fetch_crates.rs
index 37b3cb03b33..5ed21444307 100644
--- a/src/tools/rust-analyzer/crates/ide/src/fetch_crates.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/fetch_crates.rs
@@ -14,13 +14,11 @@ pub struct CrateInfo {
 //
 // Shows a view tree with all the dependencies of this project
 //
-// |===
-// | Editor  | Panel Name
+// | Editor  | Panel Name |
+// |---------|------------|
+// | VS Code | **Rust Dependencies** |
 //
-// | VS Code | **Rust Dependencies**
-// |===
-//
-// image::https://user-images.githubusercontent.com/5748995/229394139-2625beab-f4c9-484b-84ed-ad5dee0b1e1a.png[]
+// ![Show Dependency Tree](https://user-images.githubusercontent.com/5748995/229394139-2625beab-f4c9-484b-84ed-ad5dee0b1e1a.png)
 pub(crate) fn fetch_crates(db: &RootDatabase) -> FxIndexSet<CrateInfo> {
     let crate_graph = db.crate_graph();
     crate_graph
diff --git a/src/tools/rust-analyzer/crates/ide/src/file_structure.rs b/src/tools/rust-analyzer/crates/ide/src/file_structure.rs
index 50977ee840c..52fbab6fa12 100644
--- a/src/tools/rust-analyzer/crates/ide/src/file_structure.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/file_structure.rs
@@ -31,14 +31,11 @@ pub enum StructureNodeKind {
 // * draw breadcrumbs to describe the context around the cursor
 // * draw outline of the file
 //
-// |===
-// | Editor  | Shortcut
+// | Editor  | Shortcut |
+// |---------|----------|
+// | VS Code | <kbd>Ctrl+Shift+O</kbd> |
 //
-// | VS Code | kbd:[Ctrl+Shift+O]
-// |===
-//
-// image::https://user-images.githubusercontent.com/48062697/113020654-b42fc800-917a-11eb-8388-e7dc4d92b02e.gif[]
-
+// ![File Structure](https://user-images.githubusercontent.com/48062697/113020654-b42fc800-917a-11eb-8388-e7dc4d92b02e.gif)
 pub(crate) fn file_structure(file: &SourceFile) -> Vec<StructureNode> {
     let mut res = Vec::new();
     let mut stack = Vec::new();
diff --git a/src/tools/rust-analyzer/crates/ide/src/goto_definition.rs b/src/tools/rust-analyzer/crates/ide/src/goto_definition.rs
index d18732a6b84..60a904233a9 100644
--- a/src/tools/rust-analyzer/crates/ide/src/goto_definition.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/goto_definition.rs
@@ -31,13 +31,11 @@ use syntax::{
 //
 // For outline modules, this will navigate to the source file of the module.
 //
-// |===
-// | Editor  | Shortcut
+// | Editor  | Shortcut |
+// |---------|----------|
+// | VS Code | <kbd>F12</kbd> |
 //
-// | VS Code | kbd:[F12]
-// |===
-//
-// image::https://user-images.githubusercontent.com/48062697/113065563-025fbe00-91b1-11eb-83e4-a5a703610b23.gif[]
+// ![Go to Definition](https://user-images.githubusercontent.com/48062697/113065563-025fbe00-91b1-11eb-83e4-a5a703610b23.gif)
 pub(crate) fn goto_definition(
     db: &RootDatabase,
     FilePosition { file_id, offset }: FilePosition,
@@ -3274,4 +3272,56 @@ fn f() {
         "#,
         );
     }
+
+    #[test]
+    fn use_inside_body() {
+        check(
+            r#"
+fn main() {
+    mod nice_module {
+        pub(super) struct NiceStruct;
+                       // ^^^^^^^^^^
+    }
+
+    use nice_module::NiceStruct$0;
+
+    let _ = NiceStruct;
+}
+    "#,
+        );
+    }
+
+    #[test]
+    fn shadow_builtin_type_by_module() {
+        check(
+            r#"
+mod Foo{
+pub mod str {
+     // ^^^
+    pub fn foo() {}
+}
+}
+
+fn main() {
+    use Foo::str;
+    let s = st$0r::foo();
+}
+"#,
+        );
+    }
+
+    #[test]
+    fn not_goto_module_because_str_is_builtin_type() {
+        check(
+            r#"
+mod str {
+pub fn foo() {}
+}
+
+fn main() {
+    let s = st$0r::f();
+}
+"#,
+        );
+    }
 }
diff --git a/src/tools/rust-analyzer/crates/ide/src/goto_implementation.rs b/src/tools/rust-analyzer/crates/ide/src/goto_implementation.rs
index e926378367e..e1d834b5d1c 100644
--- a/src/tools/rust-analyzer/crates/ide/src/goto_implementation.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/goto_implementation.rs
@@ -12,13 +12,11 @@ use crate::{FilePosition, NavigationTarget, RangeInfo, TryToNav};
 //
 // Navigates to the impl items of types.
 //
-// |===
-// | Editor  | Shortcut
+// | Editor  | Shortcut |
+// |---------|----------|
+// | VS Code | <kbd>Ctrl+F12</kbd>
 //
-// | VS Code | kbd:[Ctrl+F12]
-// |===
-//
-// image::https://user-images.githubusercontent.com/48062697/113065566-02f85480-91b1-11eb-9288-aaad8abd8841.gif[]
+// ![Go to Implementation](https://user-images.githubusercontent.com/48062697/113065566-02f85480-91b1-11eb-9288-aaad8abd8841.gif)
 pub(crate) fn goto_implementation(
     db: &RootDatabase,
     FilePosition { file_id, offset }: FilePosition,
diff --git a/src/tools/rust-analyzer/crates/ide/src/goto_type_definition.rs b/src/tools/rust-analyzer/crates/ide/src/goto_type_definition.rs
index 2610d6c8863..ddc274a8303 100644
--- a/src/tools/rust-analyzer/crates/ide/src/goto_type_definition.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/goto_type_definition.rs
@@ -8,13 +8,11 @@ use crate::{FilePosition, NavigationTarget, RangeInfo, TryToNav};
 //
 // Navigates to the type of an identifier.
 //
-// |===
-// | Editor  | Action Name
+// | Editor  | Action Name |
+// |---------|-------------|
+// | VS Code | **Go to Type Definition** |
 //
-// | VS Code | **Go to Type Definition**
-// |===
-//
-// image::https://user-images.githubusercontent.com/48062697/113020657-b560f500-917a-11eb-9007-0f809733a338.gif[]
+// ![Go to Type Definition](https://user-images.githubusercontent.com/48062697/113020657-b560f500-917a-11eb-9007-0f809733a338.gif)
 pub(crate) fn goto_type_definition(
     db: &RootDatabase,
     FilePosition { file_id, offset }: FilePosition,
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 612bc36f628..6463206596a 100644
--- a/src/tools/rust-analyzer/crates/ide/src/highlight_related.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/highlight_related.rs
@@ -43,12 +43,12 @@ pub struct HighlightRelatedConfig {
 //
 // Highlights constructs related to the thing under the cursor:
 //
-// . if on an identifier, highlights all references to that identifier in the current file
-// .. additionally, if the identifier is a trait in a where clause, type parameter trait bound or use item, highlights all references to that trait's assoc items in the corresponding scope
-// . if on an `async` or `await` token, highlights all yield points for that async context
-// . if on a `return` or `fn` keyword, `?` character or `->` return type arrow, highlights all exit points for that context
-// . if on a `break`, `loop`, `while` or `for` token, highlights all break points for that loop or block context
-// . if on a `move` or `|` token that belongs to a closure, highlights all captures of the closure.
+// 1. if on an identifier, highlights all references to that identifier in the current file
+//      * additionally, if the identifier is a trait in a where clause, type parameter trait bound or use item, highlights all references to that trait's assoc items in the corresponding scope
+// 1. if on an `async` or `await` token, highlights all yield points for that async context
+// 1. if on a `return` or `fn` keyword, `?` character or `->` return type arrow, highlights all exit points for that context
+// 1. if on a `break`, `loop`, `while` or `for` token, highlights all break points for that loop or block context
+// 1. if on a `move` or `|` token that belongs to a closure, highlights all captures of the closure.
 //
 // Note: `?`, `|` and `->` do not currently trigger this behavior in the VSCode editor.
 pub(crate) fn highlight_related(
diff --git a/src/tools/rust-analyzer/crates/ide/src/hover.rs b/src/tools/rust-analyzer/crates/ide/src/hover.rs
index 9d4c103fc2e..95a720e7e45 100644
--- a/src/tools/rust-analyzer/crates/ide/src/hover.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/hover.rs
@@ -118,7 +118,7 @@ pub struct HoverResult {
 // Shows additional information, like the type of an expression or the documentation for a definition when "focusing" code.
 // Focusing is usually hovering with a mouse, but can also be triggered with a shortcut.
 //
-// image::https://user-images.githubusercontent.com/48062697/113020658-b5f98b80-917a-11eb-9f88-3dbc27320c95.gif[]
+// ![Hover](https://user-images.githubusercontent.com/48062697/113020658-b5f98b80-917a-11eb-9f88-3dbc27320c95.gif)
 pub(crate) fn hover(
     db: &RootDatabase,
     frange @ FileRange { file_id, range }: FileRange,
diff --git a/src/tools/rust-analyzer/crates/ide/src/hover/render.rs b/src/tools/rust-analyzer/crates/ide/src/hover/render.rs
index 40f3406b72d..c996230c3a1 100644
--- a/src/tools/rust-analyzer/crates/ide/src/hover/render.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/hover/render.rs
@@ -434,6 +434,7 @@ fn definition_owner_name(db: &RootDatabase, def: Definition, edition: Edition) -
                     None => it.name(db),
                 }
             }
+            hir::GenericDef::Static(it) => Some(it.name(db)),
         },
         Definition::DeriveHelper(derive_helper) => Some(derive_helper.derive().name(db)),
         d => {
diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints.rs
index 1f723c85df7..63039b1cd34 100644
--- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints.rs
@@ -59,7 +59,7 @@ mod range_exclusive;
 //
 // Note: inlay hints for function argument names are heuristically omitted to reduce noise and will not appear if
 // any of the
-// link:https://github.com/rust-lang/rust-analyzer/blob/6b8b8ff4c56118ddee6c531cde06add1aad4a6af/crates/ide/src/inlay_hints/param_name.rs#L92-L99[following criteria]
+// [following criteria](https://github.com/rust-lang/rust-analyzer/blob/6b8b8ff4c56118ddee6c531cde06add1aad4a6af/crates/ide/src/inlay_hints/param_name.rs#L92-L99)
 // are met:
 //
 // * the parameter name is a suffix of the function's name
@@ -68,13 +68,13 @@ mod range_exclusive;
 //   of argument with _ splitting it off
 // * the parameter name starts with `ra_fixture`
 // * the parameter name is a
-// link:https://github.com/rust-lang/rust-analyzer/blob/6b8b8ff4c56118ddee6c531cde06add1aad4a6af/crates/ide/src/inlay_hints/param_name.rs#L200[well known name]
+// [well known name](https://github.com/rust-lang/rust-analyzer/blob/6b8b8ff4c56118ddee6c531cde06add1aad4a6af/crates/ide/src/inlay_hints/param_name.rs#L200)
 // in a unary function
 // * the parameter name is a
-// link:https://github.com/rust-lang/rust-analyzer/blob/6b8b8ff4c56118ddee6c531cde06add1aad4a6af/crates/ide/src/inlay_hints/param_name.rs#L201[single character]
+// [single character](https://github.com/rust-lang/rust-analyzer/blob/6b8b8ff4c56118ddee6c531cde06add1aad4a6af/crates/ide/src/inlay_hints/param_name.rs#L201)
 // in a unary function
 //
-// image::https://user-images.githubusercontent.com/48062697/113020660-b5f98b80-917a-11eb-8d70-3be3fd558cdd.png[]
+// ![Inlay hints](https://user-images.githubusercontent.com/48062697/113020660-b5f98b80-917a-11eb-8d70-3be3fd558cdd.png)
 pub(crate) fn inlay_hints(
     db: &RootDatabase,
     file_id: FileId,
@@ -294,6 +294,7 @@ pub struct InlayHintsConfig {
     pub param_names_for_lifetime_elision_hints: bool,
     pub hide_named_constructor_hints: bool,
     pub hide_closure_initialization_hints: bool,
+    pub hide_closure_parameter_hints: bool,
     pub range_exclusive_hints: bool,
     pub closure_style: ClosureStyle,
     pub max_length: Option<usize>,
@@ -860,6 +861,7 @@ mod tests {
         binding_mode_hints: false,
         hide_named_constructor_hints: false,
         hide_closure_initialization_hints: false,
+        hide_closure_parameter_hints: false,
         closure_style: ClosureStyle::ImplFn,
         param_names_for_lifetime_elision_hints: false,
         max_length: None,
diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/adjustment.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/adjustment.rs
index 2acd4021cc1..d3b95750f7e 100644
--- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/adjustment.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/adjustment.rs
@@ -13,11 +13,7 @@ use ide_db::famous_defs::FamousDefs;
 
 use ide_db::text_edit::TextEditBuilder;
 use span::EditionedFileId;
-use stdx::never;
-use syntax::{
-    ast::{self, make, AstNode},
-    ted,
-};
+use syntax::ast::{self, prec::ExprPrecedence, AstNode};
 
 use crate::{
     AdjustmentHints, AdjustmentHintsMode, InlayHint, InlayHintLabel, InlayHintLabelPart,
@@ -104,12 +100,14 @@ pub(super) fn hints(
     };
     let iter: &mut dyn Iterator<Item = _> = iter.as_mut().either(|it| it as _, |it| it as _);
 
+    let mut has_adjustments = false;
     let mut allow_edit = !postfix;
     for Adjustment { source, target, kind } in iter {
         if source == target {
             cov_mark::hit!(same_type_adjustment);
             continue;
         }
+        has_adjustments = true;
 
         // FIXME: Add some nicer tooltips to each of these
         let (text, coercion) = match kind {
@@ -172,6 +170,10 @@ pub(super) fn hints(
         };
         if postfix { &mut post } else { &mut pre }.label.append_part(label);
     }
+    if !has_adjustments {
+        return None;
+    }
+
     if !postfix && needs_inner_parens {
         pre.label.append_str("(");
     }
@@ -254,71 +256,31 @@ fn mode_and_needs_parens_for_adjustment_hints(
 /// Returns whatever we need to add parentheses on the inside and/or outside of `expr`,
 /// if we are going to add (`postfix`) adjustments hints to it.
 fn needs_parens_for_adjustment_hints(expr: &ast::Expr, postfix: bool) -> (bool, bool) {
-    // This is a very miserable pile of hacks...
-    //
-    // `Expr::needs_parens_in` requires that the expression is the child of the other expression,
-    // that is supposed to be its parent.
-    //
-    // But we want to check what would happen if we add `*`/`.*` to the inner expression.
-    // To check for inner we need `` expr.needs_parens_in(`*expr`) ``,
-    // to check for outer we need `` `*expr`.needs_parens_in(parent) ``,
-    // where "expr" is the `expr` parameter, `*expr` is the edited `expr`,
-    // and "parent" is the parent of the original expression...
-    //
-    // For this we utilize mutable trees, which is a HACK, but it works.
-    //
-    // FIXME: comeup with a better API for `needs_parens_in`, so that we don't have to do *this*
-
-    // Make `&expr`/`expr?`
-    let dummy_expr = {
-        // `make::*` function go through a string, so they parse wrongly.
-        // for example `` make::expr_try(`|| a`) `` would result in a
-        // `|| (a?)` and not `(|| a)?`.
-        //
-        // Thus we need dummy parens to preserve the relationship we want.
-        // The parens are then simply ignored by the following code.
-        let dummy_paren = make::expr_paren(expr.clone());
-        if postfix {
-            make::expr_try(dummy_paren)
-        } else {
-            make::expr_ref(dummy_paren, false)
-        }
-    };
-
-    // Do the dark mutable tree magic.
-    // This essentially makes `dummy_expr` and `expr` switch places (families),
-    // so that `expr`'s parent is not `dummy_expr`'s parent.
-    let dummy_expr = dummy_expr.clone_for_update();
-    let expr = expr.clone_for_update();
-    ted::replace(expr.syntax(), dummy_expr.syntax());
-
-    let parent = dummy_expr.syntax().parent();
-    let Some(expr) = (|| {
-        if postfix {
-            let ast::Expr::TryExpr(e) = &dummy_expr else { return None };
-            let Some(ast::Expr::ParenExpr(e)) = e.expr() else { return None };
-
-            e.expr()
-        } else {
-            let ast::Expr::RefExpr(e) = &dummy_expr else { return None };
-            let Some(ast::Expr::ParenExpr(e)) = e.expr() else { return None };
-
-            e.expr()
-        }
-    })() else {
-        never!("broken syntax tree?\n{:?}\n{:?}", expr, dummy_expr);
-        return (true, true);
-    };
-
-    // At this point
-    // - `parent`     is the parent of the original expression
-    // - `dummy_expr` is the original expression wrapped in the operator we want (`*`/`.*`)
-    // - `expr`       is the clone of the original expression (with `dummy_expr` as the parent)
-
-    let needs_outer_parens = parent.is_some_and(|p| dummy_expr.needs_parens_in(p));
-    let needs_inner_parens = expr.needs_parens_in(dummy_expr.syntax().clone());
-
-    (needs_outer_parens, needs_inner_parens)
+    let prec = expr.precedence();
+    if postfix {
+        // postfix ops have higher precedence than any other operator, so we need to wrap
+        // any inner expression that is below (except for jumps if they don't have a value)
+        let needs_inner_parens = prec < ExprPrecedence::Unambiguous && {
+            prec != ExprPrecedence::Jump || !expr.is_ret_like_with_no_value()
+        };
+        // given we are the higher precedence, no parent expression will have stronger requirements
+        let needs_outer_parens = false;
+        (needs_outer_parens, needs_inner_parens)
+    } else {
+        // We need to wrap all binary like things, thats everything below prefix except for jumps
+        let needs_inner_parens = prec < ExprPrecedence::Prefix && prec != ExprPrecedence::Jump;
+        let parent = expr
+            .syntax()
+            .parent()
+            .and_then(ast::Expr::cast)
+            // if we are already wrapped, great, no need to wrap again
+            .filter(|it| !matches!(it, ast::Expr::ParenExpr(_)))
+            .map(|it| it.precedence());
+        // if we have no parent, we don't need outer parens to disambiguate
+        // otherwise anything with higher precedence than what we insert needs to wrap us
+        let needs_outer_parens = parent.is_some_and(|prec| prec > ExprPrecedence::Prefix);
+        (needs_outer_parens, needs_inner_parens)
+    }
 }
 
 #[cfg(test)]
diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/bind_pat.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/bind_pat.rs
index 01a1a4545c4..c2986a9aa66 100644
--- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/bind_pat.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/bind_pat.rs
@@ -36,6 +36,9 @@ pub(super) fn hints(
                 if it.ty().is_some() {
                     return None;
                 }
+                if config.hide_closure_parameter_hints && it.syntax().ancestors().nth(2).is_none_or(|n| matches!(ast::Expr::cast(n), Some(ast::Expr::ClosureExpr(_)))) {
+                    return None;
+                }
                 Some(it.colon_token())
             },
             ast::LetStmt(it) => {
@@ -950,6 +953,36 @@ fn bar(f: impl FnOnce(u8) -> u8) -> impl FnOnce(u8) -> u8 {
     }
 
     #[test]
+    fn skip_closure_parameter_hints() {
+        check_with_config(
+            InlayHintsConfig {
+                type_hints: true,
+                hide_closure_parameter_hints: true,
+                ..DISABLED_CONFIG
+            },
+            r#"
+//- minicore: fn
+struct Foo;
+impl Foo {
+    fn foo(self: Self) {}
+    fn bar(self: &Self) {}
+}
+fn main() {
+    let closure = |x, y| x + y;
+    //  ^^^^^^^ impl Fn(i32, i32) -> {unknown}
+    closure(2, 3);
+    let point = (10, 20);
+    //  ^^^^^ (i32, i32)
+    let (x,      y) = point;
+      // ^ i32   ^ i32
+    Foo::foo(Foo);
+    Foo::bar(&Foo);
+}
+"#,
+        );
+    }
+
+    #[test]
     fn hint_truncation() {
         check_with_config(
             InlayHintsConfig { max_length: Some(8), ..TEST_CONFIG },
diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/closure_captures.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/closure_captures.rs
index 3e91618d08e..9b981c0a3ac 100644
--- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/closure_captures.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/closure_captures.rs
@@ -1,4 +1,4 @@
-//! Implementation of "closure return type" inlay hints.
+//! Implementation of "closure captures" inlay hints.
 //!
 //! Tests live in [`bind_pat`][super::bind_pat] module.
 use ide_db::famous_defs::FamousDefs;
diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/extern_block.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/extern_block.rs
index 2bc91b68ed8..652dff0bc56 100644
--- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/extern_block.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/extern_block.rs
@@ -7,7 +7,7 @@ use crate::{InlayHint, InlayHintsConfig};
 
 pub(super) fn extern_block_hints(
     acc: &mut Vec<InlayHint>,
-    FamousDefs(_sema, _): &FamousDefs<'_, '_>,
+    FamousDefs(sema, _): &FamousDefs<'_, '_>,
     config: &InlayHintsConfig,
     _file_id: EditionedFileId,
     extern_block: ast::ExternBlock,
@@ -16,6 +16,7 @@ pub(super) fn extern_block_hints(
         return None;
     }
     let abi = extern_block.abi()?;
+    sema.to_def(&extern_block)?;
     acc.push(InlayHint {
         range: abi.syntax().text_range(),
         position: crate::InlayHintPosition::Before,
@@ -33,7 +34,7 @@ pub(super) fn extern_block_hints(
 
 pub(super) fn fn_hints(
     acc: &mut Vec<InlayHint>,
-    FamousDefs(_sema, _): &FamousDefs<'_, '_>,
+    FamousDefs(sema, _): &FamousDefs<'_, '_>,
     config: &InlayHintsConfig,
     _file_id: EditionedFileId,
     fn_: &ast::Fn,
@@ -43,14 +44,16 @@ pub(super) fn fn_hints(
     if !implicit_unsafe {
         return None;
     }
-    let fn_ = fn_.fn_token()?;
-    acc.push(item_hint(config, extern_block, fn_));
+    let fn_token = fn_.fn_token()?;
+    if sema.to_def(fn_).is_some_and(|def| def.extern_block(sema.db).is_some()) {
+        acc.push(item_hint(config, extern_block, fn_token));
+    }
     Some(())
 }
 
 pub(super) fn static_hints(
     acc: &mut Vec<InlayHint>,
-    FamousDefs(_sema, _): &FamousDefs<'_, '_>,
+    FamousDefs(sema, _): &FamousDefs<'_, '_>,
     config: &InlayHintsConfig,
     _file_id: EditionedFileId,
     static_: &ast::Static,
@@ -60,8 +63,10 @@ pub(super) fn static_hints(
     if !implicit_unsafe {
         return None;
     }
-    let static_ = static_.static_token()?;
-    acc.push(item_hint(config, extern_block, static_));
+    let static_token = static_.static_token()?;
+    if sema.to_def(static_).is_some_and(|def| def.extern_block(sema.db).is_some()) {
+        acc.push(item_hint(config, extern_block, static_token));
+    }
     Some(())
 }
 
diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/implicit_drop.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/implicit_drop.rs
index 27c7c3d4981..390139d214e 100644
--- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/implicit_drop.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/implicit_drop.rs
@@ -54,7 +54,8 @@ pub(super) fn hints(
             };
             let range = match terminator.span {
                 MirSpan::ExprId(e) => match source_map.expr_syntax(e) {
-                    Ok(s) => {
+                    // don't show inlay hint for macro
+                    Ok(s) if !s.file_id.is_macro() => {
                         let root = &s.file_syntax(sema.db);
                         let expr = s.value.to_node(root);
                         let expr = expr.syntax();
@@ -69,11 +70,11 @@ pub(super) fn hints(
                             }
                         }
                     }
-                    Err(_) => continue,
+                    _ => continue,
                 },
                 MirSpan::PatId(p) => match source_map.pat_syntax(p) {
-                    Ok(s) => s.value.text_range(),
-                    Err(_) => continue,
+                    Ok(s) if !s.file_id.is_macro() => s.value.text_range(),
+                    _ => continue,
                 },
                 MirSpan::BindingId(b) => {
                     match source_map
@@ -81,13 +82,13 @@ pub(super) fn hints(
                         .iter()
                         .find_map(|p| source_map.pat_syntax(*p).ok())
                     {
-                        Some(s) => s.value.text_range(),
-                        None => continue,
+                        Some(s) if !s.file_id.is_macro() => s.value.text_range(),
+                        _ => continue,
                     }
                 }
                 MirSpan::SelfParam => match source_map.self_param_syntax() {
-                    Some(s) => s.value.text_range(),
-                    None => continue,
+                    Some(s) if !s.file_id.is_macro() => s.value.text_range(),
+                    _ => continue,
                 },
                 MirSpan::Unknown => continue,
             };
@@ -231,4 +232,25 @@ mod tests {
 "#,
         );
     }
+
+    #[test]
+    fn ignore_inlay_hint_for_macro_call() {
+        check_with_config(
+            ONLY_DROP_CONFIG,
+            r#"
+    struct X;
+
+    macro_rules! my_macro {
+        () => {{
+            let bbb = X;
+            bbb
+        }};
+    }
+
+    fn test() -> X {
+        my_macro!()
+    }
+"#,
+        );
+    }
 }
diff --git a/src/tools/rust-analyzer/crates/ide/src/interpret.rs b/src/tools/rust-analyzer/crates/ide/src/interpret.rs
index e0fdc3dd6f9..ae11072e34b 100644
--- a/src/tools/rust-analyzer/crates/ide/src/interpret.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/interpret.rs
@@ -7,11 +7,9 @@ use syntax::{algo::ancestors_at_offset, ast, AstNode, TextRange};
 
 // Feature: Interpret A Function, Static Or Const.
 //
-// |===
-// | Editor  | Action Name
-//
-// | VS Code | **rust-analyzer: Interpret**
-// |===
+// | Editor  | Action Name |
+// |---------|-------------|
+// | VS Code | **rust-analyzer: Interpret** |
 pub(crate) fn interpret(db: &RootDatabase, position: FilePosition) -> String {
     match find_and_interpret(db, position) {
         Some((duration, mut result)) => {
diff --git a/src/tools/rust-analyzer/crates/ide/src/join_lines.rs b/src/tools/rust-analyzer/crates/ide/src/join_lines.rs
index e4670177ecf..ea18a97070c 100644
--- a/src/tools/rust-analyzer/crates/ide/src/join_lines.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/join_lines.rs
@@ -21,17 +21,13 @@ pub struct JoinLinesConfig {
 //
 // Join selected lines into one, smartly fixing up whitespace, trailing commas, and braces.
 //
-// See
-// https://user-images.githubusercontent.com/1711539/124515923-4504e800-dde9-11eb-8d58-d97945a1a785.gif[this gif]
-// for the cases handled specially by joined lines.
+// See [this gif](https://user-images.githubusercontent.com/1711539/124515923-4504e800-dde9-11eb-8d58-d97945a1a785.gif) for the cases handled specially by joined lines.
 //
-// |===
-// | Editor  | Action Name
+// | Editor  | Action Name |
+// |---------|-------------|
+// | VS Code | **rust-analyzer: Join lines** |
 //
-// | VS Code | **rust-analyzer: Join lines**
-// |===
-//
-// image::https://user-images.githubusercontent.com/48062697/113020661-b6922200-917a-11eb-87c4-b75acc028f11.gif[]
+// ![Join Lines](https://user-images.githubusercontent.com/48062697/113020661-b6922200-917a-11eb-87c4-b75acc028f11.gif)
 pub(crate) fn join_lines(
     config: &JoinLinesConfig,
     file: &SourceFile,
diff --git a/src/tools/rust-analyzer/crates/ide/src/lib.rs b/src/tools/rust-analyzer/crates/ide/src/lib.rs
index e942f5a6aac..27a1a510b4f 100644
--- a/src/tools/rust-analyzer/crates/ide/src/lib.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/lib.rs
@@ -252,14 +252,14 @@ impl Analysis {
             Arc::new(cfg_options),
             None,
             Env::default(),
-            false,
             CrateOrigin::Local { repo: None, name: None },
+            false,
+            None,
         );
         change.change_file(file_id, Some(text));
         let ws_data = crate_graph
             .iter()
             .zip(iter::repeat(Arc::new(CrateWorkspaceData {
-                proc_macro_cwd: None,
                 data_layout: Err("fixture has no layout".into()),
                 toolchain: None,
             })))
diff --git a/src/tools/rust-analyzer/crates/ide/src/matching_brace.rs b/src/tools/rust-analyzer/crates/ide/src/matching_brace.rs
index 57356152836..67346ea9cf9 100644
--- a/src/tools/rust-analyzer/crates/ide/src/matching_brace.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/matching_brace.rs
@@ -9,13 +9,11 @@ use syntax::{
 // moves cursor to the matching brace. It uses the actual parser to determine
 // braces, so it won't confuse generics with comparisons.
 //
-// |===
-// | Editor  | Action Name
+// | Editor  | Action Name |
+// |---------|-------------|
+// | VS Code | **rust-analyzer: Find matching brace** |
 //
-// | VS Code | **rust-analyzer: Find matching brace**
-// |===
-//
-// image::https://user-images.githubusercontent.com/48062697/113065573-04298180-91b1-11eb-8dec-d4e2a202f304.gif[]
+// ![Matching Brace](https://user-images.githubusercontent.com/48062697/113065573-04298180-91b1-11eb-8dec-d4e2a202f304.gif)
 pub(crate) fn matching_brace(file: &SourceFile, offset: TextSize) -> Option<TextSize> {
     const BRACES: &[SyntaxKind] =
         &[T!['{'], T!['}'], T!['['], T![']'], T!['('], T![')'], T![<], T![>], T![|], T![|]];
diff --git a/src/tools/rust-analyzer/crates/ide/src/moniker.rs b/src/tools/rust-analyzer/crates/ide/src/moniker.rs
index d97c12ebafb..66ea49a98a0 100644
--- a/src/tools/rust-analyzer/crates/ide/src/moniker.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/moniker.rs
@@ -289,7 +289,10 @@ fn def_to_non_local_moniker(
     definition: Definition,
     from_crate: Crate,
 ) -> Option<Moniker> {
-    let module = definition.module(db)?;
+    let module = match definition {
+        Definition::Module(module) if module.is_crate_root() => module,
+        _ => definition.module(db)?,
+    };
     let krate = module.krate();
     let edition = krate.edition(db);
 
@@ -322,12 +325,18 @@ fn def_to_non_local_moniker(
                         name: name.display(db, edition).to_string(),
                         desc: def_to_kind(db, def).into(),
                     });
-                } else if reverse_description.is_empty() {
-                    // Don't allow the last descriptor to be absent.
-                    return None;
                 } else {
                     match def {
-                        Definition::Module(module) if module.is_crate_root() => {}
+                        Definition::Module(module) if module.is_crate_root() => {
+                            // only include `crate` namespace by itself because we prefer
+                            // `rust-analyzer cargo foo . bar/` over `rust-analyzer cargo foo . crate/bar/`
+                            if reverse_description.is_empty() {
+                                reverse_description.push(MonikerDescriptor {
+                                    name: "crate".to_owned(),
+                                    desc: MonikerDescriptorKind::Namespace,
+                                });
+                            }
+                        }
                         _ => {
                             tracing::error!(?def, "Encountered enclosing definition with no name");
                         }
@@ -340,6 +349,9 @@ fn def_to_non_local_moniker(
         };
         def = next_def;
     }
+    if reverse_description.is_empty() {
+        return None;
+    }
     reverse_description.reverse();
     let description = reverse_description;
 
diff --git a/src/tools/rust-analyzer/crates/ide/src/move_item.rs b/src/tools/rust-analyzer/crates/ide/src/move_item.rs
index b0df9257ba1..3fb3a788b91 100644
--- a/src/tools/rust-analyzer/crates/ide/src/move_item.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/move_item.rs
@@ -17,14 +17,12 @@ pub enum Direction {
 //
 // Move item under cursor or selection up and down.
 //
-// |===
-// | Editor  | Action Name
-//
+// | Editor  | Action Name |
+// |---------|-------------|
 // | VS Code | **rust-analyzer: Move item up**
 // | VS Code | **rust-analyzer: Move item down**
-// |===
 //
-// image::https://user-images.githubusercontent.com/48062697/113065576-04298180-91b1-11eb-91ce-4505e99ed598.gif[]
+// ![Move Item](https://user-images.githubusercontent.com/48062697/113065576-04298180-91b1-11eb-91ce-4505e99ed598.gif)
 pub(crate) fn move_item(
     db: &RootDatabase,
     range: FileRange,
diff --git a/src/tools/rust-analyzer/crates/ide/src/parent_module.rs b/src/tools/rust-analyzer/crates/ide/src/parent_module.rs
index 7a0c28d925a..6d82f9b0634 100644
--- a/src/tools/rust-analyzer/crates/ide/src/parent_module.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/parent_module.rs
@@ -15,13 +15,11 @@ use crate::NavigationTarget;
 //
 // Navigates to the parent module of the current module.
 //
-// |===
-// | Editor  | Action Name
+// | Editor  | Action Name |
+// |---------|-------------|
+// | VS Code | **rust-analyzer: Locate parent module** |
 //
-// | VS Code | **rust-analyzer: Locate parent module**
-// |===
-//
-// image::https://user-images.githubusercontent.com/48062697/113065580-04c21800-91b1-11eb-9a32-00086161c0bd.gif[]
+// ![Parent Module](https://user-images.githubusercontent.com/48062697/113065580-04c21800-91b1-11eb-9a32-00086161c0bd.gif)
 
 /// This returns `Vec` because a module may be included from several places.
 pub(crate) fn parent_module(db: &RootDatabase, position: FilePosition) -> Vec<NavigationTarget> {
diff --git a/src/tools/rust-analyzer/crates/ide/src/references.rs b/src/tools/rust-analyzer/crates/ide/src/references.rs
index b1079312d3b..069818d50e7 100644
--- a/src/tools/rust-analyzer/crates/ide/src/references.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/references.rs
@@ -43,13 +43,11 @@ pub struct Declaration {
 //
 // Shows all references of the item at the cursor location
 //
-// |===
-// | Editor  | Shortcut
+// | Editor  | Shortcut |
+// |---------|----------|
+// | VS Code | <kbd>Shift+Alt+F12</kbd> |
 //
-// | VS Code | kbd:[Shift+Alt+F12]
-// |===
-//
-// image::https://user-images.githubusercontent.com/48062697/113020670-b7c34f00-917a-11eb-8003-370ac5f2b3cb.gif[]
+// ![Find All References](https://user-images.githubusercontent.com/48062697/113020670-b7c34f00-917a-11eb-8003-370ac5f2b3cb.gif)
 pub(crate) fn find_all_refs(
     sema: &Semantics<'_, RootDatabase>,
     position: FilePosition,
diff --git a/src/tools/rust-analyzer/crates/ide/src/rename.rs b/src/tools/rust-analyzer/crates/ide/src/rename.rs
index 07dfd83c4eb..3e8295e3f08 100644
--- a/src/tools/rust-analyzer/crates/ide/src/rename.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/rename.rs
@@ -71,13 +71,11 @@ pub(crate) fn prepare_rename(
 //
 // Renames the item below the cursor and all of its references
 //
-// |===
-// | Editor  | Shortcut
+// | Editor  | Shortcut |
+// |---------|----------|
+// | VS Code | <kbd>F2</kbd> |
 //
-// | VS Code | kbd:[F2]
-// |===
-//
-// image::https://user-images.githubusercontent.com/48062697/113065582-055aae80-91b1-11eb-8ade-2b58e6d81883.gif[]
+// ![Rename](https://user-images.githubusercontent.com/48062697/113065582-055aae80-91b1-11eb-8ade-2b58e6d81883.gif)
 pub(crate) fn rename(
     db: &RootDatabase,
     position: FilePosition,
@@ -2003,13 +2001,11 @@ impl Foo {
             "foo",
             r#"
 fn f($0self) -> i32 {
-    use self as _;
     self.i
 }
 "#,
             r#"
 fn f(foo: _) -> i32 {
-    use self as _;
     foo.i
 }
 "#,
@@ -2017,6 +2013,26 @@ fn f(foo: _) -> i32 {
     }
 
     #[test]
+    fn no_type_value_ns_confuse() {
+        // Test that we don't rename items from different namespaces.
+        check(
+            "bar",
+            r#"
+struct foo {}
+fn f(foo$0: i32) -> i32 {
+    use foo as _;
+}
+"#,
+            r#"
+struct foo {}
+fn f(bar: i32) -> i32 {
+    use foo as _;
+}
+"#,
+        );
+    }
+
+    #[test]
     fn test_self_in_path_to_parameter() {
         check(
             "foo",
diff --git a/src/tools/rust-analyzer/crates/ide/src/runnables.rs b/src/tools/rust-analyzer/crates/ide/src/runnables.rs
index 32edacee51c..509ae3204c3 100644
--- a/src/tools/rust-analyzer/crates/ide/src/runnables.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/runnables.rs
@@ -4,8 +4,8 @@ use arrayvec::ArrayVec;
 use ast::HasName;
 use cfg::{CfgAtom, CfgExpr};
 use hir::{
-    db::HirDatabase, sym, AsAssocItem, AttrsWithOwner, HasAttrs, HasCrate, HasSource, HirFileIdExt,
-    ModPath, Name, PathKind, Semantics, Symbol,
+    db::HirDatabase, sym, symbols::FxIndexSet, AsAssocItem, AttrsWithOwner, HasAttrs, HasCrate,
+    HasSource, HirFileIdExt, ModPath, Name, PathKind, Semantics, Symbol,
 };
 use ide_assists::utils::{has_test_related_attribute, test_related_attribute_syn};
 use ide_db::{
@@ -13,7 +13,7 @@ use ide_db::{
     documentation::docs_from_attrs,
     helpers::visit_file_defs,
     search::{FileReferenceNode, SearchScope},
-    FilePosition, FxHashMap, FxHashSet, RootDatabase, SymbolKind,
+    FilePosition, FxHashMap, FxIndexMap, RootDatabase, SymbolKind,
 };
 use itertools::Itertools;
 use smallvec::SmallVec;
@@ -61,8 +61,8 @@ pub enum RunnableKind {
 
 #[derive(Debug, Clone, Hash, PartialEq, Eq, PartialOrd, Ord)]
 enum RunnableDiscKind {
-    Test,
     TestMod,
+    Test,
     DocTest,
     Bench,
     Bin,
@@ -119,19 +119,18 @@ impl Runnable {
 // location**. Super useful for repeatedly running just a single test. Do bind this
 // to a shortcut!
 //
-// |===
-// | Editor  | Action Name
+// | Editor  | Action Name |
+// |---------|-------------|
+// | VS Code | **rust-analyzer: Run** |
 //
-// | VS Code | **rust-analyzer: Run**
-// |===
-// image::https://user-images.githubusercontent.com/48062697/113065583-055aae80-91b1-11eb-958f-d67efcaf6a2f.gif[]
+// ![Run](https://user-images.githubusercontent.com/48062697/113065583-055aae80-91b1-11eb-958f-d67efcaf6a2f.gif)
 pub(crate) fn runnables(db: &RootDatabase, file_id: FileId) -> Vec<Runnable> {
     let sema = Semantics::new(db);
 
     let mut res = Vec::new();
     // Record all runnables that come from macro expansions here instead.
     // In case an expansion creates multiple runnables we want to name them to avoid emitting a bunch of equally named runnables.
-    let mut in_macro_expansion = FxHashMap::<hir::HirFileId, Vec<Runnable>>::default();
+    let mut in_macro_expansion = FxIndexMap::<hir::HirFileId, Vec<Runnable>>::default();
     let mut add_opt = |runnable: Option<Runnable>, def| {
         if let Some(runnable) = runnable.filter(|runnable| runnable.nav.file_id == file_id) {
             if let Some(def) = def {
@@ -183,20 +182,7 @@ pub(crate) fn runnables(db: &RootDatabase, file_id: FileId) -> Vec<Runnable> {
             r
         })
     }));
-    res.sort_by(|Runnable { nav, kind, .. }, Runnable { nav: nav_b, kind: kind_b, .. }| {
-        // full_range.start < focus_range.start < name, should give us a decent unique ordering
-        nav.full_range
-            .start()
-            .cmp(&nav_b.full_range.start())
-            .then_with(|| {
-                let t_0 = || TextSize::from(0);
-                nav.focus_range
-                    .map_or_else(t_0, |it| it.start())
-                    .cmp(&nav_b.focus_range.map_or_else(t_0, |it| it.start()))
-            })
-            .then_with(|| kind.disc().cmp(&kind_b.disc()))
-            .then_with(|| nav.name.cmp(&nav_b.name))
-    });
+    res.sort_by(cmp_runnables);
     res
 }
 
@@ -207,23 +193,39 @@ pub(crate) fn runnables(db: &RootDatabase, file_id: FileId) -> Vec<Runnable> {
 // The simplest way to use this feature is via the context menu. Right-click on
 // the selected item. The context menu opens. Select **Peek Related Tests**.
 //
-// |===
-// | Editor  | Action Name
-//
-// | VS Code | **rust-analyzer: Peek Related Tests**
-// |===
+// | Editor  | Action Name |
+// |---------|-------------|
+// | VS Code | **rust-analyzer: Peek Related Tests** |
 pub(crate) fn related_tests(
     db: &RootDatabase,
     position: FilePosition,
     search_scope: Option<SearchScope>,
 ) -> Vec<Runnable> {
     let sema = Semantics::new(db);
-    let mut res: FxHashSet<Runnable> = FxHashSet::default();
+    let mut res: FxIndexSet<Runnable> = FxIndexSet::default();
     let syntax = sema.parse_guess_edition(position.file_id).syntax().clone();
 
     find_related_tests(&sema, &syntax, position, search_scope, &mut res);
 
-    res.into_iter().collect()
+    res.into_iter().sorted_by(cmp_runnables).collect()
+}
+
+fn cmp_runnables(
+    Runnable { nav, kind, .. }: &Runnable,
+    Runnable { nav: nav_b, kind: kind_b, .. }: &Runnable,
+) -> std::cmp::Ordering {
+    // full_range.start < focus_range.start < name, should give us a decent unique ordering
+    nav.full_range
+        .start()
+        .cmp(&nav_b.full_range.start())
+        .then_with(|| {
+            let t_0 = || TextSize::from(0);
+            nav.focus_range
+                .map_or_else(t_0, |it| it.start())
+                .cmp(&nav_b.focus_range.map_or_else(t_0, |it| it.start()))
+        })
+        .then_with(|| kind.disc().cmp(&kind_b.disc()))
+        .then_with(|| nav.name.cmp(&nav_b.name))
 }
 
 fn find_related_tests(
@@ -231,7 +233,7 @@ fn find_related_tests(
     syntax: &SyntaxNode,
     position: FilePosition,
     search_scope: Option<SearchScope>,
-    tests: &mut FxHashSet<Runnable>,
+    tests: &mut FxIndexSet<Runnable>,
 ) {
     // FIXME: why is this using references::find_defs, this should use ide_db::search
     let defs = match references::find_defs(sema, syntax, position.offset) {
@@ -271,7 +273,7 @@ fn find_related_tests_in_module(
     syntax: &SyntaxNode,
     fn_def: &ast::Fn,
     parent_module: &hir::Module,
-    tests: &mut FxHashSet<Runnable>,
+    tests: &mut FxIndexSet<Runnable>,
 ) {
     let fn_name = match fn_def.name() {
         Some(it) => it,
@@ -1231,8 +1233,8 @@ gen_main!();
                     "(TestMod, NavigationTarget { file_id: FileId(0), full_range: 0..315, name: \"\", kind: Module })",
                     "(TestMod, NavigationTarget { file_id: FileId(0), full_range: 267..292, focus_range: 271..276, name: \"tests\", kind: Module, description: \"mod tests\" })",
                     "(Test, NavigationTarget { file_id: FileId(0), full_range: 283..290, name: \"foo_test\", kind: Function })",
-                    "(Test, NavigationTarget { file_id: FileId(0), full_range: 293..301, name: \"foo_test2\", kind: Function }, true)",
                     "(TestMod, NavigationTarget { file_id: FileId(0), full_range: 293..301, name: \"tests2\", kind: Module, description: \"mod tests2\" }, true)",
+                    "(Test, NavigationTarget { file_id: FileId(0), full_range: 293..301, name: \"foo_test2\", kind: Function }, true)",
                     "(Bin, NavigationTarget { file_id: FileId(0), full_range: 302..314, name: \"main\", kind: Function })",
                 ]
             "#]],
@@ -1261,10 +1263,10 @@ foo!();
 "#,
             expect![[r#"
                 [
+                    "(TestMod, NavigationTarget { file_id: FileId(0), full_range: 210..217, name: \"foo_tests\", kind: Module, description: \"mod foo_tests\" }, true)",
                     "(Test, NavigationTarget { file_id: FileId(0), full_range: 210..217, name: \"foo0\", kind: Function }, true)",
                     "(Test, NavigationTarget { file_id: FileId(0), full_range: 210..217, name: \"foo1\", kind: Function }, true)",
                     "(Test, NavigationTarget { file_id: FileId(0), full_range: 210..217, name: \"foo2\", kind: Function }, true)",
-                    "(TestMod, NavigationTarget { file_id: FileId(0), full_range: 210..217, name: \"foo_tests\", kind: Module, description: \"mod foo_tests\" }, true)",
                 ]
             "#]],
         );
@@ -1504,18 +1506,18 @@ mod tests {
                         file_id: FileId(
                             0,
                         ),
-                        full_range: 121..185,
-                        focus_range: 136..145,
-                        name: "foo2_test",
+                        full_range: 52..115,
+                        focus_range: 67..75,
+                        name: "foo_test",
                         kind: Function,
                     },
                     NavigationTarget {
                         file_id: FileId(
                             0,
                         ),
-                        full_range: 52..115,
-                        focus_range: 67..75,
-                        name: "foo_test",
+                        full_range: 121..185,
+                        focus_range: 136..145,
+                        name: "foo2_test",
                         kind: Function,
                     },
                 ]
diff --git a/src/tools/rust-analyzer/crates/ide/src/signature_help.rs b/src/tools/rust-analyzer/crates/ide/src/signature_help.rs
index f8c60418eb0..f9972116004 100644
--- a/src/tools/rust-analyzer/crates/ide/src/signature_help.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/signature_help.rs
@@ -321,7 +321,9 @@ fn signature_help_for_generics(
             format_to!(res.signature, "type {}", it.name(db).display(db, edition));
         }
         // These don't have generic args that can be specified
-        hir::GenericDef::Impl(_) | hir::GenericDef::Const(_) => return None,
+        hir::GenericDef::Impl(_) | hir::GenericDef::Const(_) | hir::GenericDef::Static(_) => {
+            return None
+        }
     }
 
     let params = generics_def.params(sema.db);
diff --git a/src/tools/rust-analyzer/crates/ide/src/static_index.rs b/src/tools/rust-analyzer/crates/ide/src/static_index.rs
index 8050a38b3ca..07553a87d28 100644
--- a/src/tools/rust-analyzer/crates/ide/src/static_index.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/static_index.rs
@@ -154,6 +154,7 @@ impl StaticIndex<'_> {
                     implicit_drop_hints: false,
                     hide_named_constructor_hints: false,
                     hide_closure_initialization_hints: false,
+                    hide_closure_parameter_hints: false,
                     closure_style: hir::ClosureStyle::ImplFn,
                     param_names_for_lifetime_elision_hints: false,
                     binding_mode_hints: false,
@@ -169,10 +170,10 @@ impl StaticIndex<'_> {
             .unwrap();
         // hovers
         let sema = hir::Semantics::new(self.db);
-        let tokens_or_nodes = sema.parse_guess_edition(file_id).syntax().clone();
+        let root = sema.parse_guess_edition(file_id).syntax().clone();
         let edition =
             sema.attach_first_edition(file_id).map(|it| it.edition()).unwrap_or(Edition::CURRENT);
-        let tokens = tokens_or_nodes.descendants_with_tokens().filter_map(|it| match it {
+        let tokens = root.descendants_with_tokens().filter_map(|it| match it {
             syntax::NodeOrToken::Node(_) => None,
             syntax::NodeOrToken::Token(it) => Some(it),
         });
@@ -194,24 +195,19 @@ impl StaticIndex<'_> {
             )
         });
         let mut result = StaticIndexedFile { file_id, inlay_hints, folds, tokens: vec![] };
-        for token in tokens {
-            let range = token.text_range();
-            let node = token.parent().unwrap();
-            let def = match get_definition(&sema, token.clone()) {
-                Some(it) => it,
-                None => continue,
-            };
+
+        let mut add_token = |def: Definition, range: TextRange, scope_node: &SyntaxNode| {
             let id = if let Some(it) = self.def_map.get(&def) {
                 *it
             } else {
                 let it = self.tokens.insert(TokenStaticData {
-                    documentation: documentation_for_definition(&sema, def, &node),
+                    documentation: documentation_for_definition(&sema, def, scope_node),
                     hover: Some(hover_for_definition(
                         &sema,
                         file_id,
                         def,
                         None,
-                        &node,
+                        scope_node,
                         None,
                         false,
                         &hover_config,
@@ -240,6 +236,22 @@ impl StaticIndex<'_> {
                 },
             });
             result.tokens.push((range, id));
+        };
+
+        if let Some(module) = sema.file_to_module_def(file_id) {
+            let def = Definition::Module(module);
+            let range = root.text_range();
+            add_token(def, range, &root);
+        }
+
+        for token in tokens {
+            let range = token.text_range();
+            let node = token.parent().unwrap();
+            let def = match get_definition(&sema, token.clone()) {
+                Some(it) => it,
+                None => continue,
+            };
+            add_token(def, range, &node);
         }
         self.files.push(result);
     }
@@ -300,6 +312,10 @@ mod tests {
         let mut range_set: FxHashSet<_> = ranges.iter().map(|it| it.0).collect();
         for f in s.files {
             for (range, _) in f.tokens {
+                if range.start() == TextSize::from(0) {
+                    // ignore whole file range corresponding to module definition
+                    continue;
+                }
                 let it = FileRange { file_id: f.file_id, range };
                 if !range_set.contains(&it) {
                     panic!("additional range {it:?}");
diff --git a/src/tools/rust-analyzer/crates/ide/src/status.rs b/src/tools/rust-analyzer/crates/ide/src/status.rs
index 9e823daa2be..f8ecaa8fdf2 100644
--- a/src/tools/rust-analyzer/crates/ide/src/status.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/status.rs
@@ -29,12 +29,11 @@ use triomphe::Arc;
 //
 // Shows internal statistic about memory usage of rust-analyzer.
 //
-// |===
-// | Editor  | Action Name
+// | Editor  | Action Name |
+// |---------|-------------|
+// | VS Code | **rust-analyzer: Status** |
 //
-// | VS Code | **rust-analyzer: Status**
-// |===
-// image::https://user-images.githubusercontent.com/48062697/113065584-05f34500-91b1-11eb-98cc-5c196f76be7f.gif[]
+// ![Status](https://user-images.githubusercontent.com/48062697/113065584-05f34500-91b1-11eb-98cc-5c196f76be7f.gif)
 pub(crate) fn status(db: &RootDatabase, file_id: Option<FileId>) -> String {
     let mut buf = String::new();
 
@@ -69,6 +68,7 @@ pub(crate) fn status(db: &RootDatabase, file_id: Option<FileId>) -> String {
                 dependencies,
                 origin,
                 is_proc_macro,
+                proc_macro_cwd,
             } = &crate_graph[crate_id];
             format_to!(
                 buf,
@@ -86,6 +86,7 @@ pub(crate) fn status(db: &RootDatabase, file_id: Option<FileId>) -> String {
             format_to!(buf, "    Env: {:?}\n", env);
             format_to!(buf, "    Origin: {:?}\n", origin);
             format_to!(buf, "    Is a proc macro crate: {}\n", is_proc_macro);
+            format_to!(buf, "    Proc macro cwd: {:?}\n", proc_macro_cwd);
             let deps = dependencies
                 .iter()
                 .map(|dep| format!("{}={}", dep.name, dep.crate_id.into_raw()))
diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting.rs b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting.rs
index f53f0aec098..1853e3a3407 100644
--- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting.rs
@@ -76,113 +76,118 @@ pub struct HighlightConfig {
 // We also give special modifier for `mut` and `&mut` local variables.
 //
 //
-// .Token Tags
+// #### Token Tags
 //
 // Rust-analyzer currently emits the following token tags:
 //
 // - For items:
-// +
-// [horizontal]
-// attribute:: Emitted for attribute macros.
-// enum:: Emitted for enums.
-// function:: Emitted for free-standing functions.
-// derive:: Emitted for derive macros.
-// macro:: Emitted for function-like macros.
-// method:: Emitted for associated functions, also knowns as methods.
-// namespace:: Emitted for modules.
-// struct:: Emitted for structs.
-// trait:: Emitted for traits.
-// typeAlias:: Emitted for type aliases and `Self` in `impl`s.
-// union:: Emitted for unions.
+//
+// |           |                                |
+// |-----------|--------------------------------|
+// | attribute |  Emitted for attribute macros. |
+// |enum| Emitted for enums. |
+// |function| Emitted for free-standing functions. |
+// |derive| Emitted for derive macros. |
+// |macro| Emitted for function-like macros. |
+// |method| Emitted for associated functions, also knowns as methods. |
+// |namespace| Emitted for modules. |
+// |struct| Emitted for structs.|
+// |trait| Emitted for traits.|
+// |typeAlias| Emitted for type aliases and `Self` in `impl`s.|
+// |union| Emitted for unions.|
 //
 // - For literals:
-// +
-// [horizontal]
-// boolean:: Emitted for the boolean literals `true` and `false`.
-// character:: Emitted for character literals.
-// number:: Emitted for numeric literals.
-// string:: Emitted for string literals.
-// escapeSequence:: Emitted for escaped sequences inside strings like `\n`.
-// formatSpecifier:: Emitted for format specifiers `{:?}` in `format!`-like macros.
+//
+// |           |                                |
+// |-----------|--------------------------------|
+// | boolean|  Emitted for the boolean literals `true` and `false`.|
+// | character| Emitted for character literals.|
+// | number| Emitted for numeric literals.|
+// | string| Emitted for string literals.|
+// | escapeSequence| Emitted for escaped sequences inside strings like `\n`.|
+// | formatSpecifier| Emitted for format specifiers `{:?}` in `format!`-like macros.|
 //
 // - For operators:
-// +
-// [horizontal]
-// operator:: Emitted for general operators.
-// arithmetic:: Emitted for the arithmetic operators `+`, `-`, `*`, `/`, `+=`, `-=`, `*=`, `/=`.
-// bitwise:: Emitted for the bitwise operators `|`, `&`, `!`, `^`, `|=`, `&=`, `^=`.
-// comparison:: Emitted for the comparison operators `>`, `<`, `==`, `>=`, `<=`, `!=`.
-// logical:: Emitted for the logical operators `||`, `&&`, `!`.
+//
+// |           |                                |
+// |-----------|--------------------------------|
+// |operator| Emitted for general operators.|
+// |arithmetic| Emitted for the arithmetic operators `+`, `-`, `*`, `/`, `+=`, `-=`, `*=`, `/=`.|
+// |bitwise| Emitted for the bitwise operators `|`, `&`, `!`, `^`, `|=`, `&=`, `^=`.|
+// |comparison| Emitted for the comparison oerators `>`, `<`, `==`, `>=`, `<=`, `!=`.|
+// |logical| Emitted for the logical operatos `||`, `&&`, `!`.|
 //
 // - For punctuation:
-// +
-// [horizontal]
-// punctuation:: Emitted for general punctuation.
-// attributeBracket:: Emitted for attribute invocation brackets, that is the `#[` and `]` tokens.
-// angle:: Emitted for `<>` angle brackets.
-// brace:: Emitted for `{}` braces.
-// bracket:: Emitted for `[]` brackets.
-// parenthesis:: Emitted for `()` parentheses.
-// colon:: Emitted for the `:` token.
-// comma:: Emitted for the `,` token.
-// dot:: Emitted for the `.` token.
-// semi:: Emitted for the `;` token.
-// macroBang:: Emitted for the `!` token in macro calls.
 //
-// //-
+// |           |                                |
+// |-----------|--------------------------------|
+// |punctuation| Emitted for general punctuation.|
+// |attributeBracket| Emitted for attribute invocation brackets, that is the `#[` and `]` tokens.|
+// |angle| Emitted for `<>` angle brackets.|
+// |brace| Emitted for `{}` braces.|
+// |bracket| Emitted for `[]` brackets.|
+// |parenthesis| Emitted for `()` parentheses.|
+// |colon| Emitted for the `:` token.|
+// |comma| Emitted for the `,` token.|
+// |dot| Emitted for the `.` token.|
+// |semi| Emitted for the `;` token.|
+// |macroBang| Emitted for the `!` token in macro calls.|
 //
-// [horizontal]
-// builtinAttribute:: Emitted for names to builtin attributes in attribute path, the `repr` in `#[repr(u8)]` for example.
-// builtinType:: Emitted for builtin types like `u32`, `str` and `f32`.
-// comment:: Emitted for comments.
-// constParameter:: Emitted for const parameters.
-// deriveHelper:: Emitted for derive helper attributes.
-// enumMember:: Emitted for enum variants.
-// generic:: Emitted for generic tokens that have no mapping.
-// keyword:: Emitted for keywords.
-// label:: Emitted for labels.
-// lifetime:: Emitted for lifetimes.
-// parameter:: Emitted for non-self function parameters.
-// property:: Emitted for struct and union fields.
-// selfKeyword:: Emitted for the self function parameter and self path-specifier.
-// selfTypeKeyword:: Emitted for the Self type parameter.
-// toolModule:: Emitted for tool modules.
-// typeParameter:: Emitted for type parameters.
-// unresolvedReference:: Emitted for unresolved references, names that rust-analyzer can't find the definition of.
-// variable:: Emitted for locals, constants and statics.
+//-
 //
+// |           |                                |
+// |-----------|--------------------------------|
+// |builtinAttribute| Emitted for names to builtin attributes in attribute path, the `repr` in `#[repr(u8)]` for example.|
+// |builtinType| Emitted for builtin types like `u32`, `str` and `f32`.|
+// |comment| Emitted for comments.|
+// |constParameter| Emitted for const parameters.|
+// |deriveHelper| Emitted for derive helper attributes.|
+// |enumMember| Emitted for enum variants.|
+// |generic| Emitted for generic tokens that have no mapping.|
+// |keyword| Emitted for keywords.|
+// |label| Emitted for labels.|
+// |lifetime| Emitted for lifetimes.|
+// |parameter| Emitted for non-self function parameters.|
+// |property| Emitted for struct and union fields.|
+// |selfKeyword| Emitted for the self function parameter and self path-specifier.|
+// |selfTypeKeyword| Emitted for the Self type parameter.|
+// |toolModule| Emitted for tool modules.|
+// |typeParameter| Emitted for type parameters.|
+// |unresolvedReference| Emitted for unresolved references, names that rust-analyzer can't find the definition of.|
+// |variable| Emitted for locals, constants and statics.|
 //
-// .Token Modifiers
+//
+// #### Token Modifiers
 //
 // Token modifiers allow to style some elements in the source code more precisely.
 //
 // Rust-analyzer currently emits the following token modifiers:
 //
-// [horizontal]
-// async:: Emitted for async functions and the `async` and `await` keywords.
-// attribute:: Emitted for tokens inside attributes.
-// callable:: Emitted for locals whose types implements one of the `Fn*` traits.
-// constant:: Emitted for consts.
-// consuming:: Emitted for locals that are being consumed when use in a function call.
-// controlFlow:: Emitted for control-flow related tokens, this includes the `?` operator.
-// crateRoot:: Emitted for crate names, like `serde` and `crate`.
-// declaration:: Emitted for names of definitions, like `foo` in `fn foo() {}`.
-// defaultLibrary:: Emitted for items from built-in crates (std, core, alloc, test and proc_macro).
-// documentation:: Emitted for documentation comments.
-// injected:: Emitted for doc-string injected highlighting like rust source blocks in documentation.
-// intraDocLink:: Emitted for intra doc links in doc-strings.
-// library:: Emitted for items that are defined outside of the current crate.
-// macro::  Emitted for tokens inside macro calls.
-// mutable:: Emitted for mutable locals and statics as well as functions taking `&mut self`.
-// public:: Emitted for items that are from the current crate and are `pub`.
-// reference:: Emitted for locals behind a reference and functions taking `self` by reference.
-// static:: Emitted for "static" functions, also known as functions that do not take a `self` param, as well as statics and consts.
-// trait:: Emitted for associated trait items.
-// unsafe:: Emitted for unsafe operations, like unsafe function calls, as well as the `unsafe` token.
-//
+// |           |                                |
+// |-----------|--------------------------------|
+// |async| Emitted for async functions and the `async` and `await` keywords.|
+// |attribute| Emitted for tokens inside attributes.|
+// |callable| Emitted for locals whose types implements one of the `Fn*` traits.|
+// |constant| Emitted for const.|
+// |consuming| Emitted for locals that are being consumed when use in a function call.|
+// |controlFlow| Emitted for control-flow related tokens, this includes th `?` operator.|
+// |crateRoot| Emitted for crate names, like `serde` and `crate.|
+// |declaration| Emitted for names of definitions, like `foo` in `fn foo(){}`.|
+// |defaultLibrary| Emitted for items from built-in crates (std, core, allc, test and proc_macro).|
+// |documentation| Emitted for documentation comment.|
+// |injected| Emitted for doc-string injected highlighting like rust source blocks in documentation.|
+// |intraDocLink| Emitted for intra doc links in doc-string.|
+// |library| Emitted for items that are defined outside of the current crae.|
+// |macro|  Emitted for tokens inside macro call.|
+// |mutable| Emitted for mutable locals and statics as well as functions taking `&mut self`.|
+// |public| Emitted for items that are from the current crate and are `pub.|
+// |reference| Emitted for locals behind a reference and functions taking self` by reference.|
+// |static| Emitted for "static" functions, also known as functions that d not take a `self` param, as well as statics and consts.|
+// |trait| Emitted for associated trait item.|
+// |unsafe| Emitted for unsafe operations, like unsafe function calls, as ell as the `unsafe` token.|
 //
-// image::https://user-images.githubusercontent.com/48062697/113164457-06cfb980-9239-11eb-819b-0f93e646acf8.png[]
-// image::https://user-images.githubusercontent.com/48062697/113187625-f7f50100-9250-11eb-825e-91c58f236071.png[]
+// ![Semantic Syntax Highlighting](https://user-images.githubusercontent.com/48062697/113164457-06cfb980-9239-11eb-819b-0f93e646acf8.png)
+// ![Semantic Syntax Highlighting](https://user-images.githubusercontent.com/48062697/113187625-f7f50100-9250-11eb-825e-91c58f236071.png)
 pub(crate) fn highlight(
     db: &RootDatabase,
     config: HighlightConfig,
@@ -478,7 +483,15 @@ fn traverse(
                     {
                         continue;
                     }
-                    highlight_format_string(hl, sema, krate, &string, &expanded_string, range);
+                    highlight_format_string(
+                        hl,
+                        sema,
+                        krate,
+                        &string,
+                        &expanded_string,
+                        range,
+                        file_id.edition(),
+                    );
 
                     if !string.is_raw() {
                         highlight_escape_string(hl, &string, range.start());
@@ -526,6 +539,7 @@ fn traverse(
                 &mut bindings_shadow_count,
                 config.syntactic_name_ref_highlighting,
                 name_like,
+                file_id.edition(),
             ),
             NodeOrToken::Token(token) => {
                 highlight::token(sema, token, file_id.edition()).zip(Some(None))
diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/format.rs b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/format.rs
index 7234108701a..43a6bdad7e9 100644
--- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/format.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/format.rs
@@ -4,6 +4,7 @@ use ide_db::{
     syntax_helpers::format_string::{is_format_string, lex_format_specifiers, FormatSpecifier},
     SymbolKind,
 };
+use span::Edition;
 use syntax::{ast, TextRange};
 
 use crate::{
@@ -18,6 +19,7 @@ pub(super) fn highlight_format_string(
     string: &ast::String,
     expanded_string: &ast::String,
     range: TextRange,
+    edition: Edition,
 ) {
     if is_format_string(expanded_string) {
         // FIXME: Replace this with the HIR info we have now.
@@ -39,7 +41,7 @@ pub(super) fn highlight_format_string(
             if let Some(res) = res {
                 stack.add(HlRange {
                     range,
-                    highlight: highlight_def(sema, krate, Definition::from(res)),
+                    highlight: highlight_def(sema, krate, Definition::from(res), edition),
                     binding_hash: None,
                 })
             }
diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/highlight.rs b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/highlight.rs
index 22a2fe4e9eb..194fde11601 100644
--- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/highlight.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/highlight.rs
@@ -58,6 +58,7 @@ pub(super) fn name_like(
     bindings_shadow_count: &mut FxHashMap<hir::Name, u32>,
     syntactic_name_ref_highlighting: bool,
     name_like: ast::NameLike,
+    edition: Edition,
 ) -> Option<(Highlight, Option<u64>)> {
     let mut binding_hash = None;
     let highlight = match name_like {
@@ -68,16 +69,17 @@ pub(super) fn name_like(
             &mut binding_hash,
             syntactic_name_ref_highlighting,
             name_ref,
+            edition,
         ),
         ast::NameLike::Name(name) => {
-            highlight_name(sema, bindings_shadow_count, &mut binding_hash, krate, name)
+            highlight_name(sema, bindings_shadow_count, &mut binding_hash, krate, name, edition)
         }
         ast::NameLike::Lifetime(lifetime) => match IdentClass::classify_lifetime(sema, &lifetime) {
             Some(IdentClass::NameClass(NameClass::Definition(def))) => {
-                highlight_def(sema, krate, def) | HlMod::Definition
+                highlight_def(sema, krate, def, edition) | HlMod::Definition
             }
             Some(IdentClass::NameRefClass(NameRefClass::Definition(def, _))) => {
-                highlight_def(sema, krate, def)
+                highlight_def(sema, krate, def, edition)
             }
             // FIXME: Fallback for 'static and '_, as we do not resolve these yet
             _ => SymbolKind::LifetimeParam.into(),
@@ -234,16 +236,17 @@ fn highlight_name_ref(
     binding_hash: &mut Option<u64>,
     syntactic_name_ref_highlighting: bool,
     name_ref: ast::NameRef,
+    edition: Edition,
 ) -> Highlight {
     let db = sema.db;
-    if let Some(res) = highlight_method_call_by_name_ref(sema, krate, &name_ref) {
+    if let Some(res) = highlight_method_call_by_name_ref(sema, krate, &name_ref, edition) {
         return res;
     }
 
     let name_class = match NameRefClass::classify(sema, &name_ref) {
         Some(name_kind) => name_kind,
         None if syntactic_name_ref_highlighting => {
-            return highlight_name_ref_by_syntax(name_ref, sema, krate)
+            return highlight_name_ref_by_syntax(name_ref, sema, krate, edition)
         }
         // FIXME: This is required for helper attributes used by proc-macros, as those do not map down
         // to anything when used.
@@ -267,7 +270,7 @@ fn highlight_name_ref(
                 *binding_hash = Some(calc_binding_hash(&name, *shadow_count))
             };
 
-            let mut h = highlight_def(sema, krate, def);
+            let mut h = highlight_def(sema, krate, def, edition);
 
             match def {
                 Definition::Local(local) if is_consumed_lvalue(name_ref.syntax(), &local, db) => {
@@ -305,7 +308,7 @@ fn highlight_name_ref(
             h
         }
         NameRefClass::FieldShorthand { field_ref, .. } => {
-            highlight_def(sema, krate, field_ref.into())
+            highlight_def(sema, krate, field_ref.into(), edition)
         }
         NameRefClass::ExternCrateShorthand { decl, krate: resolved_krate } => {
             let mut h = HlTag::Symbol(SymbolKind::Module).into();
@@ -341,6 +344,7 @@ fn highlight_name(
     binding_hash: &mut Option<u64>,
     krate: hir::Crate,
     name: ast::Name,
+    edition: Edition,
 ) -> Highlight {
     let name_kind = NameClass::classify(sema, &name);
     if let Some(NameClass::Definition(Definition::Local(local))) = &name_kind {
@@ -351,7 +355,7 @@ fn highlight_name(
     };
     match name_kind {
         Some(NameClass::Definition(def)) => {
-            let mut h = highlight_def(sema, krate, def) | HlMod::Definition;
+            let mut h = highlight_def(sema, krate, def, edition) | HlMod::Definition;
             if let Definition::Trait(trait_) = &def {
                 if trait_.is_unsafe(sema.db) {
                     h |= HlMod::Unsafe;
@@ -359,7 +363,7 @@ fn highlight_name(
             }
             h
         }
-        Some(NameClass::ConstReference(def)) => highlight_def(sema, krate, def),
+        Some(NameClass::ConstReference(def)) => highlight_def(sema, krate, def, edition),
         Some(NameClass::PatFieldShorthand { field_ref, .. }) => {
             let mut h = HlTag::Symbol(SymbolKind::Field).into();
             if let hir::VariantDef::Union(_) = field_ref.parent_def(sema.db) {
@@ -379,6 +383,7 @@ pub(super) fn highlight_def(
     sema: &Semantics<'_, RootDatabase>,
     krate: hir::Crate,
     def: Definition,
+    edition: Edition,
 ) -> Highlight {
     let db = sema.db;
     let mut h = match def {
@@ -427,7 +432,12 @@ pub(super) fn highlight_def(
                 }
             }
 
-            if func.is_unsafe_to_call(db) {
+            // FIXME: Passing `None` here means not-unsafe functions with `#[target_feature]` will be
+            // highlighted as unsafe, even when the current target features set is a superset (RFC 2396).
+            // We probably should consider checking the current function, but I found no easy way to do
+            // that (also I'm worried about perf). There's also an instance below.
+            // FIXME: This should be the edition of the call.
+            if func.is_unsafe_to_call(db, None, edition) {
                 h |= HlMod::Unsafe;
             }
             if func.is_async(db) {
@@ -575,21 +585,23 @@ fn highlight_method_call_by_name_ref(
     sema: &Semantics<'_, RootDatabase>,
     krate: hir::Crate,
     name_ref: &ast::NameRef,
+    edition: Edition,
 ) -> Option<Highlight> {
     let mc = name_ref.syntax().parent().and_then(ast::MethodCallExpr::cast)?;
-    highlight_method_call(sema, krate, &mc)
+    highlight_method_call(sema, krate, &mc, edition)
 }
 
 fn highlight_method_call(
     sema: &Semantics<'_, RootDatabase>,
     krate: hir::Crate,
     method_call: &ast::MethodCallExpr,
+    edition: Edition,
 ) -> Option<Highlight> {
     let func = sema.resolve_method_call(method_call)?;
 
     let mut h = SymbolKind::Method.into();
 
-    if func.is_unsafe_to_call(sema.db) || sema.is_unsafe_method_call(method_call) {
+    if func.is_unsafe_to_call(sema.db, None, edition) || sema.is_unsafe_method_call(method_call) {
         h |= HlMod::Unsafe;
     }
     if func.is_async(sema.db) {
@@ -665,6 +677,12 @@ fn highlight_name_by_syntax(name: ast::Name) -> Highlight {
         STATIC => SymbolKind::Static,
         IDENT_PAT => SymbolKind::Local,
         FORMAT_ARGS_ARG => SymbolKind::Local,
+        RENAME => SymbolKind::Local,
+        MACRO_RULES => SymbolKind::Macro,
+        CONST_PARAM => SymbolKind::ConstParam,
+        SELF_PARAM => SymbolKind::SelfParam,
+        TRAIT_ALIAS => SymbolKind::TraitAlias,
+        ASM_OPERAND_NAMED => SymbolKind::Local,
         _ => return default.into(),
     };
 
@@ -675,6 +693,7 @@ fn highlight_name_ref_by_syntax(
     name: ast::NameRef,
     sema: &Semantics<'_, RootDatabase>,
     krate: hir::Crate,
+    edition: Edition,
 ) -> Highlight {
     let default = HlTag::UnresolvedReference;
 
@@ -684,8 +703,9 @@ fn highlight_name_ref_by_syntax(
     };
 
     match parent.kind() {
+        EXTERN_CRATE => HlTag::Symbol(SymbolKind::Module) | HlMod::CrateRoot,
         METHOD_CALL_EXPR => ast::MethodCallExpr::cast(parent)
-            .and_then(|it| highlight_method_call(sema, krate, &it))
+            .and_then(|it| highlight_method_call(sema, krate, &it, edition))
             .unwrap_or_else(|| SymbolKind::Method.into()),
         FIELD_EXPR => {
             let h = HlTag::Symbol(SymbolKind::Field);
diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_doctest.html b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_doctest.html
index 5ff96ae2a74..eb77c14c2a5 100644
--- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_doctest.html
+++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_doctest.html
@@ -50,6 +50,15 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
 <span class="comment documentation">//!</span><span class="comment documentation"> </span><span class="keyword injected">fn</span><span class="none injected"> </span><span class="function declaration injected">test</span><span class="parenthesis injected">(</span><span class="parenthesis injected">)</span><span class="none injected"> </span><span class="brace injected">{</span><span class="brace injected">}</span>
 <span class="comment documentation">//! ```</span>
 
+<span class="comment documentation">//! ```rust</span>
+<span class="comment documentation">//!</span><span class="comment documentation"> </span><span class="keyword injected">extern</span><span class="none injected"> </span><span class="keyword injected">crate</span><span class="none injected"> </span><span class="self_keyword crate_root injected">self</span><span class="semicolon injected">;</span>
+<span class="comment documentation">//!</span><span class="comment documentation"> </span><span class="keyword injected">extern</span><span class="none injected"> </span><span class="keyword injected">crate</span><span class="none injected"> </span><span class="module crate_root injected">std</span><span class="semicolon injected">;</span>
+<span class="comment documentation">//!</span><span class="comment documentation"> </span><span class="keyword injected">extern</span><span class="none injected"> </span><span class="keyword injected">crate</span><span class="none injected"> </span><span class="module crate_root injected">core</span><span class="semicolon injected">;</span>
+<span class="comment documentation">//!</span><span class="comment documentation"> </span><span class="keyword injected">extern</span><span class="none injected"> </span><span class="keyword injected">crate</span><span class="none injected"> </span><span class="module crate_root injected">alloc</span><span class="semicolon injected">;</span>
+<span class="comment documentation">//!</span><span class="comment documentation"> </span><span class="keyword injected">extern</span><span class="none injected"> </span><span class="keyword injected">crate</span><span class="none injected"> </span><span class="module crate_root injected">proc_macro</span><span class="semicolon injected">;</span>
+<span class="comment documentation">//!</span><span class="comment documentation"> </span><span class="keyword injected">extern</span><span class="none injected"> </span><span class="keyword injected">crate</span><span class="none injected"> </span><span class="module crate_root injected">test</span><span class="semicolon injected">;</span>
+<span class="comment documentation">//!</span><span class="comment documentation"> </span><span class="keyword injected">extern</span><span class="none injected"> </span><span class="keyword injected">crate</span><span class="none injected"> </span><span class="module crate_root injected">Krate</span><span class="semicolon injected">;</span>
+<span class="comment documentation">//! ```</span>
 <span class="keyword">mod</span> <span class="module declaration">outline_module</span><span class="semicolon">;</span>
 
 <span class="comment documentation">/// ```</span>
diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_general.html b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_general.html
index 9be7c92fc79..9477d0d1b87 100644
--- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_general.html
+++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_general.html
@@ -48,17 +48,6 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
 <pre><code><span class="keyword">use</span> <span class="module">inner</span><span class="operator">::</span><span class="brace">{</span><span class="self_keyword">self</span> <span class="keyword">as</span> <span class="module declaration">inner_mod</span><span class="brace">}</span><span class="semicolon">;</span>
 <span class="keyword">mod</span> <span class="module declaration">inner</span> <span class="brace">{</span><span class="brace">}</span>
 
-<span class="keyword">pub</span> <span class="keyword">mod</span> <span class="module declaration public">ops</span> <span class="brace">{</span>
-    <span class="attribute_bracket attribute">#</span><span class="attribute_bracket attribute">[</span><span class="builtin_attr attribute">lang</span> <span class="operator attribute">=</span> <span class="string_literal attribute">"fn_once"</span><span class="attribute_bracket attribute">]</span>
-    <span class="keyword">pub</span> <span class="keyword">trait</span> <span class="trait declaration public">FnOnce</span><span class="angle">&lt;</span><span class="type_param declaration">Args</span><span class="angle">&gt;</span> <span class="brace">{</span><span class="brace">}</span>
-
-    <span class="attribute_bracket attribute">#</span><span class="attribute_bracket attribute">[</span><span class="builtin_attr attribute">lang</span> <span class="operator attribute">=</span> <span class="string_literal attribute">"fn_mut"</span><span class="attribute_bracket attribute">]</span>
-    <span class="keyword">pub</span> <span class="keyword">trait</span> <span class="trait declaration public">FnMut</span><span class="angle">&lt;</span><span class="type_param declaration">Args</span><span class="angle">&gt;</span><span class="colon">:</span> <span class="trait public">FnOnce</span><span class="angle">&lt;</span><span class="type_param">Args</span><span class="angle">&gt;</span> <span class="brace">{</span><span class="brace">}</span>
-
-    <span class="attribute_bracket attribute">#</span><span class="attribute_bracket attribute">[</span><span class="builtin_attr attribute">lang</span> <span class="operator attribute">=</span> <span class="string_literal attribute">"fn"</span><span class="attribute_bracket attribute">]</span>
-    <span class="keyword">pub</span> <span class="keyword">trait</span> <span class="trait declaration public">Fn</span><span class="angle">&lt;</span><span class="type_param declaration">Args</span><span class="angle">&gt;</span><span class="colon">:</span> <span class="trait public">FnMut</span><span class="angle">&lt;</span><span class="type_param">Args</span><span class="angle">&gt;</span> <span class="brace">{</span><span class="brace">}</span>
-<span class="brace">}</span>
-
 <span class="keyword">struct</span> <span class="struct declaration">Foo</span> <span class="brace">{</span>
     <span class="field declaration">x</span><span class="colon">:</span> <span class="builtin_type">u32</span><span class="comma">,</span>
 <span class="brace">}</span>
@@ -125,8 +114,8 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
     <span class="const_param const">FOO</span>
 <span class="brace">}</span>
 
-<span class="keyword">use</span> <span class="module public">ops</span><span class="operator">::</span><span class="trait public">Fn</span><span class="semicolon">;</span>
-<span class="keyword">fn</span> <span class="function declaration">baz</span><span class="angle">&lt;</span><span class="type_param declaration">F</span><span class="colon">:</span> <span class="trait public">Fn</span><span class="parenthesis">(</span><span class="parenthesis">)</span> <span class="operator">-&gt;</span> <span class="parenthesis">(</span><span class="parenthesis">)</span><span class="angle">&gt;</span><span class="parenthesis">(</span><span class="value_param callable declaration">f</span><span class="colon">:</span> <span class="type_param">F</span><span class="parenthesis">)</span> <span class="brace">{</span>
+<span class="keyword">use</span> <span class="module crate_root default_library library">core</span><span class="operator">::</span><span class="module default_library library">ops</span><span class="operator">::</span><span class="trait default_library library">Fn</span><span class="semicolon">;</span>
+<span class="keyword">fn</span> <span class="function declaration">baz</span><span class="angle">&lt;</span><span class="type_param declaration">F</span><span class="colon">:</span> <span class="trait default_library library">Fn</span><span class="parenthesis">(</span><span class="parenthesis">)</span> <span class="operator">-&gt;</span> <span class="parenthesis">(</span><span class="parenthesis">)</span><span class="angle">&gt;</span><span class="parenthesis">(</span><span class="value_param callable declaration">f</span><span class="colon">:</span> <span class="type_param">F</span><span class="parenthesis">)</span> <span class="brace">{</span>
     <span class="value_param callable">f</span><span class="parenthesis">(</span><span class="parenthesis">)</span>
 <span class="brace">}</span>
 
diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_strings.html b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_strings.html
index 0a7e273950d..1794d7dbfe2 100644
--- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_strings.html
+++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_strings.html
@@ -82,6 +82,10 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
     <span class="parenthesis">(</span><span class="punctuation">$</span>literal<span class="colon">:</span>literal<span class="parenthesis">)</span> <span class="operator">=</span><span class="operator">&gt;</span> <span class="brace">{</span><span class="brace">{</span>stringify<span class="punctuation">!</span><span class="parenthesis">(</span><span class="punctuation">$</span>literal<span class="parenthesis">)</span><span class="semicolon">;</span> format_args<span class="punctuation">!</span><span class="parenthesis">(</span><span class="punctuation">$</span>literal<span class="parenthesis">)</span><span class="brace">}</span><span class="brace">}</span><span class="semicolon">;</span>
 <span class="brace">}</span>
 
+<span class="keyword">use</span> <span class="unresolved_reference">foo</span><span class="operator">::</span><span class="unresolved_reference">bar</span> <span class="keyword">as</span> <span class="variable declaration">baz</span><span class="semicolon">;</span>
+<span class="keyword">trait</span> <span class="trait_alias declaration">Bar</span> <span class="operator">=</span> <span class="unresolved_reference">Baz</span><span class="semicolon">;</span>
+<span class="keyword">trait</span> <span class="trait_alias declaration">Foo</span> <span class="operator">=</span> <span class="trait_alias">Bar</span><span class="semicolon">;</span>
+
 <span class="keyword">fn</span> <span class="function declaration">main</span><span class="parenthesis">(</span><span class="parenthesis">)</span> <span class="brace">{</span>
     <span class="keyword">let</span> <span class="variable declaration">a</span> <span class="operator">=</span> <span class="char_literal">'</span><span class="escape_sequence">\n</span><span class="char_literal">'</span><span class="semicolon">;</span>
     <span class="keyword">let</span> <span class="variable declaration">a</span> <span class="operator">=</span> <span class="char_literal">'</span><span class="escape_sequence">\t</span><span class="char_literal">'</span><span class="semicolon">;</span>
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 af52b33de64..3775265f234 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
@@ -136,22 +136,11 @@ use self::foo as bar;
 fn test_highlighting() {
     check_highlighting(
         r#"
-//- minicore: derive, copy
+//- minicore: derive, copy, fn
 //- /main.rs crate:main deps:foo
 use inner::{self as inner_mod};
 mod inner {}
 
-pub mod ops {
-    #[lang = "fn_once"]
-    pub trait FnOnce<Args> {}
-
-    #[lang = "fn_mut"]
-    pub trait FnMut<Args>: FnOnce<Args> {}
-
-    #[lang = "fn"]
-    pub trait Fn<Args>: FnMut<Args> {}
-}
-
 struct Foo {
     x: u32,
 }
@@ -218,7 +207,7 @@ fn const_param<const FOO: usize>() -> usize {
     FOO
 }
 
-use ops::Fn;
+use core::ops::Fn;
 fn baz<F: Fn() -> ()>(f: F) {
     f()
 }
@@ -466,6 +455,10 @@ macro_rules! reuse_twice {
     ($literal:literal) => {{stringify!($literal); format_args!($literal)}};
 }
 
+use foo::bar as baz;
+trait Bar = Baz;
+trait Foo = Bar;
+
 fn main() {
     let a = '\n';
     let a = '\t';
@@ -718,6 +711,15 @@ fn test_highlight_doc_comment() {
 //! fn test() {}
 //! ```
 
+//! ```rust
+//! extern crate self;
+//! extern crate std;
+//! extern crate core;
+//! extern crate alloc;
+//! extern crate proc_macro;
+//! extern crate test;
+//! extern crate Krate;
+//! ```
 mod outline_module;
 
 /// ```
@@ -1080,6 +1082,9 @@ pub struct Struct;
     );
 }
 
+// Rainbow highlighting uses a deterministic hash (fxhash) but the hashing does differ
+// depending on the pointer width so only runs this on 64-bit targets.
+#[cfg(target_pointer_width = "64")]
 #[test]
 fn test_rainbow_highlighting() {
     check_highlighting(
diff --git a/src/tools/rust-analyzer/crates/ide/src/typing.rs b/src/tools/rust-analyzer/crates/ide/src/typing.rs
index 47d75f1c957..8c9dd051452 100644
--- a/src/tools/rust-analyzer/crates/ide/src/typing.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/typing.rs
@@ -51,16 +51,15 @@ struct ExtendedTextEdit {
 // - typing `{` in a use item adds a closing `}` in the right place
 // - typing `>` to complete a return type `->` will insert a whitespace after it
 //
-// VS Code::
+// #### VS Code
 //
 // Add the following to `settings.json`:
-// [source,json]
-// ----
+// ```json
 // "editor.formatOnType": true,
-// ----
+// ```
 //
-// image::https://user-images.githubusercontent.com/48062697/113166163-69758500-923a-11eb-81ee-eb33ec380399.gif[]
-// image::https://user-images.githubusercontent.com/48062697/113171066-105c2000-923f-11eb-87ab-f4a263346567.gif[]
+// ![On Typing Assists](https://user-images.githubusercontent.com/48062697/113166163-69758500-923a-11eb-81ee-eb33ec380399.gif)
+// ![On Typing Assists](https://user-images.githubusercontent.com/48062697/113171066-105c2000-923f-11eb-87ab-f4a263346567.gif)
 pub(crate) fn on_char_typed(
     db: &RootDatabase,
     position: FilePosition,
diff --git a/src/tools/rust-analyzer/crates/ide/src/typing/on_enter.rs b/src/tools/rust-analyzer/crates/ide/src/typing/on_enter.rs
index e249c38c73d..c6d1c283f4e 100644
--- a/src/tools/rust-analyzer/crates/ide/src/typing/on_enter.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/typing/on_enter.rs
@@ -16,12 +16,12 @@ use ide_db::text_edit::TextEdit;
 
 // Feature: On Enter
 //
-// rust-analyzer can override kbd:[Enter] key to make it smarter:
+// rust-analyzer can override <kbd>Enter</kbd> key to make it smarter:
 //
-// - kbd:[Enter] inside triple-slash comments automatically inserts `///`
-// - kbd:[Enter] in the middle or after a trailing space in `//` inserts `//`
-// - kbd:[Enter] inside `//!` doc comments automatically inserts `//!`
-// - kbd:[Enter] after `{` indents contents and closing `}` of single-line block
+// - <kbd>Enter</kbd> inside triple-slash comments automatically inserts `///`
+// - <kbd>Enter</kbd> in the middle or after a trailing space in `//` inserts `//`
+// - <kbd>Enter</kbd> inside `//!` doc comments automatically inserts `//!`
+// - <kbd>Enter</kbd> after `{` indents contents and closing `}` of single-line block
 //
 // This action needs to be assigned to shortcut explicitly.
 //
@@ -29,29 +29,27 @@ use ide_db::text_edit::TextEdit;
 // Similarly, if rust-analyzer crashes or stops responding, `Enter` might not work.
 // In that case, you can still press `Shift-Enter` to insert a newline.
 //
-// VS Code::
+// #### VS Code
 //
 // Add the following to `keybindings.json`:
-// [source,json]
-// ----
+// ```json
 // {
 //   "key": "Enter",
 //   "command": "rust-analyzer.onEnter",
 //   "when": "editorTextFocus && !suggestWidgetVisible && editorLangId == rust"
 // }
-// ----
+// ````
 //
 // When using the Vim plugin:
-// [source,json]
-// ----
+// ```json
 // {
 //   "key": "Enter",
 //   "command": "rust-analyzer.onEnter",
 //   "when": "editorTextFocus && !suggestWidgetVisible && editorLangId == rust && vim.mode == 'Insert'"
 // }
-// ----
+// ````
 //
-// image::https://user-images.githubusercontent.com/48062697/113065578-04c21800-91b1-11eb-82b8-22b8c481e645.gif[]
+// ![On Enter](https://user-images.githubusercontent.com/48062697/113065578-04c21800-91b1-11eb-82b8-22b8c481e645.gif)
 pub(crate) fn on_enter(db: &RootDatabase, position: FilePosition) -> Option<TextEdit> {
     let parse = db.parse(EditionedFileId::current_edition(position.file_id));
     let file = parse.tree();
diff --git a/src/tools/rust-analyzer/crates/ide/src/view_crate_graph.rs b/src/tools/rust-analyzer/crates/ide/src/view_crate_graph.rs
index 9ff099f479e..eb6eb7da1e9 100644
--- a/src/tools/rust-analyzer/crates/ide/src/view_crate_graph.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/view_crate_graph.rs
@@ -12,11 +12,9 @@ use triomphe::Arc;
 //
 // Only workspace crates are included, no crates.io dependencies or sysroot crates.
 //
-// |===
-// | Editor  | Action Name
-//
-// | VS Code | **rust-analyzer: View Crate Graph**
-// |===
+// | Editor  | Action Name |
+// |---------|-------------|
+// | VS Code | **rust-analyzer: View Crate Graph** |
 pub(crate) fn view_crate_graph(db: &RootDatabase, full: bool) -> Result<String, String> {
     let crate_graph = db.crate_graph();
     let crates_to_render = crate_graph
@@ -86,7 +84,8 @@ impl<'a> dot::Labeller<'a, CrateId, Edge<'a>> for DotCrateGraph {
     }
 
     fn node_label(&'a self, n: &CrateId) -> LabelText<'a> {
-        let name = self.graph[*n].display_name.as_ref().map_or("(unnamed crate)", |name| name);
+        let name =
+            self.graph[*n].display_name.as_ref().map_or("(unnamed crate)", |name| name.as_str());
         LabelText::LabelStr(name.into())
     }
 }
diff --git a/src/tools/rust-analyzer/crates/ide/src/view_hir.rs b/src/tools/rust-analyzer/crates/ide/src/view_hir.rs
index fe532f4cc55..bfdf9d0f337 100644
--- a/src/tools/rust-analyzer/crates/ide/src/view_hir.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/view_hir.rs
@@ -4,12 +4,11 @@ use syntax::{algo::ancestors_at_offset, ast, AstNode};
 
 // Feature: View Hir
 //
-// |===
-// | Editor  | Action Name
-//
+// | Editor  | Action Name |
+// |---------|--------------|
 // | VS Code | **rust-analyzer: View Hir**
-// |===
-// image::https://user-images.githubusercontent.com/48062697/113065588-068bdb80-91b1-11eb-9a78-0b4ef1e972fb.gif[]
+//
+// ![View Hir](https://user-images.githubusercontent.com/48062697/113065588-068bdb80-91b1-11eb-9a78-0b4ef1e972fb.gif)
 pub(crate) fn view_hir(db: &RootDatabase, position: FilePosition) -> String {
     body_hir(db, position).unwrap_or_else(|| "Not inside a function body".to_owned())
 }
diff --git a/src/tools/rust-analyzer/crates/ide/src/view_item_tree.rs b/src/tools/rust-analyzer/crates/ide/src/view_item_tree.rs
index a6352b99d4f..67c241cbb91 100644
--- a/src/tools/rust-analyzer/crates/ide/src/view_item_tree.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/view_item_tree.rs
@@ -6,11 +6,9 @@ use span::EditionedFileId;
 //
 // Displays the ItemTree of the currently open file, for debugging.
 //
-// |===
-// | Editor  | Action Name
-//
-// | VS Code | **rust-analyzer: Debug ItemTree**
-// |===
+// | Editor  | Action Name |
+// |---------|-------------|
+// | VS Code | **rust-analyzer: Debug ItemTree** |
 pub(crate) fn view_item_tree(db: &RootDatabase, file_id: FileId) -> String {
     let sema = Semantics::new(db);
     let file_id = sema
diff --git a/src/tools/rust-analyzer/crates/ide/src/view_memory_layout.rs b/src/tools/rust-analyzer/crates/ide/src/view_memory_layout.rs
index ff74e05e943..edb83bc4eac 100644
--- a/src/tools/rust-analyzer/crates/ide/src/view_memory_layout.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/view_memory_layout.rs
@@ -75,11 +75,9 @@ impl FieldOrTupleIdx {
 //
 // Displays the recursive memory layout of a datatype.
 //
-// |===
-// | Editor  | Action Name
-//
-// | VS Code | **rust-analyzer: View Memory Layout**
-// |===
+// | Editor  | Action Name |
+// |---------|-------------|
+// | VS Code | **rust-analyzer: View Memory Layout** |
 pub(crate) fn view_memory_layout(
     db: &RootDatabase,
     position: FilePosition,
diff --git a/src/tools/rust-analyzer/crates/ide/src/view_mir.rs b/src/tools/rust-analyzer/crates/ide/src/view_mir.rs
index 7a228375d5e..aa4ff64a819 100644
--- a/src/tools/rust-analyzer/crates/ide/src/view_mir.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/view_mir.rs
@@ -4,11 +4,9 @@ use syntax::{algo::ancestors_at_offset, ast, AstNode};
 
 // Feature: View Mir
 //
-// |===
-// | Editor  | Action Name
-//
+// | Editor  | Action Name |
+// |---------|-------------|
 // | VS Code | **rust-analyzer: View Mir**
-// |===
 pub(crate) fn view_mir(db: &RootDatabase, position: FilePosition) -> String {
     body_mir(db, position).unwrap_or_else(|| "Not inside a function body".to_owned())
 }
diff --git a/src/tools/rust-analyzer/crates/ide/src/view_syntax_tree.rs b/src/tools/rust-analyzer/crates/ide/src/view_syntax_tree.rs
index 218ee15a7dd..407720864bf 100644
--- a/src/tools/rust-analyzer/crates/ide/src/view_syntax_tree.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/view_syntax_tree.rs
@@ -1,41 +1,50 @@
 use hir::Semantics;
-use ide_db::{FileId, RootDatabase};
-use span::TextRange;
+use ide_db::{
+    line_index::{LineCol, LineIndex},
+    FileId, LineIndexDatabase, RootDatabase,
+};
+use span::{TextRange, TextSize};
 use stdx::format_to;
 use syntax::{
     ast::{self, IsString},
     AstNode, AstToken, NodeOrToken, SourceFile, SyntaxNode, SyntaxToken, WalkEvent,
 };
+use triomphe::Arc;
 
 // Feature: Show Syntax Tree
 //
 // Shows a tree view with the syntax tree of the current file
 //
-// |===
-// | Editor  | Panel Name
-//
-// | VS Code | **Rust Syntax Tree**
-// |===
+// | Editor  | Panel Name |
+// |---------|-------------|
+// | VS Code | **Rust Syntax Tree** |
 pub(crate) fn view_syntax_tree(db: &RootDatabase, file_id: FileId) -> String {
     let sema = Semantics::new(db);
+    let line_index = db.line_index(file_id);
     let parse = sema.parse_guess_edition(file_id);
-    syntax_node_to_json(parse.syntax(), None)
+
+    let ctx = SyntaxTreeCtx { line_index, in_string: None };
+
+    syntax_node_to_json(parse.syntax(), &ctx)
 }
 
-fn syntax_node_to_json(node: &SyntaxNode, ctx: Option<InStringCtx>) -> String {
+fn syntax_node_to_json(node: &SyntaxNode, ctx: &SyntaxTreeCtx) -> String {
     let mut result = String::new();
     for event in node.preorder_with_tokens() {
         match event {
             WalkEvent::Enter(it) => {
                 let kind = it.kind();
-                let (text_range, inner_range_str) = match &ctx {
-                    Some(ctx) => {
+                let (text_range, inner_range_str) = match &ctx.in_string {
+                    Some(in_string) => {
+                        let start_pos = TextPosition::new(&ctx.line_index, it.text_range().start());
+                        let end_pos = TextPosition::new(&ctx.line_index, it.text_range().end());
+
                         let inner_start: u32 = it.text_range().start().into();
-                        let inner_end: u32 = it.text_range().end().into();
+                        let inner_end: u32 = it.text_range().start().into();
 
-                        let mut true_start = inner_start + ctx.offset;
-                        let mut true_end = inner_end + ctx.offset;
-                        for pos in &ctx.marker_positions {
+                        let mut true_start = inner_start + in_string.offset;
+                        let mut true_end = inner_end + in_string.offset;
+                        for pos in &in_string.marker_positions {
                             if *pos >= inner_end {
                                 break;
                             }
@@ -48,39 +57,33 @@ fn syntax_node_to_json(node: &SyntaxNode, ctx: Option<InStringCtx>) -> String {
 
                         let true_range = TextRange::new(true_start.into(), true_end.into());
 
-                        (
-                            true_range,
-                            format!(
-                                r#","istart":{:?},"iend":{:?}"#,
-                                it.text_range().start(),
-                                it.text_range().end()
-                            ),
-                        )
+                        (true_range, format!(r#","istart":{start_pos},"iend":{end_pos}"#,))
                     }
                     None => (it.text_range(), "".to_owned()),
                 };
-                let start = text_range.start();
-                let end = text_range.end();
+
+                let start = TextPosition::new(&ctx.line_index, text_range.start());
+                let end = TextPosition::new(&ctx.line_index, text_range.end());
 
                 match it {
                     NodeOrToken::Node(_) => {
                         format_to!(
                             result,
-                            r#"{{"type":"Node","kind":"{kind:?}","start":{start:?},"end":{end:?}{inner_range_str},"children":["#
+                            r#"{{"type":"Node","kind":"{kind:?}","start":{start},"end":{end}{inner_range_str},"children":["#
                         );
                     }
                     NodeOrToken::Token(token) => {
                         let comma = if token.next_sibling_or_token().is_some() { "," } else { "" };
-                        match parse_rust_string(token) {
+                        match parse_rust_string(token, ctx) {
                             Some(parsed) => {
                                 format_to!(
                                     result,
-                                    r#"{{"type":"Node","kind":"{kind:?}","start":{start:?},"end":{end:?}{inner_range_str},"children":[{parsed}]}}{comma}"#
+                                    r#"{{"type":"Node","kind":"{kind:?}","start":{start},"end":{end}{inner_range_str},"children":[{parsed}]}}{comma}"#
                                 );
                             }
                             None => format_to!(
                                 result,
-                                r#"{{"type":"Token","kind":"{kind:?}","start":{start:?},"end":{end:?}{inner_range_str}}}{comma}"#
+                                r#"{{"type":"Token","kind":"{kind:?}","start":{start},"end":{end}{inner_range_str}}}{comma}"#
                             ),
                         }
                     }
@@ -99,7 +102,26 @@ fn syntax_node_to_json(node: &SyntaxNode, ctx: Option<InStringCtx>) -> String {
     result
 }
 
-fn parse_rust_string(token: SyntaxToken) -> Option<String> {
+struct TextPosition {
+    offset: TextSize,
+    line: u32,
+    col: u32,
+}
+
+impl std::fmt::Display for TextPosition {
+    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+        write!(f, "[{:?},{},{}]", self.offset, self.line, self.col)
+    }
+}
+
+impl TextPosition {
+    pub(crate) fn new(line_index: &LineIndex, offset: TextSize) -> Self {
+        let LineCol { line, col } = line_index.line_col(offset);
+        Self { offset, line, col }
+    }
+}
+
+fn parse_rust_string(token: SyntaxToken, ctx: &SyntaxTreeCtx) -> Option<String> {
     let string_node = ast::String::cast(token)?;
     let text = string_node.value().ok()?;
 
@@ -128,13 +150,20 @@ fn parse_rust_string(token: SyntaxToken) -> Option<String> {
         return None;
     }
 
-    Some(syntax_node_to_json(
-        node,
-        Some(InStringCtx {
+    let ctx = SyntaxTreeCtx {
+        line_index: ctx.line_index.clone(),
+        in_string: Some(InStringCtx {
             offset: string_node.text_range_between_quotes()?.start().into(),
             marker_positions,
         }),
-    ))
+    };
+
+    Some(syntax_node_to_json(node, &ctx))
+}
+
+struct SyntaxTreeCtx {
+    line_index: Arc<LineIndex>,
+    in_string: Option<InStringCtx>,
 }
 
 struct InStringCtx {
@@ -160,7 +189,7 @@ mod tests {
         check(
             r#"fn foo() {}"#,
             expect![[
-                r#"{"type":"Node","kind":"SOURCE_FILE","start":0,"end":11,"children":[{"type":"Node","kind":"FN","start":0,"end":11,"children":[{"type":"Token","kind":"FN_KW","start":0,"end":2},{"type":"Token","kind":"WHITESPACE","start":2,"end":3},{"type":"Node","kind":"NAME","start":3,"end":6,"children":[{"type":"Token","kind":"IDENT","start":3,"end":6}]},{"type":"Node","kind":"PARAM_LIST","start":6,"end":8,"children":[{"type":"Token","kind":"L_PAREN","start":6,"end":7},{"type":"Token","kind":"R_PAREN","start":7,"end":8}]},{"type":"Token","kind":"WHITESPACE","start":8,"end":9},{"type":"Node","kind":"BLOCK_EXPR","start":9,"end":11,"children":[{"type":"Node","kind":"STMT_LIST","start":9,"end":11,"children":[{"type":"Token","kind":"L_CURLY","start":9,"end":10},{"type":"Token","kind":"R_CURLY","start":10,"end":11}]}]}]}]}"#
+                r#"{"type":"Node","kind":"SOURCE_FILE","start":[0,0,0],"end":[11,0,11],"children":[{"type":"Node","kind":"FN","start":[0,0,0],"end":[11,0,11],"children":[{"type":"Token","kind":"FN_KW","start":[0,0,0],"end":[2,0,2]},{"type":"Token","kind":"WHITESPACE","start":[2,0,2],"end":[3,0,3]},{"type":"Node","kind":"NAME","start":[3,0,3],"end":[6,0,6],"children":[{"type":"Token","kind":"IDENT","start":[3,0,3],"end":[6,0,6]}]},{"type":"Node","kind":"PARAM_LIST","start":[6,0,6],"end":[8,0,8],"children":[{"type":"Token","kind":"L_PAREN","start":[6,0,6],"end":[7,0,7]},{"type":"Token","kind":"R_PAREN","start":[7,0,7],"end":[8,0,8]}]},{"type":"Token","kind":"WHITESPACE","start":[8,0,8],"end":[9,0,9]},{"type":"Node","kind":"BLOCK_EXPR","start":[9,0,9],"end":[11,0,11],"children":[{"type":"Node","kind":"STMT_LIST","start":[9,0,9],"end":[11,0,11],"children":[{"type":"Token","kind":"L_CURLY","start":[9,0,9],"end":[10,0,10]},{"type":"Token","kind":"R_CURLY","start":[10,0,10],"end":[11,0,11]}]}]}]}]}"#
             ]],
         );
 
@@ -173,7 +202,7 @@ fn test() {
     ", "");
 }"#,
             expect![[
-                r#"{"type":"Node","kind":"SOURCE_FILE","start":0,"end":60,"children":[{"type":"Node","kind":"FN","start":0,"end":60,"children":[{"type":"Token","kind":"FN_KW","start":0,"end":2},{"type":"Token","kind":"WHITESPACE","start":2,"end":3},{"type":"Node","kind":"NAME","start":3,"end":7,"children":[{"type":"Token","kind":"IDENT","start":3,"end":7}]},{"type":"Node","kind":"PARAM_LIST","start":7,"end":9,"children":[{"type":"Token","kind":"L_PAREN","start":7,"end":8},{"type":"Token","kind":"R_PAREN","start":8,"end":9}]},{"type":"Token","kind":"WHITESPACE","start":9,"end":10},{"type":"Node","kind":"BLOCK_EXPR","start":10,"end":60,"children":[{"type":"Node","kind":"STMT_LIST","start":10,"end":60,"children":[{"type":"Token","kind":"L_CURLY","start":10,"end":11},{"type":"Token","kind":"WHITESPACE","start":11,"end":16},{"type":"Node","kind":"EXPR_STMT","start":16,"end":58,"children":[{"type":"Node","kind":"MACRO_EXPR","start":16,"end":57,"children":[{"type":"Node","kind":"MACRO_CALL","start":16,"end":57,"children":[{"type":"Node","kind":"PATH","start":16,"end":22,"children":[{"type":"Node","kind":"PATH_SEGMENT","start":16,"end":22,"children":[{"type":"Node","kind":"NAME_REF","start":16,"end":22,"children":[{"type":"Token","kind":"IDENT","start":16,"end":22}]}]}]},{"type":"Token","kind":"BANG","start":22,"end":23},{"type":"Node","kind":"TOKEN_TREE","start":23,"end":57,"children":[{"type":"Token","kind":"L_PAREN","start":23,"end":24},{"type":"Node","kind":"STRING","start":24,"end":52,"children":[{"type":"Node","kind":"SOURCE_FILE","start":25,"end":51,"istart":0,"iend":26,"children":[{"type":"Token","kind":"WHITESPACE","start":25,"end":30,"istart":0,"iend":5},{"type":"Node","kind":"FN","start":30,"end":46,"istart":5,"iend":21,"children":[{"type":"Token","kind":"FN_KW","start":30,"end":32,"istart":5,"iend":7},{"type":"Token","kind":"WHITESPACE","start":32,"end":33,"istart":7,"iend":8},{"type":"Node","kind":"NAME","start":33,"end":36,"istart":8,"iend":11,"children":[{"type":"Token","kind":"IDENT","start":33,"end":36,"istart":8,"iend":11}]},{"type":"Node","kind":"PARAM_LIST","start":36,"end":38,"istart":11,"iend":13,"children":[{"type":"Token","kind":"L_PAREN","start":36,"end":37,"istart":11,"iend":12},{"type":"Token","kind":"R_PAREN","start":37,"end":38,"istart":12,"iend":13}]},{"type":"Token","kind":"WHITESPACE","start":38,"end":39,"istart":13,"iend":14},{"type":"Node","kind":"BLOCK_EXPR","start":39,"end":46,"istart":14,"iend":21,"children":[{"type":"Node","kind":"STMT_LIST","start":39,"end":46,"istart":14,"iend":21,"children":[{"type":"Token","kind":"L_CURLY","start":39,"end":40,"istart":14,"iend":15},{"type":"Token","kind":"WHITESPACE","start":40,"end":45,"istart":15,"iend":20},{"type":"Token","kind":"R_CURLY","start":45,"end":46,"istart":20,"iend":21}]}]}]},{"type":"Token","kind":"WHITESPACE","start":46,"end":51,"istart":21,"iend":26}]}]},{"type":"Token","kind":"COMMA","start":52,"end":53},{"type":"Token","kind":"WHITESPACE","start":53,"end":54},{"type":"Token","kind":"STRING","start":54,"end":56},{"type":"Token","kind":"R_PAREN","start":56,"end":57}]}]}]},{"type":"Token","kind":"SEMICOLON","start":57,"end":58}]},{"type":"Token","kind":"WHITESPACE","start":58,"end":59},{"type":"Token","kind":"R_CURLY","start":59,"end":60}]}]}]}]}"#
+                r#"{"type":"Node","kind":"SOURCE_FILE","start":[0,0,0],"end":[60,5,1],"children":[{"type":"Node","kind":"FN","start":[0,0,0],"end":[60,5,1],"children":[{"type":"Token","kind":"FN_KW","start":[0,0,0],"end":[2,0,2]},{"type":"Token","kind":"WHITESPACE","start":[2,0,2],"end":[3,0,3]},{"type":"Node","kind":"NAME","start":[3,0,3],"end":[7,0,7],"children":[{"type":"Token","kind":"IDENT","start":[3,0,3],"end":[7,0,7]}]},{"type":"Node","kind":"PARAM_LIST","start":[7,0,7],"end":[9,0,9],"children":[{"type":"Token","kind":"L_PAREN","start":[7,0,7],"end":[8,0,8]},{"type":"Token","kind":"R_PAREN","start":[8,0,8],"end":[9,0,9]}]},{"type":"Token","kind":"WHITESPACE","start":[9,0,9],"end":[10,0,10]},{"type":"Node","kind":"BLOCK_EXPR","start":[10,0,10],"end":[60,5,1],"children":[{"type":"Node","kind":"STMT_LIST","start":[10,0,10],"end":[60,5,1],"children":[{"type":"Token","kind":"L_CURLY","start":[10,0,10],"end":[11,0,11]},{"type":"Token","kind":"WHITESPACE","start":[11,0,11],"end":[16,1,4]},{"type":"Node","kind":"EXPR_STMT","start":[16,1,4],"end":[58,4,11],"children":[{"type":"Node","kind":"MACRO_EXPR","start":[16,1,4],"end":[57,4,10],"children":[{"type":"Node","kind":"MACRO_CALL","start":[16,1,4],"end":[57,4,10],"children":[{"type":"Node","kind":"PATH","start":[16,1,4],"end":[22,1,10],"children":[{"type":"Node","kind":"PATH_SEGMENT","start":[16,1,4],"end":[22,1,10],"children":[{"type":"Node","kind":"NAME_REF","start":[16,1,4],"end":[22,1,10],"children":[{"type":"Token","kind":"IDENT","start":[16,1,4],"end":[22,1,10]}]}]}]},{"type":"Token","kind":"BANG","start":[22,1,10],"end":[23,1,11]},{"type":"Node","kind":"TOKEN_TREE","start":[23,1,11],"end":[57,4,10],"children":[{"type":"Token","kind":"L_PAREN","start":[23,1,11],"end":[24,1,12]},{"type":"Node","kind":"STRING","start":[24,1,12],"end":[52,4,5],"children":[{"type":"Node","kind":"SOURCE_FILE","start":[25,1,13],"end":[25,1,13],"istart":[0,0,0],"iend":[26,2,0],"children":[{"type":"Token","kind":"WHITESPACE","start":[25,1,13],"end":[25,1,13],"istart":[0,0,0],"iend":[5,0,5]},{"type":"Node","kind":"FN","start":[30,2,4],"end":[30,2,4],"istart":[5,0,5],"iend":[21,1,9],"children":[{"type":"Token","kind":"FN_KW","start":[30,2,4],"end":[30,2,4],"istart":[5,0,5],"iend":[7,0,7]},{"type":"Token","kind":"WHITESPACE","start":[32,2,6],"end":[32,2,6],"istart":[7,0,7],"iend":[8,0,8]},{"type":"Node","kind":"NAME","start":[33,2,7],"end":[33,2,7],"istart":[8,0,8],"iend":[11,0,11],"children":[{"type":"Token","kind":"IDENT","start":[33,2,7],"end":[33,2,7],"istart":[8,0,8],"iend":[11,0,11]}]},{"type":"Node","kind":"PARAM_LIST","start":[36,2,10],"end":[36,2,10],"istart":[11,0,11],"iend":[13,1,1],"children":[{"type":"Token","kind":"L_PAREN","start":[36,2,10],"end":[36,2,10],"istart":[11,0,11],"iend":[12,1,0]},{"type":"Token","kind":"R_PAREN","start":[37,2,11],"end":[37,2,11],"istart":[12,1,0],"iend":[13,1,1]}]},{"type":"Token","kind":"WHITESPACE","start":[38,2,12],"end":[38,2,12],"istart":[13,1,1],"iend":[14,1,2]},{"type":"Node","kind":"BLOCK_EXPR","start":[39,2,13],"end":[39,2,13],"istart":[14,1,2],"iend":[21,1,9],"children":[{"type":"Node","kind":"STMT_LIST","start":[39,2,13],"end":[39,2,13],"istart":[14,1,2],"iend":[21,1,9],"children":[{"type":"Token","kind":"L_CURLY","start":[39,2,13],"end":[39,2,13],"istart":[14,1,2],"iend":[15,1,3]},{"type":"Token","kind":"WHITESPACE","start":[40,2,14],"end":[40,2,14],"istart":[15,1,3],"iend":[20,1,8]},{"type":"Token","kind":"R_CURLY","start":[45,3,4],"end":[45,3,4],"istart":[20,1,8],"iend":[21,1,9]}]}]}]},{"type":"Token","kind":"WHITESPACE","start":[46,3,5],"end":[46,3,5],"istart":[21,1,9],"iend":[26,2,0]}]}]},{"type":"Token","kind":"COMMA","start":[52,4,5],"end":[53,4,6]},{"type":"Token","kind":"WHITESPACE","start":[53,4,6],"end":[54,4,7]},{"type":"Token","kind":"STRING","start":[54,4,7],"end":[56,4,9]},{"type":"Token","kind":"R_PAREN","start":[56,4,9],"end":[57,4,10]}]}]}]},{"type":"Token","kind":"SEMICOLON","start":[57,4,10],"end":[58,4,11]}]},{"type":"Token","kind":"WHITESPACE","start":[58,4,11],"end":[59,5,0]},{"type":"Token","kind":"R_CURLY","start":[59,5,0],"end":[60,5,1]}]}]}]}]}"#
             ]],
         )
     }
@@ -190,7 +219,7 @@ fn bar() {
     ", "");
 }"#,
             expect![[
-                r#"{"type":"Node","kind":"SOURCE_FILE","start":0,"end":65,"children":[{"type":"Node","kind":"FN","start":0,"end":65,"children":[{"type":"Token","kind":"FN_KW","start":0,"end":2},{"type":"Token","kind":"WHITESPACE","start":2,"end":3},{"type":"Node","kind":"NAME","start":3,"end":7,"children":[{"type":"Token","kind":"IDENT","start":3,"end":7}]},{"type":"Node","kind":"PARAM_LIST","start":7,"end":9,"children":[{"type":"Token","kind":"L_PAREN","start":7,"end":8},{"type":"Token","kind":"R_PAREN","start":8,"end":9}]},{"type":"Token","kind":"WHITESPACE","start":9,"end":10},{"type":"Node","kind":"BLOCK_EXPR","start":10,"end":65,"children":[{"type":"Node","kind":"STMT_LIST","start":10,"end":65,"children":[{"type":"Token","kind":"L_CURLY","start":10,"end":11},{"type":"Token","kind":"WHITESPACE","start":11,"end":16},{"type":"Node","kind":"EXPR_STMT","start":16,"end":63,"children":[{"type":"Node","kind":"MACRO_EXPR","start":16,"end":62,"children":[{"type":"Node","kind":"MACRO_CALL","start":16,"end":62,"children":[{"type":"Node","kind":"PATH","start":16,"end":22,"children":[{"type":"Node","kind":"PATH_SEGMENT","start":16,"end":22,"children":[{"type":"Node","kind":"NAME_REF","start":16,"end":22,"children":[{"type":"Token","kind":"IDENT","start":16,"end":22}]}]}]},{"type":"Token","kind":"BANG","start":22,"end":23},{"type":"Node","kind":"TOKEN_TREE","start":23,"end":62,"children":[{"type":"Token","kind":"L_PAREN","start":23,"end":24},{"type":"Node","kind":"STRING","start":24,"end":57,"children":[{"type":"Node","kind":"SOURCE_FILE","start":25,"end":56,"istart":0,"iend":31,"children":[{"type":"Token","kind":"WHITESPACE","start":25,"end":26,"istart":0,"iend":1},{"type":"Node","kind":"FN","start":26,"end":38,"istart":1,"iend":13,"children":[{"type":"Token","kind":"FN_KW","start":26,"end":28,"istart":1,"iend":3},{"type":"Token","kind":"WHITESPACE","start":28,"end":29,"istart":3,"iend":4},{"type":"Node","kind":"NAME","start":29,"end":32,"istart":4,"iend":7,"children":[{"type":"Token","kind":"IDENT","start":29,"end":32,"istart":4,"iend":7}]},{"type":"Node","kind":"PARAM_LIST","start":32,"end":34,"istart":7,"iend":9,"children":[{"type":"Token","kind":"L_PAREN","start":32,"end":33,"istart":7,"iend":8},{"type":"Token","kind":"R_PAREN","start":33,"end":34,"istart":8,"iend":9}]},{"type":"Token","kind":"WHITESPACE","start":34,"end":35,"istart":9,"iend":10},{"type":"Node","kind":"BLOCK_EXPR","start":35,"end":38,"istart":10,"iend":13,"children":[{"type":"Node","kind":"STMT_LIST","start":35,"end":38,"istart":10,"iend":13,"children":[{"type":"Token","kind":"L_CURLY","start":35,"end":36,"istart":10,"iend":11},{"type":"Token","kind":"WHITESPACE","start":36,"end":37,"istart":11,"iend":12},{"type":"Token","kind":"R_CURLY","start":37,"end":38,"istart":12,"iend":13}]}]}]},{"type":"Token","kind":"WHITESPACE","start":38,"end":39,"istart":13,"iend":14},{"type":"Node","kind":"FN","start":39,"end":51,"istart":14,"iend":26,"children":[{"type":"Token","kind":"FN_KW","start":39,"end":41,"istart":14,"iend":16},{"type":"Token","kind":"WHITESPACE","start":41,"end":42,"istart":16,"iend":17},{"type":"Node","kind":"NAME","start":42,"end":45,"istart":17,"iend":20,"children":[{"type":"Token","kind":"IDENT","start":42,"end":45,"istart":17,"iend":20}]},{"type":"Node","kind":"PARAM_LIST","start":45,"end":47,"istart":20,"iend":22,"children":[{"type":"Token","kind":"L_PAREN","start":45,"end":46,"istart":20,"iend":21},{"type":"Token","kind":"R_PAREN","start":46,"end":47,"istart":21,"iend":22}]},{"type":"Token","kind":"WHITESPACE","start":47,"end":48,"istart":22,"iend":23},{"type":"Node","kind":"BLOCK_EXPR","start":48,"end":51,"istart":23,"iend":26,"children":[{"type":"Node","kind":"STMT_LIST","start":48,"end":51,"istart":23,"iend":26,"children":[{"type":"Token","kind":"L_CURLY","start":48,"end":49,"istart":23,"iend":24},{"type":"Token","kind":"WHITESPACE","start":49,"end":50,"istart":24,"iend":25},{"type":"Token","kind":"R_CURLY","start":50,"end":51,"istart":25,"iend":26}]}]}]},{"type":"Token","kind":"WHITESPACE","start":51,"end":56,"istart":26,"iend":31}]}]},{"type":"Token","kind":"COMMA","start":57,"end":58},{"type":"Token","kind":"WHITESPACE","start":58,"end":59},{"type":"Token","kind":"STRING","start":59,"end":61},{"type":"Token","kind":"R_PAREN","start":61,"end":62}]}]}]},{"type":"Token","kind":"SEMICOLON","start":62,"end":63}]},{"type":"Token","kind":"WHITESPACE","start":63,"end":64},{"type":"Token","kind":"R_CURLY","start":64,"end":65}]}]}]}]}"#
+                r#"{"type":"Node","kind":"SOURCE_FILE","start":[0,0,0],"end":[65,7,1],"children":[{"type":"Node","kind":"FN","start":[0,0,0],"end":[65,7,1],"children":[{"type":"Token","kind":"FN_KW","start":[0,0,0],"end":[2,0,2]},{"type":"Token","kind":"WHITESPACE","start":[2,0,2],"end":[3,0,3]},{"type":"Node","kind":"NAME","start":[3,0,3],"end":[7,0,7],"children":[{"type":"Token","kind":"IDENT","start":[3,0,3],"end":[7,0,7]}]},{"type":"Node","kind":"PARAM_LIST","start":[7,0,7],"end":[9,0,9],"children":[{"type":"Token","kind":"L_PAREN","start":[7,0,7],"end":[8,0,8]},{"type":"Token","kind":"R_PAREN","start":[8,0,8],"end":[9,0,9]}]},{"type":"Token","kind":"WHITESPACE","start":[9,0,9],"end":[10,0,10]},{"type":"Node","kind":"BLOCK_EXPR","start":[10,0,10],"end":[65,7,1],"children":[{"type":"Node","kind":"STMT_LIST","start":[10,0,10],"end":[65,7,1],"children":[{"type":"Token","kind":"L_CURLY","start":[10,0,10],"end":[11,0,11]},{"type":"Token","kind":"WHITESPACE","start":[11,0,11],"end":[16,1,4]},{"type":"Node","kind":"EXPR_STMT","start":[16,1,4],"end":[63,6,11],"children":[{"type":"Node","kind":"MACRO_EXPR","start":[16,1,4],"end":[62,6,10],"children":[{"type":"Node","kind":"MACRO_CALL","start":[16,1,4],"end":[62,6,10],"children":[{"type":"Node","kind":"PATH","start":[16,1,4],"end":[22,1,10],"children":[{"type":"Node","kind":"PATH_SEGMENT","start":[16,1,4],"end":[22,1,10],"children":[{"type":"Node","kind":"NAME_REF","start":[16,1,4],"end":[22,1,10],"children":[{"type":"Token","kind":"IDENT","start":[16,1,4],"end":[22,1,10]}]}]}]},{"type":"Token","kind":"BANG","start":[22,1,10],"end":[23,1,11]},{"type":"Node","kind":"TOKEN_TREE","start":[23,1,11],"end":[62,6,10],"children":[{"type":"Token","kind":"L_PAREN","start":[23,1,11],"end":[24,1,12]},{"type":"Node","kind":"STRING","start":[24,1,12],"end":[57,6,5],"children":[{"type":"Node","kind":"SOURCE_FILE","start":[25,1,13],"end":[25,1,13],"istart":[0,0,0],"iend":[31,2,5],"children":[{"type":"Token","kind":"WHITESPACE","start":[25,1,13],"end":[25,1,13],"istart":[0,0,0],"iend":[1,0,1]},{"type":"Node","kind":"FN","start":[26,2,0],"end":[26,2,0],"istart":[1,0,1],"iend":[13,1,1],"children":[{"type":"Token","kind":"FN_KW","start":[26,2,0],"end":[26,2,0],"istart":[1,0,1],"iend":[3,0,3]},{"type":"Token","kind":"WHITESPACE","start":[28,2,2],"end":[28,2,2],"istart":[3,0,3],"iend":[4,0,4]},{"type":"Node","kind":"NAME","start":[29,2,3],"end":[29,2,3],"istart":[4,0,4],"iend":[7,0,7],"children":[{"type":"Token","kind":"IDENT","start":[29,2,3],"end":[29,2,3],"istart":[4,0,4],"iend":[7,0,7]}]},{"type":"Node","kind":"PARAM_LIST","start":[32,2,6],"end":[32,2,6],"istart":[7,0,7],"iend":[9,0,9],"children":[{"type":"Token","kind":"L_PAREN","start":[32,2,6],"end":[32,2,6],"istart":[7,0,7],"iend":[8,0,8]},{"type":"Token","kind":"R_PAREN","start":[33,2,7],"end":[33,2,7],"istart":[8,0,8],"iend":[9,0,9]}]},{"type":"Token","kind":"WHITESPACE","start":[34,2,8],"end":[34,2,8],"istart":[9,0,9],"iend":[10,0,10]},{"type":"Node","kind":"BLOCK_EXPR","start":[35,2,9],"end":[35,2,9],"istart":[10,0,10],"iend":[13,1,1],"children":[{"type":"Node","kind":"STMT_LIST","start":[35,2,9],"end":[35,2,9],"istart":[10,0,10],"iend":[13,1,1],"children":[{"type":"Token","kind":"L_CURLY","start":[35,2,9],"end":[35,2,9],"istart":[10,0,10],"iend":[11,0,11]},{"type":"Token","kind":"WHITESPACE","start":[36,2,10],"end":[36,2,10],"istart":[11,0,11],"iend":[12,1,0]},{"type":"Token","kind":"R_CURLY","start":[37,3,0],"end":[37,3,0],"istart":[12,1,0],"iend":[13,1,1]}]}]}]},{"type":"Token","kind":"WHITESPACE","start":[38,3,1],"end":[38,3,1],"istart":[13,1,1],"iend":[14,1,2]},{"type":"Node","kind":"FN","start":[39,4,0],"end":[39,4,0],"istart":[14,1,2],"iend":[26,2,0],"children":[{"type":"Token","kind":"FN_KW","start":[39,4,0],"end":[39,4,0],"istart":[14,1,2],"iend":[16,1,4]},{"type":"Token","kind":"WHITESPACE","start":[41,4,2],"end":[41,4,2],"istart":[16,1,4],"iend":[17,1,5]},{"type":"Node","kind":"NAME","start":[42,4,3],"end":[42,4,3],"istart":[17,1,5],"iend":[20,1,8],"children":[{"type":"Token","kind":"IDENT","start":[42,4,3],"end":[42,4,3],"istart":[17,1,5],"iend":[20,1,8]}]},{"type":"Node","kind":"PARAM_LIST","start":[45,4,6],"end":[45,4,6],"istart":[20,1,8],"iend":[22,1,10],"children":[{"type":"Token","kind":"L_PAREN","start":[45,4,6],"end":[45,4,6],"istart":[20,1,8],"iend":[21,1,9]},{"type":"Token","kind":"R_PAREN","start":[46,4,7],"end":[46,4,7],"istart":[21,1,9],"iend":[22,1,10]}]},{"type":"Token","kind":"WHITESPACE","start":[47,4,8],"end":[47,4,8],"istart":[22,1,10],"iend":[23,1,11]},{"type":"Node","kind":"BLOCK_EXPR","start":[48,4,9],"end":[48,4,9],"istart":[23,1,11],"iend":[26,2,0],"children":[{"type":"Node","kind":"STMT_LIST","start":[48,4,9],"end":[48,4,9],"istart":[23,1,11],"iend":[26,2,0],"children":[{"type":"Token","kind":"L_CURLY","start":[48,4,9],"end":[48,4,9],"istart":[23,1,11],"iend":[24,1,12]},{"type":"Token","kind":"WHITESPACE","start":[49,4,10],"end":[49,4,10],"istart":[24,1,12],"iend":[25,1,13]},{"type":"Token","kind":"R_CURLY","start":[50,5,0],"end":[50,5,0],"istart":[25,1,13],"iend":[26,2,0]}]}]}]},{"type":"Token","kind":"WHITESPACE","start":[51,5,1],"end":[51,5,1],"istart":[26,2,0],"iend":[31,2,5]}]}]},{"type":"Token","kind":"COMMA","start":[57,6,5],"end":[58,6,6]},{"type":"Token","kind":"WHITESPACE","start":[58,6,6],"end":[59,6,7]},{"type":"Token","kind":"STRING","start":[59,6,7],"end":[61,6,9]},{"type":"Token","kind":"R_PAREN","start":[61,6,9],"end":[62,6,10]}]}]}]},{"type":"Token","kind":"SEMICOLON","start":[62,6,10],"end":[63,6,11]}]},{"type":"Token","kind":"WHITESPACE","start":[63,6,11],"end":[64,7,0]},{"type":"Token","kind":"R_CURLY","start":[64,7,0],"end":[65,7,1]}]}]}]}]}"#
             ]],
         );
 
@@ -205,7 +234,7 @@ fn bar() {
     "#, "");
 }"###,
             expect![[
-                r#"{"type":"Node","kind":"SOURCE_FILE","start":0,"end":68,"children":[{"type":"Node","kind":"FN","start":0,"end":68,"children":[{"type":"Token","kind":"FN_KW","start":0,"end":2},{"type":"Token","kind":"WHITESPACE","start":2,"end":3},{"type":"Node","kind":"NAME","start":3,"end":7,"children":[{"type":"Token","kind":"IDENT","start":3,"end":7}]},{"type":"Node","kind":"PARAM_LIST","start":7,"end":9,"children":[{"type":"Token","kind":"L_PAREN","start":7,"end":8},{"type":"Token","kind":"R_PAREN","start":8,"end":9}]},{"type":"Token","kind":"WHITESPACE","start":9,"end":10},{"type":"Node","kind":"BLOCK_EXPR","start":10,"end":68,"children":[{"type":"Node","kind":"STMT_LIST","start":10,"end":68,"children":[{"type":"Token","kind":"L_CURLY","start":10,"end":11},{"type":"Token","kind":"WHITESPACE","start":11,"end":16},{"type":"Node","kind":"EXPR_STMT","start":16,"end":66,"children":[{"type":"Node","kind":"MACRO_EXPR","start":16,"end":65,"children":[{"type":"Node","kind":"MACRO_CALL","start":16,"end":65,"children":[{"type":"Node","kind":"PATH","start":16,"end":22,"children":[{"type":"Node","kind":"PATH_SEGMENT","start":16,"end":22,"children":[{"type":"Node","kind":"NAME_REF","start":16,"end":22,"children":[{"type":"Token","kind":"IDENT","start":16,"end":22}]}]}]},{"type":"Token","kind":"BANG","start":22,"end":23},{"type":"Node","kind":"TOKEN_TREE","start":23,"end":65,"children":[{"type":"Token","kind":"L_PAREN","start":23,"end":24},{"type":"Node","kind":"STRING","start":24,"end":60,"children":[{"type":"Node","kind":"SOURCE_FILE","start":27,"end":58,"istart":0,"iend":31,"children":[{"type":"Token","kind":"WHITESPACE","start":27,"end":28,"istart":0,"iend":1},{"type":"Node","kind":"FN","start":28,"end":40,"istart":1,"iend":13,"children":[{"type":"Token","kind":"FN_KW","start":28,"end":30,"istart":1,"iend":3},{"type":"Token","kind":"WHITESPACE","start":30,"end":31,"istart":3,"iend":4},{"type":"Node","kind":"NAME","start":31,"end":34,"istart":4,"iend":7,"children":[{"type":"Token","kind":"IDENT","start":31,"end":34,"istart":4,"iend":7}]},{"type":"Node","kind":"PARAM_LIST","start":34,"end":36,"istart":7,"iend":9,"children":[{"type":"Token","kind":"L_PAREN","start":34,"end":35,"istart":7,"iend":8},{"type":"Token","kind":"R_PAREN","start":35,"end":36,"istart":8,"iend":9}]},{"type":"Token","kind":"WHITESPACE","start":36,"end":37,"istart":9,"iend":10},{"type":"Node","kind":"BLOCK_EXPR","start":37,"end":40,"istart":10,"iend":13,"children":[{"type":"Node","kind":"STMT_LIST","start":37,"end":40,"istart":10,"iend":13,"children":[{"type":"Token","kind":"L_CURLY","start":37,"end":38,"istart":10,"iend":11},{"type":"Token","kind":"WHITESPACE","start":38,"end":39,"istart":11,"iend":12},{"type":"Token","kind":"R_CURLY","start":39,"end":40,"istart":12,"iend":13}]}]}]},{"type":"Token","kind":"WHITESPACE","start":40,"end":41,"istart":13,"iend":14},{"type":"Node","kind":"FN","start":41,"end":53,"istart":14,"iend":26,"children":[{"type":"Token","kind":"FN_KW","start":41,"end":43,"istart":14,"iend":16},{"type":"Token","kind":"WHITESPACE","start":43,"end":44,"istart":16,"iend":17},{"type":"Node","kind":"NAME","start":44,"end":47,"istart":17,"iend":20,"children":[{"type":"Token","kind":"IDENT","start":44,"end":47,"istart":17,"iend":20}]},{"type":"Node","kind":"PARAM_LIST","start":47,"end":49,"istart":20,"iend":22,"children":[{"type":"Token","kind":"L_PAREN","start":47,"end":48,"istart":20,"iend":21},{"type":"Token","kind":"R_PAREN","start":48,"end":49,"istart":21,"iend":22}]},{"type":"Token","kind":"WHITESPACE","start":49,"end":50,"istart":22,"iend":23},{"type":"Node","kind":"BLOCK_EXPR","start":50,"end":53,"istart":23,"iend":26,"children":[{"type":"Node","kind":"STMT_LIST","start":50,"end":53,"istart":23,"iend":26,"children":[{"type":"Token","kind":"L_CURLY","start":50,"end":51,"istart":23,"iend":24},{"type":"Token","kind":"WHITESPACE","start":51,"end":52,"istart":24,"iend":25},{"type":"Token","kind":"R_CURLY","start":52,"end":53,"istart":25,"iend":26}]}]}]},{"type":"Token","kind":"WHITESPACE","start":53,"end":58,"istart":26,"iend":31}]}]},{"type":"Token","kind":"COMMA","start":60,"end":61},{"type":"Token","kind":"WHITESPACE","start":61,"end":62},{"type":"Token","kind":"STRING","start":62,"end":64},{"type":"Token","kind":"R_PAREN","start":64,"end":65}]}]}]},{"type":"Token","kind":"SEMICOLON","start":65,"end":66}]},{"type":"Token","kind":"WHITESPACE","start":66,"end":67},{"type":"Token","kind":"R_CURLY","start":67,"end":68}]}]}]}]}"#
+                r#"{"type":"Node","kind":"SOURCE_FILE","start":[0,0,0],"end":[68,7,1],"children":[{"type":"Node","kind":"FN","start":[0,0,0],"end":[68,7,1],"children":[{"type":"Token","kind":"FN_KW","start":[0,0,0],"end":[2,0,2]},{"type":"Token","kind":"WHITESPACE","start":[2,0,2],"end":[3,0,3]},{"type":"Node","kind":"NAME","start":[3,0,3],"end":[7,0,7],"children":[{"type":"Token","kind":"IDENT","start":[3,0,3],"end":[7,0,7]}]},{"type":"Node","kind":"PARAM_LIST","start":[7,0,7],"end":[9,0,9],"children":[{"type":"Token","kind":"L_PAREN","start":[7,0,7],"end":[8,0,8]},{"type":"Token","kind":"R_PAREN","start":[8,0,8],"end":[9,0,9]}]},{"type":"Token","kind":"WHITESPACE","start":[9,0,9],"end":[10,0,10]},{"type":"Node","kind":"BLOCK_EXPR","start":[10,0,10],"end":[68,7,1],"children":[{"type":"Node","kind":"STMT_LIST","start":[10,0,10],"end":[68,7,1],"children":[{"type":"Token","kind":"L_CURLY","start":[10,0,10],"end":[11,0,11]},{"type":"Token","kind":"WHITESPACE","start":[11,0,11],"end":[16,1,4]},{"type":"Node","kind":"EXPR_STMT","start":[16,1,4],"end":[66,6,12],"children":[{"type":"Node","kind":"MACRO_EXPR","start":[16,1,4],"end":[65,6,11],"children":[{"type":"Node","kind":"MACRO_CALL","start":[16,1,4],"end":[65,6,11],"children":[{"type":"Node","kind":"PATH","start":[16,1,4],"end":[22,1,10],"children":[{"type":"Node","kind":"PATH_SEGMENT","start":[16,1,4],"end":[22,1,10],"children":[{"type":"Node","kind":"NAME_REF","start":[16,1,4],"end":[22,1,10],"children":[{"type":"Token","kind":"IDENT","start":[16,1,4],"end":[22,1,10]}]}]}]},{"type":"Token","kind":"BANG","start":[22,1,10],"end":[23,1,11]},{"type":"Node","kind":"TOKEN_TREE","start":[23,1,11],"end":[65,6,11],"children":[{"type":"Token","kind":"L_PAREN","start":[23,1,11],"end":[24,1,12]},{"type":"Node","kind":"STRING","start":[24,1,12],"end":[60,6,6],"children":[{"type":"Node","kind":"SOURCE_FILE","start":[27,1,15],"end":[27,1,15],"istart":[0,0,0],"iend":[31,2,3],"children":[{"type":"Token","kind":"WHITESPACE","start":[27,1,15],"end":[27,1,15],"istart":[0,0,0],"iend":[1,0,1]},{"type":"Node","kind":"FN","start":[28,2,0],"end":[28,2,0],"istart":[1,0,1],"iend":[13,1,1],"children":[{"type":"Token","kind":"FN_KW","start":[28,2,0],"end":[28,2,0],"istart":[1,0,1],"iend":[3,0,3]},{"type":"Token","kind":"WHITESPACE","start":[30,2,2],"end":[30,2,2],"istart":[3,0,3],"iend":[4,0,4]},{"type":"Node","kind":"NAME","start":[31,2,3],"end":[31,2,3],"istart":[4,0,4],"iend":[7,0,7],"children":[{"type":"Token","kind":"IDENT","start":[31,2,3],"end":[31,2,3],"istart":[4,0,4],"iend":[7,0,7]}]},{"type":"Node","kind":"PARAM_LIST","start":[34,2,6],"end":[34,2,6],"istart":[7,0,7],"iend":[9,0,9],"children":[{"type":"Token","kind":"L_PAREN","start":[34,2,6],"end":[34,2,6],"istart":[7,0,7],"iend":[8,0,8]},{"type":"Token","kind":"R_PAREN","start":[35,2,7],"end":[35,2,7],"istart":[8,0,8],"iend":[9,0,9]}]},{"type":"Token","kind":"WHITESPACE","start":[36,2,8],"end":[36,2,8],"istart":[9,0,9],"iend":[10,0,10]},{"type":"Node","kind":"BLOCK_EXPR","start":[37,2,9],"end":[37,2,9],"istart":[10,0,10],"iend":[13,1,1],"children":[{"type":"Node","kind":"STMT_LIST","start":[37,2,9],"end":[37,2,9],"istart":[10,0,10],"iend":[13,1,1],"children":[{"type":"Token","kind":"L_CURLY","start":[37,2,9],"end":[37,2,9],"istart":[10,0,10],"iend":[11,0,11]},{"type":"Token","kind":"WHITESPACE","start":[38,2,10],"end":[38,2,10],"istart":[11,0,11],"iend":[12,1,0]},{"type":"Token","kind":"R_CURLY","start":[39,3,0],"end":[39,3,0],"istart":[12,1,0],"iend":[13,1,1]}]}]}]},{"type":"Token","kind":"WHITESPACE","start":[40,3,1],"end":[40,3,1],"istart":[13,1,1],"iend":[14,1,2]},{"type":"Node","kind":"FN","start":[41,4,0],"end":[41,4,0],"istart":[14,1,2],"iend":[26,1,14],"children":[{"type":"Token","kind":"FN_KW","start":[41,4,0],"end":[41,4,0],"istart":[14,1,2],"iend":[16,1,4]},{"type":"Token","kind":"WHITESPACE","start":[43,4,2],"end":[43,4,2],"istart":[16,1,4],"iend":[17,1,5]},{"type":"Node","kind":"NAME","start":[44,4,3],"end":[44,4,3],"istart":[17,1,5],"iend":[20,1,8],"children":[{"type":"Token","kind":"IDENT","start":[44,4,3],"end":[44,4,3],"istart":[17,1,5],"iend":[20,1,8]}]},{"type":"Node","kind":"PARAM_LIST","start":[47,4,6],"end":[47,4,6],"istart":[20,1,8],"iend":[22,1,10],"children":[{"type":"Token","kind":"L_PAREN","start":[47,4,6],"end":[47,4,6],"istart":[20,1,8],"iend":[21,1,9]},{"type":"Token","kind":"R_PAREN","start":[48,4,7],"end":[48,4,7],"istart":[21,1,9],"iend":[22,1,10]}]},{"type":"Token","kind":"WHITESPACE","start":[49,4,8],"end":[49,4,8],"istart":[22,1,10],"iend":[23,1,11]},{"type":"Node","kind":"BLOCK_EXPR","start":[50,4,9],"end":[50,4,9],"istart":[23,1,11],"iend":[26,1,14],"children":[{"type":"Node","kind":"STMT_LIST","start":[50,4,9],"end":[50,4,9],"istart":[23,1,11],"iend":[26,1,14],"children":[{"type":"Token","kind":"L_CURLY","start":[50,4,9],"end":[50,4,9],"istart":[23,1,11],"iend":[24,1,12]},{"type":"Token","kind":"WHITESPACE","start":[51,4,10],"end":[51,4,10],"istart":[24,1,12],"iend":[25,1,13]},{"type":"Token","kind":"R_CURLY","start":[52,5,0],"end":[52,5,0],"istart":[25,1,13],"iend":[26,1,14]}]}]}]},{"type":"Token","kind":"WHITESPACE","start":[53,5,1],"end":[53,5,1],"istart":[26,1,14],"iend":[31,2,3]}]}]},{"type":"Token","kind":"COMMA","start":[60,6,6],"end":[61,6,7]},{"type":"Token","kind":"WHITESPACE","start":[61,6,7],"end":[62,6,8]},{"type":"Token","kind":"STRING","start":[62,6,8],"end":[64,6,10]},{"type":"Token","kind":"R_PAREN","start":[64,6,10],"end":[65,6,11]}]}]}]},{"type":"Token","kind":"SEMICOLON","start":[65,6,11],"end":[66,6,12]}]},{"type":"Token","kind":"WHITESPACE","start":[66,6,12],"end":[67,7,0]},{"type":"Token","kind":"R_CURLY","start":[67,7,0],"end":[68,7,1]}]}]}]}]}"#
             ]],
         );
 
@@ -219,7 +248,7 @@ fn bar() {
 }"$0#, "");
 }"###,
             expect![[
-                r#"{"type":"Node","kind":"SOURCE_FILE","start":0,"end":63,"children":[{"type":"Node","kind":"FN","start":0,"end":63,"children":[{"type":"Token","kind":"FN_KW","start":0,"end":2},{"type":"Token","kind":"WHITESPACE","start":2,"end":3},{"type":"Node","kind":"NAME","start":3,"end":7,"children":[{"type":"Token","kind":"IDENT","start":3,"end":7}]},{"type":"Node","kind":"PARAM_LIST","start":7,"end":9,"children":[{"type":"Token","kind":"L_PAREN","start":7,"end":8},{"type":"Token","kind":"R_PAREN","start":8,"end":9}]},{"type":"Token","kind":"WHITESPACE","start":9,"end":10},{"type":"Node","kind":"BLOCK_EXPR","start":10,"end":63,"children":[{"type":"Node","kind":"STMT_LIST","start":10,"end":63,"children":[{"type":"Token","kind":"L_CURLY","start":10,"end":11},{"type":"Token","kind":"WHITESPACE","start":11,"end":16},{"type":"Node","kind":"EXPR_STMT","start":16,"end":61,"children":[{"type":"Node","kind":"MACRO_EXPR","start":16,"end":60,"children":[{"type":"Node","kind":"MACRO_CALL","start":16,"end":60,"children":[{"type":"Node","kind":"PATH","start":16,"end":22,"children":[{"type":"Node","kind":"PATH_SEGMENT","start":16,"end":22,"children":[{"type":"Node","kind":"NAME_REF","start":16,"end":22,"children":[{"type":"Token","kind":"IDENT","start":16,"end":22}]}]}]},{"type":"Token","kind":"BANG","start":22,"end":23},{"type":"Node","kind":"TOKEN_TREE","start":23,"end":60,"children":[{"type":"Token","kind":"L_PAREN","start":23,"end":24},{"type":"Node","kind":"STRING","start":24,"end":55,"children":[{"type":"Node","kind":"SOURCE_FILE","start":27,"end":53,"istart":0,"iend":26,"children":[{"type":"Token","kind":"WHITESPACE","start":27,"end":28,"istart":0,"iend":1},{"type":"Node","kind":"FN","start":28,"end":40,"istart":1,"iend":13,"children":[{"type":"Token","kind":"FN_KW","start":28,"end":30,"istart":1,"iend":3},{"type":"Token","kind":"WHITESPACE","start":30,"end":31,"istart":3,"iend":4},{"type":"Node","kind":"NAME","start":31,"end":34,"istart":4,"iend":7,"children":[{"type":"Token","kind":"IDENT","start":31,"end":34,"istart":4,"iend":7}]},{"type":"Node","kind":"PARAM_LIST","start":34,"end":36,"istart":7,"iend":9,"children":[{"type":"Token","kind":"L_PAREN","start":34,"end":35,"istart":7,"iend":8},{"type":"Token","kind":"R_PAREN","start":35,"end":36,"istart":8,"iend":9}]},{"type":"Token","kind":"WHITESPACE","start":36,"end":37,"istart":9,"iend":10},{"type":"Node","kind":"BLOCK_EXPR","start":37,"end":40,"istart":10,"iend":13,"children":[{"type":"Node","kind":"STMT_LIST","start":37,"end":40,"istart":10,"iend":13,"children":[{"type":"Token","kind":"L_CURLY","start":37,"end":38,"istart":10,"iend":11},{"type":"Token","kind":"WHITESPACE","start":38,"end":39,"istart":11,"iend":12},{"type":"Token","kind":"R_CURLY","start":39,"end":40,"istart":12,"iend":13}]}]}]},{"type":"Token","kind":"WHITESPACE","start":40,"end":41,"istart":13,"iend":14},{"type":"Node","kind":"FN","start":41,"end":53,"istart":14,"iend":26,"children":[{"type":"Token","kind":"FN_KW","start":41,"end":43,"istart":14,"iend":16},{"type":"Token","kind":"WHITESPACE","start":43,"end":44,"istart":16,"iend":17},{"type":"Node","kind":"NAME","start":44,"end":47,"istart":17,"iend":20,"children":[{"type":"Token","kind":"IDENT","start":44,"end":47,"istart":17,"iend":20}]},{"type":"Node","kind":"PARAM_LIST","start":47,"end":49,"istart":20,"iend":22,"children":[{"type":"Token","kind":"L_PAREN","start":47,"end":48,"istart":20,"iend":21},{"type":"Token","kind":"R_PAREN","start":48,"end":49,"istart":21,"iend":22}]},{"type":"Token","kind":"WHITESPACE","start":49,"end":50,"istart":22,"iend":23},{"type":"Node","kind":"BLOCK_EXPR","start":50,"end":53,"istart":23,"iend":26,"children":[{"type":"Node","kind":"STMT_LIST","start":50,"end":53,"istart":23,"iend":26,"children":[{"type":"Token","kind":"L_CURLY","start":50,"end":51,"istart":23,"iend":24},{"type":"Token","kind":"WHITESPACE","start":51,"end":52,"istart":24,"iend":25},{"type":"Token","kind":"R_CURLY","start":52,"end":53,"istart":25,"iend":26}]}]}]}]}]},{"type":"Token","kind":"COMMA","start":55,"end":56},{"type":"Token","kind":"WHITESPACE","start":56,"end":57},{"type":"Token","kind":"STRING","start":57,"end":59},{"type":"Token","kind":"R_PAREN","start":59,"end":60}]}]}]},{"type":"Token","kind":"SEMICOLON","start":60,"end":61}]},{"type":"Token","kind":"WHITESPACE","start":61,"end":62},{"type":"Token","kind":"R_CURLY","start":62,"end":63}]}]}]}]}"#
+                r#"{"type":"Node","kind":"SOURCE_FILE","start":[0,0,0],"end":[63,6,1],"children":[{"type":"Node","kind":"FN","start":[0,0,0],"end":[63,6,1],"children":[{"type":"Token","kind":"FN_KW","start":[0,0,0],"end":[2,0,2]},{"type":"Token","kind":"WHITESPACE","start":[2,0,2],"end":[3,0,3]},{"type":"Node","kind":"NAME","start":[3,0,3],"end":[7,0,7],"children":[{"type":"Token","kind":"IDENT","start":[3,0,3],"end":[7,0,7]}]},{"type":"Node","kind":"PARAM_LIST","start":[7,0,7],"end":[9,0,9],"children":[{"type":"Token","kind":"L_PAREN","start":[7,0,7],"end":[8,0,8]},{"type":"Token","kind":"R_PAREN","start":[8,0,8],"end":[9,0,9]}]},{"type":"Token","kind":"WHITESPACE","start":[9,0,9],"end":[10,0,10]},{"type":"Node","kind":"BLOCK_EXPR","start":[10,0,10],"end":[63,6,1],"children":[{"type":"Node","kind":"STMT_LIST","start":[10,0,10],"end":[63,6,1],"children":[{"type":"Token","kind":"L_CURLY","start":[10,0,10],"end":[11,0,11]},{"type":"Token","kind":"WHITESPACE","start":[11,0,11],"end":[16,1,4]},{"type":"Node","kind":"EXPR_STMT","start":[16,1,4],"end":[61,5,9],"children":[{"type":"Node","kind":"MACRO_EXPR","start":[16,1,4],"end":[60,5,8],"children":[{"type":"Node","kind":"MACRO_CALL","start":[16,1,4],"end":[60,5,8],"children":[{"type":"Node","kind":"PATH","start":[16,1,4],"end":[22,1,10],"children":[{"type":"Node","kind":"PATH_SEGMENT","start":[16,1,4],"end":[22,1,10],"children":[{"type":"Node","kind":"NAME_REF","start":[16,1,4],"end":[22,1,10],"children":[{"type":"Token","kind":"IDENT","start":[16,1,4],"end":[22,1,10]}]}]}]},{"type":"Token","kind":"BANG","start":[22,1,10],"end":[23,1,11]},{"type":"Node","kind":"TOKEN_TREE","start":[23,1,11],"end":[60,5,8],"children":[{"type":"Token","kind":"L_PAREN","start":[23,1,11],"end":[24,1,12]},{"type":"Node","kind":"STRING","start":[24,1,12],"end":[55,5,3],"children":[{"type":"Node","kind":"SOURCE_FILE","start":[27,1,15],"end":[27,1,15],"istart":[0,0,0],"iend":[26,1,14],"children":[{"type":"Token","kind":"WHITESPACE","start":[27,1,15],"end":[27,1,15],"istart":[0,0,0],"iend":[1,0,1]},{"type":"Node","kind":"FN","start":[28,2,0],"end":[28,2,0],"istart":[1,0,1],"iend":[13,1,1],"children":[{"type":"Token","kind":"FN_KW","start":[28,2,0],"end":[28,2,0],"istart":[1,0,1],"iend":[3,0,3]},{"type":"Token","kind":"WHITESPACE","start":[30,2,2],"end":[30,2,2],"istart":[3,0,3],"iend":[4,0,4]},{"type":"Node","kind":"NAME","start":[31,2,3],"end":[31,2,3],"istart":[4,0,4],"iend":[7,0,7],"children":[{"type":"Token","kind":"IDENT","start":[31,2,3],"end":[31,2,3],"istart":[4,0,4],"iend":[7,0,7]}]},{"type":"Node","kind":"PARAM_LIST","start":[34,2,6],"end":[34,2,6],"istart":[7,0,7],"iend":[9,0,9],"children":[{"type":"Token","kind":"L_PAREN","start":[34,2,6],"end":[34,2,6],"istart":[7,0,7],"iend":[8,0,8]},{"type":"Token","kind":"R_PAREN","start":[35,2,7],"end":[35,2,7],"istart":[8,0,8],"iend":[9,0,9]}]},{"type":"Token","kind":"WHITESPACE","start":[36,2,8],"end":[36,2,8],"istart":[9,0,9],"iend":[10,0,10]},{"type":"Node","kind":"BLOCK_EXPR","start":[37,2,9],"end":[37,2,9],"istart":[10,0,10],"iend":[13,1,1],"children":[{"type":"Node","kind":"STMT_LIST","start":[37,2,9],"end":[37,2,9],"istart":[10,0,10],"iend":[13,1,1],"children":[{"type":"Token","kind":"L_CURLY","start":[37,2,9],"end":[37,2,9],"istart":[10,0,10],"iend":[11,0,11]},{"type":"Token","kind":"WHITESPACE","start":[38,2,10],"end":[38,2,10],"istart":[11,0,11],"iend":[12,1,0]},{"type":"Token","kind":"R_CURLY","start":[39,3,0],"end":[39,3,0],"istart":[12,1,0],"iend":[13,1,1]}]}]}]},{"type":"Token","kind":"WHITESPACE","start":[40,3,1],"end":[40,3,1],"istart":[13,1,1],"iend":[14,1,2]},{"type":"Node","kind":"FN","start":[41,4,0],"end":[41,4,0],"istart":[14,1,2],"iend":[26,1,14],"children":[{"type":"Token","kind":"FN_KW","start":[41,4,0],"end":[41,4,0],"istart":[14,1,2],"iend":[16,1,4]},{"type":"Token","kind":"WHITESPACE","start":[43,4,2],"end":[43,4,2],"istart":[16,1,4],"iend":[17,1,5]},{"type":"Node","kind":"NAME","start":[44,4,3],"end":[44,4,3],"istart":[17,1,5],"iend":[20,1,8],"children":[{"type":"Token","kind":"IDENT","start":[44,4,3],"end":[44,4,3],"istart":[17,1,5],"iend":[20,1,8]}]},{"type":"Node","kind":"PARAM_LIST","start":[47,4,6],"end":[47,4,6],"istart":[20,1,8],"iend":[22,1,10],"children":[{"type":"Token","kind":"L_PAREN","start":[47,4,6],"end":[47,4,6],"istart":[20,1,8],"iend":[21,1,9]},{"type":"Token","kind":"R_PAREN","start":[48,4,7],"end":[48,4,7],"istart":[21,1,9],"iend":[22,1,10]}]},{"type":"Token","kind":"WHITESPACE","start":[49,4,8],"end":[49,4,8],"istart":[22,1,10],"iend":[23,1,11]},{"type":"Node","kind":"BLOCK_EXPR","start":[50,4,9],"end":[50,4,9],"istart":[23,1,11],"iend":[26,1,14],"children":[{"type":"Node","kind":"STMT_LIST","start":[50,4,9],"end":[50,4,9],"istart":[23,1,11],"iend":[26,1,14],"children":[{"type":"Token","kind":"L_CURLY","start":[50,4,9],"end":[50,4,9],"istart":[23,1,11],"iend":[24,1,12]},{"type":"Token","kind":"WHITESPACE","start":[51,4,10],"end":[51,4,10],"istart":[24,1,12],"iend":[25,1,13]},{"type":"Token","kind":"R_CURLY","start":[52,5,0],"end":[52,5,0],"istart":[25,1,13],"iend":[26,1,14]}]}]}]}]}]},{"type":"Token","kind":"COMMA","start":[55,5,3],"end":[56,5,4]},{"type":"Token","kind":"WHITESPACE","start":[56,5,4],"end":[57,5,5]},{"type":"Token","kind":"STRING","start":[57,5,5],"end":[59,5,7]},{"type":"Token","kind":"R_PAREN","start":[59,5,7],"end":[60,5,8]}]}]}]},{"type":"Token","kind":"SEMICOLON","start":[60,5,8],"end":[61,5,9]}]},{"type":"Token","kind":"WHITESPACE","start":[61,5,9],"end":[62,6,0]},{"type":"Token","kind":"R_CURLY","start":[62,6,0],"end":[63,6,1]}]}]}]}]}"#
             ]],
         );
     }
diff --git a/src/tools/rust-analyzer/crates/intern/src/symbol/symbols.rs b/src/tools/rust-analyzer/crates/intern/src/symbol/symbols.rs
index ae1c6efe0cb..be0de6c9366 100644
--- a/src/tools/rust-analyzer/crates/intern/src/symbol/symbols.rs
+++ b/src/tools/rust-analyzer/crates/intern/src/symbol/symbols.rs
@@ -13,15 +13,35 @@ use crate::{
 
 macro_rules! define_symbols {
     (@WITH_NAME: $($alias:ident = $value:literal,)* @PLAIN: $($name:ident,)*) => {
-        // Ideally we would be emitting `const` here, but then we no longer have stable addresses
-        // which is what we are relying on for equality! In the future if consts can refer to
-        // statics we should swap these for `const`s and have the string literal being pointed
-        // to be statics to refer to such that their address is stable.
+        // We define symbols as both `const`s and `static`s because some const code requires const symbols,
+        // but code from before the transition relies on the lifetime of the predefined symbols and making them
+        // `const`s make it error (because now they're temporaries). In the future we probably should only
+        // use consts.
+
+        /// Predefined symbols as `const`s (instead of the default `static`s).
+        pub mod consts {
+            use super::{Symbol, TaggedArcPtr};
+
+            // The strings should be in `static`s so that symbol equality holds.
+            $(
+                pub const $name: Symbol = {
+                    static SYMBOL_STR: &str = stringify!($name);
+                    Symbol { repr: TaggedArcPtr::non_arc(&SYMBOL_STR) }
+                };
+            )*
+            $(
+                pub const $alias: Symbol = {
+                    static SYMBOL_STR: &str = $value;
+                    Symbol { repr: TaggedArcPtr::non_arc(&SYMBOL_STR) }
+                };
+            )*
+        }
+
         $(
-            pub static $name: Symbol = Symbol { repr: TaggedArcPtr::non_arc(&stringify!($name)) };
+            pub static $name: Symbol = consts::$name;
         )*
         $(
-            pub static $alias: Symbol = Symbol { repr: TaggedArcPtr::non_arc(&$value) };
+            pub static $alias: Symbol = consts::$alias;
         )*
 
 
@@ -347,6 +367,7 @@ define_symbols! {
     option,
     Option,
     Ord,
+    Ordering,
     Output,
     CallRefFuture,
     CallOnceFuture,
@@ -427,6 +448,7 @@ define_symbols! {
     rustc_layout_scalar_valid_range_start,
     rustc_legacy_const_generics,
     rustc_macro_transparency,
+    rustc_paren_sugar,
     rustc_reallocator,
     rustc_reservation_impl,
     rustc_safe_intrinsic,
@@ -458,6 +480,8 @@ define_symbols! {
     system,
     sysv64,
     Target,
+    target_feature,
+    enable,
     termination,
     test_case,
     test,
@@ -479,6 +503,7 @@ define_symbols! {
     u64,
     u8,
     unadjusted,
+    unknown,
     Unknown,
     unpin,
     unreachable_2015,
diff --git a/src/tools/rust-analyzer/crates/limit/Cargo.toml b/src/tools/rust-analyzer/crates/limit/Cargo.toml
deleted file mode 100644
index 30666f5219a..00000000000
--- a/src/tools/rust-analyzer/crates/limit/Cargo.toml
+++ /dev/null
@@ -1,16 +0,0 @@
-[package]
-name = "limit"
-version = "0.0.0"
-repository.workspace = true
-description = "A struct to enforce limits for rust-analyzer."
-
-authors.workspace = true
-edition.workspace = true
-license.workspace = true
-rust-version.workspace = true
-
-[features]
-tracking = []
-
-[lints]
-workspace = true
diff --git a/src/tools/rust-analyzer/crates/limit/src/lib.rs b/src/tools/rust-analyzer/crates/limit/src/lib.rs
deleted file mode 100644
index c1caeed2f87..00000000000
--- a/src/tools/rust-analyzer/crates/limit/src/lib.rs
+++ /dev/null
@@ -1,67 +0,0 @@
-//! limit defines a struct to enforce limits.
-
-#[cfg(feature = "tracking")]
-use std::sync::atomic::AtomicUsize;
-
-/// Represents a struct used to enforce a numerical limit.
-#[derive(Debug)]
-pub struct Limit {
-    upper_bound: usize,
-    #[cfg(feature = "tracking")]
-    max: AtomicUsize,
-}
-
-impl Limit {
-    /// Creates a new limit.
-    #[inline]
-    pub const fn new(upper_bound: usize) -> Self {
-        Self {
-            upper_bound,
-            #[cfg(feature = "tracking")]
-            max: AtomicUsize::new(0),
-        }
-    }
-
-    /// Creates a new limit.
-    #[inline]
-    #[cfg(feature = "tracking")]
-    pub const fn new_tracking(upper_bound: usize) -> Self {
-        Self {
-            upper_bound,
-            #[cfg(feature = "tracking")]
-            max: AtomicUsize::new(1),
-        }
-    }
-
-    /// Gets the underlying numeric limit.
-    #[inline]
-    pub const fn inner(&self) -> usize {
-        self.upper_bound
-    }
-
-    /// Checks whether the given value is below the limit.
-    /// Returns `Ok` when `other` is below `self`, and `Err` otherwise.
-    #[inline]
-    pub fn check(&self, other: usize) -> Result<(), ()> {
-        if other > self.upper_bound {
-            Err(())
-        } else {
-            #[cfg(feature = "tracking")]
-            loop {
-                use std::sync::atomic::Ordering;
-                let old_max = self.max.load(Ordering::Relaxed);
-                if other <= old_max || old_max == 0 {
-                    break;
-                }
-                _ = self.max.compare_exchange_weak(
-                    old_max,
-                    other,
-                    Ordering::Relaxed,
-                    Ordering::Relaxed,
-                );
-            }
-
-            Ok(())
-        }
-    }
-}
diff --git a/src/tools/rust-analyzer/crates/load-cargo/src/lib.rs b/src/tools/rust-analyzer/crates/load-cargo/src/lib.rs
index 5654c04a592..76f1a7f48b6 100644
--- a/src/tools/rust-analyzer/crates/load-cargo/src/lib.rs
+++ b/src/tools/rust-analyzer/crates/load-cargo/src/lib.rs
@@ -94,7 +94,9 @@ pub fn load_workspace(
             let contents = loader.load_sync(path);
             let path = vfs::VfsPath::from(path.to_path_buf());
             vfs.set_file_contents(path.clone(), contents);
-            vfs.file_id(&path)
+            vfs.file_id(&path).and_then(|(file_id, excluded)| {
+                (excluded == vfs::FileExcluded::No).then_some(file_id)
+            })
         },
         extra_env,
     );
@@ -454,7 +456,6 @@ fn load_crate_graph(
     let ws_data = crate_graph
         .iter()
         .zip(iter::repeat(From::from(CrateWorkspaceData {
-            proc_macro_cwd: None,
             data_layout: target_layout.clone(),
             toolchain: toolchain.clone(),
         })))
diff --git a/src/tools/rust-analyzer/crates/parser/Cargo.toml b/src/tools/rust-analyzer/crates/parser/Cargo.toml
index 3629d275c0c..48436ec71f7 100644
--- a/src/tools/rust-analyzer/crates/parser/Cargo.toml
+++ b/src/tools/rust-analyzer/crates/parser/Cargo.toml
@@ -15,7 +15,6 @@ doctest = false
 [dependencies]
 drop_bomb = "0.1.5"
 ra-ap-rustc_lexer.workspace = true
-limit.workspace = true
 tracing = { workspace = true, optional = true }
 
 edition.workspace = true
diff --git a/src/tools/rust-analyzer/crates/parser/src/grammar/expressions.rs b/src/tools/rust-analyzer/crates/parser/src/grammar/expressions.rs
index 389c01933c9..fe1316c9bfd 100644
--- a/src/tools/rust-analyzer/crates/parser/src/grammar/expressions.rs
+++ b/src/tools/rust-analyzer/crates/parser/src/grammar/expressions.rs
@@ -678,6 +678,8 @@ fn path_expr(p: &mut Parser<'_>, r: Restrictions) -> (CompletedMarker, BlockLike
 //     S { x };
 //     S { x, y: 32, };
 //     S { x, y: 32, ..Default::default() };
+//     S { x, y: 32, .. };
+//     S { .. };
 //     S { x: ::default() };
 //     TupleStruct { 0: 1 };
 // }
@@ -709,6 +711,8 @@ pub(crate) fn record_expr_field_list(p: &mut Parser<'_>) {
                 // fn main() {
                 //     S { field ..S::default() }
                 //     S { 0 ..S::default() }
+                //     S { field .. }
+                //     S { 0 .. }
                 // }
                 name_ref_or_index(p);
                 p.error("expected `:`");
@@ -739,7 +743,13 @@ pub(crate) fn record_expr_field_list(p: &mut Parser<'_>) {
                 //     S { .. } = S {};
                 // }
 
-                // We permit `.. }` on the left-hand side of a destructuring assignment.
+                // test struct_initializer_with_defaults
+                // fn foo() {
+                //     let _s = S { .. };
+                // }
+
+                // We permit `.. }` on the left-hand side of a destructuring assignment
+                // or defaults values.
                 if !p.at(T!['}']) {
                     expr(p);
 
@@ -750,6 +760,12 @@ pub(crate) fn record_expr_field_list(p: &mut Parser<'_>) {
                         //     S { ..x, a: 0 }
                         // }
 
+                        // test_err comma_after_default_values_syntax
+                        // fn foo() {
+                        //     S { .., };
+                        //     S { .., a: 0 }
+                        // }
+
                         // Do not bump, so we can support additional fields after this comma.
                         p.error("cannot use a comma after the base struct");
                     }
diff --git a/src/tools/rust-analyzer/crates/parser/src/grammar/items/adt.rs b/src/tools/rust-analyzer/crates/parser/src/grammar/items/adt.rs
index 21078175c0e..9a16c9db6da 100644
--- a/src/tools/rust-analyzer/crates/parser/src/grammar/items/adt.rs
+++ b/src/tools/rust-analyzer/crates/parser/src/grammar/items/adt.rs
@@ -135,6 +135,11 @@ pub(crate) fn record_field_list(p: &mut Parser<'_>) {
             name(p);
             p.expect(T![:]);
             types::type_(p);
+            // test record_field_default_values
+            // struct S { f: f32 = 0.0 }
+            if p.eat(T![=]) {
+                expressions::expr(p);
+            }
             m.complete(p, RECORD_FIELD);
         } else {
             m.abandon(p);
diff --git a/src/tools/rust-analyzer/crates/parser/src/parser.rs b/src/tools/rust-analyzer/crates/parser/src/parser.rs
index 2f6ba525747..b0586862764 100644
--- a/src/tools/rust-analyzer/crates/parser/src/parser.rs
+++ b/src/tools/rust-analyzer/crates/parser/src/parser.rs
@@ -3,7 +3,6 @@
 use std::cell::Cell;
 
 use drop_bomb::DropBomb;
-use limit::Limit;
 
 use crate::{
     event::Event,
@@ -30,7 +29,7 @@ pub(crate) struct Parser<'t> {
     edition: Edition,
 }
 
-static PARSER_STEP_LIMIT: Limit = Limit::new(15_000_000);
+const PARSER_STEP_LIMIT: usize = 15_000_000;
 
 impl<'t> Parser<'t> {
     pub(super) fn new(inp: &'t Input, edition: Edition) -> Parser<'t> {
@@ -54,7 +53,7 @@ impl<'t> Parser<'t> {
         assert!(n <= 3);
 
         let steps = self.steps.get();
-        assert!(PARSER_STEP_LIMIT.check(steps as usize).is_ok(), "the parser seems stuck");
+        assert!((steps as usize) < PARSER_STEP_LIMIT, "the parser seems stuck");
         self.steps.set(steps + 1);
 
         self.inp.kind(self.pos + n)
diff --git a/src/tools/rust-analyzer/crates/parser/src/syntax_kind/generated.rs b/src/tools/rust-analyzer/crates/parser/src/syntax_kind/generated.rs
index 318f71a2d4d..79900425a17 100644
--- a/src/tools/rust-analyzer/crates/parser/src/syntax_kind/generated.rs
+++ b/src/tools/rust-analyzer/crates/parser/src/syntax_kind/generated.rs
@@ -1,4 +1,4 @@
-//! Generated by `cargo codegen grammar`, do not edit by hand.
+//! Generated by `cargo xtask codegen grammar`, do not edit by hand.
 
 #![allow(bad_style, missing_docs, unreachable_pub)]
 use crate::Edition;
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/generated/runner.rs b/src/tools/rust-analyzer/crates/parser/test_data/generated/runner.rs
index b9f87b6af24..1a747731587 100644
--- a/src/tools/rust-analyzer/crates/parser/test_data/generated/runner.rs
+++ b/src/tools/rust-analyzer/crates/parser/test_data/generated/runner.rs
@@ -482,6 +482,10 @@ mod ok {
         run_and_expect_no_errors("test_data/parser/inline/ok/record_field_attrs.rs");
     }
     #[test]
+    fn record_field_default_values() {
+        run_and_expect_no_errors("test_data/parser/inline/ok/record_field_default_values.rs");
+    }
+    #[test]
     fn record_field_list() {
         run_and_expect_no_errors("test_data/parser/inline/ok/record_field_list.rs");
     }
@@ -544,6 +548,10 @@ mod ok {
         run_and_expect_no_errors("test_data/parser/inline/ok/stmt_postfix_expr_ambiguity.rs");
     }
     #[test]
+    fn struct_initializer_with_defaults() {
+        run_and_expect_no_errors("test_data/parser/inline/ok/struct_initializer_with_defaults.rs");
+    }
+    #[test]
     fn struct_item() { run_and_expect_no_errors("test_data/parser/inline/ok/struct_item.rs"); }
     #[test]
     fn trait_alias() { run_and_expect_no_errors("test_data/parser/inline/ok/trait_alias.rs"); }
@@ -713,6 +721,10 @@ mod err {
     #[test]
     fn bad_asm_expr() { run_and_expect_errors("test_data/parser/inline/err/bad_asm_expr.rs"); }
     #[test]
+    fn comma_after_default_values_syntax() {
+        run_and_expect_errors("test_data/parser/inline/err/comma_after_default_values_syntax.rs");
+    }
+    #[test]
     fn comma_after_functional_update_syntax() {
         run_and_expect_errors(
             "test_data/parser/inline/err/comma_after_functional_update_syntax.rs",
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/comma_after_default_values_syntax.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/comma_after_default_values_syntax.rast
new file mode 100644
index 00000000000..feb617e1aa2
--- /dev/null
+++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/comma_after_default_values_syntax.rast
@@ -0,0 +1,59 @@
+SOURCE_FILE
+  FN
+    FN_KW "fn"
+    WHITESPACE " "
+    NAME
+      IDENT "foo"
+    PARAM_LIST
+      L_PAREN "("
+      R_PAREN ")"
+    WHITESPACE " "
+    BLOCK_EXPR
+      STMT_LIST
+        L_CURLY "{"
+        WHITESPACE "\n    "
+        EXPR_STMT
+          RECORD_EXPR
+            PATH
+              PATH_SEGMENT
+                NAME_REF
+                  IDENT "S"
+            WHITESPACE " "
+            RECORD_EXPR_FIELD_LIST
+              L_CURLY "{"
+              WHITESPACE " "
+              DOT2 ".."
+              ERROR
+                COMMA ","
+              WHITESPACE " "
+              R_CURLY "}"
+          SEMICOLON ";"
+        WHITESPACE "\n    "
+        RECORD_EXPR
+          PATH
+            PATH_SEGMENT
+              NAME_REF
+                IDENT "S"
+          WHITESPACE " "
+          RECORD_EXPR_FIELD_LIST
+            L_CURLY "{"
+            WHITESPACE " "
+            DOT2 ".."
+            ERROR
+              COMMA ","
+            WHITESPACE " "
+            RECORD_EXPR_FIELD
+              NAME_REF
+                IDENT "a"
+              COLON ":"
+              WHITESPACE " "
+              LITERAL
+                INT_NUMBER "0"
+            WHITESPACE " "
+            R_CURLY "}"
+        WHITESPACE "\n"
+        R_CURLY "}"
+  WHITESPACE "\n"
+error 21: expected expression
+error 36: expected expression
+error 37: expected COMMA
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/comma_after_default_values_syntax.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/comma_after_default_values_syntax.rs
new file mode 100644
index 00000000000..f1ecdf89fab
--- /dev/null
+++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/comma_after_default_values_syntax.rs
@@ -0,0 +1,4 @@
+fn foo() {
+    S { .., };
+    S { .., a: 0 }
+}
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/record_literal_before_ellipsis_recovery.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/record_literal_before_ellipsis_recovery.rast
index 08ae906421c..12b4e233e30 100644
--- a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/record_literal_before_ellipsis_recovery.rast
+++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/record_literal_before_ellipsis_recovery.rast
@@ -44,6 +44,56 @@ SOURCE_FILE
               WHITESPACE " "
               R_CURLY "}"
         WHITESPACE "\n    "
+        EXPR_STMT
+          RECORD_EXPR
+            PATH
+              PATH_SEGMENT
+                NAME_REF
+                  IDENT "S"
+            WHITESPACE " "
+            RECORD_EXPR_FIELD_LIST
+              L_CURLY "{"
+              WHITESPACE " "
+              RECORD_EXPR_FIELD
+                NAME_REF
+                  INT_NUMBER "0"
+              WHITESPACE " "
+              DOT2 ".."
+              CALL_EXPR
+                PATH_EXPR
+                  PATH
+                    PATH
+                      PATH_SEGMENT
+                        NAME_REF
+                          IDENT "S"
+                    COLON2 "::"
+                    PATH_SEGMENT
+                      NAME_REF
+                        IDENT "default"
+                ARG_LIST
+                  L_PAREN "("
+                  R_PAREN ")"
+              WHITESPACE " "
+              R_CURLY "}"
+        WHITESPACE "\n    "
+        EXPR_STMT
+          RECORD_EXPR
+            PATH
+              PATH_SEGMENT
+                NAME_REF
+                  IDENT "S"
+            WHITESPACE " "
+            RECORD_EXPR_FIELD_LIST
+              L_CURLY "{"
+              WHITESPACE " "
+              RECORD_EXPR_FIELD
+                NAME_REF
+                  IDENT "field"
+              WHITESPACE " "
+              DOT2 ".."
+              WHITESPACE " "
+              R_CURLY "}"
+        WHITESPACE "\n    "
         RECORD_EXPR
           PATH
             PATH_SEGMENT
@@ -58,20 +108,6 @@ SOURCE_FILE
                 INT_NUMBER "0"
             WHITESPACE " "
             DOT2 ".."
-            CALL_EXPR
-              PATH_EXPR
-                PATH
-                  PATH
-                    PATH_SEGMENT
-                      NAME_REF
-                        IDENT "S"
-                  COLON2 "::"
-                  PATH_SEGMENT
-                    NAME_REF
-                      IDENT "default"
-              ARG_LIST
-                L_PAREN "("
-                R_PAREN ")"
             WHITESPACE " "
             R_CURLY "}"
         WHITESPACE "\n"
@@ -82,3 +118,9 @@ error 25: expected COMMA
 error 42: expected SEMICOLON
 error 52: expected `:`
 error 52: expected COMMA
+error 69: expected SEMICOLON
+error 83: expected `:`
+error 83: expected COMMA
+error 88: expected SEMICOLON
+error 98: expected `:`
+error 98: expected COMMA
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/record_literal_before_ellipsis_recovery.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/record_literal_before_ellipsis_recovery.rs
index 65398ccb88e..416cd763fdb 100644
--- a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/record_literal_before_ellipsis_recovery.rs
+++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/record_literal_before_ellipsis_recovery.rs
@@ -1,4 +1,6 @@
 fn main() {
     S { field ..S::default() }
     S { 0 ..S::default() }
+    S { field .. }
+    S { 0 .. }
 }
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/record_field_default_values.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/record_field_default_values.rast
new file mode 100644
index 00000000000..33088f2cabf
--- /dev/null
+++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/record_field_default_values.rast
@@ -0,0 +1,28 @@
+SOURCE_FILE
+  STRUCT
+    STRUCT_KW "struct"
+    WHITESPACE " "
+    NAME
+      IDENT "S"
+    WHITESPACE " "
+    RECORD_FIELD_LIST
+      L_CURLY "{"
+      WHITESPACE " "
+      RECORD_FIELD
+        NAME
+          IDENT "f"
+        COLON ":"
+        WHITESPACE " "
+        PATH_TYPE
+          PATH
+            PATH_SEGMENT
+              NAME_REF
+                IDENT "f32"
+        WHITESPACE " "
+        EQ "="
+        WHITESPACE " "
+        LITERAL
+          FLOAT_NUMBER "0.0"
+      WHITESPACE " "
+      R_CURLY "}"
+  WHITESPACE "\n"
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/record_field_default_values.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/record_field_default_values.rs
new file mode 100644
index 00000000000..d7b38944a8a
--- /dev/null
+++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/record_field_default_values.rs
@@ -0,0 +1 @@
+struct S { f: f32 = 0.0 }
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/record_lit.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/record_lit.rast
index 00948c322f4..b868da55bce 100644
--- a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/record_lit.rast
+++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/record_lit.rast
@@ -131,6 +131,53 @@ SOURCE_FILE
               L_CURLY "{"
               WHITESPACE " "
               RECORD_EXPR_FIELD
+                PATH_EXPR
+                  PATH
+                    PATH_SEGMENT
+                      NAME_REF
+                        IDENT "x"
+              COMMA ","
+              WHITESPACE " "
+              RECORD_EXPR_FIELD
+                NAME_REF
+                  IDENT "y"
+                COLON ":"
+                WHITESPACE " "
+                LITERAL
+                  INT_NUMBER "32"
+              COMMA ","
+              WHITESPACE " "
+              DOT2 ".."
+              WHITESPACE " "
+              R_CURLY "}"
+          SEMICOLON ";"
+        WHITESPACE "\n    "
+        EXPR_STMT
+          RECORD_EXPR
+            PATH
+              PATH_SEGMENT
+                NAME_REF
+                  IDENT "S"
+            WHITESPACE " "
+            RECORD_EXPR_FIELD_LIST
+              L_CURLY "{"
+              WHITESPACE " "
+              DOT2 ".."
+              WHITESPACE " "
+              R_CURLY "}"
+          SEMICOLON ";"
+        WHITESPACE "\n    "
+        EXPR_STMT
+          RECORD_EXPR
+            PATH
+              PATH_SEGMENT
+                NAME_REF
+                  IDENT "S"
+            WHITESPACE " "
+            RECORD_EXPR_FIELD_LIST
+              L_CURLY "{"
+              WHITESPACE " "
+              RECORD_EXPR_FIELD
                 NAME_REF
                   IDENT "x"
                 COLON ":"
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/record_lit.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/record_lit.rs
index 86411fbb7dc..42895f759b2 100644
--- a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/record_lit.rs
+++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/record_lit.rs
@@ -3,6 +3,8 @@ fn foo() {
     S { x };
     S { x, y: 32, };
     S { x, y: 32, ..Default::default() };
+    S { x, y: 32, .. };
+    S { .. };
     S { x: ::default() };
     TupleStruct { 0: 1 };
 }
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/struct_initializer_with_defaults.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/struct_initializer_with_defaults.rast
new file mode 100644
index 00000000000..987e219ae82
--- /dev/null
+++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/struct_initializer_with_defaults.rast
@@ -0,0 +1,39 @@
+SOURCE_FILE
+  FN
+    FN_KW "fn"
+    WHITESPACE " "
+    NAME
+      IDENT "foo"
+    PARAM_LIST
+      L_PAREN "("
+      R_PAREN ")"
+    WHITESPACE " "
+    BLOCK_EXPR
+      STMT_LIST
+        L_CURLY "{"
+        WHITESPACE "\n    "
+        LET_STMT
+          LET_KW "let"
+          WHITESPACE " "
+          IDENT_PAT
+            NAME
+              IDENT "_s"
+          WHITESPACE " "
+          EQ "="
+          WHITESPACE " "
+          RECORD_EXPR
+            PATH
+              PATH_SEGMENT
+                NAME_REF
+                  IDENT "S"
+            WHITESPACE " "
+            RECORD_EXPR_FIELD_LIST
+              L_CURLY "{"
+              WHITESPACE " "
+              DOT2 ".."
+              WHITESPACE " "
+              R_CURLY "}"
+          SEMICOLON ";"
+        WHITESPACE "\n"
+        R_CURLY "}"
+  WHITESPACE "\n"
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/struct_initializer_with_defaults.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/struct_initializer_with_defaults.rs
new file mode 100644
index 00000000000..e08204f94c4
--- /dev/null
+++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/struct_initializer_with_defaults.rs
@@ -0,0 +1,3 @@
+fn foo() {
+    let _s = S { .. };
+}
diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv-cli/src/main_loop.rs b/src/tools/rust-analyzer/crates/proc-macro-srv-cli/src/main_loop.rs
index ba1fcd8e336..569070766f1 100644
--- a/src/tools/rust-analyzer/crates/proc-macro-srv-cli/src/main_loop.rs
+++ b/src/tools/rust-analyzer/crates/proc-macro-srv-cli/src/main_loop.rs
@@ -21,18 +21,16 @@ pub(crate) fn run() -> io::Result<()> {
         }
     }
 
-    let read_request =
-        |buf: &mut String| msg::Request::read(read_json, &mut io::stdin().lock(), buf);
-
+    let mut buf = String::new();
+    let mut read_request = || msg::Request::read(read_json, &mut io::stdin().lock(), &mut buf);
     let write_response = |msg: msg::Response| msg.write(write_json, &mut io::stdout().lock());
 
     let env = EnvSnapshot::default();
-    let mut srv = proc_macro_srv::ProcMacroSrv::new(&env);
-    let mut buf = String::new();
+    let srv = proc_macro_srv::ProcMacroSrv::new(&env);
 
     let mut span_mode = SpanMode::Id;
 
-    while let Some(req) = read_request(&mut buf)? {
+    while let Some(req) = read_request()? {
         let res = match req {
             msg::Request::ListMacros { dylib_path } => {
                 msg::Response::ListMacros(srv.list_macros(&dylib_path).map(|macros| {
diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv/src/lib.rs b/src/tools/rust-analyzer/crates/proc-macro-srv/src/lib.rs
index 7ae75713ebf..f28821b4afc 100644
--- a/src/tools/rust-analyzer/crates/proc-macro-srv/src/lib.rs
+++ b/src/tools/rust-analyzer/crates/proc-macro-srv/src/lib.rs
@@ -35,6 +35,7 @@ use std::{
     ffi::OsString,
     fs,
     path::{Path, PathBuf},
+    sync::{Arc, Mutex, PoisonError},
     thread,
 };
 
@@ -53,7 +54,7 @@ pub enum ProcMacroKind {
 pub const RUSTC_VERSION_STRING: &str = env!("RUSTC_VERSION");
 
 pub struct ProcMacroSrv<'env> {
-    expanders: HashMap<Utf8PathBuf, dylib::Expander>,
+    expanders: Mutex<HashMap<Utf8PathBuf, Arc<dylib::Expander>>>,
     env: &'env EnvSnapshot,
 }
 
@@ -67,7 +68,7 @@ const EXPANDER_STACK_SIZE: usize = 8 * 1024 * 1024;
 
 impl ProcMacroSrv<'_> {
     pub fn expand<S: ProcMacroSrvSpan>(
-        &mut self,
+        &self,
         lib: impl AsRef<Utf8Path>,
         env: Vec<(String, String)>,
         current_dir: Option<impl AsRef<Path>>,
@@ -118,29 +119,37 @@ impl ProcMacroSrv<'_> {
     }
 
     pub fn list_macros(
-        &mut self,
+        &self,
         dylib_path: &Utf8Path,
     ) -> Result<Vec<(String, ProcMacroKind)>, String> {
         let expander = self.expander(dylib_path)?;
         Ok(expander.list_macros())
     }
 
-    fn expander(&mut self, path: &Utf8Path) -> Result<&dylib::Expander, String> {
+    fn expander(&self, path: &Utf8Path) -> Result<Arc<dylib::Expander>, String> {
         let expander = || {
-            dylib::Expander::new(path)
-                .map_err(|err| format!("Cannot create expander for {path}: {err}",))
+            let expander = dylib::Expander::new(path)
+                .map_err(|err| format!("Cannot create expander for {path}: {err}",));
+            expander.map(Arc::new)
         };
 
-        Ok(match self.expanders.entry(path.to_path_buf()) {
-            Entry::Vacant(v) => v.insert(expander()?),
-            Entry::Occupied(mut e) => {
-                let time = fs::metadata(path).and_then(|it| it.modified()).ok();
-                if Some(e.get().modified_time()) != time {
-                    e.insert(expander()?);
+        Ok(
+            match self
+                .expanders
+                .lock()
+                .unwrap_or_else(PoisonError::into_inner)
+                .entry(path.to_path_buf())
+            {
+                Entry::Vacant(v) => v.insert(expander()?).clone(),
+                Entry::Occupied(mut e) => {
+                    let time = fs::metadata(path).and_then(|it| it.modified()).ok();
+                    if Some(e.get().modified_time()) != time {
+                        e.insert(expander()?);
+                    }
+                    e.get().clone()
                 }
-                e.into_mut()
-            }
-        })
+            },
+        )
     }
 }
 
diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv/src/tests/utils.rs b/src/tools/rust-analyzer/crates/proc-macro-srv/src/tests/utils.rs
index 4ce4544243a..1b085520d56 100644
--- a/src/tools/rust-analyzer/crates/proc-macro-srv/src/tests/utils.rs
+++ b/src/tools/rust-analyzer/crates/proc-macro-srv/src/tests/utils.rs
@@ -107,7 +107,7 @@ fn assert_expand_impl(
 pub(crate) fn list() -> Vec<String> {
     let dylib_path = proc_macro_test_dylib_path();
     let env = EnvSnapshot::default();
-    let mut srv = ProcMacroSrv::new(&env);
+    let srv = ProcMacroSrv::new(&env);
     let res = srv.list_macros(&dylib_path).unwrap();
     res.into_iter().map(|(name, kind)| format!("{name} [{kind:?}]")).collect()
 }
diff --git a/src/tools/rust-analyzer/crates/project-model/src/cargo_workspace.rs b/src/tools/rust-analyzer/crates/project-model/src/cargo_workspace.rs
index e4a61134620..b5f4e43a115 100644
--- a/src/tools/rust-analyzer/crates/project-model/src/cargo_workspace.rs
+++ b/src/tools/rust-analyzer/crates/project-model/src/cargo_workspace.rs
@@ -277,6 +277,9 @@ impl CargoWorkspace {
     /// Fetches the metadata for the given `cargo_toml` manifest.
     /// A successful result may contain another metadata error if the initial fetching failed but
     /// the `--no-deps` retry succeeded.
+    ///
+    /// The sysroot is used to set the `RUSTUP_TOOLCHAIN` env var when invoking cargo
+    /// to ensure that the rustup proxy uses the correct toolchain.
     pub fn fetch_metadata(
         cargo_toml: &ManifestPath,
         current_dir: &AbsPath,
diff --git a/src/tools/rust-analyzer/crates/project-model/src/lib.rs b/src/tools/rust-analyzer/crates/project-model/src/lib.rs
index fc1fd7b877f..0c734474682 100644
--- a/src/tools/rust-analyzer/crates/project-model/src/lib.rs
+++ b/src/tools/rust-analyzer/crates/project-model/src/lib.rs
@@ -260,19 +260,19 @@ fn parse_cfg(s: &str) -> Result<cfg::CfgAtom, String> {
 }
 
 #[derive(Clone, Debug, PartialEq, Eq)]
-pub enum SysrootSourceWorkspaceConfig {
+pub enum RustSourceWorkspaceConfig {
     CargoMetadata(CargoMetadataConfig),
     Stitched,
 }
 
-impl Default for SysrootSourceWorkspaceConfig {
+impl Default for RustSourceWorkspaceConfig {
     fn default() -> Self {
-        SysrootSourceWorkspaceConfig::default_cargo()
+        RustSourceWorkspaceConfig::default_cargo()
     }
 }
 
-impl SysrootSourceWorkspaceConfig {
+impl RustSourceWorkspaceConfig {
     pub fn default_cargo() -> Self {
-        SysrootSourceWorkspaceConfig::CargoMetadata(Default::default())
+        RustSourceWorkspaceConfig::CargoMetadata(Default::default())
     }
 }
diff --git a/src/tools/rust-analyzer/crates/project-model/src/project_json.rs b/src/tools/rust-analyzer/crates/project-model/src/project_json.rs
index a3963967610..2f9612e3a47 100644
--- a/src/tools/rust-analyzer/crates/project-model/src/project_json.rs
+++ b/src/tools/rust-analyzer/crates/project-model/src/project_json.rs
@@ -164,6 +164,7 @@ impl ProjectJson {
                         is_proc_macro: crate_data.is_proc_macro,
                         repository: crate_data.repository,
                         build,
+                        proc_macro_cwd: crate_data.proc_macro_cwd.map(absolutize_on_base),
                     }
                 })
                 .collect(),
@@ -240,6 +241,8 @@ pub struct Crate {
     pub(crate) include: Vec<AbsPathBuf>,
     pub(crate) exclude: Vec<AbsPathBuf>,
     pub(crate) is_proc_macro: bool,
+    /// The working directory to run proc-macros in. This is usually the workspace root of cargo workspaces.
+    pub(crate) proc_macro_cwd: Option<AbsPathBuf>,
     pub(crate) repository: Option<String>,
     pub build: Option<Build>,
 }
@@ -362,6 +365,8 @@ struct CrateData {
     repository: Option<String>,
     #[serde(default)]
     build: Option<BuildData>,
+    #[serde(default)]
+    proc_macro_cwd: Option<Utf8PathBuf>,
 }
 
 mod cfg_ {
@@ -508,5 +513,5 @@ fn serialize_crate_name<S>(name: &CrateName, se: S) -> Result<S::Ok, S::Error>
 where
     S: serde::Serializer,
 {
-    se.serialize_str(name)
+    se.serialize_str(name.as_str())
 }
diff --git a/src/tools/rust-analyzer/crates/project-model/src/sysroot.rs b/src/tools/rust-analyzer/crates/project-model/src/sysroot.rs
index 8f633d24be9..fb752fe47b3 100644
--- a/src/tools/rust-analyzer/crates/project-model/src/sysroot.rs
+++ b/src/tools/rust-analyzer/crates/project-model/src/sysroot.rs
@@ -22,38 +22,40 @@ use toolchain::{probe_for_binary, Tool};
 
 use crate::{
     cargo_workspace::CargoMetadataConfig, utf8_stdout, CargoWorkspace, ManifestPath,
-    SysrootSourceWorkspaceConfig,
+    RustSourceWorkspaceConfig,
 };
 
 #[derive(Debug, Clone, PartialEq, Eq)]
 pub struct Sysroot {
     root: Option<AbsPathBuf>,
-    src_root: Option<AbsPathBuf>,
-    workspace: SysrootWorkspace,
+    rust_lib_src_root: Option<AbsPathBuf>,
+    workspace: RustLibSrcWorkspace,
     error: Option<String>,
 }
 
 #[derive(Debug, Clone, Eq, PartialEq)]
-pub(crate) enum SysrootWorkspace {
+pub enum RustLibSrcWorkspace {
     Workspace(CargoWorkspace),
     Stitched(Stitched),
     Empty,
 }
 
 #[derive(Debug, Clone, Eq, PartialEq)]
-pub(crate) struct Stitched {
-    crates: Arena<SysrootCrateData>,
+pub struct Stitched {
+    crates: Arena<RustLibSrcCrateData>,
 }
 
-impl ops::Index<SysrootCrate> for Stitched {
-    type Output = SysrootCrateData;
-    fn index(&self, index: SysrootCrate) -> &SysrootCrateData {
+impl ops::Index<RustLibSrcCrate> for Stitched {
+    type Output = RustLibSrcCrateData;
+    fn index(&self, index: RustLibSrcCrate) -> &RustLibSrcCrateData {
         &self.crates[index]
     }
 }
 
 impl Stitched {
-    pub(crate) fn public_deps(&self) -> impl Iterator<Item = (CrateName, SysrootCrate, bool)> + '_ {
+    pub(crate) fn public_deps(
+        &self,
+    ) -> impl Iterator<Item = (CrateName, RustLibSrcCrate, bool)> + '_ {
         // core is added as a dependency before std in order to
         // mimic rustcs dependency order
         [("core", true), ("alloc", false), ("std", true), ("test", false)].into_iter().filter_map(
@@ -63,32 +65,37 @@ impl Stitched {
         )
     }
 
-    pub(crate) fn proc_macro(&self) -> Option<SysrootCrate> {
+    pub(crate) fn proc_macro(&self) -> Option<RustLibSrcCrate> {
         self.by_name("proc_macro")
     }
 
-    pub(crate) fn crates(&self) -> impl ExactSizeIterator<Item = SysrootCrate> + '_ {
+    pub(crate) fn crates(&self) -> impl ExactSizeIterator<Item = RustLibSrcCrate> + '_ {
         self.crates.iter().map(|(id, _data)| id)
     }
 
-    fn by_name(&self, name: &str) -> Option<SysrootCrate> {
+    fn by_name(&self, name: &str) -> Option<RustLibSrcCrate> {
         let (id, _data) = self.crates.iter().find(|(_id, data)| data.name == name)?;
         Some(id)
     }
 }
 
-pub(crate) type SysrootCrate = Idx<SysrootCrateData>;
+pub(crate) type RustLibSrcCrate = Idx<RustLibSrcCrateData>;
 
 #[derive(Debug, Clone, Eq, PartialEq)]
-pub(crate) struct SysrootCrateData {
+pub(crate) struct RustLibSrcCrateData {
     pub(crate) name: String,
     pub(crate) root: ManifestPath,
-    pub(crate) deps: Vec<SysrootCrate>,
+    pub(crate) deps: Vec<RustLibSrcCrate>,
 }
 
 impl Sysroot {
     pub const fn empty() -> Sysroot {
-        Sysroot { root: None, src_root: None, workspace: SysrootWorkspace::Empty, error: None }
+        Sysroot {
+            root: None,
+            rust_lib_src_root: None,
+            workspace: RustLibSrcWorkspace::Empty,
+            error: None,
+        }
     }
 
     /// Returns sysroot "root" directory, where `bin/`, `etc/`, `lib/`, `libexec/`
@@ -100,15 +107,15 @@ impl Sysroot {
 
     /// Returns the sysroot "source" directory, where stdlib sources are located, like:
     /// `$HOME/.rustup/toolchains/nightly-2022-07-23-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library`
-    pub fn src_root(&self) -> Option<&AbsPath> {
-        self.src_root.as_deref()
+    pub fn rust_lib_src_root(&self) -> Option<&AbsPath> {
+        self.rust_lib_src_root.as_deref()
     }
 
-    pub fn is_empty(&self) -> bool {
+    pub fn is_rust_lib_src_empty(&self) -> bool {
         match &self.workspace {
-            SysrootWorkspace::Workspace(ws) => ws.packages().next().is_none(),
-            SysrootWorkspace::Stitched(stitched) => stitched.crates.is_empty(),
-            SysrootWorkspace::Empty => true,
+            RustLibSrcWorkspace::Workspace(ws) => ws.packages().next().is_none(),
+            RustLibSrcWorkspace::Stitched(stitched) => stitched.crates.is_empty(),
+            RustLibSrcWorkspace::Empty => true,
         }
     }
 
@@ -118,13 +125,13 @@ impl Sysroot {
 
     pub fn num_packages(&self) -> usize {
         match &self.workspace {
-            SysrootWorkspace::Workspace(ws) => ws.packages().count(),
-            SysrootWorkspace::Stitched(c) => c.crates().count(),
-            SysrootWorkspace::Empty => 0,
+            RustLibSrcWorkspace::Workspace(ws) => ws.packages().count(),
+            RustLibSrcWorkspace::Stitched(c) => c.crates().count(),
+            RustLibSrcWorkspace::Empty => 0,
         }
     }
 
-    pub(crate) fn workspace(&self) -> &SysrootWorkspace {
+    pub(crate) fn workspace(&self) -> &RustLibSrcWorkspace {
         &self.workspace
     }
 }
@@ -133,33 +140,33 @@ impl Sysroot {
     /// Attempts to discover the toolchain's sysroot from the given `dir`.
     pub fn discover(dir: &AbsPath, extra_env: &FxHashMap<String, String>) -> Sysroot {
         let sysroot_dir = discover_sysroot_dir(dir, extra_env);
-        let sysroot_src_dir = sysroot_dir.as_ref().ok().map(|sysroot_dir| {
-            discover_sysroot_src_dir_or_add_component(sysroot_dir, dir, extra_env)
+        let rust_lib_src_dir = sysroot_dir.as_ref().ok().map(|sysroot_dir| {
+            discover_rust_lib_src_dir_or_add_component(sysroot_dir, dir, extra_env)
         });
-        Sysroot::assemble(Some(sysroot_dir), sysroot_src_dir)
+        Sysroot::assemble(Some(sysroot_dir), rust_lib_src_dir)
     }
 
     pub fn discover_with_src_override(
         current_dir: &AbsPath,
         extra_env: &FxHashMap<String, String>,
-        sysroot_src_dir: AbsPathBuf,
+        rust_lib_src_dir: AbsPathBuf,
     ) -> Sysroot {
         let sysroot_dir = discover_sysroot_dir(current_dir, extra_env);
-        Sysroot::assemble(Some(sysroot_dir), Some(Ok(sysroot_src_dir)))
+        Sysroot::assemble(Some(sysroot_dir), Some(Ok(rust_lib_src_dir)))
     }
 
-    pub fn discover_sysroot_src_dir(sysroot_dir: AbsPathBuf) -> Sysroot {
-        let sysroot_src_dir = discover_sysroot_src_dir(&sysroot_dir)
+    pub fn discover_rust_lib_src_dir(sysroot_dir: AbsPathBuf) -> Sysroot {
+        let rust_lib_src_dir = discover_rust_lib_src_dir(&sysroot_dir)
             .ok_or_else(|| format_err!("can't find standard library sources in {sysroot_dir}"));
-        Sysroot::assemble(Some(Ok(sysroot_dir)), Some(sysroot_src_dir))
+        Sysroot::assemble(Some(Ok(sysroot_dir)), Some(rust_lib_src_dir))
     }
 
     pub fn discover_rustc_src(&self) -> Option<ManifestPath> {
         get_rustc_src(self.root()?)
     }
 
-    pub fn new(sysroot_dir: Option<AbsPathBuf>, sysroot_src_dir: Option<AbsPathBuf>) -> Sysroot {
-        Self::assemble(sysroot_dir.map(Ok), sysroot_src_dir.map(Ok))
+    pub fn new(sysroot_dir: Option<AbsPathBuf>, rust_lib_src_dir: Option<AbsPathBuf>) -> Sysroot {
+        Self::assemble(sysroot_dir.map(Ok), rust_lib_src_dir.map(Ok))
     }
 
     /// Returns a command to run a tool preferring the cargo proxies if the sysroot exists.
@@ -200,7 +207,7 @@ impl Sysroot {
 
     fn assemble(
         sysroot_dir: Option<Result<AbsPathBuf, anyhow::Error>>,
-        sysroot_src_dir: Option<Result<AbsPathBuf, anyhow::Error>>,
+        rust_lib_src_dir: Option<Result<AbsPathBuf, anyhow::Error>>,
     ) -> Sysroot {
         let mut errors = String::new();
         let root = match sysroot_dir {
@@ -211,8 +218,8 @@ impl Sysroot {
             }
             None => None,
         };
-        let src_root = match sysroot_src_dir {
-            Some(Ok(sysroot_src_dir)) => Some(sysroot_src_dir),
+        let rust_lib_src_root = match rust_lib_src_dir {
+            Some(Ok(rust_lib_src_dir)) => Some(rust_lib_src_dir),
             Some(Err(e)) => {
                 format_to!(errors, "{e}\n");
                 None
@@ -221,24 +228,28 @@ impl Sysroot {
         };
         Sysroot {
             root,
-            src_root,
-            workspace: SysrootWorkspace::Empty,
+            rust_lib_src_root,
+            workspace: RustLibSrcWorkspace::Empty,
             error: errors.is_empty().not().then_some(errors),
         }
     }
 
-    pub fn load_workspace(&mut self, sysroot_source_config: &SysrootSourceWorkspaceConfig) {
-        assert!(matches!(self.workspace, SysrootWorkspace::Empty), "workspace already loaded");
-        let Self { root: _, src_root: Some(src_root), workspace, error: _ } = self else { return };
-        if let SysrootSourceWorkspaceConfig::CargoMetadata(cargo_config) = sysroot_source_config {
+    pub fn load_workspace(
+        &self,
+        sysroot_source_config: &RustSourceWorkspaceConfig,
+    ) -> Option<RustLibSrcWorkspace> {
+        assert!(matches!(self.workspace, RustLibSrcWorkspace::Empty), "workspace already loaded");
+        let Self { root: _, rust_lib_src_root: Some(src_root), workspace: _, error: _ } = self
+        else {
+            return None;
+        };
+        if let RustSourceWorkspaceConfig::CargoMetadata(cargo_config) = sysroot_source_config {
             let library_manifest = ManifestPath::try_from(src_root.join("Cargo.toml")).unwrap();
             if fs::metadata(&library_manifest).is_ok() {
                 if let Some(loaded) =
-                    Self::load_library_via_cargo(library_manifest, src_root, cargo_config)
+                    self.load_library_via_cargo(library_manifest, src_root, cargo_config)
                 {
-                    *workspace = loaded;
-                    self.load_core_check();
-                    return;
+                    return Some(loaded);
                 }
             }
         }
@@ -255,7 +266,7 @@ impl Sysroot {
                 .find(|it| fs::metadata(it).is_ok());
 
             if let Some(root) = root {
-                stitched.crates.alloc(SysrootCrateData {
+                stitched.crates.alloc(RustLibSrcCrateData {
                     name: name.into(),
                     root,
                     deps: Vec::new(),
@@ -286,17 +297,19 @@ impl Sysroot {
                 }
             }
         }
-        *workspace = SysrootWorkspace::Stitched(stitched);
-        self.load_core_check();
+        Some(RustLibSrcWorkspace::Stitched(stitched))
     }
 
-    fn load_core_check(&mut self) {
+    pub fn set_workspace(&mut self, workspace: RustLibSrcWorkspace) {
+        self.workspace = workspace;
         if self.error.is_none() {
-            if let Some(src_root) = &self.src_root {
+            if let Some(src_root) = &self.rust_lib_src_root {
                 let has_core = match &self.workspace {
-                    SysrootWorkspace::Workspace(ws) => ws.packages().any(|p| ws[p].name == "core"),
-                    SysrootWorkspace::Stitched(stitched) => stitched.by_name("core").is_some(),
-                    SysrootWorkspace::Empty => true,
+                    RustLibSrcWorkspace::Workspace(ws) => {
+                        ws.packages().any(|p| ws[p].name == "core")
+                    }
+                    RustLibSrcWorkspace::Stitched(stitched) => stitched.by_name("core").is_some(),
+                    RustLibSrcWorkspace::Empty => true,
                 };
                 if !has_core {
                     let var_note = if env::var_os("RUST_SRC_PATH").is_some() {
@@ -313,10 +326,11 @@ impl Sysroot {
     }
 
     fn load_library_via_cargo(
+        &self,
         library_manifest: ManifestPath,
-        sysroot_src_dir: &AbsPathBuf,
+        rust_lib_src_dir: &AbsPathBuf,
         cargo_config: &CargoMetadataConfig,
-    ) -> Option<SysrootWorkspace> {
+    ) -> Option<RustLibSrcWorkspace> {
         tracing::debug!("Loading library metadata: {library_manifest}");
         let mut cargo_config = cargo_config.clone();
         // the sysroot uses `public-dependency`, so we make cargo think it's a nightly
@@ -327,9 +341,9 @@ impl Sysroot {
 
         let (mut res, _) = match CargoWorkspace::fetch_metadata(
             &library_manifest,
-            sysroot_src_dir,
+            rust_lib_src_dir,
             &cargo_config,
-            &Sysroot::empty(),
+            self,
             // Make sure we never attempt to write to the sysroot
             true,
             &|_| (),
@@ -391,7 +405,7 @@ impl Sysroot {
         });
 
         let cargo_workspace = CargoWorkspace::new(res, library_manifest, Default::default());
-        Some(SysrootWorkspace::Workspace(cargo_workspace))
+        Some(RustLibSrcWorkspace::Workspace(cargo_workspace))
     }
 }
 
@@ -407,7 +421,7 @@ fn discover_sysroot_dir(
     Ok(AbsPathBuf::assert(Utf8PathBuf::from(stdout)))
 }
 
-fn discover_sysroot_src_dir(sysroot_path: &AbsPathBuf) -> Option<AbsPathBuf> {
+fn discover_rust_lib_src_dir(sysroot_path: &AbsPathBuf) -> Option<AbsPathBuf> {
     if let Ok(path) = env::var("RUST_SRC_PATH") {
         if let Ok(path) = AbsPathBuf::try_from(path.as_str()) {
             let core = path.join("core");
@@ -421,22 +435,22 @@ fn discover_sysroot_src_dir(sysroot_path: &AbsPathBuf) -> Option<AbsPathBuf> {
         }
     }
 
-    get_rust_src(sysroot_path)
+    get_rust_lib_src(sysroot_path)
 }
 
-fn discover_sysroot_src_dir_or_add_component(
+fn discover_rust_lib_src_dir_or_add_component(
     sysroot_path: &AbsPathBuf,
     current_dir: &AbsPath,
     extra_env: &FxHashMap<String, String>,
 ) -> Result<AbsPathBuf> {
-    discover_sysroot_src_dir(sysroot_path)
+    discover_rust_lib_src_dir(sysroot_path)
         .or_else(|| {
             let mut rustup = toolchain::command(Tool::Rustup.prefer_proxy(), current_dir);
             rustup.envs(extra_env);
             rustup.args(["component", "add", "rust-src"]);
             tracing::info!("adding rust-src component by {:?}", rustup);
             utf8_stdout(&mut rustup).ok()?;
-            get_rust_src(sysroot_path)
+            get_rust_lib_src(sysroot_path)
         })
         .ok_or_else(|| {
             tracing::error!(%sysroot_path, "can't load standard library, try installing `rust-src`");
@@ -461,11 +475,11 @@ fn get_rustc_src(sysroot_path: &AbsPath) -> Option<ManifestPath> {
     }
 }
 
-fn get_rust_src(sysroot_path: &AbsPath) -> Option<AbsPathBuf> {
-    let rust_src = sysroot_path.join("lib/rustlib/src/rust/library");
-    tracing::debug!("checking sysroot library: {rust_src}");
-    if fs::metadata(&rust_src).is_ok() {
-        Some(rust_src)
+fn get_rust_lib_src(sysroot_path: &AbsPath) -> Option<AbsPathBuf> {
+    let rust_lib_src = sysroot_path.join("lib/rustlib/src/rust/library");
+    tracing::debug!("checking sysroot library: {rust_lib_src}");
+    if fs::metadata(&rust_lib_src).is_ok() {
+        Some(rust_lib_src)
     } else {
         None
     }
diff --git a/src/tools/rust-analyzer/crates/project-model/src/tests.rs b/src/tools/rust-analyzer/crates/project-model/src/tests.rs
index f1113831125..54eb0e3478a 100644
--- a/src/tools/rust-analyzer/crates/project-model/src/tests.rs
+++ b/src/tools/rust-analyzer/crates/project-model/src/tests.rs
@@ -12,9 +12,9 @@ use span::FileId;
 use triomphe::Arc;
 
 use crate::{
-    sysroot::SysrootWorkspace, workspace::ProjectWorkspaceKind, CargoWorkspace, CfgOverrides,
-    ManifestPath, ProjectJson, ProjectJsonData, ProjectWorkspace, Sysroot,
-    SysrootSourceWorkspaceConfig, WorkspaceBuildScripts,
+    sysroot::RustLibSrcWorkspace, workspace::ProjectWorkspaceKind, CargoWorkspace, CfgOverrides,
+    ManifestPath, ProjectJson, ProjectJsonData, ProjectWorkspace, RustSourceWorkspaceConfig,
+    Sysroot, WorkspaceBuildScripts,
 };
 
 fn load_cargo(file: &str) -> (CrateGraph, ProcMacroPaths) {
@@ -42,7 +42,6 @@ fn load_workspace_from_metadata(file: &str) -> ProjectWorkspace {
             build_scripts: WorkspaceBuildScripts::default(),
             rustc: Err(None),
             error: None,
-            set_test: true,
         },
         cfg_overrides: Default::default(),
         sysroot: Sysroot::empty(),
@@ -50,6 +49,7 @@ fn load_workspace_from_metadata(file: &str) -> ProjectWorkspace {
         toolchain: None,
         target_layout: Err("target_data_layout not loaded".into()),
         extra_includes: Vec::new(),
+        set_test: true,
     }
 }
 
@@ -65,6 +65,7 @@ fn load_rust_project(file: &str) -> (CrateGraph, ProcMacroPaths) {
         target_layout: Err(Arc::from("test has no data layout")),
         cfg_overrides: Default::default(),
         extra_includes: Vec::new(),
+        set_test: true,
     };
     to_crate_graph(project_workspace, &mut Default::default())
 }
@@ -125,7 +126,10 @@ fn get_fake_sysroot() -> Sysroot {
     let sysroot_dir = AbsPathBuf::assert(sysroot_path);
     let sysroot_src_dir = sysroot_dir.clone();
     let mut sysroot = Sysroot::new(Some(sysroot_dir), Some(sysroot_src_dir));
-    sysroot.load_workspace(&SysrootSourceWorkspaceConfig::default_cargo());
+    let loaded_sysroot = sysroot.load_workspace(&RustSourceWorkspaceConfig::default_cargo());
+    if let Some(loaded_sysroot) = loaded_sysroot {
+        sysroot.set_workspace(loaded_sysroot);
+    }
     sysroot
 }
 
@@ -230,7 +234,7 @@ fn rust_project_is_proc_macro_has_proc_macro_dep() {
     let crate_data = &crate_graph[crate_id];
     // Assert that the project crate with `is_proc_macro` has a dependency
     // on the proc_macro sysroot crate.
-    crate_data.dependencies.iter().find(|&dep| dep.name.deref() == "proc_macro").unwrap();
+    crate_data.dependencies.iter().find(|&dep| *dep.name.deref() == sym::proc_macro).unwrap();
 }
 
 #[test]
@@ -271,15 +275,17 @@ fn smoke_test_real_sysroot_cargo() {
         AbsPath::assert(Utf8Path::new(env!("CARGO_MANIFEST_DIR"))),
         &Default::default(),
     );
-    sysroot.load_workspace(&SysrootSourceWorkspaceConfig::default_cargo());
-    assert!(matches!(sysroot.workspace(), SysrootWorkspace::Workspace(_)));
+    let loaded_sysroot = sysroot.load_workspace(&RustSourceWorkspaceConfig::default_cargo());
+    if let Some(loaded_sysroot) = loaded_sysroot {
+        sysroot.set_workspace(loaded_sysroot);
+    }
+    assert!(matches!(sysroot.workspace(), RustLibSrcWorkspace::Workspace(_)));
     let project_workspace = ProjectWorkspace {
         kind: ProjectWorkspaceKind::Cargo {
             cargo: cargo_workspace,
             build_scripts: WorkspaceBuildScripts::default(),
             rustc: Err(None),
             error: None,
-            set_test: true,
         },
         sysroot,
         rustc_cfg: Vec::new(),
@@ -287,6 +293,7 @@ fn smoke_test_real_sysroot_cargo() {
         toolchain: None,
         target_layout: Err("target_data_layout not loaded".into()),
         extra_includes: Vec::new(),
+        set_test: true,
     };
     project_workspace.to_crate_graph(
         &mut {
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 dcd62753cb2..16b5bb11afa 100644
--- a/src/tools/rust-analyzer/crates/project-model/src/workspace.rs
+++ b/src/tools/rust-analyzer/crates/project-model/src/workspace.rs
@@ -2,7 +2,7 @@
 //! metadata` or `rust-project.json`) into representation stored in the salsa
 //! database -- `CrateGraph`.
 
-use std::{collections::VecDeque, fmt, fs, iter, ops::Deref, sync};
+use std::{collections::VecDeque, fmt, fs, iter, ops::Deref, sync, thread};
 
 use anyhow::Context;
 use base_db::{
@@ -23,10 +23,10 @@ use crate::{
     cargo_workspace::{CargoMetadataConfig, DepKind, PackageData, RustLibSource},
     env::{cargo_config_env, inject_cargo_env, inject_cargo_package_env, inject_rustc_tool_env},
     project_json::{Crate, CrateArrayIdx},
-    sysroot::{SysrootCrate, SysrootWorkspace},
+    sysroot::{RustLibSrcCrate, RustLibSrcWorkspace},
     toolchain_info::{rustc_cfg, target_data_layout, target_tuple, version, QueryConfig},
     CargoConfig, CargoWorkspace, CfgOverrides, InvocationStrategy, ManifestPath, Package,
-    ProjectJson, ProjectManifest, Sysroot, SysrootSourceWorkspaceConfig, TargetData, TargetKind,
+    ProjectJson, ProjectManifest, RustSourceWorkspaceConfig, Sysroot, TargetData, TargetKind,
     WorkspaceBuildScripts,
 };
 use tracing::{debug, error, info};
@@ -64,6 +64,8 @@ pub struct ProjectWorkspace {
     pub cfg_overrides: CfgOverrides,
     /// Additional includes to add for the VFS.
     pub extra_includes: Vec<AbsPathBuf>,
+    /// Set `cfg(test)` for local crates
+    pub set_test: bool,
 }
 
 #[derive(Clone)]
@@ -79,7 +81,6 @@ pub enum ProjectWorkspaceKind {
         /// The rustc workspace loaded for this workspace. An `Err(None)` means loading has been
         /// disabled or was otherwise not requested.
         rustc: Result<Box<(CargoWorkspace, WorkspaceBuildScripts)>, Option<String>>,
-        set_test: bool,
     },
     /// Project workspace was specified using a `rust-project.json` file.
     Json(ProjectJson),
@@ -98,7 +99,6 @@ pub enum ProjectWorkspaceKind {
         file: ManifestPath,
         /// Is this file a cargo script file?
         cargo: Option<(CargoWorkspace, WorkspaceBuildScripts, Option<Arc<anyhow::Error>>)>,
-        set_test: bool,
     },
 }
 
@@ -113,9 +113,10 @@ impl fmt::Debug for ProjectWorkspace {
             target_layout,
             cfg_overrides,
             extra_includes,
+            set_test,
         } = self;
         match kind {
-            ProjectWorkspaceKind::Cargo { cargo, error: _, build_scripts, rustc, set_test } => f
+            ProjectWorkspaceKind::Cargo { cargo, error: _, build_scripts, rustc } => f
                 .debug_struct("Cargo")
                 .field("root", &cargo.workspace_root().file_name())
                 .field("n_packages", &cargo.packages().len())
@@ -141,11 +142,12 @@ impl fmt::Debug for ProjectWorkspace {
                     .field("toolchain", &toolchain)
                     .field("data_layout", &target_layout)
                     .field("n_cfg_overrides", &cfg_overrides.len())
-                    .field("n_extra_includes", &extra_includes.len());
+                    .field("n_extra_includes", &extra_includes.len())
+                    .field("set_test", set_test);
 
                 debug_struct.finish()
             }
-            ProjectWorkspaceKind::DetachedFile { file, cargo: cargo_script, set_test } => f
+            ProjectWorkspaceKind::DetachedFile { file, cargo: cargo_script } => f
                 .debug_struct("DetachedFiles")
                 .field("file", &file)
                 .field("cargo_script", &cargo_script.is_some())
@@ -186,7 +188,7 @@ impl ProjectWorkspace {
                 let project_location = project_json.parent().to_path_buf();
                 let project_json: ProjectJson =
                     ProjectJson::new(Some(project_json.clone()), &project_location, data);
-                ProjectWorkspace::load_inline(project_json, config)
+                ProjectWorkspace::load_inline(project_json, config, progress)
             }
             ProjectManifest::CargoScript(rust_file) => {
                 ProjectWorkspace::load_detached_file(rust_file, config)?
@@ -204,19 +206,33 @@ impl ProjectWorkspace {
         config: &CargoConfig,
         progress: &dyn Fn(String),
     ) -> Result<ProjectWorkspace, anyhow::Error> {
-        let mut sysroot = match (&config.sysroot, &config.sysroot_src) {
+        progress("Discovering sysroot".to_owned());
+        let CargoConfig {
+            features,
+            rustc_source,
+            extra_args,
+            extra_env,
+            set_test,
+            cfg_overrides,
+            extra_includes,
+            sysroot,
+            sysroot_src,
+            target,
+            ..
+        } = config;
+        let mut sysroot = match (sysroot, sysroot_src) {
             (Some(RustLibSource::Discover), None) => {
-                Sysroot::discover(cargo_toml.parent(), &config.extra_env)
+                Sysroot::discover(cargo_toml.parent(), extra_env)
             }
             (Some(RustLibSource::Discover), Some(sysroot_src)) => {
                 Sysroot::discover_with_src_override(
                     cargo_toml.parent(),
-                    &config.extra_env,
+                    extra_env,
                     sysroot_src.clone(),
                 )
             }
             (Some(RustLibSource::Path(path)), None) => {
-                Sysroot::discover_sysroot_src_dir(path.clone())
+                Sysroot::discover_rust_lib_src_dir(path.clone())
             }
             (Some(RustLibSource::Path(sysroot)), Some(sysroot_src)) => {
                 Sysroot::new(Some(sysroot.clone()), Some(sysroot_src.clone()))
@@ -224,100 +240,147 @@ impl ProjectWorkspace {
             (None, _) => Sysroot::empty(),
         };
 
-        let rustc_dir = match &config.rustc_source {
-            Some(RustLibSource::Path(path)) => ManifestPath::try_from(path.clone())
-                .map_err(|p| Some(format!("rustc source path is not absolute: {p}"))),
-            Some(RustLibSource::Discover) => sysroot
-                .discover_rustc_src()
-                .ok_or_else(|| Some("Failed to discover rustc source for sysroot.".to_owned())),
-            None => Err(None),
-        };
-
-        tracing::info!(workspace = %cargo_toml, src_root = ?sysroot.src_root(), root = ?sysroot.root(), "Using sysroot");
+        tracing::info!(workspace = %cargo_toml, src_root = ?sysroot.rust_lib_src_root(), root = ?sysroot.root(), "Using sysroot");
+        progress("Querying project metadata".to_owned());
         let toolchain_config = QueryConfig::Cargo(&sysroot, cargo_toml);
         let targets =
-            target_tuple::get(toolchain_config, config.target.as_deref(), &config.extra_env)
-                .unwrap_or_default();
-        let toolchain = version::get(toolchain_config, &config.extra_env)
-            .inspect_err(|e| {
-                tracing::error!(%e,
-                    "failed fetching toolchain version for {cargo_toml:?} workspace"
-                )
-            })
-            .ok()
-            .flatten();
-        let rustc_cfg =
-            rustc_cfg::get(toolchain_config, targets.first().map(Deref::deref), &config.extra_env);
-        let cfg_overrides = config.cfg_overrides.clone();
-        let data_layout = target_data_layout::get(
-            toolchain_config,
-            targets.first().map(Deref::deref),
-            &config.extra_env,
-        );
-        if let Err(e) = &data_layout {
-            tracing::error!(%e, "failed fetching data layout for {cargo_toml:?} workspace");
-        }
-        sysroot.load_workspace(&SysrootSourceWorkspaceConfig::CargoMetadata(
-            sysroot_metadata_config(&config.extra_env, &targets),
-        ));
+            target_tuple::get(toolchain_config, target.as_deref(), extra_env).unwrap_or_default();
+
+        // We spawn a bunch of processes to query various information about the workspace's
+        // toolchain and sysroot
+        // We can speed up loading a bit by spawning all of these processes in parallel (especially
+        // on systems were process spawning is delayed)
+        let join = thread::scope(|s| {
+            let workspace_dir = cargo_toml.parent();
+            let toolchain = s.spawn(|| {
+                version::get(toolchain_config, extra_env)
+                    .inspect_err(|e| {
+                        tracing::error!(%e,
+                            "failed fetching toolchain version for {cargo_toml:?} workspace"
+                        )
+                    })
+                    .ok()
+                    .flatten()
+            });
 
-        let rustc = rustc_dir.and_then(|rustc_dir| {
-            info!(workspace = %cargo_toml, rustc_dir = %rustc_dir, "Using rustc source");
-            match CargoWorkspace::fetch_metadata(
-                &rustc_dir,
-                cargo_toml.parent(),
-                &CargoMetadataConfig {
-                    features: crate::CargoFeatures::default(),
-                    targets: targets.clone(),
-                    extra_args: config.extra_args.clone(),
-                    extra_env: config.extra_env.clone(),
-                },
-                &sysroot,
-                false,
-                progress,
-            ) {
-                Ok((meta, _error)) => {
-                    let workspace = CargoWorkspace::new(meta, cargo_toml.clone(), Env::default());
-                    let build_scripts = WorkspaceBuildScripts::rustc_crates(
-                        &workspace,
-                        cargo_toml.parent(),
-                        &config.extra_env,
+            let rustc_cfg = s.spawn(|| {
+                rustc_cfg::get(toolchain_config, targets.first().map(Deref::deref), extra_env)
+            });
+            let data_layout = s.spawn(|| {
+                target_data_layout::get(
+                    toolchain_config,
+                    targets.first().map(Deref::deref),
+                    extra_env,
+                ).inspect_err(|e| {
+                    tracing::error!(%e, "failed fetching data layout for {cargo_toml:?} workspace")
+                })
+            });
+
+            let rustc_dir = s.spawn(|| {
+                let rustc_dir = match rustc_source {
+                    Some(RustLibSource::Path(path)) => ManifestPath::try_from(path.clone())
+                        .map_err(|p| Some(format!("rustc source path is not absolute: {p}"))),
+                    Some(RustLibSource::Discover) => {
+                        sysroot.discover_rustc_src().ok_or_else(|| {
+                            Some("Failed to discover rustc source for sysroot.".to_owned())
+                        })
+                    }
+                    None => Err(None),
+                };
+                rustc_dir.and_then(|rustc_dir| {
+                    info!(workspace = %cargo_toml, rustc_dir = %rustc_dir, "Using rustc source");
+                    match CargoWorkspace::fetch_metadata(
+                        &rustc_dir,
+                        workspace_dir,
+                        &CargoMetadataConfig {
+                            features: crate::CargoFeatures::default(),
+                            targets: targets.clone(),
+                            extra_args: extra_args.clone(),
+                            extra_env: extra_env.clone(),
+                        },
                         &sysroot,
-                    );
-                    Ok(Box::new((workspace, build_scripts)))
-                }
-                Err(e) => {
-                    tracing::error!(
-                        %e,
-                        "Failed to read Cargo metadata from rustc source at {rustc_dir}",
-                    );
-                    Err(Some(format!(
-                        "Failed to read Cargo metadata from rustc source at {rustc_dir}: {e}"
-                    )))
-                }
-            }
+                        false,
+                        &|_| (),
+                    ) {
+                        Ok((meta, _error)) => {
+                            let workspace =
+                                CargoWorkspace::new(meta, cargo_toml.clone(), Env::default());
+                            let build_scripts = WorkspaceBuildScripts::rustc_crates(
+                                &workspace,
+                                workspace_dir,
+                                extra_env,
+                                &sysroot,
+                            );
+                            Ok(Box::new((workspace, build_scripts)))
+                        }
+                        Err(e) => {
+                            tracing::error!(
+                                %e,
+                                "Failed to read Cargo metadata from rustc source at {rustc_dir}",
+                            );
+                            Err(Some(format!(
+                            "Failed to read Cargo metadata from rustc source at {rustc_dir}: {e}"
+                        )))
+                        }
+                    }
+                })
+            });
+
+            let cargo_metadata = s.spawn(|| {
+                CargoWorkspace::fetch_metadata(
+                    cargo_toml,
+                    workspace_dir,
+                    &CargoMetadataConfig {
+                        features: features.clone(),
+                        targets: targets.clone(),
+                        extra_args: extra_args.clone(),
+                        extra_env: extra_env.clone(),
+                    },
+                    &sysroot,
+                    false,
+                    &|_| (),
+                )
+            });
+            let loaded_sysroot = s.spawn(|| {
+                sysroot.load_workspace(&RustSourceWorkspaceConfig::CargoMetadata(
+                    sysroot_metadata_config(extra_env, &targets),
+                ))
+            });
+            let cargo_config_extra_env =
+                s.spawn(|| cargo_config_env(cargo_toml, extra_env, &sysroot));
+            thread::Result::Ok((
+                toolchain.join()?,
+                rustc_cfg.join()?,
+                data_layout.join()?,
+                rustc_dir.join()?,
+                loaded_sysroot.join()?,
+                cargo_metadata.join()?,
+                cargo_config_extra_env.join()?,
+            ))
         });
 
-        let (meta, error) = CargoWorkspace::fetch_metadata(
-            cargo_toml,
-            cargo_toml.parent(),
-            &CargoMetadataConfig {
-                features: config.features.clone(),
-                targets,
-                extra_args: config.extra_args.clone(),
-                extra_env: config.extra_env.clone(),
-            },
-            &sysroot,
-            false,
-            progress,
-        )
-        .with_context(|| {
+        let (
+            toolchain,
+            rustc_cfg,
+            data_layout,
+            rustc,
+            loaded_sysroot,
+            cargo_metadata,
+            cargo_config_extra_env,
+        ) = match join {
+            Ok(it) => it,
+            Err(e) => std::panic::resume_unwind(e),
+        };
+
+        let (meta, error) = cargo_metadata.with_context(|| {
             format!(
                 "Failed to read Cargo metadata from Cargo.toml file {cargo_toml}, {toolchain:?}",
             )
         })?;
-        let cargo_config_extra_env = cargo_config_env(cargo_toml, &config.extra_env, &sysroot);
         let cargo = CargoWorkspace::new(meta, cargo_toml.clone(), cargo_config_extra_env);
+        if let Some(loaded_sysroot) = loaded_sysroot {
+            sysroot.set_workspace(loaded_sysroot);
+        }
 
         Ok(ProjectWorkspace {
             kind: ProjectWorkspaceKind::Cargo {
@@ -325,35 +388,70 @@ impl ProjectWorkspace {
                 build_scripts: WorkspaceBuildScripts::default(),
                 rustc,
                 error: error.map(Arc::new),
-                set_test: config.set_test,
             },
             sysroot,
             rustc_cfg,
-            cfg_overrides,
+            cfg_overrides: cfg_overrides.clone(),
             toolchain,
             target_layout: data_layout.map(Arc::from).map_err(|it| Arc::from(it.to_string())),
-            extra_includes: config.extra_includes.clone(),
+            extra_includes: extra_includes.clone(),
+            set_test: *set_test,
         })
     }
 
-    pub fn load_inline(project_json: ProjectJson, config: &CargoConfig) -> ProjectWorkspace {
+    pub fn load_inline(
+        project_json: ProjectJson,
+        config: &CargoConfig,
+        progress: &dyn Fn(String),
+    ) -> ProjectWorkspace {
+        progress("Discovering sysroot".to_owned());
         let mut sysroot =
             Sysroot::new(project_json.sysroot.clone(), project_json.sysroot_src.clone());
-        sysroot.load_workspace(&SysrootSourceWorkspaceConfig::Stitched);
+        let loaded_sysroot = sysroot.load_workspace(&RustSourceWorkspaceConfig::Stitched);
+        if let Some(loaded_sysroot) = loaded_sysroot {
+            sysroot.set_workspace(loaded_sysroot);
+        }
+
+        tracing::info!(workspace = %project_json.manifest_or_root(), src_root = ?sysroot.rust_lib_src_root(), root = ?sysroot.root(), "Using sysroot");
+        progress("Querying project metadata".to_owned());
         let query_config = QueryConfig::Rustc(&sysroot, project_json.path().as_ref());
-        let toolchain = version::get(query_config, &config.extra_env).ok().flatten();
+        let targets = target_tuple::get(query_config, config.target.as_deref(), &config.extra_env)
+            .unwrap_or_default();
+
+        // We spawn a bunch of processes to query various information about the workspace's
+        // toolchain and sysroot
+        // We can speed up loading a bit by spawning all of these processes in parallel (especially
+        // on systems were process spawning is delayed)
+        let join = thread::scope(|s| {
+            let toolchain =
+                s.spawn(|| version::get(query_config, &config.extra_env).ok().flatten());
+            let rustc_cfg = s.spawn(|| {
+                rustc_cfg::get(query_config, targets.first().map(Deref::deref), &config.extra_env)
+            });
+            let data_layout = s.spawn(|| {
+                target_data_layout::get(
+                    query_config,
+                    targets.first().map(Deref::deref),
+                    &config.extra_env,
+                )
+            });
+            thread::Result::Ok((toolchain.join()?, rustc_cfg.join()?, data_layout.join()?))
+        });
+
+        let (toolchain, rustc_cfg, target_layout) = match join {
+            Ok(it) => it,
+            Err(e) => std::panic::resume_unwind(e),
+        };
 
-        let target = config.target.as_deref();
-        let rustc_cfg = rustc_cfg::get(query_config, target, &config.extra_env);
-        let data_layout = target_data_layout::get(query_config, target, &config.extra_env);
         ProjectWorkspace {
             kind: ProjectWorkspaceKind::Json(project_json),
             sysroot,
             rustc_cfg,
             toolchain,
-            target_layout: data_layout.map(Arc::from).map_err(|it| Arc::from(it.to_string())),
+            target_layout: target_layout.map(Arc::from).map_err(|it| Arc::from(it.to_string())),
             cfg_overrides: config.cfg_overrides.clone(),
             extra_includes: config.extra_includes.clone(),
+            set_test: config.set_test,
         }
     }
 
@@ -363,7 +461,7 @@ impl ProjectWorkspace {
     ) -> anyhow::Result<ProjectWorkspace> {
         let dir = detached_file.parent();
         let mut sysroot = match &config.sysroot {
-            Some(RustLibSource::Path(path)) => Sysroot::discover_sysroot_src_dir(path.clone()),
+            Some(RustLibSource::Path(path)) => Sysroot::discover_rust_lib_src_dir(path.clone()),
             Some(RustLibSource::Discover) => Sysroot::discover(dir, &config.extra_env),
             None => Sysroot::empty(),
         };
@@ -374,9 +472,12 @@ impl ProjectWorkspace {
             .unwrap_or_default();
         let rustc_cfg = rustc_cfg::get(query_config, None, &config.extra_env);
         let data_layout = target_data_layout::get(query_config, None, &config.extra_env);
-        sysroot.load_workspace(&SysrootSourceWorkspaceConfig::CargoMetadata(
+        let loaded_sysroot = sysroot.load_workspace(&RustSourceWorkspaceConfig::CargoMetadata(
             sysroot_metadata_config(&config.extra_env, &targets),
         ));
+        if let Some(loaded_sysroot) = loaded_sysroot {
+            sysroot.set_workspace(loaded_sysroot);
+        }
 
         let cargo_script = CargoWorkspace::fetch_metadata(
             detached_file,
@@ -406,7 +507,6 @@ impl ProjectWorkspace {
             kind: ProjectWorkspaceKind::DetachedFile {
                 file: detached_file.to_owned(),
                 cargo: cargo_script,
-                set_test: config.set_test,
             },
             sysroot,
             rustc_cfg,
@@ -414,6 +514,7 @@ impl ProjectWorkspace {
             target_layout: data_layout.map(Arc::from).map_err(|it| Arc::from(it.to_string())),
             cfg_overrides: config.cfg_overrides.clone(),
             extra_includes: config.extra_includes.clone(),
+            set_test: config.set_test,
         })
     }
 
@@ -545,7 +646,7 @@ impl ProjectWorkspace {
     pub fn to_roots(&self) -> Vec<PackageRoot> {
         let mk_sysroot = || {
             let mut r = match self.sysroot.workspace() {
-                SysrootWorkspace::Workspace(ws) => ws
+                RustLibSrcWorkspace::Workspace(ws) => ws
                     .packages()
                     .filter_map(|pkg| {
                         if ws[pkg].is_local {
@@ -566,12 +667,17 @@ impl ProjectWorkspace {
                         Some(PackageRoot { is_local: false, include, exclude })
                     })
                     .collect(),
-                SysrootWorkspace::Stitched(_) | SysrootWorkspace::Empty => vec![],
+                RustLibSrcWorkspace::Stitched(_) | RustLibSrcWorkspace::Empty => vec![],
             };
 
             r.push(PackageRoot {
                 is_local: false,
-                include: self.sysroot.src_root().map(|it| it.to_path_buf()).into_iter().collect(),
+                include: self
+                    .sysroot
+                    .rust_lib_src_root()
+                    .map(|it| it.to_path_buf())
+                    .into_iter()
+                    .collect(),
                 exclude: Vec::new(),
             });
             r
@@ -593,7 +699,7 @@ impl ProjectWorkspace {
                 .into_iter()
                 .chain(mk_sysroot())
                 .collect::<Vec<_>>(),
-            ProjectWorkspaceKind::Cargo { cargo, rustc, build_scripts, error: _, set_test: _ } => {
+            ProjectWorkspaceKind::Cargo { cargo, rustc, build_scripts, error: _ } => {
                 cargo
                     .packages()
                     .map(|pkg| {
@@ -728,8 +834,9 @@ impl ProjectWorkspace {
                 sysroot,
                 extra_env,
                 cfg_overrides,
+                self.set_test,
             ),
-            ProjectWorkspaceKind::Cargo { cargo, rustc, build_scripts, error: _, set_test } => {
+            ProjectWorkspaceKind::Cargo { cargo, rustc, build_scripts, error: _ } => {
                 cargo_to_crate_graph(
                     load,
                     rustc.as_ref().map(|a| a.as_ref()).ok(),
@@ -738,10 +845,10 @@ impl ProjectWorkspace {
                     rustc_cfg.clone(),
                     cfg_overrides,
                     build_scripts,
-                    *set_test,
+                    self.set_test,
                 )
             }
-            ProjectWorkspaceKind::DetachedFile { file, cargo: cargo_script, set_test, .. } => {
+            ProjectWorkspaceKind::DetachedFile { file, cargo: cargo_script, .. } => {
                 if let Some((cargo, build_scripts, _)) = cargo_script {
                     cargo_to_crate_graph(
                         &mut |path| load(path),
@@ -751,7 +858,7 @@ impl ProjectWorkspace {
                         rustc_cfg.clone(),
                         cfg_overrides,
                         build_scripts,
-                        *set_test,
+                        self.set_test,
                     )
                 } else {
                     detached_file_to_crate_graph(
@@ -760,7 +867,7 @@ impl ProjectWorkspace {
                         file,
                         sysroot,
                         cfg_overrides,
-                        *set_test,
+                        self.set_test,
                     )
                 }
             }
@@ -782,34 +889,22 @@ impl ProjectWorkspace {
         } = other;
         (match (kind, o_kind) {
             (
-                ProjectWorkspaceKind::Cargo {
-                    cargo,
-                    rustc,
-                    build_scripts: _,
-                    error: _,
-                    set_test: _,
-                },
+                ProjectWorkspaceKind::Cargo { cargo, rustc, build_scripts: _, error: _ },
                 ProjectWorkspaceKind::Cargo {
                     cargo: o_cargo,
                     rustc: o_rustc,
                     build_scripts: _,
                     error: _,
-                    set_test: _,
                 },
             ) => cargo == o_cargo && rustc == o_rustc,
             (ProjectWorkspaceKind::Json(project), ProjectWorkspaceKind::Json(o_project)) => {
                 project == o_project
             }
             (
-                ProjectWorkspaceKind::DetachedFile {
-                    file,
-                    cargo: Some((cargo_script, _, _)),
-                    set_test: _,
-                },
+                ProjectWorkspaceKind::DetachedFile { file, cargo: Some((cargo_script, _, _)) },
                 ProjectWorkspaceKind::DetachedFile {
                     file: o_file,
                     cargo: Some((o_cargo_script, _, _)),
-                    set_test: _,
                 },
             ) => file == o_file && cargo_script == o_cargo_script,
             _ => return false,
@@ -837,13 +932,13 @@ fn project_json_to_crate_graph(
     sysroot: &Sysroot,
     extra_env: &FxHashMap<String, String>,
     override_cfg: &CfgOverrides,
+    set_test: bool,
 ) -> (CrateGraph, ProcMacroPaths) {
     let mut res = (CrateGraph::default(), ProcMacroPaths::default());
     let (crate_graph, proc_macros) = &mut res;
     let (public_deps, libproc_macro) =
         sysroot_to_crate_graph(crate_graph, sysroot, rustc_cfg.clone(), load);
 
-    let r_a_cfg_flag = CfgAtom::Flag(sym::rust_analyzer.clone());
     let mut cfg_cache: FxHashMap<&str, Vec<CfgAtom>> = FxHashMap::default();
 
     let idx_to_crate_id: FxHashMap<CrateArrayIdx, CrateId> = project
@@ -862,6 +957,8 @@ fn project_json_to_crate_graph(
                     proc_macro_dylib_path,
                     is_proc_macro,
                     repository,
+                    is_workspace_member,
+                    proc_macro_cwd,
                     ..
                 },
                 file_id,
@@ -879,19 +976,28 @@ fn project_json_to_crate_graph(
                     None => &rustc_cfg,
                 };
 
-                let mut cfg_options = target_cfgs
-                    .iter()
-                    .chain(cfg.iter())
-                    .chain(iter::once(&r_a_cfg_flag))
-                    .cloned()
-                    .collect();
-                override_cfg.apply(
-                    &mut cfg_options,
-                    display_name
-                        .as_ref()
-                        .map(|it| it.canonical_name().as_str())
-                        .unwrap_or_default(),
-                );
+                let cfg_options = {
+                    let mut cfg_options: CfgOptions =
+                        target_cfgs.iter().chain(cfg.iter()).cloned().collect();
+
+                    if *is_workspace_member {
+                        if set_test {
+                            // Add test cfg for local crates
+                            cfg_options.insert_atom(sym::test.clone());
+                        }
+                        cfg_options.insert_atom(sym::rust_analyzer.clone());
+                    }
+
+                    override_cfg.apply(
+                        &mut cfg_options,
+                        display_name
+                            .as_ref()
+                            .map(|it| it.canonical_name().as_str())
+                            .unwrap_or_default(),
+                    );
+                    cfg_options
+                };
+
                 let crate_graph_crate_id = crate_graph.add_crate_root(
                     file_id,
                     *edition,
@@ -900,7 +1006,6 @@ fn project_json_to_crate_graph(
                     Arc::new(cfg_options),
                     None,
                     env,
-                    *is_proc_macro,
                     if let Some(name) = display_name.clone() {
                         CrateOrigin::Local {
                             repo: repository.clone(),
@@ -909,6 +1014,8 @@ fn project_json_to_crate_graph(
                     } else {
                         CrateOrigin::Local { repo: None, name: None }
                     },
+                    *is_proc_macro,
+                    proc_macro_cwd.clone(),
                 );
                 debug!(
                     ?crate_graph_crate_id,
@@ -1178,11 +1285,12 @@ fn detached_file_to_crate_graph(
         cfg_options.clone(),
         None,
         Env::default(),
-        false,
         CrateOrigin::Local {
             repo: None,
             name: display_name.map(|n| n.canonical_name().to_owned()),
         },
+        false,
+        None,
     );
 
     public_deps.add_to_crate_graph(&mut crate_graph, detached_file_crate);
@@ -1343,8 +1451,13 @@ fn add_target_crate_root(
         Arc::new(cfg_options),
         potential_cfg_options.map(Arc::new),
         env,
-        matches!(kind, TargetKind::Lib { is_proc_macro: true }),
         origin,
+        matches!(kind, TargetKind::Lib { is_proc_macro: true }),
+        Some(if pkg.is_member {
+            cargo.workspace_root().to_path_buf()
+        } else {
+            pkg.manifest.parent().to_path_buf()
+        }),
     );
     if let TargetKind::Lib { is_proc_macro: true } = kind {
         let proc_macro = match build_data {
@@ -1385,7 +1498,7 @@ fn sysroot_to_crate_graph(
 ) -> (SysrootPublicDeps, Option<CrateId>) {
     let _p = tracing::info_span!("sysroot_to_crate_graph").entered();
     match sysroot.workspace() {
-        SysrootWorkspace::Workspace(cargo) => {
+        RustLibSrcWorkspace::Workspace(cargo) => {
             let (mut cg, mut pm) = cargo_to_crate_graph(
                 load,
                 None,
@@ -1460,7 +1573,7 @@ fn sysroot_to_crate_graph(
 
             (SysrootPublicDeps { deps: pub_deps }, libproc_macro)
         }
-        SysrootWorkspace::Stitched(stitched) => {
+        RustLibSrcWorkspace::Stitched(stitched) => {
             let cfg_options = Arc::new({
                 let mut cfg_options = CfgOptions::default();
                 cfg_options.extend(rustc_cfg);
@@ -1468,7 +1581,7 @@ fn sysroot_to_crate_graph(
                 cfg_options.insert_atom(sym::miri.clone());
                 cfg_options
             });
-            let sysroot_crates: FxHashMap<SysrootCrate, CrateId> = stitched
+            let sysroot_crates: FxHashMap<RustLibSrcCrate, CrateId> = stitched
                 .crates()
                 .filter_map(|krate| {
                     let file_id = load(&stitched[krate].root)?;
@@ -1482,8 +1595,9 @@ fn sysroot_to_crate_graph(
                         cfg_options.clone(),
                         None,
                         Env::default(),
-                        false,
                         CrateOrigin::Lang(LangCrateOrigin::from(&*stitched[krate].name)),
+                        false,
+                        None,
                     );
                     Some((krate, crate_id))
                 })
@@ -1513,7 +1627,7 @@ fn sysroot_to_crate_graph(
                 stitched.proc_macro().and_then(|it| sysroot_crates.get(&it).copied());
             (public_deps, libproc_macro)
         }
-        SysrootWorkspace::Empty => (SysrootPublicDeps { deps: vec![] }, None),
+        RustLibSrcWorkspace::Empty => (SysrootPublicDeps { deps: vec![] }, None),
     }
 }
 
diff --git a/src/tools/rust-analyzer/crates/project-model/test_data/output/cargo_hello_world_project_model.txt b/src/tools/rust-analyzer/crates/project-model/test_data/output/cargo_hello_world_project_model.txt
index 880e90c52a5..fae0b6fcca4 100644
--- a/src/tools/rust-analyzer/crates/project-model/test_data/output/cargo_hello_world_project_model.txt
+++ b/src/tools/rust-analyzer/crates/project-model/test_data/output/cargo_hello_world_project_model.txt
@@ -61,6 +61,11 @@
             ),
         },
         is_proc_macro: false,
+        proc_macro_cwd: Some(
+            AbsPathBuf(
+                "$ROOT$hello-world",
+            ),
+        ),
     },
     1: CrateData {
         root_file_id: FileId(
@@ -132,6 +137,11 @@
             ),
         },
         is_proc_macro: false,
+        proc_macro_cwd: Some(
+            AbsPathBuf(
+                "$ROOT$hello-world",
+            ),
+        ),
     },
     2: CrateData {
         root_file_id: FileId(
@@ -203,6 +213,11 @@
             ),
         },
         is_proc_macro: false,
+        proc_macro_cwd: Some(
+            AbsPathBuf(
+                "$ROOT$hello-world",
+            ),
+        ),
     },
     3: CrateData {
         root_file_id: FileId(
@@ -274,6 +289,11 @@
             ),
         },
         is_proc_macro: false,
+        proc_macro_cwd: Some(
+            AbsPathBuf(
+                "$ROOT$hello-world",
+            ),
+        ),
     },
     4: CrateData {
         root_file_id: FileId(
@@ -341,5 +361,10 @@
             name: "libc",
         },
         is_proc_macro: false,
+        proc_macro_cwd: Some(
+            AbsPathBuf(
+                "$ROOT$.cargo/registry/src/github.com-1ecc6299db9ec823/libc-0.2.98",
+            ),
+        ),
     },
 }
\ No newline at end of file
diff --git a/src/tools/rust-analyzer/crates/project-model/test_data/output/cargo_hello_world_project_model_with_selective_overrides.txt b/src/tools/rust-analyzer/crates/project-model/test_data/output/cargo_hello_world_project_model_with_selective_overrides.txt
index 880e90c52a5..fae0b6fcca4 100644
--- a/src/tools/rust-analyzer/crates/project-model/test_data/output/cargo_hello_world_project_model_with_selective_overrides.txt
+++ b/src/tools/rust-analyzer/crates/project-model/test_data/output/cargo_hello_world_project_model_with_selective_overrides.txt
@@ -61,6 +61,11 @@
             ),
         },
         is_proc_macro: false,
+        proc_macro_cwd: Some(
+            AbsPathBuf(
+                "$ROOT$hello-world",
+            ),
+        ),
     },
     1: CrateData {
         root_file_id: FileId(
@@ -132,6 +137,11 @@
             ),
         },
         is_proc_macro: false,
+        proc_macro_cwd: Some(
+            AbsPathBuf(
+                "$ROOT$hello-world",
+            ),
+        ),
     },
     2: CrateData {
         root_file_id: FileId(
@@ -203,6 +213,11 @@
             ),
         },
         is_proc_macro: false,
+        proc_macro_cwd: Some(
+            AbsPathBuf(
+                "$ROOT$hello-world",
+            ),
+        ),
     },
     3: CrateData {
         root_file_id: FileId(
@@ -274,6 +289,11 @@
             ),
         },
         is_proc_macro: false,
+        proc_macro_cwd: Some(
+            AbsPathBuf(
+                "$ROOT$hello-world",
+            ),
+        ),
     },
     4: CrateData {
         root_file_id: FileId(
@@ -341,5 +361,10 @@
             name: "libc",
         },
         is_proc_macro: false,
+        proc_macro_cwd: Some(
+            AbsPathBuf(
+                "$ROOT$.cargo/registry/src/github.com-1ecc6299db9ec823/libc-0.2.98",
+            ),
+        ),
     },
 }
\ No newline at end of file
diff --git a/src/tools/rust-analyzer/crates/project-model/test_data/output/cargo_hello_world_project_model_with_wildcard_overrides.txt b/src/tools/rust-analyzer/crates/project-model/test_data/output/cargo_hello_world_project_model_with_wildcard_overrides.txt
index 7746acd225e..566174882dd 100644
--- a/src/tools/rust-analyzer/crates/project-model/test_data/output/cargo_hello_world_project_model_with_wildcard_overrides.txt
+++ b/src/tools/rust-analyzer/crates/project-model/test_data/output/cargo_hello_world_project_model_with_wildcard_overrides.txt
@@ -60,6 +60,11 @@
             ),
         },
         is_proc_macro: false,
+        proc_macro_cwd: Some(
+            AbsPathBuf(
+                "$ROOT$hello-world",
+            ),
+        ),
     },
     1: CrateData {
         root_file_id: FileId(
@@ -130,6 +135,11 @@
             ),
         },
         is_proc_macro: false,
+        proc_macro_cwd: Some(
+            AbsPathBuf(
+                "$ROOT$hello-world",
+            ),
+        ),
     },
     2: CrateData {
         root_file_id: FileId(
@@ -200,6 +210,11 @@
             ),
         },
         is_proc_macro: false,
+        proc_macro_cwd: Some(
+            AbsPathBuf(
+                "$ROOT$hello-world",
+            ),
+        ),
     },
     3: CrateData {
         root_file_id: FileId(
@@ -270,6 +285,11 @@
             ),
         },
         is_proc_macro: false,
+        proc_macro_cwd: Some(
+            AbsPathBuf(
+                "$ROOT$hello-world",
+            ),
+        ),
     },
     4: CrateData {
         root_file_id: FileId(
@@ -337,5 +357,10 @@
             name: "libc",
         },
         is_proc_macro: false,
+        proc_macro_cwd: Some(
+            AbsPathBuf(
+                "$ROOT$.cargo/registry/src/github.com-1ecc6299db9ec823/libc-0.2.98",
+            ),
+        ),
     },
 }
\ No newline at end of file
diff --git a/src/tools/rust-analyzer/crates/project-model/test_data/output/rust_project_cfg_groups.txt b/src/tools/rust-analyzer/crates/project-model/test_data/output/rust_project_cfg_groups.txt
index 2026ab2b8c2..9b4be19c41c 100644
--- a/src/tools/rust-analyzer/crates/project-model/test_data/output/rust_project_cfg_groups.txt
+++ b/src/tools/rust-analyzer/crates/project-model/test_data/output/rust_project_cfg_groups.txt
@@ -38,6 +38,7 @@
             Alloc,
         ),
         is_proc_macro: false,
+        proc_macro_cwd: None,
     },
     1: CrateData {
         root_file_id: FileId(
@@ -69,6 +70,7 @@
             Core,
         ),
         is_proc_macro: false,
+        proc_macro_cwd: None,
     },
     2: CrateData {
         root_file_id: FileId(
@@ -100,6 +102,7 @@
             Other,
         ),
         is_proc_macro: false,
+        proc_macro_cwd: None,
     },
     3: CrateData {
         root_file_id: FileId(
@@ -131,6 +134,7 @@
             Other,
         ),
         is_proc_macro: false,
+        proc_macro_cwd: None,
     },
     4: CrateData {
         root_file_id: FileId(
@@ -179,6 +183,7 @@
             ProcMacro,
         ),
         is_proc_macro: false,
+        proc_macro_cwd: None,
     },
     5: CrateData {
         root_file_id: FileId(
@@ -210,6 +215,7 @@
             Other,
         ),
         is_proc_macro: false,
+        proc_macro_cwd: None,
     },
     6: CrateData {
         root_file_id: FileId(
@@ -306,6 +312,7 @@
             Std,
         ),
         is_proc_macro: false,
+        proc_macro_cwd: None,
     },
     7: CrateData {
         root_file_id: FileId(
@@ -337,6 +344,7 @@
             Other,
         ),
         is_proc_macro: false,
+        proc_macro_cwd: None,
     },
     8: CrateData {
         root_file_id: FileId(
@@ -368,6 +376,7 @@
             Test,
         ),
         is_proc_macro: false,
+        proc_macro_cwd: None,
     },
     9: CrateData {
         root_file_id: FileId(
@@ -399,6 +408,7 @@
             Other,
         ),
         is_proc_macro: false,
+        proc_macro_cwd: None,
     },
     10: CrateData {
         root_file_id: FileId(
@@ -420,6 +430,7 @@
                 "group1_other_cfg=other_config",
                 "group2_cfg=yet_another_config",
                 "rust_analyzer",
+                "test",
                 "true",
             ],
         ),
@@ -476,6 +487,7 @@
             ),
         },
         is_proc_macro: false,
+        proc_macro_cwd: None,
     },
     11: CrateData {
         root_file_id: FileId(
@@ -496,6 +508,7 @@
                 "group2_cfg=fourth_config",
                 "group2_cfg=yet_another_config",
                 "rust_analyzer",
+                "test",
                 "true",
                 "unrelated_cfg",
             ],
@@ -553,5 +566,6 @@
             ),
         },
         is_proc_macro: false,
+        proc_macro_cwd: None,
     },
 }
\ No newline at end of file
diff --git a/src/tools/rust-analyzer/crates/project-model/test_data/output/rust_project_hello_world_project_model.txt b/src/tools/rust-analyzer/crates/project-model/test_data/output/rust_project_hello_world_project_model.txt
index a0e14b8fcb2..4c8e66e8e96 100644
--- a/src/tools/rust-analyzer/crates/project-model/test_data/output/rust_project_hello_world_project_model.txt
+++ b/src/tools/rust-analyzer/crates/project-model/test_data/output/rust_project_hello_world_project_model.txt
@@ -38,6 +38,7 @@
             Alloc,
         ),
         is_proc_macro: false,
+        proc_macro_cwd: None,
     },
     1: CrateData {
         root_file_id: FileId(
@@ -69,6 +70,7 @@
             Core,
         ),
         is_proc_macro: false,
+        proc_macro_cwd: None,
     },
     2: CrateData {
         root_file_id: FileId(
@@ -100,6 +102,7 @@
             Other,
         ),
         is_proc_macro: false,
+        proc_macro_cwd: None,
     },
     3: CrateData {
         root_file_id: FileId(
@@ -131,6 +134,7 @@
             Other,
         ),
         is_proc_macro: false,
+        proc_macro_cwd: None,
     },
     4: CrateData {
         root_file_id: FileId(
@@ -179,6 +183,7 @@
             ProcMacro,
         ),
         is_proc_macro: false,
+        proc_macro_cwd: None,
     },
     5: CrateData {
         root_file_id: FileId(
@@ -210,6 +215,7 @@
             Other,
         ),
         is_proc_macro: false,
+        proc_macro_cwd: None,
     },
     6: CrateData {
         root_file_id: FileId(
@@ -306,6 +312,7 @@
             Std,
         ),
         is_proc_macro: false,
+        proc_macro_cwd: None,
     },
     7: CrateData {
         root_file_id: FileId(
@@ -337,6 +344,7 @@
             Other,
         ),
         is_proc_macro: false,
+        proc_macro_cwd: None,
     },
     8: CrateData {
         root_file_id: FileId(
@@ -368,6 +376,7 @@
             Test,
         ),
         is_proc_macro: false,
+        proc_macro_cwd: None,
     },
     9: CrateData {
         root_file_id: FileId(
@@ -399,6 +408,7 @@
             Other,
         ),
         is_proc_macro: false,
+        proc_macro_cwd: None,
     },
     10: CrateData {
         root_file_id: FileId(
@@ -417,6 +427,7 @@
         cfg_options: CfgOptions(
             [
                 "rust_analyzer",
+                "test",
                 "true",
             ],
         ),
@@ -473,5 +484,6 @@
             ),
         },
         is_proc_macro: false,
+        proc_macro_cwd: None,
     },
 }
\ No newline at end of file
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/Cargo.toml b/src/tools/rust-analyzer/crates/rust-analyzer/Cargo.toml
index c24cbb4a311..b8ce2b7430b 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/Cargo.toml
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/Cargo.toml
@@ -37,7 +37,7 @@ rustc-hash.workspace = true
 serde_json = { workspace = true, features = ["preserve_order"] }
 serde.workspace = true
 serde_derive.workspace = true
-tenthash = "0.4.0"
+tenthash = "1.0.0"
 num_cpus = "1.15.0"
 mimalloc = { version = "0.1.30", default-features = false, optional = true }
 lsp-server.workspace = true
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 18c27c84496..4fc6180920f 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
@@ -13,7 +13,7 @@ use hir::{
     ModuleDef, Name,
 };
 use hir_def::{
-    body::BodySourceMap,
+    expr_store::BodySourceMap,
     hir::{ExprId, PatId},
     SyntheticSyntax,
 };
@@ -1072,6 +1072,7 @@ impl flags::AnalysisStats {
                     param_names_for_lifetime_elision_hints: true,
                     hide_named_constructor_hints: false,
                     hide_closure_initialization_hints: false,
+                    hide_closure_parameter_hints: false,
                     closure_style: hir::ClosureStyle::ImplFn,
                     max_length: Some(25),
                     closing_brace_hints_min_lines: Some(20),
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/diagnostics.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/diagnostics.rs
index 28f25975d64..6a3ceb640b9 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/diagnostics.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/diagnostics.rs
@@ -4,7 +4,7 @@
 use project_model::{CargoConfig, RustLibSource};
 use rustc_hash::FxHashSet;
 
-use hir::{db::HirDatabase, Crate, HirFileIdExt, Module};
+use hir::{db::HirDatabase, sym, Crate, HirFileIdExt, Module};
 use ide::{AnalysisHost, AssistResolveStrategy, Diagnostic, DiagnosticsConfig, Severity};
 use ide_db::{base_db::SourceRootDatabase, LineIndexDatabase};
 use load_cargo::{load_workspace_at, LoadCargoConfig, ProcMacroServerChoice};
@@ -60,7 +60,7 @@ impl flags::Diagnostics {
             let file_id = module.definition_source_file_id(db).original_file(db);
             if !visited_files.contains(&file_id) {
                 let crate_name =
-                    module.krate().display_name(db).as_deref().unwrap_or("unknown").to_owned();
+                    module.krate().display_name(db).as_deref().unwrap_or(&sym::unknown).to_owned();
                 println!(
                     "processing crate: {crate_name}, module: {}",
                     _vfs.file_path(file_id.into())
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/rustc_tests.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/rustc_tests.rs
index 199f61e70f0..e9ca12deaf6 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/rustc_tests.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/rustc_tests.rs
@@ -12,8 +12,8 @@ use paths::Utf8PathBuf;
 use profile::StopWatch;
 use project_model::toolchain_info::{target_data_layout, QueryConfig};
 use project_model::{
-    CargoConfig, ManifestPath, ProjectWorkspace, ProjectWorkspaceKind, RustLibSource, Sysroot,
-    SysrootSourceWorkspaceConfig,
+    CargoConfig, ManifestPath, ProjectWorkspace, ProjectWorkspaceKind, RustLibSource,
+    RustSourceWorkspaceConfig, Sysroot,
 };
 
 use load_cargo::{load_workspace, LoadCargoConfig, ProcMacroServerChoice};
@@ -75,7 +75,11 @@ impl Tester {
         };
 
         let mut sysroot = Sysroot::discover(tmp_file.parent().unwrap(), &cargo_config.extra_env);
-        sysroot.load_workspace(&SysrootSourceWorkspaceConfig::default_cargo());
+        let loaded_sysroot = sysroot.load_workspace(&RustSourceWorkspaceConfig::default_cargo());
+        if let Some(loaded_sysroot) = loaded_sysroot {
+            sysroot.set_workspace(loaded_sysroot);
+        }
+
         let data_layout = target_data_layout::get(
             QueryConfig::Rustc(&sysroot, tmp_file.parent().unwrap().as_ref()),
             None,
@@ -86,7 +90,6 @@ impl Tester {
             kind: ProjectWorkspaceKind::DetachedFile {
                 file: ManifestPath::try_from(tmp_file).unwrap(),
                 cargo: None,
-                set_test: true,
             },
             sysroot,
             rustc_cfg: vec![],
@@ -94,6 +97,7 @@ impl Tester {
             target_layout: data_layout.map(Arc::from).map_err(|it| Arc::from(it.to_string())),
             cfg_overrides: Default::default(),
             extra_includes: vec![],
+            set_test: true,
         };
         let load_cargo_config = LoadCargoConfig {
             load_out_dirs_from_check: false,
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/scip.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/scip.rs
index dc0f722aae6..fe75872105a 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/scip.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/scip.rs
@@ -139,45 +139,42 @@ impl flags::Scip {
             let mut occurrences = Vec::new();
             let mut symbols = Vec::new();
 
-            tokens.into_iter().for_each(|(text_range, id)| {
+            for (text_range, id) in tokens.into_iter() {
                 let token = si.tokens.get(id).unwrap();
 
-                let (symbol, enclosing_symbol, is_inherent_impl) =
-                    if let Some(TokenSymbols { symbol, enclosing_symbol, is_inherent_impl }) =
-                        symbol_generator.token_symbols(id, token)
-                    {
-                        (symbol, enclosing_symbol, is_inherent_impl)
-                    } else {
-                        ("".to_owned(), None, false)
-                    };
+                let Some(TokenSymbols { symbol, enclosing_symbol, is_inherent_impl }) =
+                    symbol_generator.token_symbols(id, token)
+                else {
+                    // token did not have a moniker, so there is no reasonable occurrence to emit
+                    // see ide::moniker::def_to_moniker
+                    continue;
+                };
 
-                if !symbol.is_empty() {
-                    let is_defined_in_this_document = match token.definition {
-                        Some(def) => def.file_id == file_id,
-                        _ => false,
-                    };
-                    if is_defined_in_this_document {
-                        if token_ids_emitted.insert(id) {
-                            // token_ids_emitted does deduplication. This checks that this results
-                            // in unique emitted symbols, as otherwise references are ambiguous.
-                            let should_emit = record_error_if_symbol_already_used(
+                let is_defined_in_this_document = match token.definition {
+                    Some(def) => def.file_id == file_id,
+                    _ => false,
+                };
+                if is_defined_in_this_document {
+                    if token_ids_emitted.insert(id) {
+                        // token_ids_emitted does deduplication. This checks that this results
+                        // in unique emitted symbols, as otherwise references are ambiguous.
+                        let should_emit = record_error_if_symbol_already_used(
+                            symbol.clone(),
+                            is_inherent_impl,
+                            relative_path.as_str(),
+                            &line_index,
+                            text_range,
+                        );
+                        if should_emit {
+                            symbols.push(compute_symbol_info(
                                 symbol.clone(),
-                                is_inherent_impl,
-                                relative_path.as_str(),
-                                &line_index,
-                                text_range,
-                            );
-                            if should_emit {
-                                symbols.push(compute_symbol_info(
-                                    symbol.clone(),
-                                    enclosing_symbol,
-                                    token,
-                                ));
-                            }
+                                enclosing_symbol,
+                                token,
+                            ));
                         }
-                    } else {
-                        token_ids_referenced.insert(id);
                     }
+                } else {
+                    token_ids_referenced.insert(id);
                 }
 
                 // If the range of the def and the range of the token are the same, this must be the definition.
@@ -202,7 +199,7 @@ impl flags::Scip {
                     special_fields: Default::default(),
                     enclosing_range: Vec::new(),
                 });
-            });
+            }
 
             if occurrences.is_empty() {
                 continue;
@@ -444,14 +441,14 @@ impl SymbolGenerator {
                     MonikerResult::Moniker(moniker) => TokenSymbols {
                         symbol: scip::symbol::format_symbol(moniker_to_symbol(moniker)),
                         enclosing_symbol: None,
-                        is_inherent_impl: moniker
-                            .identifier
-                            .description
-                            .get(moniker.identifier.description.len() - 2)
-                            .is_some_and(|descriptor| {
+                        is_inherent_impl: match &moniker.identifier.description[..] {
+                            // inherent impls are represented as impl#[SelfType]
+                            [.., descriptor, _] => {
                                 descriptor.desc == MonikerDescriptorKind::Type
                                     && descriptor.name == "impl"
-                            }),
+                            }
+                            _ => false,
+                        },
                     },
                     MonikerResult::Local { enclosing_moniker } => {
                         let local_symbol = scip::types::Symbol::new_local(local_count);
@@ -549,7 +546,9 @@ mod test {
                 continue;
             }
             for &(range, id) in &file.tokens {
-                if range.contains(offset - TextSize::from(1)) {
+                // check if cursor is within token, ignoring token for the module defined by the file (whose range is the whole file)
+                if range.start() != TextSize::from(0) && range.contains(offset - TextSize::from(1))
+                {
                     let token = si.tokens.get(id).unwrap();
                     found_symbol = match token.moniker.as_ref() {
                         None => None,
@@ -885,7 +884,7 @@ pub mod example_mod {
         );
 
         let file = si.files.first().unwrap();
-        let (_, token_id) = file.tokens.first().unwrap();
+        let (_, token_id) = file.tokens.get(1).unwrap(); // first token is file module, second is `bar`
         let token = si.tokens.get(*token_id).unwrap();
 
         assert_eq!(token.documentation.as_ref().map(|d| d.as_str()), Some("foo"));
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/unresolved_references.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/unresolved_references.rs
index 986bd018b42..021b1bff393 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/unresolved_references.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/unresolved_references.rs
@@ -1,5 +1,5 @@
 //! Reports references in code that the IDE layer cannot resolve.
-use hir::{db::HirDatabase, AnyDiagnostic, Crate, HirFileIdExt as _, Module, Semantics};
+use hir::{db::HirDatabase, sym, AnyDiagnostic, Crate, HirFileIdExt as _, Module, Semantics};
 use ide::{AnalysisHost, RootDatabase, TextRange};
 use ide_db::{
     base_db::{SourceDatabase, SourceRootDatabase},
@@ -66,7 +66,7 @@ impl flags::UnresolvedReferences {
             let file_id = module.definition_source_file_id(db).original_file(db);
             if !visited_files.contains(&file_id) {
                 let crate_name =
-                    module.krate().display_name(db).as_deref().unwrap_or("unknown").to_owned();
+                    module.krate().display_name(db).as_deref().unwrap_or(&sym::unknown).to_owned();
                 let file_path = vfs.file_path(file_id.into());
                 eprintln!("processing crate: {crate_name}, module: {file_path}",);
 
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 44325fa1a29..1dce0bea1a9 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs
@@ -84,10 +84,10 @@ config_data! {
         completion_snippets_custom: FxHashMap<String, SnippetDef> = Config::completion_snippets_default(),
 
 
-        /// These directories will be ignored by rust-analyzer. They are
+        /// These paths (file/directories) will be ignored by rust-analyzer. They are
         /// relative to the workspace root, and globs are not supported. You may
         /// also need to add the folders to Code's `files.watcherExclude`.
-        files_excludeDirs: Vec<Utf8PathBuf> = vec![],
+        files_exclude | files_excludeDirs: Vec<Utf8PathBuf> = vec![],
 
 
 
@@ -208,6 +208,8 @@ config_data! {
         /// Whether to hide inlay type hints for `let` statements that initialize to a closure.
         /// Only applies to closures with blocks, same as `#rust-analyzer.inlayHints.closureReturnTypeHints.enable#`.
         inlayHints_typeHints_hideClosureInitialization: bool       = false,
+        /// Whether to hide inlay parameter type hints for closures.
+        inlayHints_typeHints_hideClosureParameter:bool             = false,
         /// Whether to hide inlay type hints for constructors.
         inlayHints_typeHints_hideNamedConstructor: bool            = false,
 
@@ -528,7 +530,7 @@ config_data! {
         imports_granularity_enforce: bool              = false,
         /// How imports should be grouped into use statements.
         imports_granularity_group: ImportGranularityDef  = ImportGranularityDef::Crate,
-        /// Group inserted imports by the https://rust-analyzer.github.io/manual.html#auto-import[following order]. Groups are separated by newlines.
+        /// Group inserted imports by the [following order](https://rust-analyzer.github.io/manual.html#auto-import). Groups are separated by newlines.
         imports_group_enable: bool                           = true,
         /// Whether to allow import insertion to merge new imports into single path glob imports like `use std::fmt::*;`.
         imports_merge_glob: bool           = true,
@@ -1474,6 +1476,7 @@ impl Config {
             prefer_absolute: self.imports_prefixExternPrelude(source_root).to_owned(),
             term_search_fuel: self.assist_termSearch_fuel(source_root).to_owned() as u64,
             term_search_borrowck: self.assist_termSearch_borrowcheck(source_root).to_owned(),
+            code_action_grouping: self.code_action_group(),
         }
     }
 
@@ -1666,6 +1669,9 @@ impl Config {
             hide_closure_initialization_hints: self
                 .inlayHints_typeHints_hideClosureInitialization()
                 .to_owned(),
+            hide_closure_parameter_hints: self
+                .inlayHints_typeHints_hideClosureParameter()
+                .to_owned(),
             closure_style: match self.inlayHints_closureStyle() {
                 ClosureStyle::ImplFn => hir::ClosureStyle::ImplFn,
                 ClosureStyle::RustAnalyzer => hir::ClosureStyle::RANotation,
@@ -1787,7 +1793,7 @@ impl Config {
 
     fn discovered_projects(&self) -> Vec<ManifestOrProjectJson> {
         let exclude_dirs: Vec<_> =
-            self.files_excludeDirs().iter().map(|p| self.root_path.join(p)).collect();
+            self.files_exclude().iter().map(|p| self.root_path.join(p)).collect();
 
         let mut projects = vec![];
         for fs_proj in &self.discovered_projects_from_filesystem {
@@ -1909,10 +1915,14 @@ impl Config {
                 }
                 _ => FilesWatcher::Server,
             },
-            exclude: self.files_excludeDirs().iter().map(|it| self.root_path.join(it)).collect(),
+            exclude: self.excluded().collect(),
         }
     }
 
+    pub fn excluded(&self) -> impl Iterator<Item = AbsPathBuf> + use<'_> {
+        self.files_exclude().iter().map(|it| self.root_path.join(it))
+    }
+
     pub fn notifications(&self) -> NotificationsConfig {
         NotificationsConfig {
             cargo_toml_not_found: self.notifications_cargoTomlNotFound().to_owned(),
@@ -3624,21 +3634,9 @@ fn manual(fields: &[SchemaField]) -> String {
         let name = format!("rust-analyzer.{}", field.replace('_', "."));
         let doc = doc_comment_to_string(doc);
         if default.contains('\n') {
-            format_to_acc!(
-                acc,
-                r#"[[{name}]]{name}::
-+
---
-Default:
-----
-{default}
-----
-{doc}
---
-"#
-            )
+            format_to_acc!(acc, " **{name}**\n\nDefault:\n\n```{default}\n\n```\n\n {doc}\n\n ")
         } else {
-            format_to_acc!(acc, "[[{name}]]{name} (default: `{default}`)::\n+\n--\n{doc}--\n")
+            format_to_acc!(acc, "**{name}** (default: {default})\n\n {doc}\n\n")
         }
     })
 }
@@ -3716,7 +3714,7 @@ mod tests {
 
     #[test]
     fn generate_config_documentation() {
-        let docs_path = project_root().join("docs/user/generated_config.adoc");
+        let docs_path = project_root().join("docs/book/src/configuration_generated.md");
         let expected = FullConfigInput::manual();
         ensure_file_contents(docs_path.as_std_path(), &expected);
     }
@@ -3805,8 +3803,10 @@ mod tests {
         (config, _, _) = config.apply_change(change);
 
         assert_eq!(config.cargo_targetDir(None), &Some(TargetDirectory::UseSubdirectory(true)));
+        let target =
+            Utf8PathBuf::from(std::env::var("CARGO_TARGET_DIR").unwrap_or("target".to_owned()));
         assert!(
-            matches!(config.flycheck(None), FlycheckConfig::CargoCommand { options, .. } if options.target_dir == Some(Utf8PathBuf::from("target/rust-analyzer")))
+            matches!(config.flycheck(None), FlycheckConfig::CargoCommand { options, .. } if options.target_dir == Some(target.join("rust-analyzer")))
         );
     }
 
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 0f2d7823b7e..70105cda006 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
@@ -396,6 +396,7 @@ impl GlobalState {
             || !self.config.same_source_root_parent_map(&self.local_roots_parent_map)
         {
             let config_change = {
+                let _p = span!(Level::INFO, "GlobalState::process_changes/config_change").entered();
                 let user_config_path = (|| {
                     let mut p = Config::user_config_dir_path()?;
                     p.push("rust-analyzer.toml");
@@ -569,12 +570,12 @@ impl GlobalState {
         if let Some((method, start)) = self.req_queue.incoming.complete(&response.id) {
             if let Some(err) = &response.error {
                 if err.message.starts_with("server panicked") {
-                    self.poke_rust_analyzer_developer(format!("{}, check the log", err.message))
+                    self.poke_rust_analyzer_developer(format!("{}, check the log", err.message));
                 }
             }
 
             let duration = start.elapsed();
-            tracing::debug!("handled {} - ({}) in {:0.2?}", method, response.id, duration);
+            tracing::debug!(name: "message response", method, %response.id, duration = format_args!("{:0.2?}", duration));
             self.send(response.into());
         }
     }
@@ -649,7 +650,8 @@ impl GlobalStateSnapshot {
         RwLockReadGuard::map(self.vfs.read(), |(it, _)| it)
     }
 
-    pub(crate) fn url_to_file_id(&self, url: &Url) -> anyhow::Result<FileId> {
+    /// Returns `None` if the file was excluded.
+    pub(crate) fn url_to_file_id(&self, url: &Url) -> anyhow::Result<Option<FileId>> {
         url_to_file_id(&self.vfs_read(), url)
     }
 
@@ -657,7 +659,8 @@ impl GlobalStateSnapshot {
         file_id_to_url(&self.vfs_read(), id)
     }
 
-    pub(crate) fn vfs_path_to_file_id(&self, vfs_path: &VfsPath) -> anyhow::Result<FileId> {
+    /// Returns `None` if the file was excluded.
+    pub(crate) fn vfs_path_to_file_id(&self, vfs_path: &VfsPath) -> anyhow::Result<Option<FileId>> {
         vfs_path_to_file_id(&self.vfs_read(), vfs_path)
     }
 
@@ -749,14 +752,21 @@ pub(crate) fn file_id_to_url(vfs: &vfs::Vfs, id: FileId) -> Url {
     url_from_abs_path(path)
 }
 
-pub(crate) fn url_to_file_id(vfs: &vfs::Vfs, url: &Url) -> anyhow::Result<FileId> {
+/// Returns `None` if the file was excluded.
+pub(crate) fn url_to_file_id(vfs: &vfs::Vfs, url: &Url) -> anyhow::Result<Option<FileId>> {
     let path = from_proto::vfs_path(url)?;
-    let res = vfs.file_id(&path).ok_or_else(|| anyhow::format_err!("file not found: {path}"))?;
-    Ok(res)
+    vfs_path_to_file_id(vfs, &path)
 }
 
-pub(crate) fn vfs_path_to_file_id(vfs: &vfs::Vfs, vfs_path: &VfsPath) -> anyhow::Result<FileId> {
-    let res =
+/// Returns `None` if the file was excluded.
+pub(crate) fn vfs_path_to_file_id(
+    vfs: &vfs::Vfs,
+    vfs_path: &VfsPath,
+) -> anyhow::Result<Option<FileId>> {
+    let (file_id, excluded) =
         vfs.file_id(vfs_path).ok_or_else(|| anyhow::format_err!("file not found: {vfs_path}"))?;
-    Ok(res)
+    match excluded {
+        vfs::FileExcluded::Yes => Ok(None),
+        vfs::FileExcluded::No => Ok(Some(file_id)),
+    }
 }
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/dispatch.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/dispatch.rs
index ff50f7533a6..4683877db69 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/dispatch.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/dispatch.rs
@@ -118,7 +118,7 @@ impl RequestDispatcher<'_> {
             }
             return self;
         }
-        self.on_with_thread_intent::<true, ALLOW_RETRYING, R>(
+        self.on_with_thread_intent::<false, ALLOW_RETRYING, R>(
             ThreadIntent::Worker,
             f,
             Self::content_modified_error,
@@ -147,7 +147,7 @@ impl RequestDispatcher<'_> {
             }
             return self;
         }
-        self.on_with_thread_intent::<true, false, R>(ThreadIntent::Worker, f, on_cancelled)
+        self.on_with_thread_intent::<false, false, R>(ThreadIntent::Worker, f, on_cancelled)
     }
 
     /// Dispatches a non-latency-sensitive request onto the thread pool. When the VFS is marked not
@@ -166,7 +166,7 @@ impl RequestDispatcher<'_> {
             }
             return self;
         }
-        self.on_with_thread_intent::<true, ALLOW_RETRYING, R>(
+        self.on_with_thread_intent::<false, ALLOW_RETRYING, R>(
             ThreadIntent::Worker,
             f,
             Self::content_modified_error,
@@ -193,7 +193,7 @@ impl RequestDispatcher<'_> {
             }
             return self;
         }
-        self.on_with_thread_intent::<true, ALLOW_RETRYING, R>(
+        self.on_with_thread_intent::<false, ALLOW_RETRYING, R>(
             ThreadIntent::LatencySensitive,
             f,
             Self::content_modified_error,
@@ -212,7 +212,7 @@ impl RequestDispatcher<'_> {
         R::Params: DeserializeOwned + panic::UnwindSafe + Send + fmt::Debug,
         R::Result: Serialize,
     {
-        self.on_with_thread_intent::<false, false, R>(
+        self.on_with_thread_intent::<true, false, R>(
             ThreadIntent::LatencySensitive,
             f,
             Self::content_modified_error,
@@ -231,7 +231,7 @@ impl RequestDispatcher<'_> {
         }
     }
 
-    fn on_with_thread_intent<const MAIN_POOL: bool, const ALLOW_RETRYING: bool, R>(
+    fn on_with_thread_intent<const RUSTFMT: bool, const ALLOW_RETRYING: bool, R>(
         &mut self,
         intent: ThreadIntent,
         f: fn(GlobalStateSnapshot, R::Params) -> anyhow::Result<R::Result>,
@@ -251,10 +251,10 @@ impl RequestDispatcher<'_> {
         tracing::debug!(?params);
 
         let world = self.global_state.snapshot();
-        if MAIN_POOL {
-            &mut self.global_state.task_pool.handle
-        } else {
+        if RUSTFMT {
             &mut self.global_state.fmt_pool.handle
+        } else {
+            &mut self.global_state.task_pool.handle
         }
         .spawn(intent, move || {
             let result = panic::catch_unwind(move || {
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/notification.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/notification.rs
index 84ba89d9f31..55344a4d6ac 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/notification.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/notification.rs
@@ -22,6 +22,7 @@ use crate::{
     mem_docs::DocumentData,
     reload,
     target_spec::TargetSpec,
+    try_default,
 };
 
 pub(crate) fn handle_cancel(state: &mut GlobalState, params: CancelParams) -> anyhow::Result<()> {
@@ -74,7 +75,16 @@ pub(crate) fn handle_did_open_text_document(
             tracing::error!("duplicate DidOpenTextDocument: {}", path);
         }
 
-        state.vfs.write().0.set_file_contents(path, Some(params.text_document.text.into_bytes()));
+        if let Some(abs_path) = path.as_path() {
+            if state.config.excluded().any(|excluded| abs_path.starts_with(&excluded)) {
+                tracing::trace!("opened excluded file {abs_path}");
+                state.vfs.write().0.insert_excluded_file(path);
+                return Ok(());
+            }
+        }
+
+        let contents = params.text_document.text.into_bytes();
+        state.vfs.write().0.set_file_contents(path, Some(contents));
         if state.config.discover_workspace_config().is_some() {
             tracing::debug!("queuing task");
             let _ = state
@@ -126,7 +136,8 @@ pub(crate) fn handle_did_close_text_document(
             tracing::error!("orphan DidCloseTextDocument: {}", path);
         }
 
-        if let Some(file_id) = state.vfs.read().0.file_id(&path) {
+        // Clear diagnostics also for excluded files, just in case.
+        if let Some((file_id, _)) = state.vfs.read().0.file_id(&path) {
             state.diagnostics.clear_native_for(file_id);
         }
 
@@ -145,7 +156,7 @@ pub(crate) fn handle_did_save_text_document(
 ) -> anyhow::Result<()> {
     if let Ok(vfs_path) = from_proto::vfs_path(&params.text_document.uri) {
         let snap = state.snapshot();
-        let file_id = snap.vfs_path_to_file_id(&vfs_path)?;
+        let file_id = try_default!(snap.vfs_path_to_file_id(&vfs_path)?);
         let sr = snap.analysis.source_root_id(file_id)?;
 
         if state.config.script_rebuild_on_save(Some(sr)) && state.build_deps_changed {
@@ -289,7 +300,7 @@ fn run_flycheck(state: &mut GlobalState, vfs_path: VfsPath) -> bool {
     let _p = tracing::info_span!("run_flycheck").entered();
 
     let file_id = state.vfs.read().0.file_id(&vfs_path);
-    if let Some(file_id) = file_id {
+    if let Some((file_id, vfs::FileExcluded::No)) = file_id {
         let world = state.snapshot();
         let invocation_strategy_once = state.config.flycheck(None).invocation_strategy_once();
         let may_flycheck_workspace = state.config.flycheck_workspace(None);
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 39cbf53eaa2..1b144d90732 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
@@ -53,6 +53,7 @@ use crate::{
     },
     target_spec::{CargoTargetSpec, TargetSpec},
     test_runner::{CargoTestHandle, TestTarget},
+    try_default,
 };
 
 pub(crate) fn handle_workspace_reload(state: &mut GlobalState, _: ()) -> anyhow::Result<()> {
@@ -83,7 +84,8 @@ pub(crate) fn handle_analyzer_status(
     let mut file_id = None;
     if let Some(tdi) = params.text_document {
         match from_proto::file_id(&snap, &tdi.uri) {
-            Ok(it) => file_id = Some(it),
+            Ok(Some(it)) => file_id = Some(it),
+            Ok(None) => {}
             Err(_) => format_to!(buf, "file {} not found in vfs", tdi.uri),
         }
     }
@@ -141,7 +143,7 @@ pub(crate) fn handle_view_syntax_tree(
     params: lsp_ext::ViewSyntaxTreeParams,
 ) -> anyhow::Result<String> {
     let _p = tracing::info_span!("handle_view_syntax_tree").entered();
-    let id = from_proto::file_id(&snap, &params.text_document.uri)?;
+    let id = try_default!(from_proto::file_id(&snap, &params.text_document.uri)?);
     let res = snap.analysis.view_syntax_tree(id)?;
     Ok(res)
 }
@@ -151,7 +153,7 @@ pub(crate) fn handle_view_hir(
     params: lsp_types::TextDocumentPositionParams,
 ) -> anyhow::Result<String> {
     let _p = tracing::info_span!("handle_view_hir").entered();
-    let position = from_proto::file_position(&snap, params)?;
+    let position = try_default!(from_proto::file_position(&snap, params)?);
     let res = snap.analysis.view_hir(position)?;
     Ok(res)
 }
@@ -161,7 +163,7 @@ pub(crate) fn handle_view_mir(
     params: lsp_types::TextDocumentPositionParams,
 ) -> anyhow::Result<String> {
     let _p = tracing::info_span!("handle_view_mir").entered();
-    let position = from_proto::file_position(&snap, params)?;
+    let position = try_default!(from_proto::file_position(&snap, params)?);
     let res = snap.analysis.view_mir(position)?;
     Ok(res)
 }
@@ -171,7 +173,7 @@ pub(crate) fn handle_interpret_function(
     params: lsp_types::TextDocumentPositionParams,
 ) -> anyhow::Result<String> {
     let _p = tracing::info_span!("handle_interpret_function").entered();
-    let position = from_proto::file_position(&snap, params)?;
+    let position = try_default!(from_proto::file_position(&snap, params)?);
     let res = snap.analysis.interpret_function(position)?;
     Ok(res)
 }
@@ -180,7 +182,7 @@ pub(crate) fn handle_view_file_text(
     snap: GlobalStateSnapshot,
     params: lsp_types::TextDocumentIdentifier,
 ) -> anyhow::Result<String> {
-    let file_id = from_proto::file_id(&snap, &params.uri)?;
+    let file_id = try_default!(from_proto::file_id(&snap, &params.uri)?);
     Ok(snap.analysis.file_text(file_id)?.to_string())
 }
 
@@ -189,7 +191,7 @@ pub(crate) fn handle_view_item_tree(
     params: lsp_ext::ViewItemTreeParams,
 ) -> anyhow::Result<String> {
     let _p = tracing::info_span!("handle_view_item_tree").entered();
-    let file_id = from_proto::file_id(&snap, &params.text_document.uri)?;
+    let file_id = try_default!(from_proto::file_id(&snap, &params.text_document.uri)?);
     let res = snap.analysis.view_item_tree(file_id)?;
     Ok(res)
 }
@@ -315,7 +317,7 @@ pub(crate) fn handle_expand_macro(
     params: lsp_ext::ExpandMacroParams,
 ) -> anyhow::Result<Option<lsp_ext::ExpandedMacro>> {
     let _p = tracing::info_span!("handle_expand_macro").entered();
-    let file_id = from_proto::file_id(&snap, &params.text_document.uri)?;
+    let file_id = try_default!(from_proto::file_id(&snap, &params.text_document.uri)?);
     let line_index = snap.file_line_index(file_id)?;
     let offset = from_proto::offset(&line_index, params.position)?;
 
@@ -328,7 +330,7 @@ pub(crate) fn handle_selection_range(
     params: lsp_types::SelectionRangeParams,
 ) -> anyhow::Result<Option<Vec<lsp_types::SelectionRange>>> {
     let _p = tracing::info_span!("handle_selection_range").entered();
-    let file_id = from_proto::file_id(&snap, &params.text_document.uri)?;
+    let file_id = try_default!(from_proto::file_id(&snap, &params.text_document.uri)?);
     let line_index = snap.file_line_index(file_id)?;
     let res: anyhow::Result<Vec<lsp_types::SelectionRange>> = params
         .positions
@@ -371,7 +373,7 @@ pub(crate) fn handle_matching_brace(
     params: lsp_ext::MatchingBraceParams,
 ) -> anyhow::Result<Vec<Position>> {
     let _p = tracing::info_span!("handle_matching_brace").entered();
-    let file_id = from_proto::file_id(&snap, &params.text_document.uri)?;
+    let file_id = try_default!(from_proto::file_id(&snap, &params.text_document.uri)?);
     let line_index = snap.file_line_index(file_id)?;
     params
         .positions
@@ -395,7 +397,7 @@ pub(crate) fn handle_join_lines(
 ) -> anyhow::Result<Vec<lsp_types::TextEdit>> {
     let _p = tracing::info_span!("handle_join_lines").entered();
 
-    let file_id = from_proto::file_id(&snap, &params.text_document.uri)?;
+    let file_id = try_default!(from_proto::file_id(&snap, &params.text_document.uri)?);
     let config = snap.config.join_lines();
     let line_index = snap.file_line_index(file_id)?;
 
@@ -419,7 +421,7 @@ pub(crate) fn handle_on_enter(
     params: lsp_types::TextDocumentPositionParams,
 ) -> anyhow::Result<Option<Vec<lsp_ext::SnippetTextEdit>>> {
     let _p = tracing::info_span!("handle_on_enter").entered();
-    let position = from_proto::file_position(&snap, params)?;
+    let position = try_default!(from_proto::file_position(&snap, params)?);
     let edit = match snap.analysis.on_enter(position)? {
         None => return Ok(None),
         Some(it) => it,
@@ -439,7 +441,8 @@ pub(crate) fn handle_on_type_formatting(
         return Ok(None);
     }
 
-    let mut position = from_proto::file_position(&snap, params.text_document_position)?;
+    let mut position =
+        try_default!(from_proto::file_position(&snap, params.text_document_position)?);
     let line_index = snap.file_line_index(position.file_id)?;
 
     // in `ide`, the `on_type` invariant is that
@@ -465,32 +468,33 @@ pub(crate) fn handle_on_type_formatting(
     Ok(Some(change))
 }
 
+pub(crate) fn empty_diagnostic_report() -> lsp_types::DocumentDiagnosticReportResult {
+    lsp_types::DocumentDiagnosticReportResult::Report(lsp_types::DocumentDiagnosticReport::Full(
+        lsp_types::RelatedFullDocumentDiagnosticReport {
+            related_documents: None,
+            full_document_diagnostic_report: lsp_types::FullDocumentDiagnosticReport {
+                result_id: Some("rust-analyzer".to_owned()),
+                items: vec![],
+            },
+        },
+    ))
+}
+
 pub(crate) fn handle_document_diagnostics(
     snap: GlobalStateSnapshot,
     params: lsp_types::DocumentDiagnosticParams,
 ) -> anyhow::Result<lsp_types::DocumentDiagnosticReportResult> {
-    let empty = || {
-        lsp_types::DocumentDiagnosticReportResult::Report(
-            lsp_types::DocumentDiagnosticReport::Full(
-                lsp_types::RelatedFullDocumentDiagnosticReport {
-                    related_documents: None,
-                    full_document_diagnostic_report: lsp_types::FullDocumentDiagnosticReport {
-                        result_id: Some("rust-analyzer".to_owned()),
-                        items: vec![],
-                    },
-                },
-            ),
-        )
+    let file_id = match from_proto::file_id(&snap, &params.text_document.uri)? {
+        Some(it) => it,
+        None => return Ok(empty_diagnostic_report()),
     };
-
-    let file_id = from_proto::file_id(&snap, &params.text_document.uri)?;
     let source_root = snap.analysis.source_root_id(file_id)?;
     if !snap.analysis.is_local_source_root(source_root)? {
-        return Ok(empty());
+        return Ok(empty_diagnostic_report());
     }
     let config = snap.config.diagnostics(Some(source_root));
     if !config.enabled {
-        return Ok(empty());
+        return Ok(empty_diagnostic_report());
     }
     let line_index = snap.file_line_index(file_id)?;
     let supports_related = snap.config.text_document_diagnostic_related_document_support();
@@ -546,7 +550,7 @@ pub(crate) fn handle_document_symbol(
     params: lsp_types::DocumentSymbolParams,
 ) -> anyhow::Result<Option<lsp_types::DocumentSymbolResponse>> {
     let _p = tracing::info_span!("handle_document_symbol").entered();
-    let file_id = from_proto::file_id(&snap, &params.text_document.uri)?;
+    let file_id = try_default!(from_proto::file_id(&snap, &params.text_document.uri)?);
     let line_index = snap.file_line_index(file_id)?;
 
     let mut parents: Vec<(lsp_types::DocumentSymbol, Option<usize>)> = Vec::new();
@@ -760,7 +764,7 @@ pub(crate) fn handle_will_rename_files(
             }
         })
         .filter_map(|(file_id, new_name)| {
-            snap.analysis.will_rename_file(file_id, &new_name).ok()?
+            snap.analysis.will_rename_file(file_id?, &new_name).ok()?
         })
         .collect();
 
@@ -782,7 +786,8 @@ pub(crate) fn handle_goto_definition(
     params: lsp_types::GotoDefinitionParams,
 ) -> anyhow::Result<Option<lsp_types::GotoDefinitionResponse>> {
     let _p = tracing::info_span!("handle_goto_definition").entered();
-    let position = from_proto::file_position(&snap, params.text_document_position_params)?;
+    let position =
+        try_default!(from_proto::file_position(&snap, params.text_document_position_params)?);
     let nav_info = match snap.analysis.goto_definition(position)? {
         None => return Ok(None),
         Some(it) => it,
@@ -797,7 +802,10 @@ pub(crate) fn handle_goto_declaration(
     params: lsp_types::request::GotoDeclarationParams,
 ) -> anyhow::Result<Option<lsp_types::request::GotoDeclarationResponse>> {
     let _p = tracing::info_span!("handle_goto_declaration").entered();
-    let position = from_proto::file_position(&snap, params.text_document_position_params.clone())?;
+    let position = try_default!(from_proto::file_position(
+        &snap,
+        params.text_document_position_params.clone()
+    )?);
     let nav_info = match snap.analysis.goto_declaration(position)? {
         None => return handle_goto_definition(snap, params),
         Some(it) => it,
@@ -812,7 +820,8 @@ pub(crate) fn handle_goto_implementation(
     params: lsp_types::request::GotoImplementationParams,
 ) -> anyhow::Result<Option<lsp_types::request::GotoImplementationResponse>> {
     let _p = tracing::info_span!("handle_goto_implementation").entered();
-    let position = from_proto::file_position(&snap, params.text_document_position_params)?;
+    let position =
+        try_default!(from_proto::file_position(&snap, params.text_document_position_params)?);
     let nav_info = match snap.analysis.goto_implementation(position)? {
         None => return Ok(None),
         Some(it) => it,
@@ -827,7 +836,8 @@ pub(crate) fn handle_goto_type_definition(
     params: lsp_types::request::GotoTypeDefinitionParams,
 ) -> anyhow::Result<Option<lsp_types::request::GotoTypeDefinitionResponse>> {
     let _p = tracing::info_span!("handle_goto_type_definition").entered();
-    let position = from_proto::file_position(&snap, params.text_document_position_params)?;
+    let position =
+        try_default!(from_proto::file_position(&snap, params.text_document_position_params)?);
     let nav_info = match snap.analysis.goto_type_definition(position)? {
         None => return Ok(None),
         Some(it) => it,
@@ -880,7 +890,7 @@ pub(crate) fn handle_parent_module(
         }
 
         // check if invoked at the crate root
-        let file_id = from_proto::file_id(&snap, &params.text_document.uri)?;
+        let file_id = try_default!(from_proto::file_id(&snap, &params.text_document.uri)?);
         let crate_id = match snap.analysis.crates_for(file_id)?.first() {
             Some(&crate_id) => crate_id,
             None => return Ok(None),
@@ -904,7 +914,7 @@ pub(crate) fn handle_parent_module(
     }
 
     // locate parent module by semantics
-    let position = from_proto::file_position(&snap, params)?;
+    let position = try_default!(from_proto::file_position(&snap, params)?);
     let navs = snap.analysis.parent_module(position)?;
     let res = to_proto::goto_definition_response(&snap, None, navs)?;
     Ok(Some(res))
@@ -915,7 +925,7 @@ pub(crate) fn handle_runnables(
     params: lsp_ext::RunnablesParams,
 ) -> anyhow::Result<Vec<lsp_ext::Runnable>> {
     let _p = tracing::info_span!("handle_runnables").entered();
-    let file_id = from_proto::file_id(&snap, &params.text_document.uri)?;
+    let file_id = try_default!(from_proto::file_id(&snap, &params.text_document.uri)?);
     let source_root = snap.analysis.source_root_id(file_id).ok();
     let line_index = snap.file_line_index(file_id)?;
     let offset = params.position.and_then(|it| from_proto::offset(&line_index, it).ok());
@@ -1035,7 +1045,7 @@ pub(crate) fn handle_related_tests(
     params: lsp_types::TextDocumentPositionParams,
 ) -> anyhow::Result<Vec<lsp_ext::TestInfo>> {
     let _p = tracing::info_span!("handle_related_tests").entered();
-    let position = from_proto::file_position(&snap, params)?;
+    let position = try_default!(from_proto::file_position(&snap, params)?);
 
     let tests = snap.analysis.related_tests(position, None)?;
     let mut res = Vec::new();
@@ -1053,7 +1063,8 @@ pub(crate) fn handle_completion(
     lsp_types::CompletionParams { text_document_position, context,.. }: lsp_types::CompletionParams,
 ) -> anyhow::Result<Option<lsp_types::CompletionResponse>> {
     let _p = tracing::info_span!("handle_completion").entered();
-    let mut position = from_proto::file_position(&snap, text_document_position.clone())?;
+    let mut position =
+        try_default!(from_proto::file_position(&snap, text_document_position.clone())?);
     let line_index = snap.file_line_index(position.file_id)?;
     let completion_trigger_character =
         context.and_then(|ctx| ctx.trigger_character).and_then(|s| s.chars().next());
@@ -1102,7 +1113,8 @@ pub(crate) fn handle_completion_resolve(
 
     let resolve_data: lsp_ext::CompletionResolveData = serde_json::from_value(data)?;
 
-    let file_id = from_proto::file_id(&snap, &resolve_data.position.text_document.uri)?;
+    let file_id = from_proto::file_id(&snap, &resolve_data.position.text_document.uri)?
+        .expect("we never provide completions for excluded files");
     let line_index = snap.file_line_index(file_id)?;
     // FIXME: We should fix up the position when retrying the cancelled request instead
     let Ok(offset) = from_proto::offset(&line_index, resolve_data.position.position) else {
@@ -1185,7 +1197,7 @@ pub(crate) fn handle_folding_range(
     params: FoldingRangeParams,
 ) -> anyhow::Result<Option<Vec<FoldingRange>>> {
     let _p = tracing::info_span!("handle_folding_range").entered();
-    let file_id = from_proto::file_id(&snap, &params.text_document.uri)?;
+    let file_id = try_default!(from_proto::file_id(&snap, &params.text_document.uri)?);
     let folds = snap.analysis.folding_ranges(file_id)?;
     let text = snap.analysis.file_text(file_id)?;
     let line_index = snap.file_line_index(file_id)?;
@@ -1202,7 +1214,8 @@ pub(crate) fn handle_signature_help(
     params: lsp_types::SignatureHelpParams,
 ) -> anyhow::Result<Option<lsp_types::SignatureHelp>> {
     let _p = tracing::info_span!("handle_signature_help").entered();
-    let position = from_proto::file_position(&snap, params.text_document_position_params)?;
+    let position =
+        try_default!(from_proto::file_position(&snap, params.text_document_position_params)?);
     let help = match snap.analysis.signature_help(position)? {
         Some(it) => it,
         None => return Ok(None),
@@ -1221,7 +1234,7 @@ pub(crate) fn handle_hover(
         PositionOrRange::Position(position) => Range::new(position, position),
         PositionOrRange::Range(range) => range,
     };
-    let file_range = from_proto::file_range(&snap, &params.text_document, range)?;
+    let file_range = try_default!(from_proto::file_range(&snap, &params.text_document, range)?);
 
     let hover = snap.config.hover();
     let info = match snap.analysis.hover(&hover, file_range)? {
@@ -1255,7 +1268,7 @@ pub(crate) fn handle_prepare_rename(
     params: lsp_types::TextDocumentPositionParams,
 ) -> anyhow::Result<Option<PrepareRenameResponse>> {
     let _p = tracing::info_span!("handle_prepare_rename").entered();
-    let position = from_proto::file_position(&snap, params)?;
+    let position = try_default!(from_proto::file_position(&snap, params)?);
 
     let change = snap.analysis.prepare_rename(position)?.map_err(to_proto::rename_error)?;
 
@@ -1269,7 +1282,7 @@ pub(crate) fn handle_rename(
     params: RenameParams,
 ) -> anyhow::Result<Option<WorkspaceEdit>> {
     let _p = tracing::info_span!("handle_rename").entered();
-    let position = from_proto::file_position(&snap, params.text_document_position)?;
+    let position = try_default!(from_proto::file_position(&snap, params.text_document_position)?);
 
     let mut change =
         snap.analysis.rename(position, &params.new_name)?.map_err(to_proto::rename_error)?;
@@ -1304,7 +1317,7 @@ pub(crate) fn handle_references(
     params: lsp_types::ReferenceParams,
 ) -> anyhow::Result<Option<Vec<Location>>> {
     let _p = tracing::info_span!("handle_references").entered();
-    let position = from_proto::file_position(&snap, params.text_document_position)?;
+    let position = try_default!(from_proto::file_position(&snap, params.text_document_position)?);
 
     let exclude_imports = snap.config.find_all_refs_exclude_imports();
     let exclude_tests = snap.config.find_all_refs_exclude_tests();
@@ -1375,9 +1388,9 @@ pub(crate) fn handle_code_action(
         return Ok(None);
     }
 
-    let file_id = from_proto::file_id(&snap, &params.text_document.uri)?;
+    let file_id = try_default!(from_proto::file_id(&snap, &params.text_document.uri)?);
     let line_index = snap.file_line_index(file_id)?;
-    let frange = from_proto::file_range(&snap, &params.text_document, params.range)?;
+    let frange = try_default!(from_proto::file_range(&snap, &params.text_document, params.range)?);
     let source_root = snap.analysis.source_root_id(file_id)?;
 
     let mut assists_config = snap.config.assist(Some(source_root));
@@ -1455,7 +1468,8 @@ pub(crate) fn handle_code_action_resolve(
         return Err(invalid_params_error("code action without data".to_owned()).into());
     };
 
-    let file_id = from_proto::file_id(&snap, &params.code_action_params.text_document.uri)?;
+    let file_id = from_proto::file_id(&snap, &params.code_action_params.text_document.uri)?
+        .expect("we never provide code actions for excluded files");
     if snap.file_version(file_id) != params.version {
         return Err(invalid_params_error("stale code action".to_owned()).into());
     }
@@ -1551,7 +1565,7 @@ pub(crate) fn handle_code_lens(
         return Ok(Some(Vec::default()));
     }
 
-    let file_id = from_proto::file_id(&snap, &params.text_document.uri)?;
+    let file_id = try_default!(from_proto::file_id(&snap, &params.text_document.uri)?);
     let target_spec = TargetSpec::for_file(&snap, file_id)?;
 
     let annotations = snap.analysis.annotations(
@@ -1613,7 +1627,8 @@ pub(crate) fn handle_document_highlight(
     params: lsp_types::DocumentHighlightParams,
 ) -> anyhow::Result<Option<Vec<lsp_types::DocumentHighlight>>> {
     let _p = tracing::info_span!("handle_document_highlight").entered();
-    let position = from_proto::file_position(&snap, params.text_document_position_params)?;
+    let position =
+        try_default!(from_proto::file_position(&snap, params.text_document_position_params)?);
     let line_index = snap.file_line_index(position.file_id)?;
     let source_root = snap.analysis.source_root_id(position.file_id)?;
 
@@ -1639,12 +1654,12 @@ pub(crate) fn handle_ssr(
     params: lsp_ext::SsrParams,
 ) -> anyhow::Result<lsp_types::WorkspaceEdit> {
     let _p = tracing::info_span!("handle_ssr").entered();
-    let selections = params
+    let selections = try_default!(params
         .selections
         .iter()
         .map(|range| from_proto::file_range(&snap, &params.position.text_document, *range))
-        .collect::<Result<Vec<_>, _>>()?;
-    let position = from_proto::file_position(&snap, params.position)?;
+        .collect::<Result<Option<Vec<_>>, _>>()?);
+    let position = try_default!(from_proto::file_position(&snap, params.position)?);
     let source_change = snap.analysis.structural_search_replace(
         &params.query,
         params.parse_only,
@@ -1660,11 +1675,11 @@ pub(crate) fn handle_inlay_hints(
 ) -> anyhow::Result<Option<Vec<InlayHint>>> {
     let _p = tracing::info_span!("handle_inlay_hints").entered();
     let document_uri = &params.text_document.uri;
-    let FileRange { file_id, range } = from_proto::file_range(
+    let FileRange { file_id, range } = try_default!(from_proto::file_range(
         &snap,
         &TextDocumentIdentifier::new(document_uri.to_owned()),
         params.range,
-    )?;
+    )?);
     let line_index = snap.file_line_index(file_id)?;
     let range = TextRange::new(
         range.start().min(line_index.index.len()),
@@ -1744,7 +1759,8 @@ pub(crate) fn handle_call_hierarchy_prepare(
     params: CallHierarchyPrepareParams,
 ) -> anyhow::Result<Option<Vec<CallHierarchyItem>>> {
     let _p = tracing::info_span!("handle_call_hierarchy_prepare").entered();
-    let position = from_proto::file_position(&snap, params.text_document_position_params)?;
+    let position =
+        try_default!(from_proto::file_position(&snap, params.text_document_position_params)?);
 
     let nav_info = match snap.analysis.call_hierarchy(position)? {
         None => return Ok(None),
@@ -1769,7 +1785,7 @@ pub(crate) fn handle_call_hierarchy_incoming(
     let item = params.item;
 
     let doc = TextDocumentIdentifier::new(item.uri);
-    let frange = from_proto::file_range(&snap, &doc, item.selection_range)?;
+    let frange = try_default!(from_proto::file_range(&snap, &doc, item.selection_range)?);
     let fpos = FilePosition { file_id: frange.file_id, offset: frange.range.start() };
 
     let config = snap.config.call_hierarchy();
@@ -1807,7 +1823,7 @@ pub(crate) fn handle_call_hierarchy_outgoing(
     let item = params.item;
 
     let doc = TextDocumentIdentifier::new(item.uri);
-    let frange = from_proto::file_range(&snap, &doc, item.selection_range)?;
+    let frange = try_default!(from_proto::file_range(&snap, &doc, item.selection_range)?);
     let fpos = FilePosition { file_id: frange.file_id, offset: frange.range.start() };
     let line_index = snap.file_line_index(fpos.file_id)?;
 
@@ -1842,7 +1858,7 @@ pub(crate) fn handle_semantic_tokens_full(
 ) -> anyhow::Result<Option<SemanticTokensResult>> {
     let _p = tracing::info_span!("handle_semantic_tokens_full").entered();
 
-    let file_id = from_proto::file_id(&snap, &params.text_document.uri)?;
+    let file_id = try_default!(from_proto::file_id(&snap, &params.text_document.uri)?);
     let text = snap.analysis.file_text(file_id)?;
     let line_index = snap.file_line_index(file_id)?;
 
@@ -1872,7 +1888,7 @@ pub(crate) fn handle_semantic_tokens_full_delta(
 ) -> anyhow::Result<Option<SemanticTokensFullDeltaResult>> {
     let _p = tracing::info_span!("handle_semantic_tokens_full_delta").entered();
 
-    let file_id = from_proto::file_id(&snap, &params.text_document.uri)?;
+    let file_id = try_default!(from_proto::file_id(&snap, &params.text_document.uri)?);
     let text = snap.analysis.file_text(file_id)?;
     let line_index = snap.file_line_index(file_id)?;
 
@@ -1915,7 +1931,7 @@ pub(crate) fn handle_semantic_tokens_range(
 ) -> anyhow::Result<Option<SemanticTokensRangeResult>> {
     let _p = tracing::info_span!("handle_semantic_tokens_range").entered();
 
-    let frange = from_proto::file_range(&snap, &params.text_document, params.range)?;
+    let frange = try_default!(from_proto::file_range(&snap, &params.text_document, params.range)?);
     let text = snap.analysis.file_text(frange.file_id)?;
     let line_index = snap.file_line_index(frange.file_id)?;
 
@@ -1940,7 +1956,7 @@ pub(crate) fn handle_open_docs(
     params: lsp_types::TextDocumentPositionParams,
 ) -> anyhow::Result<ExternalDocsResponse> {
     let _p = tracing::info_span!("handle_open_docs").entered();
-    let position = from_proto::file_position(&snap, params)?;
+    let position = try_default!(from_proto::file_position(&snap, params)?);
 
     let ws_and_sysroot = snap.workspaces.iter().find_map(|ws| match &ws.kind {
         ProjectWorkspaceKind::Cargo { cargo, .. }
@@ -1982,7 +1998,7 @@ pub(crate) fn handle_open_cargo_toml(
     params: lsp_ext::OpenCargoTomlParams,
 ) -> anyhow::Result<Option<lsp_types::GotoDefinitionResponse>> {
     let _p = tracing::info_span!("handle_open_cargo_toml").entered();
-    let file_id = from_proto::file_id(&snap, &params.text_document.uri)?;
+    let file_id = try_default!(from_proto::file_id(&snap, &params.text_document.uri)?);
 
     let cargo_spec = match TargetSpec::for_file(&snap, file_id)? {
         Some(TargetSpec::Cargo(it)) => it,
@@ -2000,8 +2016,8 @@ pub(crate) fn handle_move_item(
     params: lsp_ext::MoveItemParams,
 ) -> anyhow::Result<Vec<lsp_ext::SnippetTextEdit>> {
     let _p = tracing::info_span!("handle_move_item").entered();
-    let file_id = from_proto::file_id(&snap, &params.text_document.uri)?;
-    let range = from_proto::file_range(&snap, &params.text_document, params.range)?;
+    let file_id = try_default!(from_proto::file_id(&snap, &params.text_document.uri)?);
+    let range = try_default!(from_proto::file_range(&snap, &params.text_document, params.range)?);
 
     let direction = match params.direction {
         lsp_ext::MoveItemDirection::Up => ide::Direction::Up,
@@ -2022,7 +2038,7 @@ pub(crate) fn handle_view_recursive_memory_layout(
     params: lsp_types::TextDocumentPositionParams,
 ) -> anyhow::Result<Option<lsp_ext::RecursiveMemoryLayout>> {
     let _p = tracing::info_span!("handle_view_recursive_memory_layout").entered();
-    let file_id = from_proto::file_id(&snap, &params.text_document.uri)?;
+    let file_id = try_default!(from_proto::file_id(&snap, &params.text_document.uri)?);
     let line_index = snap.file_line_index(file_id)?;
     let offset = from_proto::offset(&line_index, params.position)?;
 
@@ -2210,7 +2226,7 @@ fn run_rustfmt(
     text_document: TextDocumentIdentifier,
     range: Option<lsp_types::Range>,
 ) -> anyhow::Result<Option<Vec<lsp_types::TextEdit>>> {
-    let file_id = from_proto::file_id(snap, &text_document.uri)?;
+    let file_id = try_default!(from_proto::file_id(snap, &text_document.uri)?);
     let file = snap.analysis.file_text(file_id)?;
 
     // Determine the edition of the crate the file belongs to (if there's multiple, we pick the
@@ -2275,7 +2291,7 @@ fn run_rustfmt(
                     .into());
                 }
 
-                let frange = from_proto::file_range(snap, &text_document, range)?;
+                let frange = try_default!(from_proto::file_range(snap, &text_document, range)?);
                 let start_line = line_index.index.line_col(frange.range.start()).line;
                 let end_line = line_index.index.line_col(frange.range.end()).line;
 
@@ -2284,7 +2300,8 @@ fn run_rustfmt(
                 cmd.arg(
                     json!([{
                         "file": "stdin",
-                        "range": [start_line, end_line]
+                        // LineCol is 0-based, but rustfmt is 1-based.
+                        "range": [start_line + 1, end_line + 1]
                     }])
                     .to_string(),
                 );
@@ -2318,18 +2335,21 @@ fn run_rustfmt(
         }
     };
 
-    tracing::debug!(?command, "created format command");
+    let output = {
+        let _p = tracing::info_span!("rustfmt", ?command).entered();
 
-    let mut rustfmt = command
-        .stdin(Stdio::piped())
-        .stdout(Stdio::piped())
-        .stderr(Stdio::piped())
-        .spawn()
-        .context(format!("Failed to spawn {command:?}"))?;
+        let mut rustfmt = command
+            .stdin(Stdio::piped())
+            .stdout(Stdio::piped())
+            .stderr(Stdio::piped())
+            .spawn()
+            .context(format!("Failed to spawn {command:?}"))?;
 
-    rustfmt.stdin.as_mut().unwrap().write_all(file.as_bytes())?;
+        rustfmt.stdin.as_mut().unwrap().write_all(file.as_bytes())?;
+
+        rustfmt.wait_with_output()?
+    };
 
-    let output = rustfmt.wait_with_output()?;
     let captured_stdout = String::from_utf8(output.stdout)?;
     let captured_stderr = String::from_utf8(output.stderr).unwrap_or_default();
 
@@ -2413,15 +2433,15 @@ pub(crate) fn internal_testing_fetch_config(
     state: GlobalStateSnapshot,
     params: InternalTestingFetchConfigParams,
 ) -> anyhow::Result<Option<InternalTestingFetchConfigResponse>> {
-    let source_root = params
-        .text_document
-        .map(|it| {
+    let source_root = match params.text_document {
+        Some(it) => Some(
             state
                 .analysis
-                .source_root_id(from_proto::file_id(&state, &it.uri)?)
-                .map_err(anyhow::Error::from)
-        })
-        .transpose()?;
+                .source_root_id(try_default!(from_proto::file_id(&state, &it.uri)?))
+                .map_err(anyhow::Error::from)?,
+        ),
+        None => None,
+    };
     Ok(Some(match params.config {
         InternalTestingFetchConfigOption::AssistEmitMustUse => {
             InternalTestingFetchConfigResponse::AssistEmitMustUse(
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/integrated_benchmarks.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/integrated_benchmarks.rs
index 5cdc51a1c19..c6aa8ba1707 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/integrated_benchmarks.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/integrated_benchmarks.rs
@@ -25,6 +25,14 @@ use vfs::{AbsPathBuf, VfsPath};
 
 use load_cargo::{load_workspace_at, LoadCargoConfig, ProcMacroServerChoice};
 
+#[track_caller]
+fn file_id(vfs: &vfs::Vfs, path: &VfsPath) -> vfs::FileId {
+    match vfs.file_id(path) {
+        Some((file_id, vfs::FileExcluded::No)) => file_id,
+        None | Some((_, vfs::FileExcluded::Yes)) => panic!("can't find virtual file for {path}"),
+    }
+}
+
 #[test]
 fn integrated_highlighting_benchmark() {
     if std::env::var("RUN_SLOW_BENCHES").is_err() {
@@ -62,7 +70,7 @@ fn integrated_highlighting_benchmark() {
     let file_id = {
         let file = workspace_to_load.join(file);
         let path = VfsPath::from(AbsPathBuf::assert(file));
-        vfs.file_id(&path).unwrap_or_else(|| panic!("can't find virtual file for {path}"))
+        file_id(&vfs, &path)
     };
 
     {
@@ -130,7 +138,7 @@ fn integrated_completion_benchmark() {
     let file_id = {
         let file = workspace_to_load.join(file);
         let path = VfsPath::from(AbsPathBuf::assert(file));
-        vfs.file_id(&path).unwrap_or_else(|| panic!("can't find virtual file for {path}"))
+        file_id(&vfs, &path)
     };
 
     // kick off parsing and index population
@@ -324,7 +332,7 @@ fn integrated_diagnostics_benchmark() {
     let file_id = {
         let file = workspace_to_load.join(file);
         let path = VfsPath::from(AbsPathBuf::assert(file));
-        vfs.file_id(&path).unwrap_or_else(|| panic!("can't find virtual file for {path}"))
+        file_id(&vfs, &path)
     };
 
     let diagnostics_config = DiagnosticsConfig {
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/lib.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/lib.rs
index ccffa7a671e..27d6225cdb7 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/lib.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/lib.rs
@@ -50,7 +50,7 @@ mod integrated_benchmarks;
 use hir::Mutability;
 use ide::{CompletionItem, CompletionItemRefMode, CompletionRelevance};
 use serde::de::DeserializeOwned;
-use tenthash::TentHasher;
+use tenthash::TentHash;
 
 pub use crate::{
     lsp::capabilities::server_capabilities, main_loop::main_loop, reload::ws_to_crate_graph,
@@ -66,7 +66,7 @@ pub fn from_json<T: DeserializeOwned>(
 }
 
 fn completion_item_hash(item: &CompletionItem, is_ref_completion: bool) -> [u8; 20] {
-    fn hash_completion_relevance(hasher: &mut TentHasher, relevance: &CompletionRelevance) {
+    fn hash_completion_relevance(hasher: &mut TentHash, relevance: &CompletionRelevance) {
         use ide_completion::{
             CompletionRelevancePostfixMatch, CompletionRelevanceReturnType,
             CompletionRelevanceTypeMatch,
@@ -79,71 +79,108 @@ fn completion_item_hash(item: &CompletionItem, is_ref_completion: bool) -> [u8;
             u8::from(relevance.requires_import),
             u8::from(relevance.is_private_editable),
         ]);
-        if let Some(type_match) = &relevance.type_match {
-            let label = match type_match {
-                CompletionRelevanceTypeMatch::CouldUnify => "could_unify",
-                CompletionRelevanceTypeMatch::Exact => "exact",
-            };
-            hasher.update(label);
+
+        match relevance.type_match {
+            None => hasher.update([0u8]),
+            Some(CompletionRelevanceTypeMatch::CouldUnify) => hasher.update([1u8]),
+            Some(CompletionRelevanceTypeMatch::Exact) => hasher.update([2u8]),
         }
+
+        hasher.update([u8::from(relevance.trait_.is_some())]);
         if let Some(trait_) = &relevance.trait_ {
             hasher.update([u8::from(trait_.is_op_method), u8::from(trait_.notable_trait)]);
         }
-        if let Some(postfix_match) = &relevance.postfix_match {
-            let label = match postfix_match {
-                CompletionRelevancePostfixMatch::NonExact => "non_exact",
-                CompletionRelevancePostfixMatch::Exact => "exact",
-            };
-            hasher.update(label);
+
+        match relevance.postfix_match {
+            None => hasher.update([0u8]),
+            Some(CompletionRelevancePostfixMatch::NonExact) => hasher.update([1u8]),
+            Some(CompletionRelevancePostfixMatch::Exact) => hasher.update([2u8]),
         }
+
+        hasher.update([u8::from(relevance.function.is_some())]);
         if let Some(function) = &relevance.function {
             hasher.update([u8::from(function.has_params), u8::from(function.has_self_param)]);
-            let label = match function.return_type {
-                CompletionRelevanceReturnType::Other => "other",
-                CompletionRelevanceReturnType::DirectConstructor => "direct_constructor",
-                CompletionRelevanceReturnType::Constructor => "constructor",
-                CompletionRelevanceReturnType::Builder => "builder",
+            let discriminant: u8 = match function.return_type {
+                CompletionRelevanceReturnType::Other => 0,
+                CompletionRelevanceReturnType::DirectConstructor => 1,
+                CompletionRelevanceReturnType::Constructor => 2,
+                CompletionRelevanceReturnType::Builder => 3,
             };
-            hasher.update(label);
+            hasher.update([discriminant]);
         }
     }
 
-    let mut hasher = TentHasher::new();
+    let mut hasher = TentHash::new();
     hasher.update([
         u8::from(is_ref_completion),
         u8::from(item.is_snippet),
         u8::from(item.deprecated),
         u8::from(item.trigger_call_info),
     ]);
+
+    hasher.update(item.label.primary.len().to_ne_bytes());
     hasher.update(&item.label.primary);
+
+    hasher.update([u8::from(item.label.detail_left.is_some())]);
     if let Some(label_detail) = &item.label.detail_left {
+        hasher.update(label_detail.len().to_ne_bytes());
         hasher.update(label_detail);
     }
+
+    hasher.update([u8::from(item.label.detail_right.is_some())]);
     if let Some(label_detail) = &item.label.detail_right {
+        hasher.update(label_detail.len().to_ne_bytes());
         hasher.update(label_detail);
     }
+
     // NB: do not hash edits or source range, as those may change between the time the client sends the resolve request
     // and the time it receives it: some editors do allow changing the buffer between that, leading to ranges being different.
     //
     // Documentation hashing is skipped too, as it's a large blob to process,
     // while not really making completion properties more unique as they are already.
-    hasher.update(item.kind.tag());
+
+    let kind_tag = item.kind.tag();
+    hasher.update(kind_tag.len().to_ne_bytes());
+    hasher.update(kind_tag);
+
+    hasher.update(item.lookup.len().to_ne_bytes());
     hasher.update(&item.lookup);
+
+    hasher.update([u8::from(item.detail.is_some())]);
     if let Some(detail) = &item.detail {
+        hasher.update(detail.len().to_ne_bytes());
         hasher.update(detail);
     }
+
     hash_completion_relevance(&mut hasher, &item.relevance);
+
+    hasher.update([u8::from(item.ref_match.is_some())]);
     if let Some((ref_mode, text_size)) = &item.ref_match {
-        let prefix = match ref_mode {
-            CompletionItemRefMode::Reference(Mutability::Shared) => "&",
-            CompletionItemRefMode::Reference(Mutability::Mut) => "&mut ",
-            CompletionItemRefMode::Dereference => "*",
+        let discriminant = match ref_mode {
+            CompletionItemRefMode::Reference(Mutability::Shared) => 0u8,
+            CompletionItemRefMode::Reference(Mutability::Mut) => 1u8,
+            CompletionItemRefMode::Dereference => 2u8,
         };
-        hasher.update(prefix);
-        hasher.update(u32::from(*text_size).to_le_bytes());
+        hasher.update([discriminant]);
+        hasher.update(u32::from(*text_size).to_ne_bytes());
     }
+
+    hasher.update(item.import_to_add.len().to_ne_bytes());
     for import_path in &item.import_to_add {
+        hasher.update(import_path.len().to_ne_bytes());
         hasher.update(import_path);
     }
+
     hasher.finalize()
 }
+
+#[doc(hidden)]
+macro_rules! try_default_ {
+    ($it:expr $(,)?) => {
+        match $it {
+            Some(it) => it,
+            None => return Ok(Default::default()),
+        }
+    };
+}
+pub(crate) use try_default_ as try_default;
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/from_proto.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/from_proto.rs
index 47e9961cf13..6375a1a054b 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/from_proto.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/from_proto.rs
@@ -9,7 +9,7 @@ use vfs::AbsPathBuf;
 use crate::{
     global_state::GlobalStateSnapshot,
     line_index::{LineIndex, PositionEncoding},
-    lsp_ext,
+    lsp_ext, try_default,
 };
 
 pub(crate) fn abs_path(url: &lsp_types::Url) -> anyhow::Result<AbsPathBuf> {
@@ -61,37 +61,44 @@ pub(crate) fn text_range(
     }
 }
 
-pub(crate) fn file_id(snap: &GlobalStateSnapshot, url: &lsp_types::Url) -> anyhow::Result<FileId> {
+/// Returns `None` if the file was excluded.
+pub(crate) fn file_id(
+    snap: &GlobalStateSnapshot,
+    url: &lsp_types::Url,
+) -> anyhow::Result<Option<FileId>> {
     snap.url_to_file_id(url)
 }
 
+/// Returns `None` if the file was excluded.
 pub(crate) fn file_position(
     snap: &GlobalStateSnapshot,
     tdpp: lsp_types::TextDocumentPositionParams,
-) -> anyhow::Result<FilePosition> {
-    let file_id = file_id(snap, &tdpp.text_document.uri)?;
+) -> anyhow::Result<Option<FilePosition>> {
+    let file_id = try_default!(file_id(snap, &tdpp.text_document.uri)?);
     let line_index = snap.file_line_index(file_id)?;
     let offset = offset(&line_index, tdpp.position)?;
-    Ok(FilePosition { file_id, offset })
+    Ok(Some(FilePosition { file_id, offset }))
 }
 
+/// Returns `None` if the file was excluded.
 pub(crate) fn file_range(
     snap: &GlobalStateSnapshot,
     text_document_identifier: &lsp_types::TextDocumentIdentifier,
     range: lsp_types::Range,
-) -> anyhow::Result<FileRange> {
+) -> anyhow::Result<Option<FileRange>> {
     file_range_uri(snap, &text_document_identifier.uri, range)
 }
 
+/// Returns `None` if the file was excluded.
 pub(crate) fn file_range_uri(
     snap: &GlobalStateSnapshot,
     document: &lsp_types::Url,
     range: lsp_types::Range,
-) -> anyhow::Result<FileRange> {
-    let file_id = file_id(snap, document)?;
+) -> anyhow::Result<Option<FileRange>> {
+    let file_id = try_default!(file_id(snap, document)?);
     let line_index = snap.file_line_index(file_id)?;
     let range = text_range(&line_index, range)?;
-    Ok(FileRange { file_id, range })
+    Ok(Some(FileRange { file_id, range }))
 }
 
 pub(crate) fn assist_kind(kind: lsp_types::CodeActionKind) -> Option<AssistKind> {
@@ -108,6 +115,7 @@ pub(crate) fn assist_kind(kind: lsp_types::CodeActionKind) -> Option<AssistKind>
     Some(assist_kind)
 }
 
+/// Returns `None` if the file was excluded.
 pub(crate) fn annotation(
     snap: &GlobalStateSnapshot,
     range: lsp_types::Range,
@@ -121,7 +129,7 @@ pub(crate) fn annotation(
                 return Ok(None);
             }
             let pos @ FilePosition { file_id, .. } =
-                file_position(snap, params.text_document_position_params)?;
+                try_default!(file_position(snap, params.text_document_position_params)?);
             let line_index = snap.file_line_index(file_id)?;
 
             Ok(Annotation {
@@ -133,7 +141,7 @@ pub(crate) fn annotation(
             if snap.url_file_version(&params.text_document.uri) != Some(data.version) {
                 return Ok(None);
             }
-            let pos @ FilePosition { file_id, .. } = file_position(snap, params)?;
+            let pos @ FilePosition { file_id, .. } = try_default!(file_position(snap, params)?);
             let line_index = snap.file_line_index(file_id)?;
 
             Ok(Annotation {
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/semantic_tokens.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/semantic_tokens.rs
index 991c10743f7..3c21e199252 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/semantic_tokens.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/semantic_tokens.rs
@@ -24,7 +24,7 @@ macro_rules! define_semantic_token_types {
         }
 
         pub(crate) const SUPPORTED_TYPES: &[SemanticTokenType] = &[
-            $(SemanticTokenType::$standard,)*
+            $(self::types::$standard,)*
             $(self::types::$custom),*
         ];
 
@@ -32,7 +32,7 @@ macro_rules! define_semantic_token_types {
             use self::types::*;
             $(
                 if token == $custom {
-                    None $(.or(Some(SemanticTokenType::$fallback)))?
+                    None $(.or(Some(self::types::$fallback)))?
                 } else
             )*
             { Some(token )}
@@ -60,6 +60,7 @@ define_semantic_token_types![
         STRUCT,
         TYPE_PARAMETER,
         VARIABLE,
+        TYPE,
     }
 
     custom {
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 d6dc8b521fd..f5d9469f262 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
@@ -27,7 +27,10 @@ use crate::{
         FetchWorkspaceResponse, GlobalState,
     },
     hack_recover_crate_name,
-    handlers::dispatch::{NotificationDispatcher, RequestDispatcher},
+    handlers::{
+        dispatch::{NotificationDispatcher, RequestDispatcher},
+        request::empty_diagnostic_report,
+    },
     lsp::{
         from_proto, to_proto,
         utils::{notification_is, Progress},
@@ -253,6 +256,11 @@ impl GlobalState {
         &self,
         inbox: &Receiver<lsp_server::Message>,
     ) -> Result<Option<Event>, crossbeam_channel::RecvError> {
+        // Make sure we reply to formatting requests ASAP so the editor doesn't block
+        if let Ok(task) = self.fmt_pool.receiver.try_recv() {
+            return Ok(Some(Event::Task(task)));
+        }
+
         select! {
             recv(inbox) -> msg =>
                 return Ok(msg.ok().map(Event::Lsp)),
@@ -320,26 +328,30 @@ impl GlobalState {
                 }
 
                 for progress in prime_caches_progress {
-                    let (state, message, fraction);
+                    let (state, message, fraction, title);
                     match progress {
                         PrimeCachesProgress::Begin => {
                             state = Progress::Begin;
                             message = None;
                             fraction = 0.0;
+                            title = "Indexing";
                         }
                         PrimeCachesProgress::Report(report) => {
                             state = Progress::Report;
+                            title = report.work_type;
 
-                            message = match &report.crates_currently_indexing[..] {
+                            message = match &*report.crates_currently_indexing {
                                 [crate_name] => Some(format!(
-                                    "{}/{} ({crate_name})",
-                                    report.crates_done, report.crates_total
+                                    "{}/{} ({})",
+                                    report.crates_done,
+                                    report.crates_total,
+                                    crate_name.as_str(),
                                 )),
                                 [crate_name, rest @ ..] => Some(format!(
                                     "{}/{} ({} + {} more)",
                                     report.crates_done,
                                     report.crates_total,
-                                    crate_name,
+                                    crate_name.as_str(),
                                     rest.len()
                                 )),
                                 _ => None,
@@ -351,6 +363,7 @@ impl GlobalState {
                             state = Progress::End;
                             message = None;
                             fraction = 1.0;
+                            title = "Indexing";
 
                             self.prime_caches_queue.op_completed(());
                             if cancelled {
@@ -360,7 +373,13 @@ impl GlobalState {
                         }
                     };
 
-                    self.report_progress("Indexing", state, message, Some(fraction), None);
+                    self.report_progress(
+                        title,
+                        state,
+                        message,
+                        Some(fraction),
+                        Some("rustAnalyzer/cachePriming".to_owned()),
+                    );
                 }
             }
             Event::Vfs(message) => {
@@ -532,6 +551,9 @@ impl GlobalState {
             self.mem_docs
                 .iter()
                 .map(|path| vfs.file_id(path).unwrap())
+                .filter_map(|(file_id, excluded)| {
+                    (excluded == vfs::FileExcluded::No).then_some(file_id)
+                })
                 .filter(|&file_id| {
                     let source_root = db.file_source_root(file_id);
                     // Only publish diagnostics for files in the workspace, not from crates.io deps
@@ -616,6 +638,9 @@ impl GlobalState {
             .mem_docs
             .iter()
             .map(|path| self.vfs.read().0.file_id(path).unwrap())
+            .filter_map(|(file_id, excluded)| {
+                (excluded == vfs::FileExcluded::No).then_some(file_id)
+            })
             .filter(|&file_id| {
                 let source_root = db.file_source_root(file_id);
                 !db.source_root(source_root).is_library
@@ -863,7 +888,10 @@ impl GlobalState {
                 self.task_pool.handle.spawn_with_sender(ThreadIntent::Worker, move |sender| {
                     let _p = tracing::info_span!("GlobalState::check_if_indexed").entered();
                     tracing::debug!(?uri, "handling uri");
-                    let id = from_proto::file_id(&snap, &uri).expect("unable to get FileId");
+                    let Some(id) = from_proto::file_id(&snap, &uri).expect("unable to get FileId")
+                    else {
+                        return;
+                    };
                     if let Ok(crates) = &snap.analysis.crates_for(id) {
                         if crates.is_empty() {
                             if snap.config.discover_workspace_config().is_some() {
@@ -971,13 +999,14 @@ impl GlobalState {
                 );
                 for diag in diagnostics {
                     match url_to_file_id(&self.vfs.read().0, &diag.url) {
-                        Ok(file_id) => self.diagnostics.add_check_diagnostic(
+                        Ok(Some(file_id)) => self.diagnostics.add_check_diagnostic(
                             id,
                             &package_id,
                             file_id,
                             diag.diagnostic,
                             diag.fix,
                         ),
+                        Ok(None) => {}
                         Err(err) => {
                             error!(
                                 "flycheck {id}: File with cargo diagnostic not found in VFS: {}",
@@ -1099,17 +1128,7 @@ impl GlobalState {
             .on_latency_sensitive::<NO_RETRY, lsp_request::SemanticTokensRangeRequest>(handlers::handle_semantic_tokens_range)
             // FIXME: Some of these NO_RETRY could be retries if the file they are interested didn't change.
             // All other request handlers
-            .on_with_vfs_default::<lsp_request::DocumentDiagnosticRequest>(handlers::handle_document_diagnostics, || lsp_types::DocumentDiagnosticReportResult::Report(
-                lsp_types::DocumentDiagnosticReport::Full(
-                    lsp_types::RelatedFullDocumentDiagnosticReport {
-                        related_documents: None,
-                        full_document_diagnostic_report: lsp_types::FullDocumentDiagnosticReport {
-                            result_id: Some("rust-analyzer".to_owned()),
-                            items: vec![],
-                        },
-                    },
-                ),
-            ), || lsp_server::ResponseError {
+            .on_with_vfs_default::<lsp_request::DocumentDiagnosticRequest>(handlers::handle_document_diagnostics, empty_diagnostic_report, || lsp_server::ResponseError {
                 code: lsp_server::ErrorCode::ServerCancelled as i32,
                 message: "server cancelled the request".to_owned(),
                 data: serde_json::to_value(lsp_types::DiagnosticServerCancellationData {
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 0add2cdf5a7..56dcad0eb18 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/reload.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/reload.rs
@@ -316,6 +316,7 @@ impl GlobalState {
                             let workspace = project_model::ProjectWorkspace::load_inline(
                                 it.clone(),
                                 &cargo_config,
+                                &progress,
                             );
                             Ok(workspace)
                         }
@@ -701,12 +702,13 @@ impl GlobalState {
 
         let (crate_graph, proc_macro_paths, ws_data) = {
             // Create crate graph from all the workspaces
-            let vfs = &mut self.vfs.write().0;
-
+            let vfs = &self.vfs.read().0;
             let load = |path: &AbsPath| {
                 let vfs_path = vfs::VfsPath::from(path.to_path_buf());
                 self.crate_graph_file_dependencies.insert(vfs_path.clone());
-                vfs.file_id(&vfs_path)
+                vfs.file_id(&vfs_path).and_then(|(file_id, excluded)| {
+                    (excluded == vfs::FileExcluded::No).then_some(file_id)
+                })
             };
 
             ws_to_crate_graph(&self.workspaces, self.config.extra_env(None), load)
@@ -883,7 +885,6 @@ pub fn ws_to_crate_graph(
         ws_data.extend(mapping.values().copied().zip(iter::repeat(Arc::new(CrateWorkspaceData {
             toolchain: toolchain.clone(),
             data_layout: target_layout.clone(),
-            proc_macro_cwd: Some(ws.workspace_root().to_owned()),
         }))));
         proc_macro_paths.push(crate_proc_macros);
     }
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/task_pool.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/task_pool.rs
index 2bcd8505e81..c5de69bb9fc 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/task_pool.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/task_pool.rs
@@ -1,6 +1,8 @@
 //! A thin wrapper around [`stdx::thread::Pool`] which threads a sender through spawned jobs.
 //! It is used in [`crate::global_state::GlobalState`] throughout the main loop.
 
+use std::panic::UnwindSafe;
+
 use crossbeam_channel::Sender;
 use stdx::thread::{Pool, ThreadIntent};
 
@@ -18,7 +20,7 @@ impl<T> TaskPool<T> {
 
     pub(crate) fn spawn<F>(&mut self, intent: ThreadIntent, task: F)
     where
-        F: FnOnce() -> T + Send + 'static,
+        F: FnOnce() -> T + Send + UnwindSafe + 'static,
         T: Send + 'static,
     {
         self.pool.spawn(intent, {
@@ -29,7 +31,7 @@ impl<T> TaskPool<T> {
 
     pub(crate) fn spawn_with_sender<F>(&mut self, intent: ThreadIntent, task: F)
     where
-        F: FnOnce(Sender<T>) + Send + 'static,
+        F: FnOnce(Sender<T>) + Send + UnwindSafe + 'static,
         T: Send + 'static,
     {
         self.pool.spawn(intent, {
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/cli.rs b/src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/cli.rs
index fba54666912..4ef930e9854 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/cli.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/cli.rs
@@ -43,89 +43,93 @@ mod tests {
     expect![[r#"
         {"id":2,"type":"vertex","label":"foldingRangeResult","result":[{"startLine":2,"startCharacter":43,"endLine":6,"endCharacter":1},{"startLine":3,"startCharacter":19,"endLine":5,"endCharacter":5},{"startLine":9,"startCharacter":10,"endLine":12,"endCharacter":1}]}
         {"id":3,"type":"edge","label":"textDocument/foldingRange","inV":2,"outV":1}
-        {"id":4,"type":"vertex","label":"range","start":{"line":0,"character":3},"end":{"line":0,"character":8}}
+        {"id":4,"type":"vertex","label":"range","start":{"line":0,"character":0},"end":{"line":13,"character":0}}
         {"id":5,"type":"vertex","label":"resultSet"}
         {"id":6,"type":"edge","label":"next","inV":5,"outV":4}
-        {"id":7,"type":"vertex","label":"range","start":{"line":2,"character":13},"end":{"line":2,"character":43}}
+        {"id":7,"type":"vertex","label":"range","start":{"line":0,"character":3},"end":{"line":0,"character":8}}
         {"id":8,"type":"vertex","label":"resultSet"}
         {"id":9,"type":"edge","label":"next","inV":8,"outV":7}
-        {"id":10,"type":"vertex","label":"range","start":{"line":8,"character":0},"end":{"line":8,"character":30}}
-        {"id":11,"type":"edge","label":"next","inV":8,"outV":10}
-        {"id":12,"type":"vertex","label":"range","start":{"line":8,"character":32},"end":{"line":8,"character":39}}
-        {"id":13,"type":"vertex","label":"resultSet"}
-        {"id":14,"type":"edge","label":"next","inV":13,"outV":12}
-        {"id":15,"type":"vertex","label":"range","start":{"line":9,"character":4},"end":{"line":9,"character":9}}
+        {"id":10,"type":"vertex","label":"range","start":{"line":2,"character":13},"end":{"line":2,"character":43}}
+        {"id":11,"type":"vertex","label":"resultSet"}
+        {"id":12,"type":"edge","label":"next","inV":11,"outV":10}
+        {"id":13,"type":"vertex","label":"range","start":{"line":8,"character":0},"end":{"line":8,"character":30}}
+        {"id":14,"type":"edge","label":"next","inV":11,"outV":13}
+        {"id":15,"type":"vertex","label":"range","start":{"line":8,"character":32},"end":{"line":8,"character":39}}
         {"id":16,"type":"vertex","label":"resultSet"}
         {"id":17,"type":"edge","label":"next","inV":16,"outV":15}
-        {"id":18,"type":"vertex","label":"range","start":{"line":10,"character":8},"end":{"line":10,"character":13}}
+        {"id":18,"type":"vertex","label":"range","start":{"line":9,"character":4},"end":{"line":9,"character":9}}
         {"id":19,"type":"vertex","label":"resultSet"}
         {"id":20,"type":"edge","label":"next","inV":19,"outV":18}
-        {"id":21,"type":"vertex","label":"range","start":{"line":11,"character":4},"end":{"line":11,"character":34}}
-        {"id":22,"type":"edge","label":"next","inV":8,"outV":21}
-        {"id":23,"type":"vertex","label":"range","start":{"line":11,"character":36},"end":{"line":11,"character":43}}
-        {"id":24,"type":"vertex","label":"resultSet"}
-        {"id":25,"type":"edge","label":"next","inV":24,"outV":23}
-        {"id":26,"type":"edge","label":"contains","inVs":[4,7,10,12,15,18,21,23],"outV":1}
-        {"id":27,"type":"vertex","label":"hoverResult","result":{"contents":{"kind":"markdown","value":"\n```rust\n#[allow]\n```\n\n---\n\nValid forms are:\n\n* \\#\\[allow(lint1, lint2, ..., /\\*opt\\*/ reason = \"...\")\\]"}}}
-        {"id":28,"type":"edge","label":"textDocument/hover","inV":27,"outV":5}
-        {"id":29,"type":"vertex","label":"referenceResult"}
-        {"id":30,"type":"edge","label":"textDocument/references","inV":29,"outV":5}
-        {"id":31,"type":"edge","label":"item","document":1,"property":"references","inVs":[4],"outV":29}
-        {"id":32,"type":"vertex","label":"hoverResult","result":{"contents":{"kind":"markdown","value":"\n```rust\nfoo\n```\n\n```rust\nmacro_rules! generate_const_from_identifier\n```"}}}
-        {"id":33,"type":"edge","label":"textDocument/hover","inV":32,"outV":8}
-        {"id":34,"type":"vertex","label":"packageInformation","name":"foo","manager":"cargo","version":"0.0.0"}
-        {"id":35,"type":"vertex","label":"moniker","scheme":"rust-analyzer","identifier":"foo::generate_const_from_identifier","unique":"scheme","kind":"export"}
-        {"id":36,"type":"edge","label":"packageInformation","inV":34,"outV":35}
-        {"id":37,"type":"edge","label":"moniker","inV":35,"outV":8}
-        {"id":38,"type":"vertex","label":"definitionResult"}
-        {"id":39,"type":"edge","label":"item","document":1,"inVs":[7],"outV":38}
-        {"id":40,"type":"edge","label":"textDocument/definition","inV":38,"outV":8}
-        {"id":41,"type":"vertex","label":"referenceResult"}
-        {"id":42,"type":"edge","label":"textDocument/references","inV":41,"outV":8}
-        {"id":43,"type":"edge","label":"item","document":1,"property":"definitions","inVs":[7],"outV":41}
-        {"id":44,"type":"edge","label":"item","document":1,"property":"references","inVs":[10,21],"outV":41}
-        {"id":45,"type":"vertex","label":"hoverResult","result":{"contents":{"kind":"markdown","value":"\n```rust\nfoo\n```\n\n```rust\nconst REQ_001: &str = \"encoded_data\"\n```"}}}
-        {"id":46,"type":"edge","label":"textDocument/hover","inV":45,"outV":13}
-        {"id":47,"type":"vertex","label":"moniker","scheme":"rust-analyzer","identifier":"foo::REQ_001","unique":"scheme","kind":"export"}
-        {"id":48,"type":"edge","label":"packageInformation","inV":34,"outV":47}
-        {"id":49,"type":"edge","label":"moniker","inV":47,"outV":13}
-        {"id":50,"type":"vertex","label":"definitionResult"}
-        {"id":51,"type":"edge","label":"item","document":1,"inVs":[12],"outV":50}
-        {"id":52,"type":"edge","label":"textDocument/definition","inV":50,"outV":13}
-        {"id":53,"type":"vertex","label":"referenceResult"}
-        {"id":54,"type":"edge","label":"textDocument/references","inV":53,"outV":13}
-        {"id":55,"type":"edge","label":"item","document":1,"property":"definitions","inVs":[12],"outV":53}
-        {"id":56,"type":"vertex","label":"hoverResult","result":{"contents":{"kind":"markdown","value":"\n```rust\nfoo\n```\n\n```rust\nmod tests\n```"}}}
-        {"id":57,"type":"edge","label":"textDocument/hover","inV":56,"outV":16}
-        {"id":58,"type":"vertex","label":"moniker","scheme":"rust-analyzer","identifier":"foo::tests","unique":"scheme","kind":"export"}
-        {"id":59,"type":"edge","label":"packageInformation","inV":34,"outV":58}
-        {"id":60,"type":"edge","label":"moniker","inV":58,"outV":16}
-        {"id":61,"type":"vertex","label":"definitionResult"}
-        {"id":62,"type":"edge","label":"item","document":1,"inVs":[15],"outV":61}
-        {"id":63,"type":"edge","label":"textDocument/definition","inV":61,"outV":16}
-        {"id":64,"type":"vertex","label":"referenceResult"}
-        {"id":65,"type":"edge","label":"textDocument/references","inV":64,"outV":16}
-        {"id":66,"type":"edge","label":"item","document":1,"property":"definitions","inVs":[15],"outV":64}
-        {"id":67,"type":"vertex","label":"hoverResult","result":{"contents":{"kind":"markdown","value":"\n```rust\nextern crate foo\n```"}}}
-        {"id":68,"type":"edge","label":"textDocument/hover","inV":67,"outV":19}
-        {"id":69,"type":"vertex","label":"definitionResult"}
-        {"id":70,"type":"vertex","label":"range","start":{"line":0,"character":0},"end":{"line":13,"character":0}}
-        {"id":71,"type":"edge","label":"contains","inVs":[70],"outV":1}
-        {"id":72,"type":"edge","label":"item","document":1,"inVs":[70],"outV":69}
-        {"id":73,"type":"edge","label":"textDocument/definition","inV":69,"outV":19}
-        {"id":74,"type":"vertex","label":"referenceResult"}
-        {"id":75,"type":"edge","label":"textDocument/references","inV":74,"outV":19}
-        {"id":76,"type":"edge","label":"item","document":1,"property":"references","inVs":[18],"outV":74}
-        {"id":77,"type":"vertex","label":"hoverResult","result":{"contents":{"kind":"markdown","value":"\n```rust\nfoo::tests\n```\n\n```rust\nconst REQ_002: &str = \"encoded_data\"\n```"}}}
-        {"id":78,"type":"edge","label":"textDocument/hover","inV":77,"outV":24}
-        {"id":79,"type":"vertex","label":"moniker","scheme":"rust-analyzer","identifier":"foo::tests::REQ_002","unique":"scheme","kind":"export"}
-        {"id":80,"type":"edge","label":"packageInformation","inV":34,"outV":79}
-        {"id":81,"type":"edge","label":"moniker","inV":79,"outV":24}
-        {"id":82,"type":"vertex","label":"definitionResult"}
-        {"id":83,"type":"edge","label":"item","document":1,"inVs":[23],"outV":82}
-        {"id":84,"type":"edge","label":"textDocument/definition","inV":82,"outV":24}
-        {"id":85,"type":"vertex","label":"referenceResult"}
-        {"id":86,"type":"edge","label":"textDocument/references","inV":85,"outV":24}
-        {"id":87,"type":"edge","label":"item","document":1,"property":"definitions","inVs":[23],"outV":85}
+        {"id":21,"type":"vertex","label":"range","start":{"line":10,"character":8},"end":{"line":10,"character":13}}
+        {"id":22,"type":"edge","label":"next","inV":5,"outV":21}
+        {"id":23,"type":"vertex","label":"range","start":{"line":11,"character":4},"end":{"line":11,"character":34}}
+        {"id":24,"type":"edge","label":"next","inV":11,"outV":23}
+        {"id":25,"type":"vertex","label":"range","start":{"line":11,"character":36},"end":{"line":11,"character":43}}
+        {"id":26,"type":"vertex","label":"resultSet"}
+        {"id":27,"type":"edge","label":"next","inV":26,"outV":25}
+        {"id":28,"type":"edge","label":"contains","inVs":[4,7,10,13,15,18,21,23,25],"outV":1}
+        {"id":29,"type":"vertex","label":"hoverResult","result":{"contents":{"kind":"markdown","value":"\n```rust\nextern crate foo\n```"}}}
+        {"id":30,"type":"edge","label":"textDocument/hover","inV":29,"outV":5}
+        {"id":31,"type":"vertex","label":"packageInformation","name":"foo","manager":"cargo","version":"0.0.0"}
+        {"id":32,"type":"vertex","label":"moniker","scheme":"rust-analyzer","identifier":"foo::crate","unique":"scheme","kind":"export"}
+        {"id":33,"type":"edge","label":"packageInformation","inV":31,"outV":32}
+        {"id":34,"type":"edge","label":"moniker","inV":32,"outV":5}
+        {"id":35,"type":"vertex","label":"definitionResult"}
+        {"id":36,"type":"edge","label":"item","document":1,"inVs":[4],"outV":35}
+        {"id":37,"type":"edge","label":"textDocument/definition","inV":35,"outV":5}
+        {"id":38,"type":"vertex","label":"referenceResult"}
+        {"id":39,"type":"edge","label":"textDocument/references","inV":38,"outV":5}
+        {"id":40,"type":"edge","label":"item","document":1,"property":"definitions","inVs":[4],"outV":38}
+        {"id":41,"type":"edge","label":"item","document":1,"property":"references","inVs":[21],"outV":38}
+        {"id":42,"type":"vertex","label":"hoverResult","result":{"contents":{"kind":"markdown","value":"\n```rust\n#[allow]\n```\n\n---\n\nValid forms are:\n\n* \\#\\[allow(lint1, lint2, ..., /\\*opt\\*/ reason = \"...\")\\]"}}}
+        {"id":43,"type":"edge","label":"textDocument/hover","inV":42,"outV":8}
+        {"id":44,"type":"vertex","label":"referenceResult"}
+        {"id":45,"type":"edge","label":"textDocument/references","inV":44,"outV":8}
+        {"id":46,"type":"edge","label":"item","document":1,"property":"references","inVs":[7],"outV":44}
+        {"id":47,"type":"vertex","label":"hoverResult","result":{"contents":{"kind":"markdown","value":"\n```rust\nfoo\n```\n\n```rust\nmacro_rules! generate_const_from_identifier\n```"}}}
+        {"id":48,"type":"edge","label":"textDocument/hover","inV":47,"outV":11}
+        {"id":49,"type":"vertex","label":"moniker","scheme":"rust-analyzer","identifier":"foo::generate_const_from_identifier","unique":"scheme","kind":"export"}
+        {"id":50,"type":"edge","label":"packageInformation","inV":31,"outV":49}
+        {"id":51,"type":"edge","label":"moniker","inV":49,"outV":11}
+        {"id":52,"type":"vertex","label":"definitionResult"}
+        {"id":53,"type":"edge","label":"item","document":1,"inVs":[10],"outV":52}
+        {"id":54,"type":"edge","label":"textDocument/definition","inV":52,"outV":11}
+        {"id":55,"type":"vertex","label":"referenceResult"}
+        {"id":56,"type":"edge","label":"textDocument/references","inV":55,"outV":11}
+        {"id":57,"type":"edge","label":"item","document":1,"property":"definitions","inVs":[10],"outV":55}
+        {"id":58,"type":"edge","label":"item","document":1,"property":"references","inVs":[13,23],"outV":55}
+        {"id":59,"type":"vertex","label":"hoverResult","result":{"contents":{"kind":"markdown","value":"\n```rust\nfoo\n```\n\n```rust\nconst REQ_001: &str = \"encoded_data\"\n```"}}}
+        {"id":60,"type":"edge","label":"textDocument/hover","inV":59,"outV":16}
+        {"id":61,"type":"vertex","label":"moniker","scheme":"rust-analyzer","identifier":"foo::REQ_001","unique":"scheme","kind":"export"}
+        {"id":62,"type":"edge","label":"packageInformation","inV":31,"outV":61}
+        {"id":63,"type":"edge","label":"moniker","inV":61,"outV":16}
+        {"id":64,"type":"vertex","label":"definitionResult"}
+        {"id":65,"type":"edge","label":"item","document":1,"inVs":[15],"outV":64}
+        {"id":66,"type":"edge","label":"textDocument/definition","inV":64,"outV":16}
+        {"id":67,"type":"vertex","label":"referenceResult"}
+        {"id":68,"type":"edge","label":"textDocument/references","inV":67,"outV":16}
+        {"id":69,"type":"edge","label":"item","document":1,"property":"definitions","inVs":[15],"outV":67}
+        {"id":70,"type":"vertex","label":"hoverResult","result":{"contents":{"kind":"markdown","value":"\n```rust\nfoo\n```\n\n```rust\nmod tests\n```"}}}
+        {"id":71,"type":"edge","label":"textDocument/hover","inV":70,"outV":19}
+        {"id":72,"type":"vertex","label":"moniker","scheme":"rust-analyzer","identifier":"foo::tests","unique":"scheme","kind":"export"}
+        {"id":73,"type":"edge","label":"packageInformation","inV":31,"outV":72}
+        {"id":74,"type":"edge","label":"moniker","inV":72,"outV":19}
+        {"id":75,"type":"vertex","label":"definitionResult"}
+        {"id":76,"type":"edge","label":"item","document":1,"inVs":[18],"outV":75}
+        {"id":77,"type":"edge","label":"textDocument/definition","inV":75,"outV":19}
+        {"id":78,"type":"vertex","label":"referenceResult"}
+        {"id":79,"type":"edge","label":"textDocument/references","inV":78,"outV":19}
+        {"id":80,"type":"edge","label":"item","document":1,"property":"definitions","inVs":[18],"outV":78}
+        {"id":81,"type":"vertex","label":"hoverResult","result":{"contents":{"kind":"markdown","value":"\n```rust\nfoo::tests\n```\n\n```rust\nconst REQ_002: &str = \"encoded_data\"\n```"}}}
+        {"id":82,"type":"edge","label":"textDocument/hover","inV":81,"outV":26}
+        {"id":83,"type":"vertex","label":"moniker","scheme":"rust-analyzer","identifier":"foo::tests::REQ_002","unique":"scheme","kind":"export"}
+        {"id":84,"type":"edge","label":"packageInformation","inV":31,"outV":83}
+        {"id":85,"type":"edge","label":"moniker","inV":83,"outV":26}
+        {"id":86,"type":"vertex","label":"definitionResult"}
+        {"id":87,"type":"edge","label":"item","document":1,"inVs":[25],"outV":86}
+        {"id":88,"type":"edge","label":"textDocument/definition","inV":86,"outV":26}
+        {"id":89,"type":"vertex","label":"referenceResult"}
+        {"id":90,"type":"edge","label":"textDocument/references","inV":89,"outV":26}
+        {"id":91,"type":"edge","label":"item","document":1,"property":"definitions","inVs":[25],"outV":89}
     "#]].assert_eq(stdout);
 }
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/main.rs b/src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/main.rs
index 2b3c0a47a22..6f26bdc2cf0 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/main.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/main.rs
@@ -21,12 +21,14 @@ use lsp_types::{
     notification::DidOpenTextDocument,
     request::{
         CodeActionRequest, Completion, Formatting, GotoTypeDefinition, HoverRequest,
-        InlayHintRequest, InlayHintResolveRequest, WillRenameFiles, WorkspaceSymbolRequest,
+        InlayHintRequest, InlayHintResolveRequest, RangeFormatting, WillRenameFiles,
+        WorkspaceSymbolRequest,
     },
     CodeActionContext, CodeActionParams, CompletionParams, DidOpenTextDocumentParams,
-    DocumentFormattingParams, FileRename, FormattingOptions, GotoDefinitionParams, HoverParams,
-    InlayHint, InlayHintLabel, InlayHintParams, PartialResultParams, Position, Range,
-    RenameFilesParams, TextDocumentItem, TextDocumentPositionParams, WorkDoneProgressParams,
+    DocumentFormattingParams, DocumentRangeFormattingParams, FileRename, FormattingOptions,
+    GotoDefinitionParams, HoverParams, InlayHint, InlayHintLabel, InlayHintParams,
+    PartialResultParams, Position, Range, RenameFilesParams, TextDocumentItem,
+    TextDocumentPositionParams, WorkDoneProgressParams,
 };
 use rust_analyzer::lsp::ext::{OnEnter, Runnables, RunnablesParams};
 use serde_json::json;
@@ -661,6 +663,70 @@ fn main() {}
 }
 
 #[test]
+fn test_format_document_range() {
+    if skip_slow_tests() {
+        return;
+    }
+
+    let server = Project::with_fixture(
+        r#"
+//- /Cargo.toml
+[package]
+name = "foo"
+version = "0.0.0"
+
+//- /src/lib.rs
+fn main() {
+    let unit_offsets_cache = collect(dwarf.units  ())  ?;
+}
+"#,
+    )
+    .with_config(serde_json::json!({
+        "rustfmt": {
+            "overrideCommand": [ "rustfmt", "+nightly", ],
+            "rangeFormatting": { "enable": true }
+        },
+    }))
+    .server()
+    .wait_until_workspace_is_loaded();
+
+    server.request::<RangeFormatting>(
+        DocumentRangeFormattingParams {
+            range: Range {
+                end: Position { line: 1, character: 0 },
+                start: Position { line: 1, character: 0 },
+            },
+            text_document: server.doc_id("src/lib.rs"),
+            options: FormattingOptions {
+                tab_size: 4,
+                insert_spaces: false,
+                insert_final_newline: None,
+                trim_final_newlines: None,
+                trim_trailing_whitespace: None,
+                properties: HashMap::new(),
+            },
+            work_done_progress_params: WorkDoneProgressParams::default(),
+        },
+        json!([
+            {
+                "newText": "",
+                "range": {
+                    "start": { "character": 48, "line": 1 },
+                    "end": { "character": 50, "line": 1 },
+                },
+            },
+            {
+                "newText": "",
+                "range": {
+                    "start": { "character": 53, "line": 1 },
+                    "end": { "character": 55, "line": 1 },
+                },
+            }
+        ]),
+    );
+}
+
+#[test]
 fn test_missing_module_code_action() {
     if skip_slow_tests() {
         return;
@@ -1086,7 +1152,11 @@ fn resolve_proc_macro() {
         &AbsPathBuf::assert_utf8(std::env::current_dir().unwrap()),
         &Default::default(),
     );
-    sysroot.load_workspace(&project_model::SysrootSourceWorkspaceConfig::default_cargo());
+    let loaded_sysroot =
+        sysroot.load_workspace(&project_model::RustSourceWorkspaceConfig::default_cargo());
+    if let Some(loaded_sysroot) = loaded_sysroot {
+        sysroot.set_workspace(loaded_sysroot);
+    }
 
     let proc_macro_server_path = sysroot.discover_proc_macro_srv().unwrap();
 
@@ -1372,6 +1442,40 @@ pub fn foo() {}
 name = "bar"
 version = "0.0.0"
 
+[dependencies]
+foo = { path = "../foo" }
+
+//- /bar/src/lib.rs
+"#,
+    )
+    .root("foo")
+    .root("bar")
+    .root("baz")
+    .with_config(json!({
+       "files": {
+           "exclude": ["foo"]
+        }
+    }))
+    .server()
+    .wait_until_workspace_is_loaded();
+
+    server.request::<WorkspaceSymbolRequest>(Default::default(), json!([]));
+
+    let server = Project::with_fixture(
+        r#"
+//- /foo/Cargo.toml
+[package]
+name = "foo"
+version = "0.0.0"
+
+//- /foo/src/lib.rs
+pub fn foo() {}
+
+//- /bar/Cargo.toml
+[package]
+name = "bar"
+version = "0.0.0"
+
 //- /bar/src/lib.rs
 pub fn bar() {}
 
@@ -1388,7 +1492,7 @@ version = "0.0.0"
     .root("baz")
     .with_config(json!({
        "files": {
-           "excludeDirs": ["foo", "bar"]
+           "exclude": ["foo", "bar"]
         }
     }))
     .server()
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/testdir.rs b/src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/testdir.rs
index d113bd51278..409be2894fe 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/testdir.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/testdir.rs
@@ -43,10 +43,15 @@ impl TestDir {
             }
             fs::create_dir_all(&path).unwrap();
 
-            #[cfg(any(target_os = "macos", target_os = "linux", target_os = "windows"))]
+            #[cfg(any(
+                target_os = "macos",
+                target_os = "linux",
+                target_os = "windows",
+                target_os = "freebsd"
+            ))]
             if symlink {
                 let symlink_path = base.join(format!("{pid}_{cnt}_symlink"));
-                #[cfg(any(target_os = "macos", target_os = "linux"))]
+                #[cfg(any(target_os = "macos", target_os = "linux", target_os = "freebsd"))]
                 std::os::unix::fs::symlink(path, &symlink_path).unwrap();
 
                 #[cfg(target_os = "windows")]
diff --git a/src/tools/rust-analyzer/crates/stdx/src/panic_context.rs b/src/tools/rust-analyzer/crates/stdx/src/panic_context.rs
index 4ec74c0742a..a35d50b78df 100644
--- a/src/tools/rust-analyzer/crates/stdx/src/panic_context.rs
+++ b/src/tools/rust-analyzer/crates/stdx/src/panic_context.rs
@@ -1,28 +1,25 @@
 //! A micro-crate to enhance panic messages with context info.
-//!
-//! FIXME: upstream to <https://github.com/kriomant/panic-context> ?
 
 use std::{cell::RefCell, panic, sync::Once};
 
-pub fn enter(context: String) -> PanicContext {
-    static ONCE: Once = Once::new();
-    ONCE.call_once(PanicContext::init);
-
-    with_ctx(|ctx| ctx.push(context));
-    PanicContext { _priv: () }
-}
-
+/// Dummy for leveraging RAII cleanup to pop frames.
 #[must_use]
 pub struct PanicContext {
+    // prevent arbitrary construction
     _priv: (),
 }
 
-impl PanicContext {
+impl Drop for PanicContext {
+    fn drop(&mut self) {
+        with_ctx(|ctx| assert!(ctx.pop().is_some()));
+    }
+}
+
+pub fn enter(frame: String) -> PanicContext {
     #[allow(clippy::print_stderr)]
-    fn init() {
+    fn set_hook() {
         let default_hook = panic::take_hook();
-        #[allow(deprecated)]
-        let hook = move |panic_info: &panic::PanicInfo<'_>| {
+        panic::set_hook(Box::new(move |panic_info| {
             with_ctx(|ctx| {
                 if !ctx.is_empty() {
                     eprintln!("Panic context:");
@@ -30,17 +27,16 @@ impl PanicContext {
                         eprintln!("> {frame}\n");
                     }
                 }
-                default_hook(panic_info);
             });
-        };
-        panic::set_hook(Box::new(hook));
+            default_hook(panic_info);
+        }));
     }
-}
 
-impl Drop for PanicContext {
-    fn drop(&mut self) {
-        with_ctx(|ctx| assert!(ctx.pop().is_some()));
-    }
+    static SET_HOOK: Once = Once::new();
+    SET_HOOK.call_once(set_hook);
+
+    with_ctx(|ctx| ctx.push(frame));
+    PanicContext { _priv: () }
 }
 
 fn with_ctx(f: impl FnOnce(&mut Vec<String>)) {
diff --git a/src/tools/rust-analyzer/crates/stdx/src/thread/pool.rs b/src/tools/rust-analyzer/crates/stdx/src/thread/pool.rs
index 2ddd7da74c2..9acc1de922a 100644
--- a/src/tools/rust-analyzer/crates/stdx/src/thread/pool.rs
+++ b/src/tools/rust-analyzer/crates/stdx/src/thread/pool.rs
@@ -7,9 +7,12 @@
 //! The thread pool is implemented entirely using
 //! the threading utilities in [`crate::thread`].
 
-use std::sync::{
-    atomic::{AtomicUsize, Ordering},
-    Arc,
+use std::{
+    panic::{self, UnwindSafe},
+    sync::{
+        atomic::{AtomicUsize, Ordering},
+        Arc,
+    },
 };
 
 use crossbeam_channel::{Receiver, Sender};
@@ -25,13 +28,13 @@ pub struct Pool {
     // so that the channel is actually closed
     // before we join the worker threads!
     job_sender: Sender<Job>,
-    _handles: Vec<JoinHandle>,
+    _handles: Box<[JoinHandle]>,
     extant_tasks: Arc<AtomicUsize>,
 }
 
 struct Job {
     requested_intent: ThreadIntent,
-    f: Box<dyn FnOnce() + Send + 'static>,
+    f: Box<dyn FnOnce() + Send + UnwindSafe + 'static>,
 }
 
 impl Pool {
@@ -47,6 +50,7 @@ impl Pool {
             let handle = Builder::new(INITIAL_INTENT)
                 .stack_size(STACK_SIZE)
                 .name("Worker".into())
+                .allow_leak(true)
                 .spawn({
                     let extant_tasks = Arc::clone(&extant_tasks);
                     let job_receiver: Receiver<Job> = job_receiver.clone();
@@ -58,7 +62,8 @@ impl Pool {
                                 current_intent = job.requested_intent;
                             }
                             extant_tasks.fetch_add(1, Ordering::SeqCst);
-                            (job.f)();
+                            // discard the panic, we should've logged the backtrace already
+                            _ = panic::catch_unwind(job.f);
                             extant_tasks.fetch_sub(1, Ordering::SeqCst);
                         }
                     }
@@ -68,12 +73,12 @@ impl Pool {
             handles.push(handle);
         }
 
-        Pool { _handles: handles, extant_tasks, job_sender }
+        Pool { _handles: handles.into_boxed_slice(), extant_tasks, job_sender }
     }
 
     pub fn spawn<F>(&self, intent: ThreadIntent, f: F)
     where
-        F: FnOnce() + Send + 'static,
+        F: FnOnce() + Send + UnwindSafe + 'static,
     {
         let f = Box::new(move || {
             if cfg!(debug_assertions) {
diff --git a/src/tools/rust-analyzer/crates/syntax/rust.ungram b/src/tools/rust-analyzer/crates/syntax/rust.ungram
index 4e2a70d6cd9..bbb8413cbc0 100644
--- a/src/tools/rust-analyzer/crates/syntax/rust.ungram
+++ b/src/tools/rust-analyzer/crates/syntax/rust.ungram
@@ -241,7 +241,7 @@ RecordFieldList =
 
 RecordField =
   Attr* Visibility?
-  Name ':' Type
+  Name ':' Type ('=' Expr)?
 
 TupleFieldList =
   '(' fields:(TupleField (',' TupleField)* ','?)? ')'
diff --git a/src/tools/rust-analyzer/crates/syntax/src/ast/edit_in_place.rs b/src/tools/rust-analyzer/crates/syntax/src/ast/edit_in_place.rs
index 291fc646e21..aedf810b794 100644
--- a/src/tools/rust-analyzer/crates/syntax/src/ast/edit_in_place.rs
+++ b/src/tools/rust-analyzer/crates/syntax/src/ast/edit_in_place.rs
@@ -710,52 +710,6 @@ impl ast::Fn {
     }
 }
 
-impl Removable for ast::MatchArm {
-    fn remove(&self) {
-        if let Some(sibling) = self.syntax().prev_sibling_or_token() {
-            if sibling.kind() == SyntaxKind::WHITESPACE {
-                ted::remove(sibling);
-            }
-        }
-        if let Some(sibling) = self.syntax().next_sibling_or_token() {
-            if sibling.kind() == T![,] {
-                ted::remove(sibling);
-            }
-        }
-        ted::remove(self.syntax());
-    }
-}
-
-impl ast::MatchArmList {
-    pub fn add_arm(&self, arm: ast::MatchArm) {
-        normalize_ws_between_braces(self.syntax());
-        let mut elements = Vec::new();
-        let position = match self.arms().last() {
-            Some(last_arm) => {
-                if needs_comma(&last_arm) {
-                    ted::append_child(last_arm.syntax(), make::token(SyntaxKind::COMMA));
-                }
-                Position::after(last_arm.syntax().clone())
-            }
-            None => match self.l_curly_token() {
-                Some(it) => Position::after(it),
-                None => Position::last_child_of(self.syntax()),
-            },
-        };
-        let indent = IndentLevel::from_node(self.syntax()) + 1;
-        elements.push(make::tokens::whitespace(&format!("\n{indent}")).into());
-        elements.push(arm.syntax().clone().into());
-        if needs_comma(&arm) {
-            ted::append_child(arm.syntax(), make::token(SyntaxKind::COMMA));
-        }
-        ted::insert_all(position, elements);
-
-        fn needs_comma(arm: &ast::MatchArm) -> bool {
-            arm.expr().is_some_and(|e| !e.is_block_like()) && arm.comma_token().is_none()
-        }
-    }
-}
-
 impl ast::LetStmt {
     pub fn set_ty(&self, ty: Option<ast::Type>) {
         match ty {
diff --git a/src/tools/rust-analyzer/crates/syntax/src/ast/generated/nodes.rs b/src/tools/rust-analyzer/crates/syntax/src/ast/generated/nodes.rs
index 69e2a9f9c1b..58c76a456ab 100644
--- a/src/tools/rust-analyzer/crates/syntax/src/ast/generated/nodes.rs
+++ b/src/tools/rust-analyzer/crates/syntax/src/ast/generated/nodes.rs
@@ -1,4 +1,4 @@
-//! Generated by `cargo codegen grammar`, do not edit by hand.
+//! Generated by `cargo xtask codegen grammar`, do not edit by hand.
 
 #![allow(non_snake_case)]
 use crate::{
@@ -1539,9 +1539,13 @@ impl ast::HasName for RecordField {}
 impl ast::HasVisibility for RecordField {}
 impl RecordField {
     #[inline]
+    pub fn expr(&self) -> Option<Expr> { support::child(&self.syntax) }
+    #[inline]
     pub fn ty(&self) -> Option<Type> { support::child(&self.syntax) }
     #[inline]
     pub fn colon_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![:]) }
+    #[inline]
+    pub fn eq_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![=]) }
 }
 
 #[derive(Debug, Clone, PartialEq, Eq, Hash)]
diff --git a/src/tools/rust-analyzer/crates/syntax/src/ast/generated/tokens.rs b/src/tools/rust-analyzer/crates/syntax/src/ast/generated/tokens.rs
index 85d20c2bd8c..df2e9619db1 100644
--- a/src/tools/rust-analyzer/crates/syntax/src/ast/generated/tokens.rs
+++ b/src/tools/rust-analyzer/crates/syntax/src/ast/generated/tokens.rs
@@ -1,4 +1,4 @@
-//! Generated by `cargo codegen grammar`, do not edit by hand.
+//! Generated by `cargo xtask codegen grammar`, do not edit by hand.
 
 use crate::{
     ast::AstToken,
diff --git a/src/tools/rust-analyzer/crates/syntax/src/ast/make.rs b/src/tools/rust-analyzer/crates/syntax/src/ast/make.rs
index ff027ac5848..9dc2d832530 100644
--- a/src/tools/rust-analyzer/crates/syntax/src/ast/make.rs
+++ b/src/tools/rust-analyzer/crates/syntax/src/ast/make.rs
@@ -837,7 +837,8 @@ pub fn match_guard(condition: ast::Expr) -> ast::MatchGuard {
 
 pub fn match_arm_list(arms: impl IntoIterator<Item = ast::MatchArm>) -> ast::MatchArmList {
     let arms_str = arms.into_iter().fold(String::new(), |mut acc, arm| {
-        let needs_comma = arm.expr().is_none_or(|it| !it.is_block_like());
+        let needs_comma =
+            arm.comma_token().is_none() && arm.expr().is_none_or(|it| !it.is_block_like());
         let comma = if needs_comma { "," } else { "" };
         let arm = arm.syntax();
         format_to_acc!(acc, "    {arm}{comma}\n")
diff --git a/src/tools/rust-analyzer/crates/syntax/src/ast/prec.rs b/src/tools/rust-analyzer/crates/syntax/src/ast/prec.rs
index 28089ffb377..5d33f132ac1 100644
--- a/src/tools/rust-analyzer/crates/syntax/src/ast/prec.rs
+++ b/src/tools/rust-analyzer/crates/syntax/src/ast/prec.rs
@@ -5,7 +5,122 @@ use crate::{
     match_ast, AstNode, SyntaxNode,
 };
 
+#[derive(Debug, Clone, Copy, PartialEq, PartialOrd)]
+pub enum ExprPrecedence {
+    // return, break, yield, closures
+    Jump,
+    // = += -= *= /= %= &= |= ^= <<= >>=
+    Assign,
+    // .. ..=
+    Range,
+    // ||
+    LOr,
+    // &&
+    LAnd,
+    // == != < > <= >=
+    Compare,
+    // |
+    BitOr,
+    // ^
+    BitXor,
+    // &
+    BitAnd,
+    // << >>
+    Shift,
+    // + -
+    Sum,
+    // * / %
+    Product,
+    // as
+    Cast,
+    // unary - * ! & &mut
+    Prefix,
+    // paths, loops, function calls, array indexing, field expressions, method calls
+    Unambiguous,
+}
+
+#[derive(PartialEq, Debug)]
+pub enum Fixity {
+    /// The operator is left-associative
+    Left,
+    /// The operator is right-associative
+    Right,
+    /// The operator is not associative
+    None,
+}
+
+pub fn precedence(expr: &ast::Expr) -> ExprPrecedence {
+    match expr {
+        Expr::ClosureExpr(closure) => match closure.ret_type() {
+            None => ExprPrecedence::Jump,
+            Some(_) => ExprPrecedence::Unambiguous,
+        },
+
+        Expr::BreakExpr(_)
+        | Expr::ContinueExpr(_)
+        | Expr::ReturnExpr(_)
+        | Expr::YeetExpr(_)
+        | Expr::YieldExpr(_) => ExprPrecedence::Jump,
+
+        Expr::RangeExpr(..) => ExprPrecedence::Range,
+
+        Expr::BinExpr(bin_expr) => match bin_expr.op_kind() {
+            Some(it) => match it {
+                BinaryOp::LogicOp(logic_op) => match logic_op {
+                    ast::LogicOp::And => ExprPrecedence::LAnd,
+                    ast::LogicOp::Or => ExprPrecedence::LOr,
+                },
+                BinaryOp::ArithOp(arith_op) => match arith_op {
+                    ast::ArithOp::Add | ast::ArithOp::Sub => ExprPrecedence::Sum,
+                    ast::ArithOp::Div | ast::ArithOp::Rem | ast::ArithOp::Mul => {
+                        ExprPrecedence::Product
+                    }
+                    ast::ArithOp::Shl | ast::ArithOp::Shr => ExprPrecedence::Shift,
+                    ast::ArithOp::BitXor => ExprPrecedence::BitXor,
+                    ast::ArithOp::BitOr => ExprPrecedence::BitOr,
+                    ast::ArithOp::BitAnd => ExprPrecedence::BitAnd,
+                },
+                BinaryOp::CmpOp(_) => ExprPrecedence::Compare,
+                BinaryOp::Assignment { .. } => ExprPrecedence::Assign,
+            },
+            None => ExprPrecedence::Unambiguous,
+        },
+        Expr::CastExpr(_) => ExprPrecedence::Cast,
+
+        Expr::LetExpr(_) | Expr::PrefixExpr(_) | Expr::RefExpr(_) => ExprPrecedence::Prefix,
+
+        Expr::ArrayExpr(_)
+        | Expr::AsmExpr(_)
+        | Expr::AwaitExpr(_)
+        | Expr::BecomeExpr(_)
+        | Expr::BlockExpr(_)
+        | Expr::CallExpr(_)
+        | Expr::FieldExpr(_)
+        | Expr::ForExpr(_)
+        | Expr::FormatArgsExpr(_)
+        | Expr::IfExpr(_)
+        | Expr::IndexExpr(_)
+        | Expr::Literal(_)
+        | Expr::LoopExpr(_)
+        | Expr::MacroExpr(_)
+        | Expr::MatchExpr(_)
+        | Expr::MethodCallExpr(_)
+        | Expr::OffsetOfExpr(_)
+        | Expr::ParenExpr(_)
+        | Expr::PathExpr(_)
+        | Expr::RecordExpr(_)
+        | Expr::TryExpr(_)
+        | Expr::TupleExpr(_)
+        | Expr::UnderscoreExpr(_)
+        | Expr::WhileExpr(_) => ExprPrecedence::Unambiguous,
+    }
+}
+
 impl Expr {
+    pub fn precedence(&self) -> ExprPrecedence {
+        precedence(self)
+    }
+
     // Implementation is based on
     // - https://doc.rust-lang.org/reference/expressions.html#expression-precedence
     // - https://matklad.github.io/2020/04/13/simple-but-powerful-pratt-parsing.html
@@ -261,7 +376,7 @@ impl Expr {
     }
 
     /// Returns true if self is one of `return`, `break`, `continue` or `yield` with **no associated value**.
-    fn is_ret_like_with_no_value(&self) -> bool {
+    pub fn is_ret_like_with_no_value(&self) -> bool {
         use Expr::*;
 
         match self {
diff --git a/src/tools/rust-analyzer/crates/test-fixture/src/lib.rs b/src/tools/rust-analyzer/crates/test-fixture/src/lib.rs
index 866379d940e..37dfb87721c 100644
--- a/src/tools/rust-analyzer/crates/test-fixture/src/lib.rs
+++ b/src/tools/rust-analyzer/crates/test-fixture/src/lib.rs
@@ -17,7 +17,7 @@ use hir_expand::{
     tt::{Leaf, TokenTree, TopSubtree, TopSubtreeBuilder, TtElement, TtIter},
     FileRange,
 };
-use intern::Symbol;
+use intern::{sym, Symbol};
 use rustc_hash::FxHashMap;
 use span::{Edition, EditionedFileId, FileId, Span};
 use stdx::itertools::Itertools;
@@ -211,8 +211,9 @@ impl ChangeFixture {
                     From::from(meta.cfg.clone()),
                     Some(From::from(meta.cfg)),
                     meta.env,
-                    false,
                     origin,
+                    false,
+                    None,
                 );
                 let prev = crates.insert(crate_name.clone(), crate_id);
                 assert!(prev.is_none(), "multiple crates with same name: {crate_name}");
@@ -249,8 +250,9 @@ impl ChangeFixture {
                 From::from(default_cfg.clone()),
                 Some(From::from(default_cfg)),
                 default_env,
-                false,
                 CrateOrigin::Local { repo: None, name: None },
+                false,
+                None,
             );
         } else {
             for (from, to, prelude) in crate_deps {
@@ -258,15 +260,7 @@ impl ChangeFixture {
                 let to_id = crates[&to];
                 let sysroot = crate_graph[to_id].origin.is_lang();
                 crate_graph
-                    .add_dep(
-                        from_id,
-                        Dependency::with_prelude(
-                            CrateName::new(&to).unwrap(),
-                            to_id,
-                            prelude,
-                            sysroot,
-                        ),
-                    )
+                    .add_dep(from_id, Dependency::with_prelude(to.clone(), to_id, prelude, sysroot))
                     .unwrap();
             }
         }
@@ -294,8 +288,9 @@ impl ChangeFixture {
                     String::from("__ra_is_test_fixture"),
                     String::from("__ra_is_test_fixture"),
                 )]),
-                false,
                 CrateOrigin::Lang(LangCrateOrigin::Core),
+                false,
+                None,
             );
 
             for krate in all_crates {
@@ -341,8 +336,9 @@ impl ChangeFixture {
                     String::from("__ra_is_test_fixture"),
                     String::from("__ra_is_test_fixture"),
                 )]),
-                true,
                 CrateOrigin::Local { repo: None, name: None },
+                true,
+                None,
             );
             proc_macros.insert(proc_macros_crate, Ok(proc_macro));
 
@@ -370,7 +366,6 @@ impl ChangeFixture {
             crate_graph
                 .iter()
                 .zip(iter::repeat(From::from(CrateWorkspaceData {
-                    proc_macro_cwd: None,
                     data_layout: target_data_layout,
                     toolchain,
                 })))
@@ -519,6 +514,21 @@ pub fn issue_18898(_attr: TokenStream, input: TokenStream) -> TokenStream {
                 disabled: false,
             },
         ),
+        (
+            r#"
+#[proc_macro_attribute]
+pub fn disallow_cfg(_attr: TokenStream, input: TokenStream) -> TokenStream {
+    input
+}
+"#
+            .into(),
+            ProcMacro {
+                name: Symbol::intern("disallow_cfg"),
+                kind: ProcMacroKind::Attr,
+                expander: sync::Arc::new(DisallowCfgProcMacroExpander),
+                disabled: false,
+            },
+        ),
     ])
 }
 
@@ -873,3 +883,30 @@ impl ProcMacroExpander for Issue18898ProcMacroExpander {
         })
     }
 }
+
+// Reads ident type within string quotes, for issue #17479.
+#[derive(Debug)]
+struct DisallowCfgProcMacroExpander;
+impl ProcMacroExpander for DisallowCfgProcMacroExpander {
+    fn expand(
+        &self,
+        subtree: &TopSubtree,
+        _: Option<&TopSubtree>,
+        _: &Env,
+        _: Span,
+        _: Span,
+        _: Span,
+        _: Option<String>,
+    ) -> Result<TopSubtree, ProcMacroExpansionError> {
+        for tt in subtree.token_trees().flat_tokens() {
+            if let tt::TokenTree::Leaf(tt::Leaf::Ident(ident)) = tt {
+                if ident.sym == sym::cfg || ident.sym == sym::cfg_attr {
+                    return Err(ProcMacroExpansionError::Panic(
+                        "cfg or cfg_attr found in DisallowCfgProcMacroExpander".to_owned(),
+                    ));
+                }
+            }
+        }
+        Ok(subtree.clone())
+    }
+}
diff --git a/src/tools/rust-analyzer/crates/test-utils/src/lib.rs b/src/tools/rust-analyzer/crates/test-utils/src/lib.rs
index 36be9937d3f..e7279fa1f66 100644
--- a/src/tools/rust-analyzer/crates/test-utils/src/lib.rs
+++ b/src/tools/rust-analyzer/crates/test-utils/src/lib.rs
@@ -396,12 +396,19 @@ pub fn skip_slow_tests() -> bool {
     if should_skip {
         eprintln!("ignoring slow test");
     } else {
-        let path = project_root().join("./target/.slow_tests_cookie");
+        let path = target_dir().join(".slow_tests_cookie");
         fs::write(path, ".").unwrap();
     }
     should_skip
 }
 
+pub fn target_dir() -> Utf8PathBuf {
+    match std::env::var("CARGO_TARGET_DIR") {
+        Ok(target) => Utf8PathBuf::from(target),
+        Err(_) => project_root().join("target"),
+    }
+}
+
 /// Returns the path to the root directory of `rust-analyzer` project.
 pub fn project_root() -> Utf8PathBuf {
     let dir = env!("CARGO_MANIFEST_DIR");
diff --git a/src/tools/rust-analyzer/crates/test-utils/src/minicore.rs b/src/tools/rust-analyzer/crates/test-utils/src/minicore.rs
index 4ed68d18e80..202afebde70 100644
--- a/src/tools/rust-analyzer/crates/test-utils/src/minicore.rs
+++ b/src/tools/rust-analyzer/crates/test-utils/src/minicore.rs
@@ -647,18 +647,21 @@ pub mod ops {
 
         #[lang = "fn"]
         #[fundamental]
+        #[rustc_paren_sugar]
         pub trait Fn<Args: Tuple>: FnMut<Args> {
             extern "rust-call" fn call(&self, args: Args) -> Self::Output;
         }
 
         #[lang = "fn_mut"]
         #[fundamental]
+        #[rustc_paren_sugar]
         pub trait FnMut<Args: Tuple>: FnOnce<Args> {
             extern "rust-call" fn call_mut(&mut self, args: Args) -> Self::Output;
         }
 
         #[lang = "fn_once"]
         #[fundamental]
+        #[rustc_paren_sugar]
         pub trait FnOnce<Args: Tuple> {
             #[lang = "fn_once_output"]
             type Output;
@@ -736,12 +739,14 @@ pub mod ops {
 
         #[lang = "async_fn"]
         #[fundamental]
+        #[rustc_paren_sugar]
         pub trait AsyncFn<Args: Tuple>: AsyncFnMut<Args> {
             extern "rust-call" fn async_call(&self, args: Args) -> Self::CallRefFuture<'_>;
         }
 
         #[lang = "async_fn_mut"]
         #[fundamental]
+        #[rustc_paren_sugar]
         pub trait AsyncFnMut<Args: Tuple>: AsyncFnOnce<Args> {
             #[lang = "call_ref_future"]
             type CallRefFuture<'a>: Future<Output = Self::Output>
@@ -752,6 +757,7 @@ pub mod ops {
 
         #[lang = "async_fn_once"]
         #[fundamental]
+        #[rustc_paren_sugar]
         pub trait AsyncFnOnce<Args: Tuple> {
             #[lang = "async_fn_once_output"]
             type Output;
diff --git a/src/tools/rust-analyzer/crates/vfs-notify/src/lib.rs b/src/tools/rust-analyzer/crates/vfs-notify/src/lib.rs
index 0ae8b7baf46..32003341764 100644
--- a/src/tools/rust-analyzer/crates/vfs-notify/src/lib.rs
+++ b/src/tools/rust-analyzer/crates/vfs-notify/src/lib.rs
@@ -280,8 +280,9 @@ impl NotifyActor {
                                 return false;
                             }
 
-                            root == path
-                                || dirs.exclude.iter().chain(&dirs.include).all(|it| it != path)
+                            // We want to filter out subdirectories that are roots themselves, because they will be visited separately.
+                            dirs.exclude.iter().all(|it| it != path)
+                                && (root == path || dirs.include.iter().all(|it| it != path))
                         });
 
                     let files = walkdir.filter_map(|it| it.ok()).filter_map(|entry| {
diff --git a/src/tools/rust-analyzer/crates/vfs/src/lib.rs b/src/tools/rust-analyzer/crates/vfs/src/lib.rs
index a26444e9ea2..3feca512e55 100644
--- a/src/tools/rust-analyzer/crates/vfs/src/lib.rs
+++ b/src/tools/rust-analyzer/crates/vfs/src/lib.rs
@@ -100,6 +100,9 @@ pub enum FileState {
     Exists(u64),
     /// The file is deleted.
     Deleted,
+    /// The file was specifically excluded by the user. We still include excluded files
+    /// when they're opened (without their contents).
+    Excluded,
 }
 
 /// Changed file in the [`Vfs`].
@@ -164,10 +167,22 @@ pub enum ChangeKind {
     Delete,
 }
 
+#[derive(Debug, Clone, Copy, PartialEq, Eq)]
+pub enum FileExcluded {
+    Yes,
+    No,
+}
+
 impl Vfs {
     /// Id of the given path if it exists in the `Vfs` and is not deleted.
-    pub fn file_id(&self, path: &VfsPath) -> Option<FileId> {
-        self.interner.get(path).filter(|&it| matches!(self.get(it), FileState::Exists(_)))
+    pub fn file_id(&self, path: &VfsPath) -> Option<(FileId, FileExcluded)> {
+        let file_id = self.interner.get(path)?;
+        let file_state = self.get(file_id);
+        match file_state {
+            FileState::Exists(_) => Some((file_id, FileExcluded::No)),
+            FileState::Deleted => None,
+            FileState::Excluded => Some((file_id, FileExcluded::Yes)),
+        }
     }
 
     /// File path corresponding to the given `file_id`.
@@ -216,6 +231,7 @@ impl Vfs {
                 }
                 Change::Modify(v, new_hash)
             }
+            (FileState::Excluded, _) => return false,
         };
 
         let mut set_data = |change_kind| {
@@ -297,6 +313,13 @@ impl Vfs {
     fn get(&self, file_id: FileId) -> FileState {
         self.data[file_id.0 as usize]
     }
+
+    /// We cannot ignore excluded files, because this will lead to errors when the client
+    /// requests semantic information for them, so we instead mark them specially.
+    pub fn insert_excluded_file(&mut self, path: VfsPath) {
+        let file_id = self.alloc_file_id(path);
+        self.data[file_id.0 as usize] = FileState::Excluded;
+    }
 }
 
 impl fmt::Debug for Vfs {
diff --git a/src/tools/rust-analyzer/docs/book/README.md b/src/tools/rust-analyzer/docs/book/README.md
new file mode 100644
index 00000000000..043524b2341
--- /dev/null
+++ b/src/tools/rust-analyzer/docs/book/README.md
@@ -0,0 +1,29 @@
+# rust-analyzer documentation
+
+The rust analyzer manual uses [mdbook](https://rust-lang.github.io/mdBook/).
+
+## Quick start
+
+To run the documentation site locally:
+
+```shell
+cargo install mdbook
+cd docs/book
+mdbook serve
+# make changes to documentation files in doc/book/src
+# ...
+```
+
+mdbook will rebuild the documentation as changes are made.
+
+## Making updates
+
+While not required, installing the mdbook binary can be helfpul in order to see the changes.
+Start with the mdbook [User Guide](https://rust-lang.github.io/mdBook/guide/installation.html) to familiarize yourself with the tool.
+
+## Generated documentation
+
+Four sections are generated dynamically: assists, configuration, diagnostics and features. Their content is found in the `generated.md` files
+of the respective book section, for example `src/configuration_generated.md`, and are included in the book via mdbook's
+[include](https://rust-lang.github.io/mdBook/format/mdbook.html#including-files) functionality. Generated files can be rebuilt by running the various
+test cases that generate them, or by simply running all of the `rust-analyzer` tests with `cargo test` and `cargo xtask codegen`.
diff --git a/src/tools/rust-analyzer/docs/book/book.toml b/src/tools/rust-analyzer/docs/book/book.toml
new file mode 100644
index 00000000000..a6f6a6ed784
--- /dev/null
+++ b/src/tools/rust-analyzer/docs/book/book.toml
@@ -0,0 +1,41 @@
+[book]
+authors = ["The rust-analyzer authors"]
+language = "en"
+multilingual = false
+src = "src"
+title = "rust-analyzer"
+
+[rust]
+edition = "2021"
+
+[output.html]
+edit-url-template = "https://github.com/rust-lang/rust-analyzer/edit/master/docs/book/{path}"
+git-repository-url = "https://github.com/rust-lang/rust-analyzer/tree/master/docs/book"
+mathjax-support = true
+site-url = "/book/"
+
+[output.html.playground]
+editable = true
+runnable = false
+line-numbers = true
+
+[output.html.search]
+boost-hierarchy = 2
+boost-paragraph = 1
+boost-title = 2
+expand = true
+heading-split-level = 2
+limit-results = 20
+use-boolean-and = true
+
+[output.html.redirect]
+"/manual.html" = "/index.html"
+
+[output.html.fold]
+enable = true
+level = 3
+
+[preprocessor.toc]
+command = "mdbook-toc"
+renderer = ["html"]
+max-level = 3
diff --git a/src/tools/rust-analyzer/docs/book/src/README.md b/src/tools/rust-analyzer/docs/book/src/README.md
new file mode 100644
index 00000000000..71f34e03466
--- /dev/null
+++ b/src/tools/rust-analyzer/docs/book/src/README.md
@@ -0,0 +1,21 @@
+# rust-analyzer
+
+At its core, rust-analyzer is a **library** for semantic analysis of
+Rust code as it changes over time. This manual focuses on a specific
+usage of the library -- running it as part of a server that implements
+the [Language Server
+Protocol](https://microsoft.github.io/language-server-protocol/) (LSP).
+The LSP allows various code editors, like VS Code, Emacs or Vim, to
+implement semantic features like completion or goto definition by
+talking to an external language server process.
+
+To improve this document, send a pull request:
+[https://github.com/rust-lang/rust-analyzer](https://github.com/rust-lang/rust-analyzer/blob/master/docs/book/README.md)
+
+The manual is written in markdown and includes
+some extra files which are generated from the source code. Run
+`cargo test` and `cargo xtask codegen` to create these.
+
+If you have questions about using rust-analyzer, please ask them in the
+["IDEs and Editors"](https://users.rust-lang.org/c/ide/14) topic of Rust
+users forum.
diff --git a/src/tools/rust-analyzer/docs/book/src/SUMMARY.md b/src/tools/rust-analyzer/docs/book/src/SUMMARY.md
new file mode 100644
index 00000000000..1f211a97d78
--- /dev/null
+++ b/src/tools/rust-analyzer/docs/book/src/SUMMARY.md
@@ -0,0 +1,24 @@
+# Summary
+
+- [Introduction](README.md)
+- [Installation](installation.md)
+  - [VS Code](vs_code.md)
+  - [rust-analyzer Binary](rust_analyzer_binary.md)
+  - [Other Editors](other_editors.md)
+- [Troubleshooting](troubleshooting.md)
+- [Configuration](configuration.md)
+  - [Non-Cargo Based Projects](non_cargo_based_projects.md)
+- [Security](security.md)
+- [Privacy](privacy.md)
+- [Features](features.md)
+  - [Assists (Code Actions)](assists.md)
+  - [Diagnostics](diagnostics.md)
+- [Editor Features](editor_features.md)
+- [Contributing](contributing/README.md)
+  - [Architecture](contributing/architecture.md)
+  - [Debugging](contributing/debugging.md)
+  - [Guide](contributing/guide.md)
+  - [LSP Extensions](contributing/lsp-extensions.md)
+  - [Setup](contributing/setup.md)
+  - [Style](contributing/style.md)
+  - [Syntax](contributing/syntax.md)
diff --git a/src/tools/rust-analyzer/docs/book/src/assists.md b/src/tools/rust-analyzer/docs/book/src/assists.md
new file mode 100644
index 00000000000..9d7c3bc1d5b
--- /dev/null
+++ b/src/tools/rust-analyzer/docs/book/src/assists.md
@@ -0,0 +1,8 @@
+# Assists
+
+Assists, or code actions, are small local refactorings, available in a
+particular context. They are usually triggered by a shortcut or by
+clicking a light bulb icon in the editor. Cursor position or selection
+is signified by `┃` character.
+
+{{#include assists_generated.md:2:}}
diff --git a/src/tools/rust-analyzer/docs/book/src/assists_generated.md b/src/tools/rust-analyzer/docs/book/src/assists_generated.md
new file mode 100644
index 00000000000..9d68a873ffe
--- /dev/null
+++ b/src/tools/rust-analyzer/docs/book/src/assists_generated.md
@@ -0,0 +1,3846 @@
+//! Generated by `cargo xtask codegen assists-doc-tests`, do not edit by hand.
+
+### `add_braces`
+**Source:**  [add_braces.rs](https://github.com/rust-lang/rust-analyzer/blob/master/crates/ide-assists/src/handlers/add_braces.rs#L8) 
+
+Adds braces to lambda and match arm expressions.
+
+#### Before
+```rust
+fn foo(n: i32) -> i32 {
+    match n {
+        1 =>┃ n + 1,
+        _ => 0
+    }
+}
+```
+
+#### After
+```rust
+fn foo(n: i32) -> i32 {
+    match n {
+        1 => {
+            n + 1
+        },
+        _ => 0
+    }
+}
+```
+
+
+### `add_explicit_type`
+**Source:**  [add_explicit_type.rs](https://github.com/rust-lang/rust-analyzer/blob/master/crates/ide-assists/src/handlers/add_explicit_type.rs#L7) 
+
+Specify type for a let binding.
+
+#### Before
+```rust
+fn main() {
+    let x┃ = 92;
+}
+```
+
+#### After
+```rust
+fn main() {
+    let x: i32 = 92;
+}
+```
+
+
+### `add_hash`
+**Source:**  [raw_string.rs](https://github.com/rust-lang/rust-analyzer/blob/master/crates/ide-assists/src/handlers/raw_string.rs#L89) 
+
+Adds a hash to a raw string literal.
+
+#### Before
+```rust
+fn main() {
+    r#"Hello,┃ World!"#;
+}
+```
+
+#### After
+```rust
+fn main() {
+    r##"Hello, World!"##;
+}
+```
+
+
+### `add_impl_default_members`
+**Source:**  [add_missing_impl_members.rs](https://github.com/rust-lang/rust-analyzer/blob/master/crates/ide-assists/src/handlers/add_missing_impl_members.rs#L58) 
+
+Adds scaffold for overriding default impl members.
+
+#### Before
+```rust
+trait Trait {
+    type X;
+    fn foo(&self);
+    fn bar(&self) {}
+}
+
+impl Trait for () {
+    type X = ();
+    fn foo(&self) {}┃
+}
+```
+
+#### After
+```rust
+trait Trait {
+    type X;
+    fn foo(&self);
+    fn bar(&self) {}
+}
+
+impl Trait for () {
+    type X = ();
+    fn foo(&self) {}
+
+    ┃fn bar(&self) {}
+}
+```
+
+
+### `add_impl_missing_members`
+**Source:**  [add_missing_impl_members.rs](https://github.com/rust-lang/rust-analyzer/blob/master/crates/ide-assists/src/handlers/add_missing_impl_members.rs#L16) 
+
+Adds scaffold for required impl members.
+
+#### Before
+```rust
+trait Trait<T> {
+    type X;
+    fn foo(&self) -> T;
+    fn bar(&self) {}
+}
+
+impl Trait<u32> for () {┃
+
+}
+```
+
+#### After
+```rust
+trait Trait<T> {
+    type X;
+    fn foo(&self) -> T;
+    fn bar(&self) {}
+}
+
+impl Trait<u32> for () {
+    ┃type X;
+
+    fn foo(&self) -> u32 {
+        todo!()
+    }
+}
+```
+
+
+### `add_label_to_loop`
+**Source:**  [add_label_to_loop.rs](https://github.com/rust-lang/rust-analyzer/blob/master/crates/ide-assists/src/handlers/add_label_to_loop.rs#L9) 
+
+Adds a label to a loop.
+
+#### Before
+```rust
+fn main() {
+    loop┃ {
+        break;
+        continue;
+    }
+}
+```
+
+#### After
+```rust
+fn main() {
+    'l: loop {
+        break 'l;
+        continue 'l;
+    }
+}
+```
+
+
+### `add_lifetime_to_type`
+**Source:**  [add_lifetime_to_type.rs](https://github.com/rust-lang/rust-analyzer/blob/master/crates/ide-assists/src/handlers/add_lifetime_to_type.rs#L5) 
+
+Adds a new lifetime to a struct, enum or union.
+
+#### Before
+```rust
+struct Point {
+    x: &┃u32,
+    y: u32,
+}
+```
+
+#### After
+```rust
+struct Point<'a> {
+    x: &'a u32,
+    y: u32,
+}
+```
+
+
+### `add_missing_match_arms`
+**Source:**  [add_missing_match_arms.rs](https://github.com/rust-lang/rust-analyzer/blob/master/crates/ide-assists/src/handlers/add_missing_match_arms.rs#L16) 
+
+Adds missing clauses to a `match` expression.
+
+#### Before
+```rust
+enum Action { Move { distance: u32 }, Stop }
+
+fn handle(action: Action) {
+    match action {
+        ┃
+    }
+}
+```
+
+#### After
+```rust
+enum Action { Move { distance: u32 }, Stop }
+
+fn handle(action: Action) {
+    match action {
+        Action::Move { distance } => ${1:todo!()},
+        Action::Stop => ${2:todo!()},┃
+    }
+}
+```
+
+
+### `add_return_type`
+**Source:**  [add_return_type.rs](https://github.com/rust-lang/rust-analyzer/blob/master/crates/ide-assists/src/handlers/add_return_type.rs#L6) 
+
+Adds the return type to a function or closure inferred from its tail expression if it doesn't have a return
+type specified. This assists is useable in a functions or closures tail expression or return type position.
+
+#### Before
+```rust
+fn foo() { 4┃2i32 }
+```
+
+#### After
+```rust
+fn foo() -> i32 { 42i32 }
+```
+
+
+### `add_turbo_fish`
+**Source:**  [add_turbo_fish.rs](https://github.com/rust-lang/rust-analyzer/blob/master/crates/ide-assists/src/handlers/add_turbo_fish.rs#L14) 
+
+Adds `::<_>` to a call of a generic method or function.
+
+#### Before
+```rust
+fn make<T>() -> T { todo!() }
+fn main() {
+    let x = make┃();
+}
+```
+
+#### After
+```rust
+fn make<T>() -> T { todo!() }
+fn main() {
+    let x = make::<${0:_}>();
+}
+```
+
+
+### `apply_demorgan`
+**Source:**  [apply_demorgan.rs](https://github.com/rust-lang/rust-analyzer/blob/master/crates/ide-assists/src/handlers/apply_demorgan.rs#L16) 
+
+Apply [De Morgan's law](https://en.wikipedia.org/wiki/De_Morgan%27s_laws).
+This transforms expressions of the form `!l || !r` into `!(l && r)`.
+This also works with `&&`. This assist can only be applied with the cursor
+on either `||` or `&&`.
+
+#### Before
+```rust
+fn main() {
+    if x != 4 ||┃ y < 3.14 {}
+}
+```
+
+#### After
+```rust
+fn main() {
+    if !(x == 4 && y >= 3.14) {}
+}
+```
+
+
+### `apply_demorgan_iterator`
+**Source:**  [apply_demorgan.rs](https://github.com/rust-lang/rust-analyzer/blob/master/crates/ide-assists/src/handlers/apply_demorgan.rs#L132) 
+
+Apply [De Morgan's law](https://en.wikipedia.org/wiki/De_Morgan%27s_laws) to
+`Iterator::all` and `Iterator::any`.
+
+This transforms expressions of the form `!iter.any(|x| predicate(x))` into
+`iter.all(|x| !predicate(x))` and vice versa. This also works the other way for
+`Iterator::all` into `Iterator::any`.
+
+#### Before
+```rust
+fn main() {
+    let arr = [1, 2, 3];
+    if !arr.into_iter().┃any(|num| num == 4) {
+        println!("foo");
+    }
+}
+```
+
+#### After
+```rust
+fn main() {
+    let arr = [1, 2, 3];
+    if arr.into_iter().all(|num| num != 4) {
+        println!("foo");
+    }
+}
+```
+
+
+### `auto_import`
+**Source:**  [auto_import.rs](https://github.com/rust-lang/rust-analyzer/blob/master/crates/ide-assists/src/handlers/auto_import.rs#L73) 
+
+If the name is unresolved, provides all possible imports for it.
+
+#### Before
+```rust
+fn main() {
+    let map = HashMap┃::new();
+}
+```
+
+#### After
+```rust
+use std::collections::HashMap;
+
+fn main() {
+    let map = HashMap::new();
+}
+```
+
+
+### `bind_unused_param`
+**Source:**  [bind_unused_param.rs](https://github.com/rust-lang/rust-analyzer/blob/master/crates/ide-assists/src/handlers/bind_unused_param.rs#L12) 
+
+Binds unused function parameter to an underscore.
+
+#### Before
+```rust
+fn some_function(x: i32┃) {}
+```
+
+#### After
+```rust
+fn some_function(x: i32) {
+    let _ = x;
+}
+```
+
+
+### `bool_to_enum`
+**Source:**  [bool_to_enum.rs](https://github.com/rust-lang/rust-analyzer/blob/master/crates/ide-assists/src/handlers/bool_to_enum.rs#L29) 
+
+This converts boolean local variables, fields, constants, and statics into a new
+enum with two variants `Bool::True` and `Bool::False`, as well as replacing
+all assignments with the variants and replacing all usages with `== Bool::True` or
+`== Bool::False`.
+
+#### Before
+```rust
+fn main() {
+    let ┃bool = true;
+
+    if bool {
+        println!("foo");
+    }
+}
+```
+
+#### After
+```rust
+#[derive(PartialEq, Eq)]
+enum Bool { True, False }
+
+fn main() {
+    let bool = Bool::True;
+
+    if bool == Bool::True {
+        println!("foo");
+    }
+}
+```
+
+
+### `change_visibility`
+**Source:**  [change_visibility.rs](https://github.com/rust-lang/rust-analyzer/blob/master/crates/ide-assists/src/handlers/change_visibility.rs#L13) 
+
+Adds or changes existing visibility specifier.
+
+#### Before
+```rust
+┃fn frobnicate() {}
+```
+
+#### After
+```rust
+pub(crate) fn frobnicate() {}
+```
+
+
+### `comment_to_doc`
+**Source:**  [convert_comment_from_or_to_doc.rs](https://github.com/rust-lang/rust-analyzer/blob/master/crates/ide-assists/src/handlers/convert_comment_from_or_to_doc.rs#L9) 
+
+Converts comments to documentation.
+
+#### Before
+```rust
+// Wow what ┃a nice module
+// I sure hope this shows up when I hover over it
+```
+
+#### After
+```rust
+//! Wow what a nice module
+//! I sure hope this shows up when I hover over it
+```
+
+
+### `convert_bool_then_to_if`
+**Source:**  [convert_bool_then.rs](https://github.com/rust-lang/rust-analyzer/blob/master/crates/ide-assists/src/handlers/convert_bool_then.rs#L131) 
+
+Converts a `bool::then` method call to an equivalent if expression.
+
+#### Before
+```rust
+fn main() {
+    (0 == 0).then┃(|| val)
+}
+```
+
+#### After
+```rust
+fn main() {
+    if 0 == 0 {
+        Some(val)
+    } else {
+        None
+    }
+}
+```
+
+
+### `convert_closure_to_fn`
+**Source:**  [convert_closure_to_fn.rs](https://github.com/rust-lang/rust-analyzer/blob/master/crates/ide-assists/src/handlers/convert_closure_to_fn.rs#L25) 
+
+This converts a closure to a freestanding function, changing all captures to parameters.
+
+#### Before
+```rust
+fn main() {
+    let mut s = String::new();
+    let closure = |┃a| s.push_str(a);
+    closure("abc");
+}
+```
+
+#### After
+```rust
+fn main() {
+    let mut s = String::new();
+    fn closure(a: &str, s: &mut String) {
+        s.push_str(a)
+    }
+    closure("abc", &mut s);
+}
+```
+
+
+### `convert_for_loop_with_for_each`
+**Source:**  [convert_iter_for_each_to_for.rs](https://github.com/rust-lang/rust-analyzer/blob/master/crates/ide-assists/src/handlers/convert_iter_for_each_to_for.rs#L76) 
+
+Converts a for loop into a for_each loop on the Iterator.
+
+#### Before
+```rust
+fn main() {
+    let x = vec![1, 2, 3];
+    for┃ v in x {
+        let y = v * 2;
+    }
+}
+```
+
+#### After
+```rust
+fn main() {
+    let x = vec![1, 2, 3];
+    x.into_iter().for_each(|v| {
+        let y = v * 2;
+    });
+}
+```
+
+
+### `convert_from_to_tryfrom`
+**Source:**  [convert_from_to_tryfrom.rs](https://github.com/rust-lang/rust-analyzer/blob/master/crates/ide-assists/src/handlers/convert_from_to_tryfrom.rs#L10) 
+
+Converts a From impl to a TryFrom impl, wrapping returns in `Ok`.
+
+#### Before
+```rust
+impl ┃From<usize> for Thing {
+    fn from(val: usize) -> Self {
+        Thing {
+            b: val.to_string(),
+            a: val
+        }
+    }
+}
+```
+
+#### After
+```rust
+impl TryFrom<usize> for Thing {
+    type Error = ${0:()};
+
+    fn try_from(val: usize) -> Result<Self, Self::Error> {
+        Ok(Thing {
+            b: val.to_string(),
+            a: val
+        })
+    }
+}
+```
+
+
+### `convert_if_to_bool_then`
+**Source:**  [convert_bool_then.rs](https://github.com/rust-lang/rust-analyzer/blob/master/crates/ide-assists/src/handlers/convert_bool_then.rs#L20) 
+
+Converts an if expression into a corresponding `bool::then` call.
+
+#### Before
+```rust
+fn main() {
+    if┃ cond {
+        Some(val)
+    } else {
+        None
+    }
+}
+```
+
+#### After
+```rust
+fn main() {
+    cond.then(|| val)
+}
+```
+
+
+### `convert_integer_literal`
+**Source:**  [convert_integer_literal.rs](https://github.com/rust-lang/rust-analyzer/blob/master/crates/ide-assists/src/handlers/convert_integer_literal.rs#L5) 
+
+Converts the base of integer literals to other bases.
+
+#### Before
+```rust
+const _: i32 = 10┃;
+```
+
+#### After
+```rust
+const _: i32 = 0b1010;
+```
+
+
+### `convert_into_to_from`
+**Source:**  [convert_into_to_from.rs](https://github.com/rust-lang/rust-analyzer/blob/master/crates/ide-assists/src/handlers/convert_into_to_from.rs#L8) 
+
+Converts an Into impl to an equivalent From impl.
+
+#### Before
+```rust
+impl ┃Into<Thing> for usize {
+    fn into(self) -> Thing {
+        Thing {
+            b: self.to_string(),
+            a: self
+        }
+    }
+}
+```
+
+#### After
+```rust
+impl From<usize> for Thing {
+    fn from(val: usize) -> Self {
+        Thing {
+            b: val.to_string(),
+            a: val
+        }
+    }
+}
+```
+
+
+### `convert_iter_for_each_to_for`
+**Source:**  [convert_iter_for_each_to_for.rs](https://github.com/rust-lang/rust-analyzer/blob/master/crates/ide-assists/src/handlers/convert_iter_for_each_to_for.rs#L11) 
+
+Converts an Iterator::for_each function into a for loop.
+
+#### Before
+```rust
+fn main() {
+    let iter = iter::repeat((9, 2));
+    iter.for_each┃(|(x, y)| {
+        println!("x: {}, y: {}", x, y);
+    });
+}
+```
+
+#### After
+```rust
+fn main() {
+    let iter = iter::repeat((9, 2));
+    for (x, y) in iter {
+        println!("x: {}, y: {}", x, y);
+    }
+}
+```
+
+
+### `convert_let_else_to_match`
+**Source:**  [convert_let_else_to_match.rs](https://github.com/rust-lang/rust-analyzer/blob/master/crates/ide-assists/src/handlers/convert_let_else_to_match.rs#L9) 
+
+Converts let-else statement to let statement and match expression.
+
+#### Before
+```rust
+fn main() {
+    let Ok(mut x) = f() else┃ { return };
+}
+```
+
+#### After
+```rust
+fn main() {
+    let mut x = match f() {
+        Ok(x) => x,
+        _ => return,
+    };
+}
+```
+
+
+### `convert_match_to_let_else`
+**Source:**  [convert_match_to_let_else.rs](https://github.com/rust-lang/rust-analyzer/blob/master/crates/ide-assists/src/handlers/convert_match_to_let_else.rs#L12) 
+
+Converts let statement with match initializer to let-else statement.
+
+#### Before
+```rust
+fn foo(opt: Option<()>) {
+    let val┃ = match opt {
+        Some(it) => it,
+        None => return,
+    };
+}
+```
+
+#### After
+```rust
+fn foo(opt: Option<()>) {
+    let Some(val) = opt else { return };
+}
+```
+
+
+### `convert_named_struct_to_tuple_struct`
+**Source:**  [convert_named_struct_to_tuple_struct.rs](https://github.com/rust-lang/rust-analyzer/blob/master/crates/ide-assists/src/handlers/convert_named_struct_to_tuple_struct.rs#L11) 
+
+Converts struct with named fields to tuple struct, and analogously for enum variants with named
+fields.
+
+#### Before
+```rust
+struct Point┃ { x: f32, y: f32 }
+
+impl Point {
+    pub fn new(x: f32, y: f32) -> Self {
+        Point { x, y }
+    }
+
+    pub fn x(&self) -> f32 {
+        self.x
+    }
+
+    pub fn y(&self) -> f32 {
+        self.y
+    }
+}
+```
+
+#### After
+```rust
+struct Point(f32, f32);
+
+impl Point {
+    pub fn new(x: f32, y: f32) -> Self {
+        Point(x, y)
+    }
+
+    pub fn x(&self) -> f32 {
+        self.0
+    }
+
+    pub fn y(&self) -> f32 {
+        self.1
+    }
+}
+```
+
+
+### `convert_nested_function_to_closure`
+**Source:**  [convert_nested_function_to_closure.rs](https://github.com/rust-lang/rust-analyzer/blob/master/crates/ide-assists/src/handlers/convert_nested_function_to_closure.rs#L7) 
+
+Converts a function that is defined within the body of another function into a closure.
+
+#### Before
+```rust
+fn main() {
+    fn fo┃o(label: &str, number: u64) {
+        println!("{}: {}", label, number);
+    }
+
+    foo("Bar", 100);
+}
+```
+
+#### After
+```rust
+fn main() {
+    let foo = |label: &str, number: u64| {
+        println!("{}: {}", label, number);
+    };
+
+    foo("Bar", 100);
+}
+```
+
+
+### `convert_to_guarded_return`
+**Source:**  [convert_to_guarded_return.rs](https://github.com/rust-lang/rust-analyzer/blob/master/crates/ide-assists/src/handlers/convert_to_guarded_return.rs#L24) 
+
+Replace a large conditional with a guarded return.
+
+#### Before
+```rust
+fn main() {
+    ┃if cond {
+        foo();
+        bar();
+    }
+}
+```
+
+#### After
+```rust
+fn main() {
+    if !cond {
+        return;
+    }
+    foo();
+    bar();
+}
+```
+
+
+### `convert_tuple_return_type_to_struct`
+**Source:**  [convert_tuple_return_type_to_struct.rs](https://github.com/rust-lang/rust-analyzer/blob/master/crates/ide-assists/src/handlers/convert_tuple_return_type_to_struct.rs#L20) 
+
+This converts the return type of a function from a tuple type
+into a tuple struct and updates the body accordingly.
+
+#### Before
+```rust
+fn bar() {
+    let (a, b, c) = foo();
+}
+
+fn foo() -> (┃u32, u32, u32) {
+    (1, 2, 3)
+}
+```
+
+#### After
+```rust
+fn bar() {
+    let FooResult(a, b, c) = foo();
+}
+
+struct FooResult(u32, u32, u32);
+
+fn foo() -> FooResult {
+    FooResult(1, 2, 3)
+}
+```
+
+
+### `convert_tuple_struct_to_named_struct`
+**Source:**  [convert_tuple_struct_to_named_struct.rs](https://github.com/rust-lang/rust-analyzer/blob/master/crates/ide-assists/src/handlers/convert_tuple_struct_to_named_struct.rs#L10) 
+
+Converts tuple struct to struct with named fields, and analogously for tuple enum variants.
+
+#### Before
+```rust
+struct Point┃(f32, f32);
+
+impl Point {
+    pub fn new(x: f32, y: f32) -> Self {
+        Point(x, y)
+    }
+
+    pub fn x(&self) -> f32 {
+        self.0
+    }
+
+    pub fn y(&self) -> f32 {
+        self.1
+    }
+}
+```
+
+#### After
+```rust
+struct Point { field1: f32, field2: f32 }
+
+impl Point {
+    pub fn new(x: f32, y: f32) -> Self {
+        Point { field1: x, field2: y }
+    }
+
+    pub fn x(&self) -> f32 {
+        self.field1
+    }
+
+    pub fn y(&self) -> f32 {
+        self.field2
+    }
+}
+```
+
+
+### `convert_two_arm_bool_match_to_matches_macro`
+**Source:**  [convert_two_arm_bool_match_to_matches_macro.rs](https://github.com/rust-lang/rust-analyzer/blob/master/crates/ide-assists/src/handlers/convert_two_arm_bool_match_to_matches_macro.rs#L8) 
+
+Convert 2-arm match that evaluates to a boolean into the equivalent matches! invocation.
+
+#### Before
+```rust
+fn main() {
+    match scrutinee┃ {
+        Some(val) if val.cond() => true,
+        _ => false,
+    }
+}
+```
+
+#### After
+```rust
+fn main() {
+    matches!(scrutinee, Some(val) if val.cond())
+}
+```
+
+
+### `convert_while_to_loop`
+**Source:**  [convert_while_to_loop.rs](https://github.com/rust-lang/rust-analyzer/blob/master/crates/ide-assists/src/handlers/convert_while_to_loop.rs#L20) 
+
+Replace a while with a loop.
+
+#### Before
+```rust
+fn main() {
+    ┃while cond {
+        foo();
+    }
+}
+```
+
+#### After
+```rust
+fn main() {
+    loop {
+        if !cond {
+            break;
+        }
+        foo();
+    }
+}
+```
+
+
+### `destructure_struct_binding`
+**Source:**  [destructure_struct_binding.rs](https://github.com/rust-lang/rust-analyzer/blob/master/crates/ide-assists/src/handlers/destructure_struct_binding.rs#L18) 
+
+Destructures a struct binding in place.
+
+#### Before
+```rust
+struct Foo {
+    bar: i32,
+    baz: i32,
+}
+fn main() {
+    let ┃foo = Foo { bar: 1, baz: 2 };
+    let bar2 = foo.bar;
+    let baz2 = &foo.baz;
+}
+```
+
+#### After
+```rust
+struct Foo {
+    bar: i32,
+    baz: i32,
+}
+fn main() {
+    let Foo { bar, baz } = Foo { bar: 1, baz: 2 };
+    let bar2 = bar;
+    let baz2 = &baz;
+}
+```
+
+
+### `destructure_tuple_binding`
+**Source:**  [destructure_tuple_binding.rs](https://github.com/rust-lang/rust-analyzer/blob/master/crates/ide-assists/src/handlers/destructure_tuple_binding.rs#L19) 
+
+Destructures a tuple binding in place.
+
+#### Before
+```rust
+fn main() {
+    let ┃t = (1,2);
+    let v = t.0;
+}
+```
+
+#### After
+```rust
+fn main() {
+    let (┃_0, _1) = (1,2);
+    let v = _0;
+}
+```
+
+
+### `desugar_async_into_impl_future`
+**Source:**  [toggle_async_sugar.rs](https://github.com/rust-lang/rust-analyzer/blob/master/crates/ide-assists/src/handlers/toggle_async_sugar.rs#L103) 
+
+Rewrites asynchronous function from `async fn` into `-> impl Future`.
+This action does not touch the function body and therefore `0`
+block does not transform to `async { 0 }`.
+
+#### Before
+```rust
+pub as┃ync fn foo() -> usize {
+    0
+}
+```
+
+#### After
+```rust
+pub fn foo() -> impl core::future::Future<Output = usize> {
+    0
+}
+```
+
+
+### `desugar_doc_comment`
+**Source:**  [desugar_doc_comment.rs](https://github.com/rust-lang/rust-analyzer/blob/master/crates/ide-assists/src/handlers/desugar_doc_comment.rs#L14) 
+
+Desugars doc-comments to the attribute form.
+
+#### Before
+```rust
+/// Multi-line┃
+/// comment
+```
+
+#### After
+```rust
+#[doc = r"Multi-line
+comment"]
+```
+
+
+### `expand_glob_import`
+**Source:**  [expand_glob_import.rs](https://github.com/rust-lang/rust-analyzer/blob/master/crates/ide-assists/src/handlers/expand_glob_import.rs#L19) 
+
+Expands glob imports.
+
+#### Before
+```rust
+mod foo {
+    pub struct Bar;
+    pub struct Baz;
+}
+
+use foo::*┃;
+
+fn qux(bar: Bar, baz: Baz) {}
+```
+
+#### After
+```rust
+mod foo {
+    pub struct Bar;
+    pub struct Baz;
+}
+
+use foo::{Bar, Baz};
+
+fn qux(bar: Bar, baz: Baz) {}
+```
+
+
+### `expand_glob_reexport`
+**Source:**  [expand_glob_import.rs](https://github.com/rust-lang/rust-analyzer/blob/master/crates/ide-assists/src/handlers/expand_glob_import.rs#L81) 
+
+Expands non-private glob imports.
+
+#### Before
+```rust
+mod foo {
+    pub struct Bar;
+    pub struct Baz;
+}
+
+pub use foo::*┃;
+```
+
+#### After
+```rust
+mod foo {
+    pub struct Bar;
+    pub struct Baz;
+}
+
+pub use foo::{Bar, Baz};
+```
+
+
+### `explicit_enum_discriminant`
+**Source:**  [explicit_enum_discriminant.rs](https://github.com/rust-lang/rust-analyzer/blob/master/crates/ide-assists/src/handlers/explicit_enum_discriminant.rs#L11) 
+
+Adds explicit discriminant to all enum variants.
+
+#### Before
+```rust
+enum TheEnum┃ {
+    Foo,
+    Bar,
+    Baz = 42,
+    Quux,
+}
+```
+
+#### After
+```rust
+enum TheEnum {
+    Foo = 0,
+    Bar = 1,
+    Baz = 42,
+    Quux = 43,
+}
+```
+
+
+### `extract_constant`
+**Source:**  [extract_variable.rs](https://github.com/rust-lang/rust-analyzer/blob/master/crates/ide-assists/src/handlers/extract_variable.rs#L35) 
+
+Extracts subexpression into a constant.
+
+#### Before
+```rust
+fn main() {
+    ┃(1 + 2)┃ * 4;
+}
+```
+
+#### After
+```rust
+fn main() {
+    const ┃VAR_NAME: i32 = 1 + 2;
+    VAR_NAME * 4;
+}
+```
+
+
+### `extract_expressions_from_format_string`
+**Source:**  [extract_expressions_from_format_string.rs](https://github.com/rust-lang/rust-analyzer/blob/master/crates/ide-assists/src/handlers/extract_expressions_from_format_string.rs#L14) 
+
+Move an expression out of a format string.
+
+#### Before
+```rust
+fn main() {
+    print!("{var} {x + 1}┃");
+}
+```
+
+#### After
+```rust
+fn main() {
+    print!("{var} {}"┃, x + 1);
+}
+```
+
+
+### `extract_function`
+**Source:**  [extract_function.rs](https://github.com/rust-lang/rust-analyzer/blob/master/crates/ide-assists/src/handlers/extract_function.rs#L39) 
+
+Extracts selected statements and comments into new function.
+
+#### Before
+```rust
+fn main() {
+    let n = 1;
+    ┃let m = n + 2;
+    // calculate
+    let k = m + n;┃
+    let g = 3;
+}
+```
+
+#### After
+```rust
+fn main() {
+    let n = 1;
+    fun_name(n);
+    let g = 3;
+}
+
+fn ┃fun_name(n: i32) {
+    let m = n + 2;
+    // calculate
+    let k = m + n;
+}
+```
+
+
+### `extract_module`
+**Source:**  [extract_module.rs](https://github.com/rust-lang/rust-analyzer/blob/master/crates/ide-assists/src/handlers/extract_module.rs#L29) 
+
+Extracts a selected region as separate module. All the references, visibility and imports are
+resolved.
+
+#### Before
+```rust
+┃fn foo(name: i32) -> i32 {
+    name + 1
+}┃
+
+fn bar(name: i32) -> i32 {
+    name + 2
+}
+```
+
+#### After
+```rust
+mod modname {
+    pub(crate) fn foo(name: i32) -> i32 {
+        name + 1
+    }
+}
+
+fn bar(name: i32) -> i32 {
+    name + 2
+}
+```
+
+
+### `extract_static`
+**Source:**  [extract_variable.rs](https://github.com/rust-lang/rust-analyzer/blob/master/crates/ide-assists/src/handlers/extract_variable.rs#L52) 
+
+Extracts subexpression into a static.
+
+#### Before
+```rust
+fn main() {
+    ┃(1 + 2)┃ * 4;
+}
+```
+
+#### After
+```rust
+fn main() {
+    static ┃VAR_NAME: i32 = 1 + 2;
+    VAR_NAME * 4;
+}
+```
+
+
+### `extract_struct_from_enum_variant`
+**Source:**  [extract_struct_from_enum_variant.rs](https://github.com/rust-lang/rust-analyzer/blob/master/crates/ide-assists/src/handlers/extract_struct_from_enum_variant.rs#L26) 
+
+Extracts a struct from enum variant.
+
+#### Before
+```rust
+enum A { ┃One(u32, u32) }
+```
+
+#### After
+```rust
+struct One(u32, u32);
+
+enum A { One(One) }
+```
+
+
+### `extract_type_alias`
+**Source:**  [extract_type_alias.rs](https://github.com/rust-lang/rust-analyzer/blob/master/crates/ide-assists/src/handlers/extract_type_alias.rs#L10) 
+
+Extracts the selected type as a type alias.
+
+#### Before
+```rust
+struct S {
+    field: ┃(u8, u8, u8)┃,
+}
+```
+
+#### After
+```rust
+type ┃Type = (u8, u8, u8);
+
+struct S {
+    field: Type,
+}
+```
+
+
+### `extract_variable`
+**Source:**  [extract_variable.rs](https://github.com/rust-lang/rust-analyzer/blob/master/crates/ide-assists/src/handlers/extract_variable.rs#L18) 
+
+Extracts subexpression into a variable.
+
+#### Before
+```rust
+fn main() {
+    ┃(1 + 2)┃ * 4;
+}
+```
+
+#### After
+```rust
+fn main() {
+    let ┃var_name = 1 + 2;
+    var_name * 4;
+}
+```
+
+
+### `fill_record_pattern_fields`
+**Source:**  [fill_record_pattern_fields.rs](https://github.com/rust-lang/rust-analyzer/blob/master/crates/ide-assists/src/handlers/fill_record_pattern_fields.rs#L8) 
+
+Fills fields by replacing rest pattern in record patterns.
+
+#### Before
+```rust
+struct Bar { y: Y, z: Z }
+
+fn foo(bar: Bar) {
+    let Bar { ..┃ } = bar;
+}
+```
+
+#### After
+```rust
+struct Bar { y: Y, z: Z }
+
+fn foo(bar: Bar) {
+    let Bar { y, z  } = bar;
+}
+```
+
+
+### `fix_visibility`
+**Source:**  [fix_visibility.rs](https://github.com/rust-lang/rust-analyzer/blob/master/crates/ide-assists/src/handlers/fix_visibility.rs#L14) 
+
+Makes inaccessible item public.
+
+#### Before
+```rust
+mod m {
+    fn frobnicate() {}
+}
+fn main() {
+    m::frobnicate┃();
+}
+```
+
+#### After
+```rust
+mod m {
+    ┃pub(crate) fn frobnicate() {}
+}
+fn main() {
+    m::frobnicate();
+}
+```
+
+
+### `flip_binexpr`
+**Source:**  [flip_binexpr.rs](https://github.com/rust-lang/rust-analyzer/blob/master/crates/ide-assists/src/handlers/flip_binexpr.rs#L8) 
+
+Flips operands of a binary expression.
+
+#### Before
+```rust
+fn main() {
+    let _ = 90 +┃ 2;
+}
+```
+
+#### After
+```rust
+fn main() {
+    let _ = 2 + 90;
+}
+```
+
+
+### `flip_comma`
+**Source:**  [flip_comma.rs](https://github.com/rust-lang/rust-analyzer/blob/master/crates/ide-assists/src/handlers/flip_comma.rs#L10) 
+
+Flips two comma-separated items.
+
+#### Before
+```rust
+fn main() {
+    ((1, 2),┃ (3, 4));
+}
+```
+
+#### After
+```rust
+fn main() {
+    ((3, 4), (1, 2));
+}
+```
+
+
+### `flip_trait_bound`
+**Source:**  [flip_trait_bound.rs](https://github.com/rust-lang/rust-analyzer/blob/master/crates/ide-assists/src/handlers/flip_trait_bound.rs#L9) 
+
+Flips two trait bounds.
+
+#### Before
+```rust
+fn foo<T: Clone +┃ Copy>() { }
+```
+
+#### After
+```rust
+fn foo<T: Copy + Clone>() { }
+```
+
+
+### `generate_constant`
+**Source:**  [generate_constant.rs](https://github.com/rust-lang/rust-analyzer/blob/master/crates/ide-assists/src/handlers/generate_constant.rs#L14) 
+
+Generate a named constant.
+
+#### Before
+```rust
+struct S { i: usize }
+impl S { pub fn new(n: usize) {} }
+fn main() {
+    let v = S::new(CAPA┃CITY);
+}
+```
+
+#### After
+```rust
+struct S { i: usize }
+impl S { pub fn new(n: usize) {} }
+fn main() {
+    const CAPACITY: usize = ┃;
+    let v = S::new(CAPACITY);
+}
+```
+
+
+### `generate_default_from_enum_variant`
+**Source:**  [generate_default_from_enum_variant.rs](https://github.com/rust-lang/rust-analyzer/blob/master/crates/ide-assists/src/handlers/generate_default_from_enum_variant.rs#L6) 
+
+Adds a Default impl for an enum using a variant.
+
+#### Before
+```rust
+enum Version {
+ Undefined,
+ Minor┃,
+ Major,
+}
+```
+
+#### After
+```rust
+enum Version {
+ Undefined,
+ Minor,
+ Major,
+}
+
+impl Default for Version {
+    fn default() -> Self {
+        Self::Minor
+    }
+}
+```
+
+
+### `generate_default_from_new`
+**Source:**  [generate_default_from_new.rs](https://github.com/rust-lang/rust-analyzer/blob/master/crates/ide-assists/src/handlers/generate_default_from_new.rs#L13) 
+
+Generates default implementation from new method.
+
+#### Before
+```rust
+struct Example { _inner: () }
+
+impl Example {
+    pub fn n┃ew() -> Self {
+        Self { _inner: () }
+    }
+}
+```
+
+#### After
+```rust
+struct Example { _inner: () }
+
+impl Example {
+    pub fn new() -> Self {
+        Self { _inner: () }
+    }
+}
+
+impl Default for Example {
+    fn default() -> Self {
+        Self::new()
+    }
+}
+```
+
+
+### `generate_delegate_methods`
+**Source:**  [generate_delegate_methods.rs](https://github.com/rust-lang/rust-analyzer/blob/master/crates/ide-assists/src/handlers/generate_delegate_methods.rs#L15) 
+
+Generate delegate methods.
+
+#### Before
+```rust
+struct Age(u8);
+impl Age {
+    fn age(&self) -> u8 {
+        self.0
+    }
+}
+
+struct Person {
+    ag┃e: Age,
+}
+```
+
+#### After
+```rust
+struct Age(u8);
+impl Age {
+    fn age(&self) -> u8 {
+        self.0
+    }
+}
+
+struct Person {
+    age: Age,
+}
+
+impl Person {
+    ┃fn age(&self) -> u8 {
+        self.age.age()
+    }
+}
+```
+
+
+### `generate_delegate_trait`
+**Source:**  [generate_delegate_trait.rs](https://github.com/rust-lang/rust-analyzer/blob/master/crates/ide-assists/src/handlers/generate_delegate_trait.rs#L29) 
+
+Generate delegate trait implementation for `StructField`s.
+
+#### Before
+```rust
+trait SomeTrait {
+    type T;
+    fn fn_(arg: u32) -> u32;
+    fn method_(&mut self) -> bool;
+}
+struct A;
+impl SomeTrait for A {
+    type T = u32;
+
+    fn fn_(arg: u32) -> u32 {
+        42
+    }
+
+    fn method_(&mut self) -> bool {
+        false
+    }
+}
+struct B {
+    a┃: A,
+}
+```
+
+#### After
+```rust
+trait SomeTrait {
+    type T;
+    fn fn_(arg: u32) -> u32;
+    fn method_(&mut self) -> bool;
+}
+struct A;
+impl SomeTrait for A {
+    type T = u32;
+
+    fn fn_(arg: u32) -> u32 {
+        42
+    }
+
+    fn method_(&mut self) -> bool {
+        false
+    }
+}
+struct B {
+    a: A,
+}
+
+impl SomeTrait for B {
+    type T = <A as SomeTrait>::T;
+
+    fn fn_(arg: u32) -> u32 {
+        <A as SomeTrait>::fn_(arg)
+    }
+
+    fn method_(&mut self) -> bool {
+        <A as SomeTrait>::method_(&mut self.a)
+    }
+}
+```
+
+
+### `generate_deref`
+**Source:**  [generate_deref.rs](https://github.com/rust-lang/rust-analyzer/blob/master/crates/ide-assists/src/handlers/generate_deref.rs#L16) 
+
+Generate `Deref` impl using the given struct field.
+
+#### Before
+```rust
+struct A;
+struct B {
+   ┃a: A
+}
+```
+
+#### After
+```rust
+struct A;
+struct B {
+   a: A
+}
+
+impl core::ops::Deref for B {
+    type Target = A;
+
+    fn deref(&self) -> &Self::Target {
+        &self.a
+    }
+}
+```
+
+
+### `generate_derive`
+**Source:**  [generate_derive.rs](https://github.com/rust-lang/rust-analyzer/blob/master/crates/ide-assists/src/handlers/generate_derive.rs#L8) 
+
+Adds a new `#[derive()]` clause to a struct or enum.
+
+#### Before
+```rust
+struct Point {
+    x: u32,
+    y: u32,┃
+}
+```
+
+#### After
+```rust
+#[derive(┃)]
+struct Point {
+    x: u32,
+    y: u32,
+}
+```
+
+
+### `generate_doc_example`
+**Source:**  [generate_documentation_template.rs](https://github.com/rust-lang/rust-analyzer/blob/master/crates/ide-assists/src/handlers/generate_documentation_template.rs#L76) 
+
+Generates a rustdoc example when editing an item's documentation.
+
+#### Before
+```rust
+/// Adds two numbers.┃
+pub fn add(a: i32, b: i32) -> i32 { a + b }
+```
+
+#### After
+```rust
+/// Adds two numbers.
+///
+/// # Examples
+///
+/// ```
+/// use ra_test_fixture::add;
+///
+/// assert_eq!(add(a, b), );
+/// ```
+pub fn add(a: i32, b: i32) -> i32 { a + b }
+```
+
+
+### `generate_documentation_template`
+**Source:**  [generate_documentation_template.rs](https://github.com/rust-lang/rust-analyzer/blob/master/crates/ide-assists/src/handlers/generate_documentation_template.rs#L13) 
+
+Adds a documentation template above a function definition / declaration.
+
+#### Before
+```rust
+pub struct S;
+impl S {
+    pub unsafe fn set_len┃(&mut self, len: usize) -> Result<(), std::io::Error> {
+        /* ... */
+    }
+}
+```
+
+#### After
+```rust
+pub struct S;
+impl S {
+    /// Sets the length of this [`S`].
+    ///
+    /// # Errors
+    ///
+    /// This function will return an error if .
+    ///
+    /// # Safety
+    ///
+    /// .
+    pub unsafe fn set_len(&mut self, len: usize) -> Result<(), std::io::Error> {
+        /* ... */
+    }
+}
+```
+
+
+### `generate_enum_as_method`
+**Source:**  [generate_enum_projection_method.rs](https://github.com/rust-lang/rust-analyzer/blob/master/crates/ide-assists/src/handlers/generate_enum_projection_method.rs#L59) 
+
+Generate an `as_` method for this enum variant.
+
+#### Before
+```rust
+enum Value {
+ Number(i32),
+ Text(String)┃,
+}
+```
+
+#### After
+```rust
+enum Value {
+ Number(i32),
+ Text(String),
+}
+
+impl Value {
+    fn as_text(&self) -> Option<&String> {
+        if let Self::Text(v) = self {
+            Some(v)
+        } else {
+            None
+        }
+    }
+}
+```
+
+
+### `generate_enum_is_method`
+**Source:**  [generate_enum_is_method.rs](https://github.com/rust-lang/rust-analyzer/blob/master/crates/ide-assists/src/handlers/generate_enum_is_method.rs#L11) 
+
+Generate an `is_` method for this enum variant.
+
+#### Before
+```rust
+enum Version {
+ Undefined,
+ Minor┃,
+ Major,
+}
+```
+
+#### After
+```rust
+enum Version {
+ Undefined,
+ Minor,
+ Major,
+}
+
+impl Version {
+    /// Returns `true` if the version is [`Minor`].
+    ///
+    /// [`Minor`]: Version::Minor
+    #[must_use]
+    fn is_minor(&self) -> bool {
+        matches!(self, Self::Minor)
+    }
+}
+```
+
+
+### `generate_enum_try_into_method`
+**Source:**  [generate_enum_projection_method.rs](https://github.com/rust-lang/rust-analyzer/blob/master/crates/ide-assists/src/handlers/generate_enum_projection_method.rs#L12) 
+
+Generate a `try_into_` method for this enum variant.
+
+#### Before
+```rust
+enum Value {
+ Number(i32),
+ Text(String)┃,
+}
+```
+
+#### After
+```rust
+enum Value {
+ Number(i32),
+ Text(String),
+}
+
+impl Value {
+    fn try_into_text(self) -> Result<String, Self> {
+        if let Self::Text(v) = self {
+            Ok(v)
+        } else {
+            Err(self)
+        }
+    }
+}
+```
+
+
+### `generate_enum_variant`
+**Source:**  [generate_enum_variant.rs](https://github.com/rust-lang/rust-analyzer/blob/master/crates/ide-assists/src/handlers/generate_enum_variant.rs#L10) 
+
+Adds a variant to an enum.
+
+#### Before
+```rust
+enum Countries {
+    Ghana,
+}
+
+fn main() {
+    let country = Countries::Lesotho┃;
+}
+```
+
+#### After
+```rust
+enum Countries {
+    Ghana,
+    Lesotho,
+}
+
+fn main() {
+    let country = Countries::Lesotho;
+}
+```
+
+
+### `generate_fn_type_alias_named`
+**Source:**  [generate_fn_type_alias.rs](https://github.com/rust-lang/rust-analyzer/blob/master/crates/ide-assists/src/handlers/generate_fn_type_alias.rs#L10) 
+
+Generate a type alias for the function with named parameters.
+
+#### Before
+```rust
+unsafe fn fo┃o(n: i32) -> i32 { 42i32 }
+```
+
+#### After
+```rust
+type ${0:FooFn} = unsafe fn(n: i32) -> i32;
+
+unsafe fn foo(n: i32) -> i32 { 42i32 }
+```
+
+
+### `generate_fn_type_alias_unnamed`
+**Source:**  [generate_fn_type_alias.rs](https://github.com/rust-lang/rust-analyzer/blob/master/crates/ide-assists/src/handlers/generate_fn_type_alias.rs#L24) 
+
+Generate a type alias for the function with unnamed parameters.
+
+#### Before
+```rust
+unsafe fn fo┃o(n: i32) -> i32 { 42i32 }
+```
+
+#### After
+```rust
+type ${0:FooFn} = unsafe fn(i32) -> i32;
+
+unsafe fn foo(n: i32) -> i32 { 42i32 }
+```
+
+
+### `generate_from_impl_for_enum`
+**Source:**  [generate_from_impl_for_enum.rs](https://github.com/rust-lang/rust-analyzer/blob/master/crates/ide-assists/src/handlers/generate_from_impl_for_enum.rs#L8) 
+
+Adds a From impl for this enum variant with one tuple field.
+
+#### Before
+```rust
+enum A { ┃One(u32) }
+```
+
+#### After
+```rust
+enum A { One(u32) }
+
+impl From<u32> for A {
+    fn from(v: u32) -> Self {
+        Self::One(v)
+    }
+}
+```
+
+
+### `generate_function`
+**Source:**  [generate_function.rs](https://github.com/rust-lang/rust-analyzer/blob/master/crates/ide-assists/src/handlers/generate_function.rs#L28) 
+
+Adds a stub function with a signature matching the function under the cursor.
+
+#### Before
+```rust
+struct Baz;
+fn baz() -> Baz { Baz }
+fn foo() {
+    bar┃("", baz());
+}
+
+```
+
+#### After
+```rust
+struct Baz;
+fn baz() -> Baz { Baz }
+fn foo() {
+    bar("", baz());
+}
+
+fn bar(arg: &str, baz: Baz) ${0:-> _} {
+    todo!()
+}
+
+```
+
+
+### `generate_getter`
+**Source:**  [generate_getter_or_setter.rs](https://github.com/rust-lang/rust-analyzer/blob/master/crates/ide-assists/src/handlers/generate_getter_or_setter.rs#L73) 
+
+Generate a getter method.
+
+#### Before
+```rust
+struct Person {
+    nam┃e: String,
+}
+```
+
+#### After
+```rust
+struct Person {
+    name: String,
+}
+
+impl Person {
+    fn ┃name(&self) -> &str {
+        &self.name
+    }
+}
+```
+
+
+### `generate_getter_mut`
+**Source:**  [generate_getter_or_setter.rs](https://github.com/rust-lang/rust-analyzer/blob/master/crates/ide-assists/src/handlers/generate_getter_or_setter.rs#L127) 
+
+Generate a mut getter method.
+
+#### Before
+```rust
+struct Person {
+    nam┃e: String,
+}
+```
+
+#### After
+```rust
+struct Person {
+    name: String,
+}
+
+impl Person {
+    fn ┃name_mut(&mut self) -> &mut String {
+        &mut self.name
+    }
+}
+```
+
+
+### `generate_impl`
+**Source:**  [generate_impl.rs](https://github.com/rust-lang/rust-analyzer/blob/master/crates/ide-assists/src/handlers/generate_impl.rs#L20) 
+
+Adds a new inherent impl for a type.
+
+#### Before
+```rust
+struct Ctx┃<T: Clone> {
+    data: T,
+}
+```
+
+#### After
+```rust
+struct Ctx<T: Clone> {
+    data: T,
+}
+
+impl<T: Clone> Ctx<T> {┃}
+```
+
+
+### `generate_is_empty_from_len`
+**Source:**  [generate_is_empty_from_len.rs](https://github.com/rust-lang/rust-analyzer/blob/master/crates/ide-assists/src/handlers/generate_is_empty_from_len.rs#L12) 
+
+Generates is_empty implementation from the len method.
+
+#### Before
+```rust
+struct MyStruct { data: Vec<String> }
+
+impl MyStruct {
+    #[must_use]
+    p┃ub fn len(&self) -> usize {
+        self.data.len()
+    }
+}
+```
+
+#### After
+```rust
+struct MyStruct { data: Vec<String> }
+
+impl MyStruct {
+    #[must_use]
+    pub fn len(&self) -> usize {
+        self.data.len()
+    }
+
+    #[must_use]
+    pub fn is_empty(&self) -> bool {
+        self.len() == 0
+    }
+}
+```
+
+
+### `generate_mut_trait_impl`
+**Source:**  [generate_mut_trait_impl.rs](https://github.com/rust-lang/rust-analyzer/blob/master/crates/ide-assists/src/handlers/generate_mut_trait_impl.rs#L12) 
+
+Adds a IndexMut impl from the `Index` trait.
+
+#### Before
+```rust
+pub enum Axis { X = 0, Y = 1, Z = 2 }
+
+impl<T> core::ops::Index┃<Axis> for [T; 3] {
+    type Output = T;
+
+    fn index(&self, index: Axis) -> &Self::Output {
+        &self[index as usize]
+    }
+}
+```
+
+#### After
+```rust
+pub enum Axis { X = 0, Y = 1, Z = 2 }
+
+┃impl<T> core::ops::IndexMut<Axis> for [T; 3] {
+    fn index_mut(&mut self, index: Axis) -> &mut Self::Output {
+        &self[index as usize]
+    }
+}
+
+impl<T> core::ops::Index<Axis> for [T; 3] {
+    type Output = T;
+
+    fn index(&self, index: Axis) -> &Self::Output {
+        &self[index as usize]
+    }
+}
+```
+
+
+### `generate_new`
+**Source:**  [generate_new.rs](https://github.com/rust-lang/rust-analyzer/blob/master/crates/ide-assists/src/handlers/generate_new.rs#L14) 
+
+Adds a `fn new` for a type.
+
+#### Before
+```rust
+struct Ctx<T: Clone> {
+     data: T,┃
+}
+```
+
+#### After
+```rust
+struct Ctx<T: Clone> {
+     data: T,
+}
+
+impl<T: Clone> Ctx<T> {
+    fn ┃new(data: T) -> Self {
+        Self { data }
+    }
+}
+```
+
+
+### `generate_setter`
+**Source:**  [generate_getter_or_setter.rs](https://github.com/rust-lang/rust-analyzer/blob/master/crates/ide-assists/src/handlers/generate_getter_or_setter.rs#L13) 
+
+Generate a setter method.
+
+#### Before
+```rust
+struct Person {
+    nam┃e: String,
+}
+```
+
+#### After
+```rust
+struct Person {
+    name: String,
+}
+
+impl Person {
+    fn ┃set_name(&mut self, name: String) {
+        self.name = name;
+    }
+}
+```
+
+
+### `generate_trait_from_impl`
+**Source:**  [generate_trait_from_impl.rs](https://github.com/rust-lang/rust-analyzer/blob/master/crates/ide-assists/src/handlers/generate_trait_from_impl.rs#L18) 
+
+Generate trait for an already defined inherent impl and convert impl to a trait impl.
+
+#### Before
+```rust
+struct Foo<const N: usize>([i32; N]);
+
+macro_rules! const_maker {
+    ($t:ty, $v:tt) => {
+        const CONST: $t = $v;
+    };
+}
+
+impl<const N: usize> Fo┃o<N> {
+    // Used as an associated constant.
+    const CONST_ASSOC: usize = N * 4;
+
+    fn create() -> Option<()> {
+        Some(())
+    }
+
+    const_maker! {i32, 7}
+}
+```
+
+#### After
+```rust
+struct Foo<const N: usize>([i32; N]);
+
+macro_rules! const_maker {
+    ($t:ty, $v:tt) => {
+        const CONST: $t = $v;
+    };
+}
+
+trait ${0:NewTrait}<const N: usize> {
+    // Used as an associated constant.
+    const CONST_ASSOC: usize = N * 4;
+
+    fn create() -> Option<()>;
+
+    const_maker! {i32, 7}
+}
+
+impl<const N: usize> ${0:NewTrait}<N> for Foo<N> {
+    // Used as an associated constant.
+    const CONST_ASSOC: usize = N * 4;
+
+    fn create() -> Option<()> {
+        Some(())
+    }
+
+    const_maker! {i32, 7}
+}
+```
+
+
+### `generate_trait_impl`
+**Source:**  [generate_impl.rs](https://github.com/rust-lang/rust-analyzer/blob/master/crates/ide-assists/src/handlers/generate_impl.rs#L66) 
+
+Adds a new trait impl for a type.
+
+#### Before
+```rust
+struct ┃Ctx<T: Clone> {
+    data: T,
+}
+```
+
+#### After
+```rust
+struct Ctx<T: Clone> {
+    data: T,
+}
+
+impl<T: Clone> ${0:_} for Ctx<T> {}
+```
+
+
+### `inline_call`
+**Source:**  [inline_call.rs](https://github.com/rust-lang/rust-analyzer/blob/master/crates/ide-assists/src/handlers/inline_call.rs#L170) 
+
+Inlines a function or method body creating a `let` statement per parameter unless the parameter
+can be inlined. The parameter will be inlined either if it the supplied argument is a simple local
+or if the parameter is only accessed inside the function body once.
+
+#### Before
+```rust
+fn foo(name: Option<&str>) {
+    let name = name.unwrap┃();
+}
+```
+
+#### After
+```rust
+fn foo(name: Option<&str>) {
+    let name = match name {
+            Some(val) => val,
+            None => panic!("called `Option::unwrap()` on a `None` value"),
+        };
+}
+```
+
+
+### `inline_const_as_literal`
+**Source:**  [inline_const_as_literal.rs](https://github.com/rust-lang/rust-analyzer/blob/master/crates/ide-assists/src/handlers/inline_const_as_literal.rs#L6) 
+
+Evaluate and inline const variable as literal.
+
+#### Before
+```rust
+const STRING: &str = "Hello, World!";
+
+fn something() -> &'static str {
+    STRING┃
+}
+```
+
+#### After
+```rust
+const STRING: &str = "Hello, World!";
+
+fn something() -> &'static str {
+    "Hello, World!"
+}
+```
+
+
+### `inline_into_callers`
+**Source:**  [inline_call.rs](https://github.com/rust-lang/rust-analyzer/blob/master/crates/ide-assists/src/handlers/inline_call.rs#L32) 
+
+Inline a function or method body into all of its callers where possible, creating a `let` statement per parameter
+unless the parameter can be inlined. The parameter will be inlined either if it the supplied argument is a simple local
+or if the parameter is only accessed inside the function body once.
+If all calls can be inlined the function will be removed.
+
+#### Before
+```rust
+fn print(_: &str) {}
+fn foo┃(word: &str) {
+    if !word.is_empty() {
+        print(word);
+    }
+}
+fn bar() {
+    foo("안녕하세요");
+    foo("여러분");
+}
+```
+
+#### After
+```rust
+fn print(_: &str) {}
+
+fn bar() {
+    {
+        let word: &str = "안녕하세요";
+        if !word.is_empty() {
+            print(word);
+        }
+    };
+    {
+        let word: &str = "여러분";
+        if !word.is_empty() {
+            print(word);
+        }
+    };
+}
+```
+
+
+### `inline_local_variable`
+**Source:**  [inline_local_variable.rs](https://github.com/rust-lang/rust-analyzer/blob/master/crates/ide-assists/src/handlers/inline_local_variable.rs#L17) 
+
+Inlines a local variable.
+
+#### Before
+```rust
+fn main() {
+    let x┃ = 1 + 2;
+    x * 4;
+}
+```
+
+#### After
+```rust
+fn main() {
+    (1 + 2) * 4;
+}
+```
+
+
+### `inline_macro`
+**Source:**  [inline_macro.rs](https://github.com/rust-lang/rust-analyzer/blob/master/crates/ide-assists/src/handlers/inline_macro.rs#L7) 
+
+Takes a macro and inlines it one step.
+
+#### Before
+```rust
+macro_rules! num {
+    (+$($t:tt)+) => (1 + num!($($t )+));
+    (-$($t:tt)+) => (-1 + num!($($t )+));
+    (+) => (1);
+    (-) => (-1);
+}
+
+fn main() {
+    let number = num┃!(+ + + - + +);
+    println!("{number}");
+}
+```
+
+#### After
+```rust
+macro_rules! num {
+    (+$($t:tt)+) => (1 + num!($($t )+));
+    (-$($t:tt)+) => (-1 + num!($($t )+));
+    (+) => (1);
+    (-) => (-1);
+}
+
+fn main() {
+    let number = 1+num!(+ + - + +);
+    println!("{number}");
+}
+```
+
+
+### `inline_type_alias`
+**Source:**  [inline_type_alias.rs](https://github.com/rust-lang/rust-analyzer/blob/master/crates/ide-assists/src/handlers/inline_type_alias.rs#L106) 
+
+Replace a type alias with its concrete type.
+
+#### Before
+```rust
+type A<T = u32> = Vec<T>;
+
+fn main() {
+    let a: ┃A;
+}
+```
+
+#### After
+```rust
+type A<T = u32> = Vec<T>;
+
+fn main() {
+    let a: Vec<u32>;
+}
+```
+
+
+### `inline_type_alias_uses`
+**Source:**  [inline_type_alias.rs](https://github.com/rust-lang/rust-analyzer/blob/master/crates/ide-assists/src/handlers/inline_type_alias.rs#L24) 
+
+Inline a type alias into all of its uses where possible.
+
+#### Before
+```rust
+type ┃A = i32;
+fn id(x: A) -> A {
+    x
+};
+fn foo() {
+    let _: A = 3;
+}
+```
+
+#### After
+```rust
+
+fn id(x: i32) -> i32 {
+    x
+};
+fn foo() {
+    let _: i32 = 3;
+}
+```
+
+
+### `into_to_qualified_from`
+**Source:**  [into_to_qualified_from.rs](https://github.com/rust-lang/rust-analyzer/blob/master/crates/ide-assists/src/handlers/into_to_qualified_from.rs#L10) 
+
+Convert an `into` method call to a fully qualified `from` call.
+
+#### Before
+```rust
+//- minicore: from
+struct B;
+impl From<i32> for B {
+    fn from(a: i32) -> Self {
+       B
+    }
+}
+
+fn main() -> () {
+    let a = 3;
+    let b: B = a.in┃to();
+}
+```
+
+#### After
+```rust
+struct B;
+impl From<i32> for B {
+    fn from(a: i32) -> Self {
+       B
+    }
+}
+
+fn main() -> () {
+    let a = 3;
+    let b: B = B::from(a);
+}
+```
+
+
+### `introduce_named_generic`
+**Source:**  [introduce_named_generic.rs](https://github.com/rust-lang/rust-analyzer/blob/master/crates/ide-assists/src/handlers/introduce_named_generic.rs#L7) 
+
+Replaces `impl Trait` function argument with the named generic.
+
+#### Before
+```rust
+fn foo(bar: ┃impl Bar) {}
+```
+
+#### After
+```rust
+fn foo<┃B: Bar>(bar: B) {}
+```
+
+
+### `introduce_named_lifetime`
+**Source:**  [introduce_named_lifetime.rs](https://github.com/rust-lang/rust-analyzer/blob/master/crates/ide-assists/src/handlers/introduce_named_lifetime.rs#L13) 
+
+Change an anonymous lifetime to a named lifetime.
+
+#### Before
+```rust
+impl Cursor<'_┃> {
+    fn node(self) -> &SyntaxNode {
+        match self {
+            Cursor::Replace(node) | Cursor::Before(node) => node,
+        }
+    }
+}
+```
+
+#### After
+```rust
+impl<'a> Cursor<'a> {
+    fn node(self) -> &SyntaxNode {
+        match self {
+            Cursor::Replace(node) | Cursor::Before(node) => node,
+        }
+    }
+}
+```
+
+
+### `invert_if`
+**Source:**  [invert_if.rs](https://github.com/rust-lang/rust-analyzer/blob/master/crates/ide-assists/src/handlers/invert_if.rs#L13) 
+
+This transforms if expressions of the form `if !x {A} else {B}` into `if x {B} else {A}`
+This also works with `!=`. This assist can only be applied with the cursor on `if`.
+
+#### Before
+```rust
+fn main() {
+    if┃ !y { A } else { B }
+}
+```
+
+#### After
+```rust
+fn main() {
+    if y { B } else { A }
+}
+```
+
+
+### `line_to_block`
+**Source:**  [convert_comment_block.rs](https://github.com/rust-lang/rust-analyzer/blob/master/crates/ide-assists/src/handlers/convert_comment_block.rs#L9) 
+
+Converts comments between block and single-line form.
+
+#### Before
+```rust
+   // Multi-line┃
+   // comment
+```
+
+#### After
+```rust
+  /*
+  Multi-line
+  comment
+  */
+```
+
+
+### `make_raw_string`
+**Source:**  [raw_string.rs](https://github.com/rust-lang/rust-analyzer/blob/master/crates/ide-assists/src/handlers/raw_string.rs#L7) 
+
+Adds `r#` to a plain string literal.
+
+#### Before
+```rust
+fn main() {
+    "Hello,┃ World!";
+}
+```
+
+#### After
+```rust
+fn main() {
+    r#"Hello, World!"#;
+}
+```
+
+
+### `make_usual_string`
+**Source:**  [raw_string.rs](https://github.com/rust-lang/rust-analyzer/blob/master/crates/ide-assists/src/handlers/raw_string.rs#L47) 
+
+Turns a raw string into a plain string.
+
+#### Before
+```rust
+fn main() {
+    r#"Hello,┃ "World!""#;
+}
+```
+
+#### After
+```rust
+fn main() {
+    "Hello, \"World!\"";
+}
+```
+
+
+### `merge_imports`
+**Source:**  [merge_imports.rs](https://github.com/rust-lang/rust-analyzer/blob/master/crates/ide-assists/src/handlers/merge_imports.rs#L21) 
+
+Merges neighbor imports with a common prefix.
+
+#### Before
+```rust
+use std::┃fmt::Formatter;
+use std::io;
+```
+
+#### After
+```rust
+use std::{fmt::Formatter, io};
+```
+
+
+### `merge_match_arms`
+**Source:**  [merge_match_arms.rs](https://github.com/rust-lang/rust-analyzer/blob/master/crates/ide-assists/src/handlers/merge_match_arms.rs#L12) 
+
+Merges the current match arm with the following if their bodies are identical.
+
+#### Before
+```rust
+enum Action { Move { distance: u32 }, Stop }
+
+fn handle(action: Action) {
+    match action {
+        ┃Action::Move(..) => foo(),
+        Action::Stop => foo(),
+    }
+}
+```
+
+#### After
+```rust
+enum Action { Move { distance: u32 }, Stop }
+
+fn handle(action: Action) {
+    match action {
+        Action::Move(..) | Action::Stop => foo(),
+    }
+}
+```
+
+
+### `merge_nested_if`
+**Source:**  [merge_nested_if.rs](https://github.com/rust-lang/rust-analyzer/blob/master/crates/ide-assists/src/handlers/merge_nested_if.rs#L11) 
+
+This transforms if expressions of the form `if x { if y {A} }` into `if x && y {A}`
+This assist can only be applied with the cursor on `if`.
+
+#### Before
+```rust
+fn main() {
+   i┃f x == 3 { if y == 4 { 1 } }
+}
+```
+
+#### After
+```rust
+fn main() {
+   if x == 3 && y == 4 { 1 }
+}
+```
+
+
+### `move_arm_cond_to_match_guard`
+**Source:**  [move_guard.rs](https://github.com/rust-lang/rust-analyzer/blob/master/crates/ide-assists/src/handlers/move_guard.rs#L69) 
+
+Moves if expression from match arm body into a guard.
+
+#### Before
+```rust
+enum Action { Move { distance: u32 }, Stop }
+
+fn handle(action: Action) {
+    match action {
+        Action::Move { distance } => ┃if distance > 10 { foo() },
+        _ => (),
+    }
+}
+```
+
+#### After
+```rust
+enum Action { Move { distance: u32 }, Stop }
+
+fn handle(action: Action) {
+    match action {
+        Action::Move { distance } if distance > 10 => foo(),
+        _ => (),
+    }
+}
+```
+
+
+### `move_bounds_to_where_clause`
+**Source:**  [move_bounds.rs](https://github.com/rust-lang/rust-analyzer/blob/master/crates/ide-assists/src/handlers/move_bounds.rs#L12) 
+
+Moves inline type bounds to a where clause.
+
+#### Before
+```rust
+fn apply<T, U, ┃F: FnOnce(T) -> U>(f: F, x: T) -> U {
+    f(x)
+}
+```
+
+#### After
+```rust
+fn apply<T, U, F>(f: F, x: T) -> U where F: FnOnce(T) -> U {
+    f(x)
+}
+```
+
+
+### `move_const_to_impl`
+**Source:**  [move_const_to_impl.rs](https://github.com/rust-lang/rust-analyzer/blob/master/crates/ide-assists/src/handlers/move_const_to_impl.rs#L14) 
+
+Move a local constant item in a method to impl's associated constant. All the references will be
+qualified with `Self::`.
+
+#### Before
+```rust
+struct S;
+impl S {
+    fn foo() -> usize {
+        /// The answer.
+        const C┃: usize = 42;
+
+        C * C
+    }
+}
+```
+
+#### After
+```rust
+struct S;
+impl S {
+    /// The answer.
+    const C: usize = 42;
+
+    fn foo() -> usize {
+        Self::C * Self::C
+    }
+}
+```
+
+
+### `move_from_mod_rs`
+**Source:**  [move_from_mod_rs.rs](https://github.com/rust-lang/rust-analyzer/blob/master/crates/ide-assists/src/handlers/move_from_mod_rs.rs#L12) 
+
+Moves xxx/mod.rs to xxx.rs.
+
+#### Before
+```rust
+//- /main.rs
+mod a;
+//- /a/mod.rs
+┃fn t() {}┃
+```
+
+#### After
+```rust
+fn t() {}
+```
+
+
+### `move_guard_to_arm_body`
+**Source:**  [move_guard.rs](https://github.com/rust-lang/rust-analyzer/blob/master/crates/ide-assists/src/handlers/move_guard.rs#L8) 
+
+Moves match guard into match arm body.
+
+#### Before
+```rust
+enum Action { Move { distance: u32 }, Stop }
+
+fn handle(action: Action) {
+    match action {
+        Action::Move { distance } ┃if distance > 10 => foo(),
+        _ => (),
+    }
+}
+```
+
+#### After
+```rust
+enum Action { Move { distance: u32 }, Stop }
+
+fn handle(action: Action) {
+    match action {
+        Action::Move { distance } => if distance > 10 {
+            foo()
+        },
+        _ => (),
+    }
+}
+```
+
+
+### `move_module_to_file`
+**Source:**  [move_module_to_file.rs](https://github.com/rust-lang/rust-analyzer/blob/master/crates/ide-assists/src/handlers/move_module_to_file.rs#L15) 
+
+Moves inline module's contents to a separate file.
+
+#### Before
+```rust
+mod ┃foo {
+    fn t() {}
+}
+```
+
+#### After
+```rust
+mod foo;
+```
+
+
+### `move_to_mod_rs`
+**Source:**  [move_to_mod_rs.rs](https://github.com/rust-lang/rust-analyzer/blob/master/crates/ide-assists/src/handlers/move_to_mod_rs.rs#L12) 
+
+Moves xxx.rs to xxx/mod.rs.
+
+#### Before
+```rust
+//- /main.rs
+mod a;
+//- /a.rs
+┃fn t() {}┃
+```
+
+#### After
+```rust
+fn t() {}
+```
+
+
+### `normalize_import`
+**Source:**  [normalize_import.rs](https://github.com/rust-lang/rust-analyzer/blob/master/crates/ide-assists/src/handlers/normalize_import.rs#L9) 
+
+Normalizes an import.
+
+#### Before
+```rust
+use┃ std::{io, {fmt::Formatter}};
+```
+
+#### After
+```rust
+use std::{fmt::Formatter, io};
+```
+
+
+### `promote_local_to_const`
+**Source:**  [promote_local_to_const.rs](https://github.com/rust-lang/rust-analyzer/blob/master/crates/ide-assists/src/handlers/promote_local_to_const.rs#L17) 
+
+Promotes a local variable to a const item changing its name to a `SCREAMING_SNAKE_CASE` variant
+if the local uses no non-const expressions.
+
+#### Before
+```rust
+fn main() {
+    let foo┃ = true;
+
+    if foo {
+        println!("It's true");
+    } else {
+        println!("It's false");
+    }
+}
+```
+
+#### After
+```rust
+fn main() {
+    const ┃FOO: bool = true;
+
+    if FOO {
+        println!("It's true");
+    } else {
+        println!("It's false");
+    }
+}
+```
+
+
+### `pull_assignment_up`
+**Source:**  [pull_assignment_up.rs](https://github.com/rust-lang/rust-analyzer/blob/master/crates/ide-assists/src/handlers/pull_assignment_up.rs#L11) 
+
+Extracts variable assignment to outside an if or match statement.
+
+#### Before
+```rust
+fn main() {
+    let mut foo = 6;
+
+    if true {
+        ┃foo = 5;
+    } else {
+        foo = 4;
+    }
+}
+```
+
+#### After
+```rust
+fn main() {
+    let mut foo = 6;
+
+    foo = if true {
+        5
+    } else {
+        4
+    };
+}
+```
+
+
+### `qualify_method_call`
+**Source:**  [qualify_method_call.rs](https://github.com/rust-lang/rust-analyzer/blob/master/crates/ide-assists/src/handlers/qualify_method_call.rs#L10) 
+
+Replaces the method call with a qualified function call.
+
+#### Before
+```rust
+struct Foo;
+impl Foo {
+    fn foo(&self) {}
+}
+fn main() {
+    let foo = Foo;
+    foo.fo┃o();
+}
+```
+
+#### After
+```rust
+struct Foo;
+impl Foo {
+    fn foo(&self) {}
+}
+fn main() {
+    let foo = Foo;
+    Foo::foo(&foo);
+}
+```
+
+
+### `qualify_path`
+**Source:**  [qualify_path.rs](https://github.com/rust-lang/rust-analyzer/blob/master/crates/ide-assists/src/handlers/qualify_path.rs#L24) 
+
+If the name is unresolved, provides all possible qualified paths for it.
+
+#### Before
+```rust
+fn main() {
+    let map = HashMap┃::new();
+}
+```
+
+#### After
+```rust
+fn main() {
+    let map = std::collections::HashMap::new();
+}
+```
+
+
+### `reformat_number_literal`
+**Source:**  [number_representation.rs](https://github.com/rust-lang/rust-analyzer/blob/master/crates/ide-assists/src/handlers/number_representation.rs#L7) 
+
+Adds or removes separators from integer literal.
+
+#### Before
+```rust
+const _: i32 = 1012345┃;
+```
+
+#### After
+```rust
+const _: i32 = 1_012_345;
+```
+
+
+### `remove_dbg`
+**Source:**  [remove_dbg.rs](https://github.com/rust-lang/rust-analyzer/blob/master/crates/ide-assists/src/handlers/remove_dbg.rs#L9) 
+
+Removes `dbg!()` macro call.
+
+#### Before
+```rust
+fn main() {
+    let x = ┃dbg!(42 * dbg!(4 + 2));┃
+}
+```
+
+#### After
+```rust
+fn main() {
+    let x = 42 * (4 + 2);
+}
+```
+
+
+### `remove_hash`
+**Source:**  [raw_string.rs](https://github.com/rust-lang/rust-analyzer/blob/master/crates/ide-assists/src/handlers/raw_string.rs#L117) 
+
+Removes a hash from a raw string literal.
+
+#### Before
+```rust
+fn main() {
+    r#"Hello,┃ World!"#;
+}
+```
+
+#### After
+```rust
+fn main() {
+    r"Hello, World!";
+}
+```
+
+
+### `remove_mut`
+**Source:**  [remove_mut.rs](https://github.com/rust-lang/rust-analyzer/blob/master/crates/ide-assists/src/handlers/remove_mut.rs#L5) 
+
+Removes the `mut` keyword.
+
+#### Before
+```rust
+impl Walrus {
+    fn feed(&mut┃ self, amount: u32) {}
+}
+```
+
+#### After
+```rust
+impl Walrus {
+    fn feed(&self, amount: u32) {}
+}
+```
+
+
+### `remove_parentheses`
+**Source:**  [remove_parentheses.rs](https://github.com/rust-lang/rust-analyzer/blob/master/crates/ide-assists/src/handlers/remove_parentheses.rs#L5) 
+
+Removes redundant parentheses.
+
+#### Before
+```rust
+fn main() {
+    _ = ┃(2) + 2;
+}
+```
+
+#### After
+```rust
+fn main() {
+    _ = 2 + 2;
+}
+```
+
+
+### `remove_unused_imports`
+**Source:**  [remove_unused_imports.rs](https://github.com/rust-lang/rust-analyzer/blob/master/crates/ide-assists/src/handlers/remove_unused_imports.rs#L17) 
+
+Removes any use statements in the current selection that are unused.
+
+#### Before
+```rust
+struct X();
+mod foo {
+    use super::X┃;
+}
+```
+
+#### After
+```rust
+struct X();
+mod foo {
+}
+```
+
+
+### `remove_unused_param`
+**Source:**  [remove_unused_param.rs](https://github.com/rust-lang/rust-analyzer/blob/master/crates/ide-assists/src/handlers/remove_unused_param.rs#L15) 
+
+Removes unused function parameter.
+
+#### Before
+```rust
+fn frobnicate(x: i32┃) {}
+
+fn main() {
+    frobnicate(92);
+}
+```
+
+#### After
+```rust
+fn frobnicate() {}
+
+fn main() {
+    frobnicate();
+}
+```
+
+
+### `reorder_fields`
+**Source:**  [reorder_fields.rs](https://github.com/rust-lang/rust-analyzer/blob/master/crates/ide-assists/src/handlers/reorder_fields.rs#L8) 
+
+Reorder the fields of record literals and record patterns in the same order as in
+the definition.
+
+#### Before
+```rust
+struct Foo {foo: i32, bar: i32};
+const test: Foo = ┃Foo {bar: 0, foo: 1}
+```
+
+#### After
+```rust
+struct Foo {foo: i32, bar: i32};
+const test: Foo = Foo {foo: 1, bar: 0}
+```
+
+
+### `reorder_impl_items`
+**Source:**  [reorder_impl_items.rs](https://github.com/rust-lang/rust-analyzer/blob/master/crates/ide-assists/src/handlers/reorder_impl_items.rs#L11) 
+
+Reorder the items of an `impl Trait`. The items will be ordered
+in the same order as in the trait definition.
+
+#### Before
+```rust
+trait Foo {
+    type A;
+    const B: u8;
+    fn c();
+}
+
+struct Bar;
+┃impl Foo for Bar┃ {
+    const B: u8 = 17;
+    fn c() {}
+    type A = String;
+}
+```
+
+#### After
+```rust
+trait Foo {
+    type A;
+    const B: u8;
+    fn c();
+}
+
+struct Bar;
+impl Foo for Bar {
+    type A = String;
+    const B: u8 = 17;
+    fn c() {}
+}
+```
+
+
+### `replace_arith_with_checked`
+**Source:**  [replace_arith_op.rs](https://github.com/rust-lang/rust-analyzer/blob/master/crates/ide-assists/src/handlers/replace_arith_op.rs#L9) 
+
+Replaces arithmetic on integers with the `checked_*` equivalent.
+
+#### Before
+```rust
+fn main() {
+  let x = 1 ┃+ 2;
+}
+```
+
+#### After
+```rust
+fn main() {
+  let x = 1.checked_add(2);
+}
+```
+
+
+### `replace_arith_with_saturating`
+**Source:**  [replace_arith_op.rs](https://github.com/rust-lang/rust-analyzer/blob/master/crates/ide-assists/src/handlers/replace_arith_op.rs#L28) 
+
+Replaces arithmetic on integers with the `saturating_*` equivalent.
+
+#### Before
+```rust
+fn main() {
+  let x = 1 ┃+ 2;
+}
+```
+
+#### After
+```rust
+fn main() {
+  let x = 1.saturating_add(2);
+}
+```
+
+
+### `replace_arith_with_wrapping`
+**Source:**  [replace_arith_op.rs](https://github.com/rust-lang/rust-analyzer/blob/master/crates/ide-assists/src/handlers/replace_arith_op.rs#L50) 
+
+Replaces arithmetic on integers with the `wrapping_*` equivalent.
+
+#### Before
+```rust
+fn main() {
+  let x = 1 ┃+ 2;
+}
+```
+
+#### After
+```rust
+fn main() {
+  let x = 1.wrapping_add(2);
+}
+```
+
+
+### `replace_char_with_string`
+**Source:**  [replace_string_with_char.rs](https://github.com/rust-lang/rust-analyzer/blob/master/crates/ide-assists/src/handlers/replace_string_with_char.rs#L51) 
+
+Replace a char literal with a string literal.
+
+#### Before
+```rust
+fn main() {
+    find('{┃');
+}
+```
+
+#### After
+```rust
+fn main() {
+    find("{");
+}
+```
+
+
+### `replace_derive_with_manual_impl`
+**Source:**  [replace_derive_with_manual_impl.rs](https://github.com/rust-lang/rust-analyzer/blob/master/crates/ide-assists/src/handlers/replace_derive_with_manual_impl.rs#L20) 
+
+Converts a `derive` impl into a manual one.
+
+#### Before
+```rust
+#[derive(Deb┃ug, Display)]
+struct S;
+```
+
+#### After
+```rust
+#[derive(Display)]
+struct S;
+
+impl Debug for S {
+    ┃fn fmt(&self, f: &mut Formatter) -> Result<()> {
+        f.debug_struct("S").finish()
+    }
+}
+```
+
+
+### `replace_if_let_with_match`
+**Source:**  [replace_if_let_with_match.rs](https://github.com/rust-lang/rust-analyzer/blob/master/crates/ide-assists/src/handlers/replace_if_let_with_match.rs#L20) 
+
+Replaces a `if let` expression with a `match` expression.
+
+#### Before
+```rust
+enum Action { Move { distance: u32 }, Stop }
+
+fn handle(action: Action) {
+    ┃if let Action::Move { distance } = action {
+        foo(distance)
+    } else {
+        bar()
+    }
+}
+```
+
+#### After
+```rust
+enum Action { Move { distance: u32 }, Stop }
+
+fn handle(action: Action) {
+    match action {
+        Action::Move { distance } => foo(distance),
+        _ => bar(),
+    }
+}
+```
+
+
+### `replace_is_some_with_if_let_some`
+**Source:**  [replace_is_method_with_if_let_method.rs](https://github.com/rust-lang/rust-analyzer/blob/master/crates/ide-assists/src/handlers/replace_is_method_with_if_let_method.rs#L9) 
+
+Replace `if x.is_some()` with `if let Some(_tmp) = x` or `if x.is_ok()` with `if let Ok(_tmp) = x`.
+
+#### Before
+```rust
+fn main() {
+    let x = Some(1);
+    if x.is_som┃e() {}
+}
+```
+
+#### After
+```rust
+fn main() {
+    let x = Some(1);
+    if let Some(${0:x1}) = x {}
+}
+```
+
+
+### `replace_let_with_if_let`
+**Source:**  [replace_let_with_if_let.rs](https://github.com/rust-lang/rust-analyzer/blob/master/crates/ide-assists/src/handlers/replace_let_with_if_let.rs#L9) 
+
+Replaces `let` with an `if let`.
+
+#### Before
+```rust
+
+fn main(action: Action) {
+    ┃let x = compute();
+}
+
+fn compute() -> Option<i32> { None }
+```
+
+#### After
+```rust
+
+fn main(action: Action) {
+    if let Some(x) = compute() {
+    }
+}
+
+fn compute() -> Option<i32> { None }
+```
+
+
+### `replace_match_with_if_let`
+**Source:**  [replace_if_let_with_match.rs](https://github.com/rust-lang/rust-analyzer/blob/master/crates/ide-assists/src/handlers/replace_if_let_with_match.rs#L188) 
+
+Replaces a binary `match` with a wildcard pattern and no guards with an `if let` expression.
+
+#### Before
+```rust
+enum Action { Move { distance: u32 }, Stop }
+
+fn handle(action: Action) {
+    ┃match action {
+        Action::Move { distance } => foo(distance),
+        _ => bar(),
+    }
+}
+```
+
+#### After
+```rust
+enum Action { Move { distance: u32 }, Stop }
+
+fn handle(action: Action) {
+    if let Action::Move { distance } = action {
+        foo(distance)
+    } else {
+        bar()
+    }
+}
+```
+
+
+### `replace_named_generic_with_impl`
+**Source:**  [replace_named_generic_with_impl.rs](https://github.com/rust-lang/rust-analyzer/blob/master/crates/ide-assists/src/handlers/replace_named_generic_with_impl.rs#L18) 
+
+Replaces named generic with an `impl Trait` in function argument.
+
+#### Before
+```rust
+fn new<P┃: AsRef<Path>>(location: P) -> Self {}
+```
+
+#### After
+```rust
+fn new(location: impl AsRef<Path>) -> Self {}
+```
+
+
+### `replace_qualified_name_with_use`
+**Source:**  [replace_qualified_name_with_use.rs](https://github.com/rust-lang/rust-analyzer/blob/master/crates/ide-assists/src/handlers/replace_qualified_name_with_use.rs#L13) 
+
+Adds a use statement for a given fully-qualified name.
+
+#### Before
+```rust
+fn process(map: std::collections::┃HashMap<String, String>) {}
+```
+
+#### After
+```rust
+use std::collections::HashMap;
+
+fn process(map: HashMap<String, String>) {}
+```
+
+
+### `replace_string_with_char`
+**Source:**  [replace_string_with_char.rs](https://github.com/rust-lang/rust-analyzer/blob/master/crates/ide-assists/src/handlers/replace_string_with_char.rs#L11) 
+
+Replace string literal with char literal.
+
+#### Before
+```rust
+fn main() {
+    find("{┃");
+}
+```
+
+#### After
+```rust
+fn main() {
+    find('{');
+}
+```
+
+
+### `replace_try_expr_with_match`
+**Source:**  [replace_try_expr_with_match.rs](https://github.com/rust-lang/rust-analyzer/blob/master/crates/ide-assists/src/handlers/replace_try_expr_with_match.rs#L18) 
+
+Replaces a `try` expression with a `match` expression.
+
+#### Before
+```rust
+fn handle() {
+    let pat = Some(true)┃?;
+}
+```
+
+#### After
+```rust
+fn handle() {
+    let pat = match Some(true) {
+        Some(it) => it,
+        None => return None,
+    };
+}
+```
+
+
+### `replace_turbofish_with_explicit_type`
+**Source:**  [replace_turbofish_with_explicit_type.rs](https://github.com/rust-lang/rust-analyzer/blob/master/crates/ide-assists/src/handlers/replace_turbofish_with_explicit_type.rs#L12) 
+
+Converts `::<_>` to an explicit type assignment.
+
+#### Before
+```rust
+fn make<T>() -> T { ) }
+fn main() {
+    let a = make┃::<i32>();
+}
+```
+
+#### After
+```rust
+fn make<T>() -> T { ) }
+fn main() {
+    let a: i32 = make();
+}
+```
+
+
+### `replace_with_eager_method`
+**Source:**  [replace_method_eager_lazy.rs](https://github.com/rust-lang/rust-analyzer/blob/master/crates/ide-assists/src/handlers/replace_method_eager_lazy.rs#L89) 
+
+Replace `unwrap_or_else` with `unwrap_or` and `ok_or_else` with `ok_or`.
+
+#### Before
+```rust
+fn foo() {
+    let a = Some(1);
+    a.unwra┃p_or_else(|| 2);
+}
+```
+
+#### After
+```rust
+fn foo() {
+    let a = Some(1);
+    a.unwrap_or(2);
+}
+```
+
+
+### `replace_with_lazy_method`
+**Source:**  [replace_method_eager_lazy.rs](https://github.com/rust-lang/rust-analyzer/blob/master/crates/ide-assists/src/handlers/replace_method_eager_lazy.rs#L9) 
+
+Replace `unwrap_or` with `unwrap_or_else` and `ok_or` with `ok_or_else`.
+
+#### Before
+```rust
+fn foo() {
+    let a = Some(1);
+    a.unwra┃p_or(2);
+}
+```
+
+#### After
+```rust
+fn foo() {
+    let a = Some(1);
+    a.unwrap_or_else(|| 2);
+}
+```
+
+
+### `sort_items`
+**Source:**  [sort_items.rs](https://github.com/rust-lang/rust-analyzer/blob/master/crates/ide-assists/src/handlers/sort_items.rs#L12) 
+
+Sorts item members alphabetically: fields, enum variants and methods.
+
+#### Before
+```rust
+struct ┃Foo┃ { second: u32, first: String }
+```
+
+#### After
+```rust
+struct Foo { first: String, second: u32 }
+```
+
+---
+
+#### Before
+```rust
+trait ┃Bar┃ {
+    fn second(&self) -> u32;
+    fn first(&self) -> String;
+}
+```
+
+#### After
+```rust
+trait Bar {
+    fn first(&self) -> String;
+    fn second(&self) -> u32;
+}
+```
+
+---
+
+#### Before
+```rust
+struct Baz;
+impl ┃Baz┃ {
+    fn second(&self) -> u32;
+    fn first(&self) -> String;
+}
+```
+
+#### After
+```rust
+struct Baz;
+impl Baz {
+    fn first(&self) -> String;
+    fn second(&self) -> u32;
+}
+```
+
+---
+There is a difference between sorting enum variants:
+
+#### Before
+```rust
+enum ┃Animal┃ {
+  Dog(String, f64),
+  Cat { weight: f64, name: String },
+}
+```
+
+#### After
+```rust
+enum Animal {
+  Cat { weight: f64, name: String },
+  Dog(String, f64),
+}
+```
+
+and sorting a single enum struct variant:
+
+#### Before
+```rust
+enum Animal {
+  Dog(String, f64),
+  Cat ┃{ weight: f64, name: String }┃,
+}
+```
+
+#### After
+```rust
+enum Animal {
+  Dog(String, f64),
+  Cat { name: String, weight: f64 },
+}
+```
+
+
+### `split_import`
+**Source:**  [split_import.rs](https://github.com/rust-lang/rust-analyzer/blob/master/crates/ide-assists/src/handlers/split_import.rs#L5) 
+
+Wraps the tail of import into braces.
+
+#### Before
+```rust
+use std::┃collections::HashMap;
+```
+
+#### After
+```rust
+use std::{collections::HashMap};
+```
+
+
+### `sugar_impl_future_into_async`
+**Source:**  [toggle_async_sugar.rs](https://github.com/rust-lang/rust-analyzer/blob/master/crates/ide-assists/src/handlers/toggle_async_sugar.rs#L13) 
+
+Rewrites asynchronous function from `-> impl Future` into `async fn`.
+This action does not touch the function body and therefore `async { 0 }`
+block does not transform to just `0`.
+
+#### Before
+```rust
+pub fn foo() -> impl core::future::F┃uture<Output = usize> {
+    async { 0 }
+}
+```
+
+#### After
+```rust
+pub async fn foo() -> usize {
+    async { 0 }
+}
+```
+
+
+### `toggle_ignore`
+**Source:**  [toggle_ignore.rs](https://github.com/rust-lang/rust-analyzer/blob/master/crates/ide-assists/src/handlers/toggle_ignore.rs#L8) 
+
+Adds `#[ignore]` attribute to the test.
+
+#### Before
+```rust
+┃#[test]
+fn arithmetics {
+    assert_eq!(2 + 2, 5);
+}
+```
+
+#### After
+```rust
+#[test]
+#[ignore]
+fn arithmetics {
+    assert_eq!(2 + 2, 5);
+}
+```
+
+
+### `toggle_macro_delimiter`
+**Source:**  [toggle_macro_delimiter.rs](https://github.com/rust-lang/rust-analyzer/blob/master/crates/ide-assists/src/handlers/toggle_macro_delimiter.rs#L9) 
+
+Change macro delimiters in the order of `( -> { -> [ -> (`.
+
+#### Before
+```rust
+macro_rules! sth {
+    () => {};
+}
+
+sth!┃( );
+```
+
+#### After
+```rust
+macro_rules! sth {
+    () => {};
+}
+
+sth!{ }
+```
+
+
+### `unmerge_match_arm`
+**Source:**  [unmerge_match_arm.rs](https://github.com/rust-lang/rust-analyzer/blob/master/crates/ide-assists/src/handlers/unmerge_match_arm.rs#L10) 
+
+Splits the current match with a `|` pattern into two arms with identical bodies.
+
+#### Before
+```rust
+enum Action { Move { distance: u32 }, Stop }
+
+fn handle(action: Action) {
+    match action {
+        Action::Move(..) ┃| Action::Stop => foo(),
+    }
+}
+```
+
+#### After
+```rust
+enum Action { Move { distance: u32 }, Stop }
+
+fn handle(action: Action) {
+    match action {
+        Action::Move(..) => foo(),
+        Action::Stop => foo(),
+    }
+}
+```
+
+
+### `unmerge_use`
+**Source:**  [unmerge_use.rs](https://github.com/rust-lang/rust-analyzer/blob/master/crates/ide-assists/src/handlers/unmerge_use.rs#L12) 
+
+Extracts single use item from use list.
+
+#### Before
+```rust
+use std::fmt::{Debug, Display┃};
+```
+
+#### After
+```rust
+use std::fmt::{Debug};
+use std::fmt::Display;
+```
+
+
+### `unnecessary_async`
+**Source:**  [unnecessary_async.rs](https://github.com/rust-lang/rust-analyzer/blob/master/crates/ide-assists/src/handlers/unnecessary_async.rs#L17) 
+
+Removes the `async` mark from functions which have no `.await` in their body.
+Looks for calls to the functions and removes the `.await` on the call site.
+
+#### Before
+```rust
+pub asy┃nc fn foo() {}
+pub async fn bar() { foo().await }
+```
+
+#### After
+```rust
+pub fn foo() {}
+pub async fn bar() { foo() }
+```
+
+
+### `unqualify_method_call`
+**Source:**  [unqualify_method_call.rs](https://github.com/rust-lang/rust-analyzer/blob/master/crates/ide-assists/src/handlers/unqualify_method_call.rs#L9) 
+
+Transforms universal function call syntax into a method call.
+
+#### Before
+```rust
+fn main() {
+    std::ops::Add::add┃(1, 2);
+}
+```
+
+#### After
+```rust
+use std::ops::Add;
+
+fn main() {
+    1.add(2);
+}
+```
+
+
+### `unwrap_block`
+**Source:**  [unwrap_block.rs](https://github.com/rust-lang/rust-analyzer/blob/master/crates/ide-assists/src/handlers/unwrap_block.rs#L12) 
+
+This assist removes if...else, for, while and loop control statements to just keep the body.
+
+#### Before
+```rust
+fn foo() {
+    if true {┃
+        println!("foo");
+    }
+}
+```
+
+#### After
+```rust
+fn foo() {
+    println!("foo");
+}
+```
+
+
+### `unwrap_option_return_type`
+**Source:**  [unwrap_return_type.rs](https://github.com/rust-lang/rust-analyzer/blob/master/crates/ide-assists/src/handlers/unwrap_return_type.rs#L13) 
+
+Unwrap the function's return type.
+
+#### Before
+```rust
+fn foo() -> Option<i32>┃ { Some(42i32) }
+```
+
+#### After
+```rust
+fn foo() -> i32 { 42i32 }
+```
+
+
+### `unwrap_result_return_type`
+**Source:**  [unwrap_return_type.rs](https://github.com/rust-lang/rust-analyzer/blob/master/crates/ide-assists/src/handlers/unwrap_return_type.rs#L26) 
+
+Unwrap the function's return type.
+
+#### Before
+```rust
+fn foo() -> Result<i32>┃ { Ok(42i32) }
+```
+
+#### After
+```rust
+fn foo() -> i32 { 42i32 }
+```
+
+
+### `unwrap_tuple`
+**Source:**  [unwrap_tuple.rs](https://github.com/rust-lang/rust-analyzer/blob/master/crates/ide-assists/src/handlers/unwrap_tuple.rs#L8) 
+
+Unwrap the tuple to different variables.
+
+#### Before
+```rust
+fn main() {
+    ┃let (foo, bar) = ("Foo", "Bar");
+}
+```
+
+#### After
+```rust
+fn main() {
+    let foo = "Foo";
+    let bar = "Bar";
+}
+```
+
+
+### `wrap_return_type_in_option`
+**Source:**  [wrap_return_type.rs](https://github.com/rust-lang/rust-analyzer/blob/master/crates/ide-assists/src/handlers/wrap_return_type.rs#L16) 
+
+Wrap the function's return type into Option.
+
+#### Before
+```rust
+fn foo() -> i32┃ { 42i32 }
+```
+
+#### After
+```rust
+fn foo() -> Option<i32> { Some(42i32) }
+```
+
+
+### `wrap_return_type_in_result`
+**Source:**  [wrap_return_type.rs](https://github.com/rust-lang/rust-analyzer/blob/master/crates/ide-assists/src/handlers/wrap_return_type.rs#L29) 
+
+Wrap the function's return type into Result.
+
+#### Before
+```rust
+fn foo() -> i32┃ { 42i32 }
+```
+
+#### After
+```rust
+fn foo() -> Result<i32, ${0:_}> { Ok(42i32) }
+```
+
+
+### `wrap_unwrap_cfg_attr`
+**Source:**  [wrap_unwrap_cfg_attr.rs](https://github.com/rust-lang/rust-analyzer/blob/master/crates/ide-assists/src/handlers/wrap_unwrap_cfg_attr.rs#L12) 
+
+Wraps an attribute to a cfg_attr attribute or unwraps a cfg_attr attribute to the inner attributes.
+
+#### Before
+```rust
+#[derive┃(Debug)]
+struct S {
+   field: i32
+}
+```
+
+#### After
+```rust
+#[cfg_attr(┃, derive(Debug))]
+struct S {
+   field: i32
+}
+```
diff --git a/src/tools/rust-analyzer/docs/book/src/configuration.md b/src/tools/rust-analyzer/docs/book/src/configuration.md
new file mode 100644
index 00000000000..fd94a4221a9
--- /dev/null
+++ b/src/tools/rust-analyzer/docs/book/src/configuration.md
@@ -0,0 +1,51 @@
+# Configuration
+
+**Source:**
+[config.rs](https://github.com/rust-lang/rust-analyzer/blob/master/crates/rust-analyzer/src/config.rs)
+
+The [Installation](./installation.md) section contains details on
+configuration for some of the editors. In general `rust-analyzer` is
+configured via LSP messages, which means that it’s up to the editor to
+decide on the exact format and location of configuration files.
+
+Some clients, such as [VS Code](./vs_code.md) or [COC plugin in
+Vim](./other_editors.md#coc-rust-analyzer) provide `rust-analyzer` specific configuration
+UIs. Others may require you to know a bit more about the interaction
+with `rust-analyzer`.
+
+For the later category, it might help to know that the initial
+configuration is specified as a value of the `initializationOptions`
+field of the [`InitializeParams` message, in the LSP
+protocol](https://microsoft.github.io/language-server-protocol/specifications/specification-current/#initialize).
+The spec says that the field type is `any?`, but `rust-analyzer` is
+looking for a JSON object that is constructed using settings from the
+list below. Name of the setting, ignoring the `rust-analyzer.` prefix,
+is used as a path, and value of the setting becomes the JSON property
+value.
+
+For example, a very common configuration is to enable proc-macro
+support, can be achieved by sending this JSON:
+
+    {
+      "cargo": {
+        "buildScripts": {
+          "enable": true,
+        },
+      },
+      "procMacro": {
+        "enable": true,
+      }
+    }
+
+Please consult your editor’s documentation to learn more about how to
+configure [LSP
+servers](https://microsoft.github.io/language-server-protocol/).
+
+To verify which configuration is actually used by `rust-analyzer`, set
+`RA_LOG` environment variable to `rust_analyzer=info` and look for
+config-related messages. Logs should show both the JSON that
+`rust-analyzer` sees as well as the updated config.
+
+This is the list of config options `rust-analyzer` supports:
+
+{{#include configuration_generated.md}}
diff --git a/src/tools/rust-analyzer/docs/book/src/configuration_generated.md b/src/tools/rust-analyzer/docs/book/src/configuration_generated.md
new file mode 100644
index 00000000000..0c6674b1408
--- /dev/null
+++ b/src/tools/rust-analyzer/docs/book/src/configuration_generated.md
@@ -0,0 +1,1206 @@
+**rust-analyzer.assist.emitMustUse** (default: false)
+
+ Whether to insert #[must_use] when generating `as_` methods
+for enum variants.
+
+
+**rust-analyzer.assist.expressionFillDefault** (default: "todo")
+
+ Placeholder expression to use for missing expressions in assists.
+
+
+**rust-analyzer.assist.termSearch.borrowcheck** (default: true)
+
+ Enable borrow checking for term search code assists. If set to false, also there will be more suggestions, but some of them may not borrow-check.
+
+
+**rust-analyzer.assist.termSearch.fuel** (default: 1800)
+
+ Term search fuel in "units of work" for assists (Defaults to 1800).
+
+
+**rust-analyzer.cachePriming.enable** (default: true)
+
+ Warm up caches on project load.
+
+
+**rust-analyzer.cachePriming.numThreads** (default: "physical")
+
+ How many worker threads to handle priming caches. The default `0` means to pick automatically.
+
+
+**rust-analyzer.cargo.allTargets** (default: true)
+
+ Pass `--all-targets` to cargo invocation.
+
+
+**rust-analyzer.cargo.autoreload** (default: true)
+
+ Automatically refresh project info via `cargo metadata` on
+`Cargo.toml` or `.cargo/config.toml` changes.
+
+
+**rust-analyzer.cargo.buildScripts.enable** (default: true)
+
+ Run build scripts (`build.rs`) for more precise code analysis.
+
+
+**rust-analyzer.cargo.buildScripts.invocationStrategy** (default: "per_workspace")
+
+ Specifies the invocation strategy to use when running the build scripts command.
+If `per_workspace` is set, the command will be executed for each Rust workspace with the
+workspace as the working directory.
+If `once` is set, the command will be executed once with the opened project as the
+working directory.
+This config only has an effect when `#rust-analyzer.cargo.buildScripts.overrideCommand#`
+is set.
+
+
+**rust-analyzer.cargo.buildScripts.overrideCommand** (default: null)
+
+ Override the command rust-analyzer uses to run build scripts and
+build procedural macros. The command is required to output json
+and should therefore include `--message-format=json` or a similar
+option.
+
+If there are multiple linked projects/workspaces, this command is invoked for
+each of them, with the working directory being the workspace root
+(i.e., the folder containing the `Cargo.toml`). This can be overwritten
+by changing `#rust-analyzer.cargo.buildScripts.invocationStrategy#`.
+
+By default, a cargo invocation will be constructed for the configured
+targets and features, with the following base command line:
+
+```bash
+cargo check --quiet --workspace --message-format=json --all-targets --keep-going
+```
+.
+
+
+**rust-analyzer.cargo.buildScripts.rebuildOnSave** (default: true)
+
+ Rerun proc-macros building/build-scripts running when proc-macro
+or build-script sources change and are saved.
+
+
+**rust-analyzer.cargo.buildScripts.useRustcWrapper** (default: true)
+
+ Use `RUSTC_WRAPPER=rust-analyzer` when running build scripts to
+avoid checking unnecessary things.
+
+
+ **rust-analyzer.cargo.cfgs**
+
+Default:
+
+```[
+  "debug_assertions",
+  "miri"
+]
+
+```
+
+ List of cfg options to enable with the given values.
+
+
+ **rust-analyzer.cargo.extraArgs** (default: [])
+
+ Extra arguments that are passed to every cargo invocation.
+
+
+**rust-analyzer.cargo.extraEnv** (default: {})
+
+ Extra environment variables that will be set when running cargo, rustc
+or other commands within the workspace. Useful for setting RUSTFLAGS.
+
+
+**rust-analyzer.cargo.features** (default: [])
+
+ List of features to activate.
+
+Set this to `"all"` to pass `--all-features` to cargo.
+
+
+**rust-analyzer.cargo.noDefaultFeatures** (default: false)
+
+ Whether to pass `--no-default-features` to cargo.
+
+
+**rust-analyzer.cargo.sysroot** (default: "discover")
+
+ Relative path to the sysroot, or "discover" to try to automatically find it via
+"rustc --print sysroot".
+
+Unsetting this disables sysroot loading.
+
+This option does not take effect until rust-analyzer is restarted.
+
+
+**rust-analyzer.cargo.sysrootSrc** (default: null)
+
+ Relative path to the sysroot library sources. If left unset, this will default to
+`{cargo.sysroot}/lib/rustlib/src/rust/library`.
+
+This option does not take effect until rust-analyzer is restarted.
+
+
+**rust-analyzer.cargo.target** (default: null)
+
+ Compilation target override (target tuple).
+
+
+**rust-analyzer.cargo.targetDir** (default: null)
+
+ Optional path to a rust-analyzer specific target directory.
+This prevents rust-analyzer's `cargo check` and initial build-script and proc-macro
+building from locking the `Cargo.lock` at the expense of duplicating build artifacts.
+
+Set to `true` to use a subdirectory of the existing target directory or
+set to a path relative to the workspace to use that path.
+
+
+**rust-analyzer.cfg.setTest** (default: true)
+
+ Set `cfg(test)` for local crates. Defaults to true.
+
+
+**rust-analyzer.checkOnSave** (default: true)
+
+ Run the check command for diagnostics on save.
+
+
+**rust-analyzer.check.allTargets** (default: null)
+
+ Check all targets and tests (`--all-targets`). Defaults to
+`#rust-analyzer.cargo.allTargets#`.
+
+
+**rust-analyzer.check.command** (default: "check")
+
+ Cargo command to use for `cargo check`.
+
+
+**rust-analyzer.check.extraArgs** (default: [])
+
+ Extra arguments for `cargo check`.
+
+
+**rust-analyzer.check.extraEnv** (default: {})
+
+ Extra environment variables that will be set when running `cargo check`.
+Extends `#rust-analyzer.cargo.extraEnv#`.
+
+
+**rust-analyzer.check.features** (default: null)
+
+ List of features to activate. Defaults to
+`#rust-analyzer.cargo.features#`.
+
+Set to `"all"` to pass `--all-features` to Cargo.
+
+
+**rust-analyzer.check.ignore** (default: [])
+
+ List of `cargo check` (or other command specified in `check.command`) diagnostics to ignore.
+
+For example for `cargo check`: `dead_code`, `unused_imports`, `unused_variables`,...
+
+
+**rust-analyzer.check.invocationStrategy** (default: "per_workspace")
+
+ Specifies the invocation strategy to use when running the check command.
+If `per_workspace` is set, the command will be executed for each workspace.
+If `once` is set, the command will be executed once.
+This config only has an effect when `#rust-analyzer.check.overrideCommand#`
+is set.
+
+
+**rust-analyzer.check.noDefaultFeatures** (default: null)
+
+ Whether to pass `--no-default-features` to Cargo. Defaults to
+`#rust-analyzer.cargo.noDefaultFeatures#`.
+
+
+**rust-analyzer.check.overrideCommand** (default: null)
+
+ Override the command rust-analyzer uses instead of `cargo check` for
+diagnostics on save. The command is required to output json and
+should therefore include `--message-format=json` or a similar option
+(if your client supports the `colorDiagnosticOutput` experimental
+capability, you can use `--message-format=json-diagnostic-rendered-ansi`).
+
+If you're changing this because you're using some tool wrapping
+Cargo, you might also want to change
+`#rust-analyzer.cargo.buildScripts.overrideCommand#`.
+
+If there are multiple linked projects/workspaces, this command is invoked for
+each of them, with the working directory being the workspace root
+(i.e., the folder containing the `Cargo.toml`). This can be overwritten
+by changing `#rust-analyzer.check.invocationStrategy#`.
+
+If `$saved_file` is part of the command, rust-analyzer will pass
+the absolute path of the saved file to the provided command. This is
+intended to be used with non-Cargo build systems.
+Note that `$saved_file` is experimental and may be removed in the future.
+
+An example command would be:
+
+```bash
+cargo check --workspace --message-format=json --all-targets
+```
+.
+
+
+**rust-analyzer.check.targets** (default: null)
+
+ Check for specific targets. Defaults to `#rust-analyzer.cargo.target#` if empty.
+
+Can be a single target, e.g. `"x86_64-unknown-linux-gnu"` or a list of targets, e.g.
+`["aarch64-apple-darwin", "x86_64-apple-darwin"]`.
+
+Aliased as `"checkOnSave.targets"`.
+
+
+**rust-analyzer.check.workspace** (default: true)
+
+ Whether `--workspace` should be passed to `cargo check`.
+If false, `-p <package>` will be passed instead if applicable. In case it is not, no
+check will be performed.
+
+
+**rust-analyzer.completion.addSemicolonToUnit** (default: true)
+
+ Whether to automatically add a semicolon when completing unit-returning functions.
+
+In `match` arms it completes a comma instead.
+
+
+**rust-analyzer.completion.autoAwait.enable** (default: true)
+
+ Toggles the additional completions that automatically show method calls and field accesses with `await` prefixed to them when completing on a future.
+
+
+**rust-analyzer.completion.autoIter.enable** (default: true)
+
+ Toggles the additional completions that automatically show method calls with `iter()` or `into_iter()` prefixed to them when completing on a type that has them.
+
+
+**rust-analyzer.completion.autoimport.enable** (default: true)
+
+ Toggles the additional completions that automatically add imports when completed.
+Note that your client must specify the `additionalTextEdits` LSP client capability to truly have this feature enabled.
+
+
+ **rust-analyzer.completion.autoimport.exclude**
+
+Default:
+
+```[
+  {
+    "path": "core::borrow::Borrow",
+    "type": "methods"
+  },
+  {
+    "path": "core::borrow::BorrowMut",
+    "type": "methods"
+  }
+]
+
+```
+
+ A list of full paths to items to exclude from auto-importing completions.
+
+Traits in this list won't have their methods suggested in completions unless the trait
+is in scope.
+
+You can either specify a string path which defaults to type "always" or use the more verbose
+form `{ "path": "path::to::item", type: "always" }`.
+
+For traits the type "methods" can be used to only exclude the methods but not the trait itself.
+
+This setting also inherits `#rust-analyzer.completion.excludeTraits#`.
+
+
+ **rust-analyzer.completion.autoself.enable** (default: true)
+
+ Toggles the additional completions that automatically show method calls and field accesses
+with `self` prefixed to them when inside a method.
+
+
+**rust-analyzer.completion.callable.snippets** (default: "fill_arguments")
+
+ Whether to add parenthesis and argument snippets when completing function.
+
+
+**rust-analyzer.completion.excludeTraits** (default: [])
+
+ A list of full paths to traits whose methods to exclude from completion.
+
+Methods from these traits won't be completed, even if the trait is in scope. However, they will still be suggested on expressions whose type is `dyn Trait`, `impl Trait` or `T where T: Trait`.
+
+Note that the trait themselves can still be completed.
+
+
+**rust-analyzer.completion.fullFunctionSignatures.enable** (default: false)
+
+ Whether to show full function/method signatures in completion docs.
+
+
+**rust-analyzer.completion.hideDeprecated** (default: false)
+
+ Whether to omit deprecated items from autocompletion. By default they are marked as deprecated but not hidden.
+
+
+**rust-analyzer.completion.limit** (default: null)
+
+ Maximum number of completions to return. If `None`, the limit is infinite.
+
+
+**rust-analyzer.completion.postfix.enable** (default: true)
+
+ Whether to show postfix snippets like `dbg`, `if`, `not`, etc.
+
+
+**rust-analyzer.completion.privateEditable.enable** (default: false)
+
+ Enables completions of private items and fields that are defined in the current workspace even if they are not visible at the current position.
+
+
+ **rust-analyzer.completion.snippets.custom**
+
+Default:
+
+```{
+  "Ok": {
+    "postfix": "ok",
+    "body": "Ok(${receiver})",
+    "description": "Wrap the expression in a `Result::Ok`",
+    "scope": "expr"
+  },
+  "Box::pin": {
+    "postfix": "pinbox",
+    "body": "Box::pin(${receiver})",
+    "requires": "std::boxed::Box",
+    "description": "Put the expression into a pinned `Box`",
+    "scope": "expr"
+  },
+  "Arc::new": {
+    "postfix": "arc",
+    "body": "Arc::new(${receiver})",
+    "requires": "std::sync::Arc",
+    "description": "Put the expression into an `Arc`",
+    "scope": "expr"
+  },
+  "Some": {
+    "postfix": "some",
+    "body": "Some(${receiver})",
+    "description": "Wrap the expression in an `Option::Some`",
+    "scope": "expr"
+  },
+  "Err": {
+    "postfix": "err",
+    "body": "Err(${receiver})",
+    "description": "Wrap the expression in a `Result::Err`",
+    "scope": "expr"
+  },
+  "Rc::new": {
+    "postfix": "rc",
+    "body": "Rc::new(${receiver})",
+    "requires": "std::rc::Rc",
+    "description": "Put the expression into an `Rc`",
+    "scope": "expr"
+  }
+}
+
+```
+
+ Custom completion snippets.
+
+
+ **rust-analyzer.completion.termSearch.enable** (default: false)
+
+ Whether to enable term search based snippets like `Some(foo.bar().baz())`.
+
+
+**rust-analyzer.completion.termSearch.fuel** (default: 1000)
+
+ Term search fuel in "units of work" for autocompletion (Defaults to 1000).
+
+
+**rust-analyzer.diagnostics.disabled** (default: [])
+
+ List of rust-analyzer diagnostics to disable.
+
+
+**rust-analyzer.diagnostics.enable** (default: true)
+
+ Whether to show native rust-analyzer diagnostics.
+
+
+**rust-analyzer.diagnostics.experimental.enable** (default: false)
+
+ Whether to show experimental rust-analyzer diagnostics that might
+have more false positives than usual.
+
+
+**rust-analyzer.diagnostics.remapPrefix** (default: {})
+
+ Map of prefixes to be substituted when parsing diagnostic file paths.
+This should be the reverse mapping of what is passed to `rustc` as `--remap-path-prefix`.
+
+
+**rust-analyzer.diagnostics.styleLints.enable** (default: false)
+
+ Whether to run additional style lints.
+
+
+**rust-analyzer.diagnostics.warningsAsHint** (default: [])
+
+ List of warnings that should be displayed with hint severity.
+
+The warnings will be indicated by faded text or three dots in code
+and will not show up in the `Problems Panel`.
+
+
+**rust-analyzer.diagnostics.warningsAsInfo** (default: [])
+
+ List of warnings that should be displayed with info severity.
+
+The warnings will be indicated by a blue squiggly underline in code
+and a blue icon in the `Problems Panel`.
+
+
+**rust-analyzer.files.exclude** (default: [])
+
+ These paths (file/directories) will be ignored by rust-analyzer. They are
+relative to the workspace root, and globs are not supported. You may
+also need to add the folders to Code's `files.watcherExclude`.
+
+
+**rust-analyzer.files.watcher** (default: "client")
+
+ Controls file watching implementation.
+
+
+**rust-analyzer.highlightRelated.breakPoints.enable** (default: true)
+
+ Enables highlighting of related references while the cursor is on `break`, `loop`, `while`, or `for` keywords.
+
+
+**rust-analyzer.highlightRelated.closureCaptures.enable** (default: true)
+
+ Enables highlighting of all captures of a closure while the cursor is on the `|` or move keyword of a closure.
+
+
+**rust-analyzer.highlightRelated.exitPoints.enable** (default: true)
+
+ Enables highlighting of all exit points while the cursor is on any `return`, `?`, `fn`, or return type arrow (`->`).
+
+
+**rust-analyzer.highlightRelated.references.enable** (default: true)
+
+ Enables highlighting of related references while the cursor is on any identifier.
+
+
+**rust-analyzer.highlightRelated.yieldPoints.enable** (default: true)
+
+ Enables highlighting of all break points for a loop or block context while the cursor is on any `async` or `await` keywords.
+
+
+**rust-analyzer.hover.actions.debug.enable** (default: true)
+
+ Whether to show `Debug` action. Only applies when
+`#rust-analyzer.hover.actions.enable#` is set.
+
+
+**rust-analyzer.hover.actions.enable** (default: true)
+
+ Whether to show HoverActions in Rust files.
+
+
+**rust-analyzer.hover.actions.gotoTypeDef.enable** (default: true)
+
+ Whether to show `Go to Type Definition` action. Only applies when
+`#rust-analyzer.hover.actions.enable#` is set.
+
+
+**rust-analyzer.hover.actions.implementations.enable** (default: true)
+
+ Whether to show `Implementations` action. Only applies when
+`#rust-analyzer.hover.actions.enable#` is set.
+
+
+**rust-analyzer.hover.actions.references.enable** (default: false)
+
+ Whether to show `References` action. Only applies when
+`#rust-analyzer.hover.actions.enable#` is set.
+
+
+**rust-analyzer.hover.actions.run.enable** (default: true)
+
+ Whether to show `Run` action. Only applies when
+`#rust-analyzer.hover.actions.enable#` is set.
+
+
+**rust-analyzer.hover.actions.updateTest.enable** (default: true)
+
+ Whether to show `Update Test` action. Only applies when
+`#rust-analyzer.hover.actions.enable#` and `#rust-analyzer.hover.actions.run.enable#` are set.
+
+
+**rust-analyzer.hover.documentation.enable** (default: true)
+
+ Whether to show documentation on hover.
+
+
+**rust-analyzer.hover.documentation.keywords.enable** (default: true)
+
+ Whether to show keyword hover popups. Only applies when
+`#rust-analyzer.hover.documentation.enable#` is set.
+
+
+**rust-analyzer.hover.links.enable** (default: true)
+
+ Use markdown syntax for links on hover.
+
+
+**rust-analyzer.hover.maxSubstitutionLength** (default: 20)
+
+ Whether to show what types are used as generic arguments in calls etc. on hover, and what is their max length to show such types, beyond it they will be shown with ellipsis.
+
+This can take three values: `null` means "unlimited", the string `"hide"` means to not show generic substitutions at all, and a number means to limit them to X characters.
+
+The default is 20 characters.
+
+
+**rust-analyzer.hover.memoryLayout.alignment** (default: "hexadecimal")
+
+ How to render the align information in a memory layout hover.
+
+
+**rust-analyzer.hover.memoryLayout.enable** (default: true)
+
+ Whether to show memory layout data on hover.
+
+
+**rust-analyzer.hover.memoryLayout.niches** (default: false)
+
+ How to render the niche information in a memory layout hover.
+
+
+**rust-analyzer.hover.memoryLayout.offset** (default: "hexadecimal")
+
+ How to render the offset information in a memory layout hover.
+
+
+**rust-analyzer.hover.memoryLayout.size** (default: "both")
+
+ How to render the size information in a memory layout hover.
+
+
+**rust-analyzer.hover.show.enumVariants** (default: 5)
+
+ How many variants of an enum to display when hovering on. Show none if empty.
+
+
+**rust-analyzer.hover.show.fields** (default: 5)
+
+ How many fields of a struct, variant or union to display when hovering on. Show none if empty.
+
+
+**rust-analyzer.hover.show.traitAssocItems** (default: null)
+
+ How many associated items of a trait to display when hovering a trait.
+
+
+**rust-analyzer.imports.granularity.enforce** (default: false)
+
+ Whether to enforce the import granularity setting for all files. If set to false rust-analyzer will try to keep import styles consistent per file.
+
+
+**rust-analyzer.imports.granularity.group** (default: "crate")
+
+ How imports should be grouped into use statements.
+
+
+**rust-analyzer.imports.group.enable** (default: true)
+
+ Group inserted imports by the [following order](https://rust-analyzer.github.io/manual.html#auto-import). Groups are separated by newlines.
+
+
+**rust-analyzer.imports.merge.glob** (default: true)
+
+ Whether to allow import insertion to merge new imports into single path glob imports like `use std::fmt::*;`.
+
+
+**rust-analyzer.imports.preferNoStd** (default: false)
+
+ Prefer to unconditionally use imports of the core and alloc crate, over the std crate.
+
+
+**rust-analyzer.imports.preferPrelude** (default: false)
+
+ Whether to prefer import paths containing a `prelude` module.
+
+
+**rust-analyzer.imports.prefix** (default: "plain")
+
+ The path structure for newly inserted paths to use.
+
+
+**rust-analyzer.imports.prefixExternPrelude** (default: false)
+
+ Whether to prefix external (including std, core) crate imports with `::`. e.g. "use ::std::io::Read;".
+
+
+**rust-analyzer.inlayHints.bindingModeHints.enable** (default: false)
+
+ Whether to show inlay type hints for binding modes.
+
+
+**rust-analyzer.inlayHints.chainingHints.enable** (default: true)
+
+ Whether to show inlay type hints for method chains.
+
+
+**rust-analyzer.inlayHints.closingBraceHints.enable** (default: true)
+
+ Whether to show inlay hints after a closing `}` to indicate what item it belongs to.
+
+
+**rust-analyzer.inlayHints.closingBraceHints.minLines** (default: 25)
+
+ Minimum number of lines required before the `}` until the hint is shown (set to 0 or 1
+to always show them).
+
+
+**rust-analyzer.inlayHints.closureCaptureHints.enable** (default: false)
+
+ Whether to show inlay hints for closure captures.
+
+
+**rust-analyzer.inlayHints.closureReturnTypeHints.enable** (default: "never")
+
+ Whether to show inlay type hints for return types of closures.
+
+
+**rust-analyzer.inlayHints.closureStyle** (default: "impl_fn")
+
+ Closure notation in type and chaining inlay hints.
+
+
+**rust-analyzer.inlayHints.discriminantHints.enable** (default: "never")
+
+ Whether to show enum variant discriminant hints.
+
+
+**rust-analyzer.inlayHints.expressionAdjustmentHints.enable** (default: "never")
+
+ Whether to show inlay hints for type adjustments.
+
+
+**rust-analyzer.inlayHints.expressionAdjustmentHints.hideOutsideUnsafe** (default: false)
+
+ Whether to hide inlay hints for type adjustments outside of `unsafe` blocks.
+
+
+**rust-analyzer.inlayHints.expressionAdjustmentHints.mode** (default: "prefix")
+
+ Whether to show inlay hints as postfix ops (`.*` instead of `*`, etc).
+
+
+**rust-analyzer.inlayHints.genericParameterHints.const.enable** (default: true)
+
+ Whether to show const generic parameter name inlay hints.
+
+
+**rust-analyzer.inlayHints.genericParameterHints.lifetime.enable** (default: false)
+
+ Whether to show generic lifetime parameter name inlay hints.
+
+
+**rust-analyzer.inlayHints.genericParameterHints.type.enable** (default: false)
+
+ Whether to show generic type parameter name inlay hints.
+
+
+**rust-analyzer.inlayHints.implicitDrops.enable** (default: false)
+
+ Whether to show implicit drop hints.
+
+
+**rust-analyzer.inlayHints.implicitSizedBoundHints.enable** (default: false)
+
+ Whether to show inlay hints for the implied type parameter `Sized` bound.
+
+
+**rust-analyzer.inlayHints.lifetimeElisionHints.enable** (default: "never")
+
+ Whether to show inlay type hints for elided lifetimes in function signatures.
+
+
+**rust-analyzer.inlayHints.lifetimeElisionHints.useParameterNames** (default: false)
+
+ Whether to prefer using parameter names as the name for elided lifetime hints if possible.
+
+
+**rust-analyzer.inlayHints.maxLength** (default: 25)
+
+ Maximum length for inlay hints. Set to null to have an unlimited length.
+
+
+**rust-analyzer.inlayHints.parameterHints.enable** (default: true)
+
+ Whether to show function parameter name inlay hints at the call
+site.
+
+
+**rust-analyzer.inlayHints.rangeExclusiveHints.enable** (default: false)
+
+ Whether to show exclusive range inlay hints.
+
+
+**rust-analyzer.inlayHints.reborrowHints.enable** (default: "never")
+
+ Whether to show inlay hints for compiler inserted reborrows.
+This setting is deprecated in favor of #rust-analyzer.inlayHints.expressionAdjustmentHints.enable#.
+
+
+**rust-analyzer.inlayHints.renderColons** (default: true)
+
+ Whether to render leading colons for type hints, and trailing colons for parameter hints.
+
+
+**rust-analyzer.inlayHints.typeHints.enable** (default: true)
+
+ Whether to show inlay type hints for variables.
+
+
+**rust-analyzer.inlayHints.typeHints.hideClosureInitialization** (default: false)
+
+ Whether to hide inlay type hints for `let` statements that initialize to a closure.
+Only applies to closures with blocks, same as `#rust-analyzer.inlayHints.closureReturnTypeHints.enable#`.
+
+
+**rust-analyzer.inlayHints.typeHints.hideClosureParameter** (default: false)
+
+ Whether to hide inlay parameter type hints for closures.
+
+
+**rust-analyzer.inlayHints.typeHints.hideNamedConstructor** (default: false)
+
+ Whether to hide inlay type hints for constructors.
+
+
+**rust-analyzer.interpret.tests** (default: false)
+
+ Enables the experimental support for interpreting tests.
+
+
+**rust-analyzer.joinLines.joinAssignments** (default: true)
+
+ Join lines merges consecutive declaration and initialization of an assignment.
+
+
+**rust-analyzer.joinLines.joinElseIf** (default: true)
+
+ Join lines inserts else between consecutive ifs.
+
+
+**rust-analyzer.joinLines.removeTrailingComma** (default: true)
+
+ Join lines removes trailing commas.
+
+
+**rust-analyzer.joinLines.unwrapTrivialBlock** (default: true)
+
+ Join lines unwraps trivial blocks.
+
+
+**rust-analyzer.lens.debug.enable** (default: true)
+
+ Whether to show `Debug` lens. Only applies when
+`#rust-analyzer.lens.enable#` is set.
+
+
+**rust-analyzer.lens.enable** (default: true)
+
+ Whether to show CodeLens in Rust files.
+
+
+**rust-analyzer.lens.implementations.enable** (default: true)
+
+ Whether to show `Implementations` lens. Only applies when
+`#rust-analyzer.lens.enable#` is set.
+
+
+**rust-analyzer.lens.location** (default: "above_name")
+
+ Where to render annotations.
+
+
+**rust-analyzer.lens.references.adt.enable** (default: false)
+
+ Whether to show `References` lens for Struct, Enum, and Union.
+Only applies when `#rust-analyzer.lens.enable#` is set.
+
+
+**rust-analyzer.lens.references.enumVariant.enable** (default: false)
+
+ Whether to show `References` lens for Enum Variants.
+Only applies when `#rust-analyzer.lens.enable#` is set.
+
+
+**rust-analyzer.lens.references.method.enable** (default: false)
+
+ Whether to show `Method References` lens. Only applies when
+`#rust-analyzer.lens.enable#` is set.
+
+
+**rust-analyzer.lens.references.trait.enable** (default: false)
+
+ Whether to show `References` lens for Trait.
+Only applies when `#rust-analyzer.lens.enable#` is set.
+
+
+**rust-analyzer.lens.run.enable** (default: true)
+
+ Whether to show `Run` lens. Only applies when
+`#rust-analyzer.lens.enable#` is set.
+
+
+**rust-analyzer.lens.updateTest.enable** (default: true)
+
+ Whether to show `Update Test` lens. Only applies when
+`#rust-analyzer.lens.enable#` and `#rust-analyzer.lens.run.enable#` are set.
+
+
+**rust-analyzer.linkedProjects** (default: [])
+
+ Disable project auto-discovery in favor of explicitly specified set
+of projects.
+
+Elements must be paths pointing to `Cargo.toml`,
+`rust-project.json`, `.rs` files (which will be treated as standalone files) or JSON
+objects in `rust-project.json` format.
+
+
+**rust-analyzer.lru.capacity** (default: null)
+
+ Number of syntax trees rust-analyzer keeps in memory. Defaults to 128.
+
+
+**rust-analyzer.lru.query.capacities** (default: {})
+
+ Sets the LRU capacity of the specified queries.
+
+
+**rust-analyzer.notifications.cargoTomlNotFound** (default: true)
+
+ Whether to show `can't find Cargo.toml` error message.
+
+
+**rust-analyzer.numThreads** (default: null)
+
+ How many worker threads in the main loop. The default `null` means to pick automatically.
+
+
+**rust-analyzer.procMacro.attributes.enable** (default: true)
+
+ Expand attribute macros. Requires `#rust-analyzer.procMacro.enable#` to be set.
+
+
+**rust-analyzer.procMacro.enable** (default: true)
+
+ Enable support for procedural macros, implies `#rust-analyzer.cargo.buildScripts.enable#`.
+
+
+**rust-analyzer.procMacro.ignored** (default: {})
+
+ These proc-macros will be ignored when trying to expand them.
+
+This config takes a map of crate names with the exported proc-macro names to ignore as values.
+
+
+**rust-analyzer.procMacro.server** (default: null)
+
+ Internal config, path to proc-macro server executable.
+
+
+**rust-analyzer.references.excludeImports** (default: false)
+
+ Exclude imports from find-all-references.
+
+
+**rust-analyzer.references.excludeTests** (default: false)
+
+ Exclude tests from find-all-references and call-hierarchy.
+
+
+**rust-analyzer.runnables.command** (default: null)
+
+ Command to be executed instead of 'cargo' for runnables.
+
+
+**rust-analyzer.runnables.extraArgs** (default: [])
+
+ Additional arguments to be passed to cargo for runnables such as
+tests or binaries. For example, it may be `--release`.
+
+
+ **rust-analyzer.runnables.extraTestBinaryArgs**
+
+Default:
+
+```[
+  "--show-output"
+]
+
+```
+
+ Additional arguments to be passed through Cargo to launched tests, benchmarks, or
+doc-tests.
+
+Unless the launched target uses a
+[custom test harness](https://doc.rust-lang.org/cargo/reference/cargo-targets.html#the-harness-field),
+they will end up being interpreted as options to
+[`rustc`’s built-in test harness (“libtest”)](https://doc.rust-lang.org/rustc/tests/index.html#cli-arguments).
+
+
+ **rust-analyzer.rustc.source** (default: null)
+
+ Path to the Cargo.toml of the rust compiler workspace, for usage in rustc_private
+projects, or "discover" to try to automatically find it if the `rustc-dev` component
+is installed.
+
+Any project which uses rust-analyzer with the rustcPrivate
+crates must set `[package.metadata.rust-analyzer] rustc_private=true` to use it.
+
+This option does not take effect until rust-analyzer is restarted.
+
+
+**rust-analyzer.rustfmt.extraArgs** (default: [])
+
+ Additional arguments to `rustfmt`.
+
+
+**rust-analyzer.rustfmt.overrideCommand** (default: null)
+
+ Advanced option, fully override the command rust-analyzer uses for
+formatting. This should be the equivalent of `rustfmt` here, and
+not that of `cargo fmt`. The file contents will be passed on the
+standard input and the formatted result will be read from the
+standard output.
+
+
+**rust-analyzer.rustfmt.rangeFormatting.enable** (default: false)
+
+ Enables the use of rustfmt's unstable range formatting command for the
+`textDocument/rangeFormatting` request. The rustfmt option is unstable and only
+available on a nightly build.
+
+
+**rust-analyzer.semanticHighlighting.doc.comment.inject.enable** (default: true)
+
+ Inject additional highlighting into doc comments.
+
+When enabled, rust-analyzer will highlight rust source in doc comments as well as intra
+doc links.
+
+
+**rust-analyzer.semanticHighlighting.nonStandardTokens** (default: true)
+
+ Whether the server is allowed to emit non-standard tokens and modifiers.
+
+
+**rust-analyzer.semanticHighlighting.operator.enable** (default: true)
+
+ Use semantic tokens for operators.
+
+When disabled, rust-analyzer will emit semantic tokens only for operator tokens when
+they are tagged with modifiers.
+
+
+**rust-analyzer.semanticHighlighting.operator.specialization.enable** (default: false)
+
+ Use specialized semantic tokens for operators.
+
+When enabled, rust-analyzer will emit special token types for operator tokens instead
+of the generic `operator` token type.
+
+
+**rust-analyzer.semanticHighlighting.punctuation.enable** (default: false)
+
+ Use semantic tokens for punctuation.
+
+When disabled, rust-analyzer will emit semantic tokens only for punctuation tokens when
+they are tagged with modifiers or have a special role.
+
+
+**rust-analyzer.semanticHighlighting.punctuation.separate.macro.bang** (default: false)
+
+ When enabled, rust-analyzer will emit a punctuation semantic token for the `!` of macro
+calls.
+
+
+**rust-analyzer.semanticHighlighting.punctuation.specialization.enable** (default: false)
+
+ Use specialized semantic tokens for punctuation.
+
+When enabled, rust-analyzer will emit special token types for punctuation tokens instead
+of the generic `punctuation` token type.
+
+
+**rust-analyzer.semanticHighlighting.strings.enable** (default: true)
+
+ Use semantic tokens for strings.
+
+In some editors (e.g. vscode) semantic tokens override other highlighting grammars.
+By disabling semantic tokens for strings, other grammars can be used to highlight
+their contents.
+
+
+**rust-analyzer.signatureInfo.detail** (default: "full")
+
+ Show full signature of the callable. Only shows parameters if disabled.
+
+
+**rust-analyzer.signatureInfo.documentation.enable** (default: true)
+
+ Show documentation.
+
+
+**rust-analyzer.typing.triggerChars** (default: "=.")
+
+ Specify the characters allowed to invoke special on typing triggers.
+- typing `=` after `let` tries to smartly add `;` if `=` is followed by an existing expression
+- typing `=` between two expressions adds `;` when in statement position
+- typing `=` to turn an assignment into an equality comparison removes `;` when in expression position
+- typing `.` in a chain method call auto-indents
+- typing `{` or `(` in front of an expression inserts a closing `}` or `)` after the expression
+- typing `{` in a use item adds a closing `}` in the right place
+- typing `>` to complete a return type `->` will insert a whitespace after it
+- typing `<` in a path or type position inserts a closing `>` after the path or type.
+
+
+**rust-analyzer.vfs.extraIncludes** (default: [])
+
+ Additional paths to include in the VFS. Generally for code that is
+generated or otherwise managed by a build system outside of Cargo,
+though Cargo might be the eventual consumer.
+
+
+**rust-analyzer.workspace.discoverConfig** (default: null)
+
+ Enables automatic discovery of projects using [`DiscoverWorkspaceConfig::command`].
+
+[`DiscoverWorkspaceConfig`] also requires setting `progress_label` and `files_to_watch`.
+`progress_label` is used for the title in progress indicators, whereas `files_to_watch`
+is used to determine which build system-specific files should be watched in order to
+reload rust-analyzer.
+
+Below is an example of a valid configuration:
+```json
+"rust-analyzer.workspace.discoverConfig": {
+    "command": [
+        "rust-project",
+        "develop-json"
+    ],
+    "progressLabel": "rust-analyzer",
+    "filesToWatch": [
+        "BUCK"
+    ]
+}
+```
+
+## On `DiscoverWorkspaceConfig::command`
+
+**Warning**: This format is provisional and subject to change.
+
+[`DiscoverWorkspaceConfig::command`] *must* return a JSON object
+corresponding to `DiscoverProjectData::Finished`:
+
+```norun
+#[derive(Debug, Clone, Deserialize, Serialize)]
+#[serde(tag = "kind")]
+#[serde(rename_all = "snake_case")]
+enum DiscoverProjectData {
+    Finished { buildfile: Utf8PathBuf, project: ProjectJsonData },
+    Error { error: String, source: Option<String> },
+    Progress { message: String },
+}
+```
+
+As JSON, `DiscoverProjectData::Finished` is:
+
+```json
+{
+    // the internally-tagged representation of the enum.
+    "kind": "finished",
+    // the file used by a non-Cargo build system to define
+    // a package or target.
+    "buildfile": "rust-analyzer/BUILD",
+    // the contents of a rust-project.json, elided for brevity
+    "project": {
+        "sysroot": "foo",
+        "crates": []
+    }
+}
+```
+
+It is encouraged, but not required, to use the other variants on
+`DiscoverProjectData` to provide a more polished end-user experience.
+
+`DiscoverWorkspaceConfig::command` may *optionally* include an `{arg}`,
+which will be substituted with the JSON-serialized form of the following
+enum:
+
+```norun
+#[derive(PartialEq, Clone, Debug, Serialize)]
+#[serde(rename_all = "camelCase")]
+pub enum DiscoverArgument {
+   Path(AbsPathBuf),
+   Buildfile(AbsPathBuf),
+}
+```
+
+The JSON representation of `DiscoverArgument::Path` is:
+
+```json
+{
+    "path": "src/main.rs"
+}
+```
+
+Similarly, the JSON representation of `DiscoverArgument::Buildfile` is:
+
+```
+{
+    "buildfile": "BUILD"
+}
+```
+
+`DiscoverArgument::Path` is used to find and generate a `rust-project.json`,
+and therefore, a workspace, whereas `DiscoverArgument::buildfile` is used to
+to update an existing workspace. As a reference for implementors,
+buck2's `rust-project` will likely be useful:
+https://github.com/facebook/buck2/tree/main/integrations/rust-project.
+
+
+**rust-analyzer.workspace.symbol.search.kind** (default: "only_types")
+
+ Workspace symbol search kind.
+
+
+**rust-analyzer.workspace.symbol.search.limit** (default: 128)
+
+ Limits the number of items returned from a workspace symbol search (Defaults to 128).
+Some clients like vs-code issue new searches on result filtering and don't require all results to be returned in the initial search.
+Other clients requires all results upfront and might require a higher limit.
+
+
+**rust-analyzer.workspace.symbol.search.scope** (default: "workspace")
+
+ Workspace symbol search scope.
+
+
diff --git a/src/tools/rust-analyzer/docs/dev/README.md b/src/tools/rust-analyzer/docs/book/src/contributing/README.md
index c990212d585..cbbf6acf3e5 100644
--- a/src/tools/rust-analyzer/docs/dev/README.md
+++ b/src/tools/rust-analyzer/docs/book/src/contributing/README.md
@@ -9,7 +9,7 @@ $ cargo test
 
 should be enough to get you started!
 
-To learn more about how rust-analyzer works, see [./architecture.md](./architecture.md).
+To learn more about how rust-analyzer works, see [Architecture](architecture.md).
 It also explains the high-level layout of the source code.
 Do skim through that document.
 
@@ -24,7 +24,9 @@ rust-analyzer is a part of the [RLS-2.0 working
 group](https://github.com/rust-lang/compiler-team/tree/6a769c13656c0a6959ebc09e7b1f7c09b86fb9c0/working-groups/rls-2.0).
 Discussion happens in this Zulip stream:
 
-https://rust-lang.zulipchat.com/#narrow/stream/185405-t-compiler.2Frust-analyzer
+<https://rust-lang.zulipchat.com/#narrow/stream/185405-t-compiler.2Frust-analyzer>
+
+<!-- toc -->
 
 # Issue Labels
 
@@ -54,7 +56,7 @@ https://rust-lang.zulipchat.com/#narrow/stream/185405-t-compiler.2Frust-analyzer
 
 # Code Style & Review Process
 
-Do see [./style.md](./style.md).
+See the [Style Guide](style.md).
 
 # Cookbook
 
@@ -88,11 +90,13 @@ As a sanity check after I'm done, I use `cargo xtask install --server` and **Rel
 If the problem concerns only the VS Code extension, I use **Run Installed Extension** launch configuration from `launch.json`.
 Notably, this uses the usual `rust-analyzer` binary from `PATH`.
 For this, it is important to have the following in your `settings.json` file:
+
 ```json
 {
     "rust-analyzer.server.path": "rust-analyzer"
 }
 ```
+
 After I am done with the fix, I use `cargo xtask install --client` to try the new extension for real.
 
 If I need to fix something in the `rust-analyzer` crate, I feel sad because it's on the boundary between the two processes, and working there is slow.
@@ -117,6 +121,7 @@ cd editors/code
 npm ci
 npm run lint
 ```
+
 ## How to ...
 
 * ... add an assist? [#7535](https://github.com/rust-lang/rust-analyzer/pull/7535)
@@ -142,14 +147,15 @@ Note that `stdout` is used for the actual protocol, so `println!` will break thi
 To log all communication between the server and the client, there are two choices:
 
 * You can log on the server side, by running something like
+
   ```
   env RA_LOG=lsp_server=debug code .
   ```
+
 * You can log on the client side, by the `rust-analyzer: Toggle LSP Logs` command or enabling `"rust-analyzer.trace.server": "verbose"` workspace setting.
   These logs are shown in a separate tab in the output and could be used with LSP inspector.
   Kudos to [@DJMcNab](https://github.com/DJMcNab) for setting this awesome infra up!
 
-
 There are also several VS Code commands which might be of interest:
 
 * `rust-analyzer: Status` shows some memory-usage statistics.
diff --git a/src/tools/rust-analyzer/docs/dev/architecture.md b/src/tools/rust-analyzer/docs/book/src/contributing/architecture.md
index 9c9e05a429b..1cc13b3b964 100644
--- a/src/tools/rust-analyzer/docs/dev/architecture.md
+++ b/src/tools/rust-analyzer/docs/book/src/contributing/architecture.md
@@ -8,19 +8,20 @@ It goes deeper than what is covered in this document, but will take some time to
 
 See also these implementation-related blog posts:
 
-* https://rust-analyzer.github.io/blog/2019/11/13/find-usages.html
-* https://rust-analyzer.github.io/blog/2020/07/20/three-architectures-for-responsive-ide.html
-* https://rust-analyzer.github.io/blog/2020/09/16/challeging-LR-parsing.html
-* https://rust-analyzer.github.io/blog/2020/09/28/how-to-make-a-light-bulb.html
-* https://rust-analyzer.github.io/blog/2020/10/24/introducing-ungrammar.html
+* <https://rust-analyzer.github.io/blog/2019/11/13/find-usages.html>
+* <https://rust-analyzer.github.io/blog/2020/07/20/three-architectures-for-responsive-ide.html>
+* <https://rust-analyzer.github.io/blog/2020/09/16/challeging-LR-parsing.html>
+* <https://rust-analyzer.github.io/blog/2020/09/28/how-to-make-a-light-bulb.html>
+* <https://rust-analyzer.github.io/blog/2020/10/24/introducing-ungrammar.html>
 
 For older, by now mostly outdated stuff, see the [guide](./guide.md) and [another playlist](https://www.youtube.com/playlist?list=PL85XCvVPmGQho7MZkdW-wtPtuJcFpzycE).
 
-
 ## Bird's Eye View
 
 ![](https://user-images.githubusercontent.com/4789492/107129398-0ab70f00-687a-11eb-9bfc-d4eb023aec06.png)
 
+<!-- toc -->
+
 On the highest level, rust-analyzer is a thing which accepts input source code from the client and produces a structured semantic model of the code.
 
 More specifically, input data consists of a set of test files (`(PathBuf, String)` pairs) and information about project structure, captured in the so called `CrateGraph`.
@@ -295,7 +296,7 @@ For this reason, all path APIs generally take some existing path as a "file syst
 ### `crates/stdx`
 
 This crate contains various non-rust-analyzer specific utils, which could have been in std, as well
-as copies of unstable std items we would like to make use of already, like `std::str::split_once`.
+as copies of unstable std items we would like to make use of already.
 
 ### `crates/profile`
 
diff --git a/src/tools/rust-analyzer/docs/dev/debugging.md b/src/tools/rust-analyzer/docs/book/src/contributing/debugging.md
index 48caec1d8fa..db3a28eed1c 100644
--- a/src/tools/rust-analyzer/docs/dev/debugging.md
+++ b/src/tools/rust-analyzer/docs/book/src/contributing/debugging.md
@@ -8,6 +8,7 @@
   <img height=150px src="https://user-images.githubusercontent.com/36276403/74611090-92ec5380-5101-11ea-8a41-598f51f3f3e3.png" alt="Debug options view">
 
 - Install all TypeScript dependencies
+
   ```bash
   cd editors/code
   npm ci
@@ -19,7 +20,6 @@
 where **only** the `rust-analyzer` extension being debugged is enabled.
 * To activate the extension you need to open any Rust project folder in `[Extension Development Host]`.
 
-
 ## Debug TypeScript VSCode extension
 
 - `Run Installed Extension` - runs the extension with the globally installed `rust-analyzer` binary.
@@ -36,12 +36,12 @@ To apply changes to an already running debug process, press <kbd>Ctrl+Shift+P</k
 
 - When attaching a debugger to an already running `rust-analyzer` server on Linux you might need to enable `ptrace` for unrelated processes by running:
 
-  ```
+  ```bash
   echo 0 | sudo tee /proc/sys/kernel/yama/ptrace_scope
   ```
 
-
 - By default, the LSP server is built without debug information. To enable it, you'll need to change `Cargo.toml`:
+
   ```toml
     [profile.dev]
     debug = 2
@@ -58,6 +58,7 @@ To apply changes to an already running debug process, press <kbd>Ctrl+Shift+P</k
 - Go back to the `[Extension Development Host]` instance and hover over a Rust variable and your breakpoint should hit.
 
 If you need to debug the server from the very beginning, including its initialization code, you can use the `--wait-dbg` command line argument or `RA_WAIT_DBG` environment variable. The server will spin at the beginning of the `try_main` function (see `crates\rust-analyzer\src\bin\main.rs`)
+
 ```rust
     let mut d = 4;
     while d == 4 { // set a breakpoint here and change the value
@@ -66,6 +67,7 @@ If you need to debug the server from the very beginning, including its initializ
 ```
 
 However for this to work, you will need to enable debug_assertions in your build
+
 ```rust
 RUSTFLAGS='--cfg debug_assertions' cargo build --release
 ```
diff --git a/src/tools/rust-analyzer/docs/dev/guide.md b/src/tools/rust-analyzer/docs/book/src/contributing/guide.md
index bb77aa0eaae..2a2a39af264 100644
--- a/src/tools/rust-analyzer/docs/dev/guide.md
+++ b/src/tools/rust-analyzer/docs/book/src/contributing/guide.md
@@ -12,6 +12,8 @@ https://youtu.be/ANKBNiSWyfc.
 [guide-2019-01]: https://github.com/rust-lang/rust-analyzer/tree/guide-2019-01
 [2024-01-01]: https://github.com/rust-lang/rust-analyzer/tree/2024-01-01
 
+<!-- toc -->
+
 ## The big picture
 
 On the highest possible level, rust-analyzer is a stateful component. A client may
@@ -76,10 +78,10 @@ to study its methods to understand all the input data.
 
 The `change_file` method controls the set of the input files, where each file
 has an integer id (`FileId`, picked by the client) and text (`Option<Arc<str>>`).
-Paths are tricky; they'll be explained below, in source roots section, 
+Paths are tricky; they'll be explained below, in source roots section,
 together with the `set_roots` method. The "source root" [`is_library`] flag
-along with the concept of [`durability`] allows us to add a group of files which 
-are assumed to rarely change. It's mostly an optimization and does not change 
+along with the concept of [`durability`] allows us to add a group of files which
+are assumed to rarely change. It's mostly an optimization and does not change
 the fundamental picture.
 
 [`is_library`]: https://github.com/rust-lang/rust-analyzer/blob/2024-01-01/crates/base-db/src/input.rs#L38
@@ -141,7 +143,7 @@ the source root, even `/dev/random`.
 
 ## Language Server Protocol
 
-Now let's see how the `Analysis` API is exposed via the JSON RPC based language server protocol. 
+Now let's see how the `Analysis` API is exposed via the JSON RPC based language server protocol.
 The hard part here is managing changes (which can come either from the file system
 or from the editor) and concurrency (we want to spawn background jobs for things
 like syntax highlighting). We use the event loop pattern to manage the zoo, and
@@ -152,13 +154,12 @@ the loop is the [`GlobalState::run`] function initiated by [`main_loop`] after
 [`GlobalState::new`]: https://github.com/rust-lang/rust-analyzer/blob/2024-01-01/crates/rust-analyzer/src/global_state.rs#L148-L215
 [`GlobalState::run`]: https://github.com/rust-lang/rust-analyzer/blob/2024-01-01/crates/rust-analyzer/src/main_loop.rs#L114-L140
 
-
 Let's walk through a typical analyzer session!
 
 First, we need to figure out what to analyze. To do this, we run `cargo
 metadata` to learn about Cargo packages for current workspace and dependencies,
 and we run `rustc --print sysroot` and scan the "sysroot"
-(the directory containing the current Rust toolchain's files) to learn about crates 
+(the directory containing the current Rust toolchain's files) to learn about crates
 like `std`. This happens in the [`GlobalState::fetch_workspaces`] method.
 We load this configuration at the start of the server in [`GlobalState::new`],
 but it's also triggered by workspace change events and requests to reload the
diff --git a/src/tools/rust-analyzer/docs/dev/lsp-extensions.md b/src/tools/rust-analyzer/docs/book/src/contributing/lsp-extensions.md
index c7ee4e40236..14a3fd1ebd1 100644
--- a/src/tools/rust-analyzer/docs/dev/lsp-extensions.md
+++ b/src/tools/rust-analyzer/docs/book/src/contributing/lsp-extensions.md
@@ -19,6 +19,8 @@ Requests, which are likely to always remain specific to `rust-analyzer` are unde
 
 If you want to be notified about the changes to this document, subscribe to [#4604](https://github.com/rust-lang/rust-analyzer/issues/4604).
 
+<!-- toc -->
+
 ## Configuration in `initializationOptions`
 
 **Upstream Issue:** https://github.com/microsoft/language-server-protocol/issues/567
diff --git a/src/tools/rust-analyzer/docs/dev/setup.md b/src/tools/rust-analyzer/docs/book/src/contributing/setup.md
index d8a7840d376..d8a7840d376 100644
--- a/src/tools/rust-analyzer/docs/dev/setup.md
+++ b/src/tools/rust-analyzer/docs/book/src/contributing/setup.md
diff --git a/src/tools/rust-analyzer/docs/dev/style.md b/src/tools/rust-analyzer/docs/book/src/contributing/style.md
index 4c5299bde3e..5654e37753a 100644
--- a/src/tools/rust-analyzer/docs/dev/style.md
+++ b/src/tools/rust-analyzer/docs/book/src/contributing/style.md
@@ -1,3 +1,5 @@
+# Style
+
 Our approach to "clean code" is two-fold:
 
 * We generally don't block PRs on style changes.
@@ -274,7 +276,7 @@ fn f() {
 Assert liberally.
 Prefer [`stdx::never!`](https://docs.rs/always-assert/0.1.2/always_assert/macro.never.html) to standard `assert!`.
 
-**Rationale:** See [cross cutting concern: error handling](https://github.com/rust-lang/rust-analyzer/blob/master/docs/dev/architecture.md#error-handling).
+**Rationale:** See [cross cutting concern: error handling](https://github.com/rust-lang/rust-analyzer/blob/master/docs/book/src/contributing/architecture.md#error-handling).
 
 ## Getters & Setters
 
@@ -873,7 +875,7 @@ Use `anyhow::format_err!` rather than `anyhow::anyhow`.
 **Rationale:** consistent, boring, avoids stuttering.
 
 There's no specific guidance on the formatting of error messages, see [anyhow/#209](https://github.com/dtolnay/anyhow/issues/209).
-Do not end error and context messages with `.` though. 
+Do not end error and context messages with `.` though.
 
 ## Early Returns
 
@@ -1172,7 +1174,7 @@ MergeBehavior::Last => {
 **Rationale:** writing a sentence (or maybe even a paragraph) rather just "a comment" creates a more appropriate frame of mind.
 It tricks you into writing down more of the context you keep in your head while coding.
 
-For `.md` and `.adoc` files, prefer a sentence-per-line format, don't wrap lines.
+For `.md` files prefer a sentence-per-line format, don't wrap lines.
 If the line is too long, you want to split the sentence in two :-)
 
 **Rationale:** much easier to edit the text and read the diff, see [this link](https://asciidoctor.org/docs/asciidoc-recommended-practices/#one-sentence-per-line).
diff --git a/src/tools/rust-analyzer/docs/dev/syntax.md b/src/tools/rust-analyzer/docs/book/src/contributing/syntax.md
index 3dcd430cea5..3dcd430cea5 100644
--- a/src/tools/rust-analyzer/docs/dev/syntax.md
+++ b/src/tools/rust-analyzer/docs/book/src/contributing/syntax.md
diff --git a/src/tools/rust-analyzer/docs/book/src/diagnostics.md b/src/tools/rust-analyzer/docs/book/src/diagnostics.md
new file mode 100644
index 00000000000..60685c98da7
--- /dev/null
+++ b/src/tools/rust-analyzer/docs/book/src/diagnostics.md
@@ -0,0 +1,16 @@
+# Diagnostics
+
+While most errors and warnings provided by rust-analyzer come from the
+`cargo check` integration, there’s a growing number of diagnostics
+implemented using rust-analyzer’s own analysis. Some of these
+diagnostics don’t respect `#[allow]` or `#[deny]` attributes yet, but
+can be turned off using the `rust-analyzer.diagnostics.enable`,
+`rust-analyzer.diagnostics.experimental.enable` or
+`rust-analyzer.diagnostics.disabled` settings.
+
+## Clippy
+
+To run `cargo clippy` instead of `cargo check`, you can set
+`"rust-analyzer.check.command": "clippy"`.
+
+{{#include diagnostics_generated.md:2:}}
diff --git a/src/tools/rust-analyzer/docs/book/src/diagnostics_generated.md b/src/tools/rust-analyzer/docs/book/src/diagnostics_generated.md
new file mode 100644
index 00000000000..d34c459ad02
--- /dev/null
+++ b/src/tools/rust-analyzer/docs/book/src/diagnostics_generated.md
@@ -0,0 +1,516 @@
+//! Generated by `cargo xtask codegen diagnostics-docs`, do not edit by hand.
+
+#### attribute-expansion-disabled
+
+Source:  [macro_error.rs](crates/ide-diagnostics/src/handlers/macro_error.rs#7) 
+
+
+This diagnostic is shown for attribute proc macros when attribute expansions have been disabled.
+
+
+
+
+#### await-outside-of-async
+
+Source:  [await_outside_of_async.rs](crates/ide-diagnostics/src/handlers/await_outside_of_async.rs#3) 
+
+
+This diagnostic is triggered if the `await` keyword is used outside of an async function or block
+
+
+
+
+#### break-outside-of-loop
+
+Source:  [break_outside_of_loop.rs](crates/ide-diagnostics/src/handlers/break_outside_of_loop.rs#3) 
+
+
+This diagnostic is triggered if the `break` keyword is used outside of a loop.
+
+
+
+
+#### cast-to-unsized
+
+Source:  [invalid_cast.rs](crates/ide-diagnostics/src/handlers/invalid_cast.rs#106) 
+
+
+This diagnostic is triggered when casting to an unsized type
+
+
+
+
+#### expected-function
+
+Source:  [expected_function.rs](crates/ide-diagnostics/src/handlers/expected_function.rs#5) 
+
+
+This diagnostic is triggered if a call is made on something that is not callable.
+
+
+
+
+#### generic-args-prohibited
+
+Source:  [generic_args_prohibited.rs](crates/ide-diagnostics/src/handlers/generic_args_prohibited.rs#10) 
+
+
+This diagnostic is shown when generic arguments are provided for a type that does not accept
+generic arguments.
+
+
+
+
+#### inactive-code
+
+Source:  [inactive_code.rs](crates/ide-diagnostics/src/handlers/inactive_code.rs#6) 
+
+
+This diagnostic is shown for code with inactive `#[cfg]` attributes.
+
+
+
+
+#### incoherent-impl
+
+Source:  [incoherent_impl.rs](crates/ide-diagnostics/src/handlers/incoherent_impl.rs#6) 
+
+
+This diagnostic is triggered if the targe type of an impl is from a foreign crate.
+
+
+
+
+#### incorrect-ident-case
+
+Source:  [incorrect_case.rs](crates/ide-diagnostics/src/handlers/incorrect_case.rs#13) 
+
+
+This diagnostic is triggered if an item name doesn't follow [Rust naming convention](https://doc.rust-lang.org/1.0.0/style/style/naming/README.html).
+
+
+
+
+#### invalid-cast
+
+Source:  [invalid_cast.rs](crates/ide-diagnostics/src/handlers/invalid_cast.rs#18) 
+
+
+This diagnostic is triggered if the code contains an illegal cast
+
+
+
+
+#### invalid-derive-target
+
+Source:  [invalid_derive_target.rs](crates/ide-diagnostics/src/handlers/invalid_derive_target.rs#3) 
+
+
+This diagnostic is shown when the derive attribute is used on an item other than a `struct`,
+`enum` or `union`.
+
+
+
+
+#### macro-def-error
+
+Source:  [macro_error.rs](crates/ide-diagnostics/src/handlers/macro_error.rs#24) 
+
+
+This diagnostic is shown for macro expansion errors.
+
+
+
+
+#### macro-error
+
+Source:  [macro_error.rs](crates/ide-diagnostics/src/handlers/macro_error.rs#3) 
+
+
+This diagnostic is shown for macro expansion errors.
+
+
+
+
+#### malformed-derive
+
+Source:  [malformed_derive.rs](crates/ide-diagnostics/src/handlers/malformed_derive.rs#3) 
+
+
+This diagnostic is shown when the derive attribute has invalid input.
+
+
+
+
+#### mismatched-arg-count
+
+Source:  [mismatched_arg_count.rs](crates/ide-diagnostics/src/handlers/mismatched_arg_count.rs#31) 
+
+
+This diagnostic is triggered if a function is invoked with an incorrect amount of arguments.
+
+
+
+
+#### mismatched-tuple-struct-pat-arg-count
+
+Source:  [mismatched_arg_count.rs](crates/ide-diagnostics/src/handlers/mismatched_arg_count.rs#11) 
+
+
+This diagnostic is triggered if a function is invoked with an incorrect amount of arguments.
+
+
+
+
+#### missing-fields
+
+Source:  [missing_fields.rs](crates/ide-diagnostics/src/handlers/missing_fields.rs#19) 
+
+
+This diagnostic is triggered if record lacks some fields that exist in the corresponding structure.
+
+Example:
+
+```rust
+struct A { a: u8, b: u8 }
+
+let a = A { a: 10 };
+```
+
+
+
+
+#### missing-match-arm
+
+Source:  [missing_match_arms.rs](crates/ide-diagnostics/src/handlers/missing_match_arms.rs#3) 
+
+
+This diagnostic is triggered if `match` block is missing one or more match arms.
+
+
+
+
+#### missing-unsafe
+
+Source:  [missing_unsafe.rs](crates/ide-diagnostics/src/handlers/missing_unsafe.rs#10) 
+
+
+This diagnostic is triggered if an operation marked as `unsafe` is used outside of an `unsafe` function or block.
+
+
+
+
+#### moved-out-of-ref
+
+Source:  [moved_out_of_ref.rs](crates/ide-diagnostics/src/handlers/moved_out_of_ref.rs#4) 
+
+
+This diagnostic is triggered on moving non copy things out of references.
+
+
+
+
+#### need-mut
+
+Source:  [mutability_errors.rs](crates/ide-diagnostics/src/handlers/mutability_errors.rs#8) 
+
+
+This diagnostic is triggered on mutating an immutable variable.
+
+
+
+
+#### no-such-field
+
+Source:  [no_such_field.rs](crates/ide-diagnostics/src/handlers/no_such_field.rs#12) 
+
+
+This diagnostic is triggered if created structure does not have field provided in record.
+
+
+
+
+#### non-exhaustive-let
+
+Source:  [non_exhaustive_let.rs](crates/ide-diagnostics/src/handlers/non_exhaustive_let.rs#3) 
+
+
+This diagnostic is triggered if a `let` statement without an `else` branch has a non-exhaustive
+pattern.
+
+
+
+
+#### private-assoc-item
+
+Source:  [private_assoc_item.rs](crates/ide-diagnostics/src/handlers/private_assoc_item.rs#3) 
+
+
+This diagnostic is triggered if the referenced associated item is not visible from the current
+module.
+
+
+
+
+#### private-field
+
+Source:  [private_field.rs](crates/ide-diagnostics/src/handlers/private_field.rs#3) 
+
+
+This diagnostic is triggered if the accessed field is not visible from the current module.
+
+
+
+
+#### proc-macro-disabled
+
+Source:  [macro_error.rs](crates/ide-diagnostics/src/handlers/macro_error.rs#11) 
+
+
+This diagnostic is shown for proc macros that have been specifically disabled via `rust-analyzer.procMacro.ignored`.
+
+
+
+
+#### remove-trailing-return
+
+Source:  [remove_trailing_return.rs](crates/ide-diagnostics/src/handlers/remove_trailing_return.rs#8) 
+
+
+This diagnostic is triggered when there is a redundant `return` at the end of a function
+or closure.
+
+
+
+
+#### remove-unnecessary-else
+
+Source:  [remove_unnecessary_else.rs](crates/ide-diagnostics/src/handlers/remove_unnecessary_else.rs#17) 
+
+
+This diagnostic is triggered when there is an `else` block for an `if` expression whose
+then branch diverges (e.g. ends with a `return`, `continue`, `break` e.t.c).
+
+
+
+
+#### replace-filter-map-next-with-find-map
+
+Source:  [replace_filter_map_next_with_find_map.rs](crates/ide-diagnostics/src/handlers/replace_filter_map_next_with_find_map.rs#11) 
+
+
+This diagnostic is triggered when `.filter_map(..).next()` is used, rather than the more concise `.find_map(..)`.
+
+
+
+
+#### trait-impl-incorrect-safety
+
+Source:  [trait_impl_incorrect_safety.rs](crates/ide-diagnostics/src/handlers/trait_impl_incorrect_safety.rs#6) 
+
+
+Diagnoses incorrect safety annotations of trait impls.
+
+
+
+
+#### trait-impl-missing-assoc_item
+
+Source:  [trait_impl_missing_assoc_item.rs](crates/ide-diagnostics/src/handlers/trait_impl_missing_assoc_item.rs#7) 
+
+
+Diagnoses missing trait items in a trait impl.
+
+
+
+
+#### trait-impl-orphan
+
+Source:  [trait_impl_orphan.rs](crates/ide-diagnostics/src/handlers/trait_impl_orphan.rs#5) 
+
+
+Only traits defined in the current crate can be implemented for arbitrary types
+
+
+
+
+#### trait-impl-redundant-assoc_item
+
+Source:  [trait_impl_redundant_assoc_item.rs](crates/ide-diagnostics/src/handlers/trait_impl_redundant_assoc_item.rs#12) 
+
+
+Diagnoses redundant trait items in a trait impl.
+
+
+
+
+#### type-mismatch
+
+Source:  [type_mismatch.rs](crates/ide-diagnostics/src/handlers/type_mismatch.rs#20) 
+
+
+This diagnostic is triggered when the type of an expression or pattern does not match
+the expected type.
+
+
+
+
+#### typed-hole
+
+Source:  [typed_hole.rs](crates/ide-diagnostics/src/handlers/typed_hole.rs#18) 
+
+
+This diagnostic is triggered when an underscore expression is used in an invalid position.
+
+
+
+
+#### undeclared-label
+
+Source:  [undeclared_label.rs](crates/ide-diagnostics/src/handlers/undeclared_label.rs#3) 
+
+
+
+
+
+
+#### unimplemented-builtin-macro
+
+Source:  [unimplemented_builtin_macro.rs](crates/ide-diagnostics/src/handlers/unimplemented_builtin_macro.rs#3) 
+
+
+This diagnostic is shown for builtin macros which are not yet implemented by rust-analyzer
+
+
+
+
+#### unlinked-file
+
+Source:  [unlinked_file.rs](crates/ide-diagnostics/src/handlers/unlinked_file.rs#20) 
+
+
+This diagnostic is shown for files that are not included in any crate, or files that are part of
+crates rust-analyzer failed to discover. The file will not have IDE features available.
+
+
+
+
+#### unnecessary-braces
+
+Source:  [useless_braces.rs](crates/ide-diagnostics/src/handlers/useless_braces.rs#9) 
+
+
+Diagnostic for unnecessary braces in `use` items.
+
+
+
+
+#### unreachable-label
+
+Source:  [unreachable_label.rs](crates/ide-diagnostics/src/handlers/unreachable_label.rs#3) 
+
+
+
+
+
+
+#### unresolved-assoc-item
+
+Source:  [unresolved_assoc_item.rs](crates/ide-diagnostics/src/handlers/unresolved_assoc_item.rs#3) 
+
+
+This diagnostic is triggered if the referenced associated item does not exist.
+
+
+
+
+#### unresolved-extern-crate
+
+Source:  [unresolved_extern_crate.rs](crates/ide-diagnostics/src/handlers/unresolved_extern_crate.rs#3) 
+
+
+This diagnostic is triggered if rust-analyzer is unable to discover referred extern crate.
+
+
+
+
+#### unresolved-field
+
+Source:  [unresolved_field.rs](crates/ide-diagnostics/src/handlers/unresolved_field.rs#23) 
+
+
+This diagnostic is triggered if a field does not exist on a given type.
+
+
+
+
+#### unresolved-ident
+
+Source:  [unresolved_ident.rs](crates/ide-diagnostics/src/handlers/unresolved_ident.rs#3) 
+
+
+This diagnostic is triggered if an expr-position ident is invalid.
+
+
+
+
+#### unresolved-import
+
+Source:  [unresolved_import.rs](crates/ide-diagnostics/src/handlers/unresolved_import.rs#3) 
+
+
+This diagnostic is triggered if rust-analyzer is unable to resolve a path in
+a `use` declaration.
+
+
+
+
+#### unresolved-macro-call
+
+Source:  [unresolved_macro_call.rs](crates/ide-diagnostics/src/handlers/unresolved_macro_call.rs#3) 
+
+
+This diagnostic is triggered if rust-analyzer is unable to resolve the path
+to a macro in a macro invocation.
+
+
+
+
+#### unresolved-method
+
+Source:  [unresolved_method.rs](crates/ide-diagnostics/src/handlers/unresolved_method.rs#15) 
+
+
+This diagnostic is triggered if a method does not exist on a given type.
+
+
+
+
+#### unresolved-module
+
+Source:  [unresolved_module.rs](crates/ide-diagnostics/src/handlers/unresolved_module.rs#8) 
+
+
+This diagnostic is triggered if rust-analyzer is unable to discover referred module.
+
+
+
+
+#### unused-mut
+
+Source:  [mutability_errors.rs](crates/ide-diagnostics/src/handlers/mutability_errors.rs#62) 
+
+
+This diagnostic is triggered when a mutable variable isn't actually mutated.
+
+
+
+
+#### unused-variables
+
+Source:  [unused_variables.rs](crates/ide-diagnostics/src/handlers/unused_variables.rs#13) 
+
+
+This diagnostic is triggered when a local variable is not used.
+
+
diff --git a/src/tools/rust-analyzer/docs/book/src/editor_features.md b/src/tools/rust-analyzer/docs/book/src/editor_features.md
new file mode 100644
index 00000000000..5521e395c73
--- /dev/null
+++ b/src/tools/rust-analyzer/docs/book/src/editor_features.md
@@ -0,0 +1,203 @@
+# Editor Features
+
+## VS Code
+
+### Color configurations
+
+It is possible to change the foreground/background color and font
+family/size of inlay hints. Just add this to your `settings.json`:
+
+```json
+{
+  "editor.inlayHints.fontFamily": "Courier New",
+  "editor.inlayHints.fontSize": 11,
+
+  "workbench.colorCustomizations": {
+    // Name of the theme you are currently using
+    "[Default Dark+]": {
+      "editorInlayHint.foreground": "#868686f0",
+      "editorInlayHint.background": "#3d3d3d48",
+
+      // Overrides for specific kinds of inlay hints
+      "editorInlayHint.typeForeground": "#fdb6fdf0",
+      "editorInlayHint.parameterForeground": "#fdb6fdf0",
+    }
+  }
+}
+```
+
+### Semantic style customizations
+
+You can customize the look of different semantic elements in the source
+code. For example, mutable bindings are underlined by default and you
+can override this behavior by adding the following section to your
+`settings.json`:
+
+```json
+{
+  "editor.semanticTokenColorCustomizations": {
+    "rules": {
+      "*.mutable": {
+        "fontStyle": "", // underline is the default
+      },
+    }
+  },
+}
+```
+
+Most themes doesn’t support styling unsafe operations differently yet.
+You can fix this by adding overrides for the rules `operator.unsafe`,
+`function.unsafe`, and `method.unsafe`:
+
+```json
+{
+   "editor.semanticTokenColorCustomizations": {
+         "rules": {
+             "operator.unsafe": "#ff6600",
+             "function.unsafe": "#ff6600",
+             "method.unsafe": "#ff6600"
+         }
+    },
+}
+```
+
+In addition to the top-level rules you can specify overrides for
+specific themes. For example, if you wanted to use a darker text color
+on a specific light theme, you might write:
+
+```json
+{
+   "editor.semanticTokenColorCustomizations": {
+         "rules": {
+             "operator.unsafe": "#ff6600"
+         },
+         "[Ayu Light]": {
+            "rules": {
+               "operator.unsafe": "#572300"
+            }
+         }
+    },
+}
+```
+
+Make sure you include the brackets around the theme name. For example,
+use `"[Ayu Light]"` to customize the theme Ayu Light.
+
+### Special `when` clause context for keybindings.
+
+You may use `inRustProject` context to configure keybindings for rust
+projects only. For example:
+
+```json
+{
+    "key": "ctrl+alt+d",
+    "command": "rust-analyzer.openDocs",
+    "when": "inRustProject"
+}
+```
+
+More about `when` clause contexts
+[here](https://code.visualstudio.com/docs/getstarted/keybindings#_when-clause-contexts).
+
+### Setting runnable environment variables
+
+You can use "rust-analyzer.runnables.extraEnv" setting to define
+runnable environment-specific substitution variables. The simplest way
+for all runnables in a bunch:
+
+```json
+"rust-analyzer.runnables.extraEnv": {
+    "RUN_SLOW_TESTS": "1"
+}
+```
+
+Or it is possible to specify vars more granularly:
+
+```json
+"rust-analyzer.runnables.extraEnv": [
+    {
+        // "mask": null, // null mask means that this rule will be applied for all runnables
+        "env": {
+                "APP_ID": "1",
+                "APP_DATA": "asdf"
+        }
+    },
+    {
+        "mask": "test_name",
+        "env": {
+                "APP_ID": "2", // overwrites only APP_ID
+        }
+    }
+]
+```
+
+You can use any valid regular expression as a mask. Also note that a
+full runnable name is something like **run bin\_or\_example\_name**,
+**test some::mod::test\_name** or **test-mod some::mod**, so it is
+possible to distinguish binaries, single tests, and test modules with
+this masks: `"^run"`, `"^test "` (the trailing space matters!), and
+`"^test-mod"` respectively.
+
+If needed, you can set different values for different platforms:
+
+```json
+"rust-analyzer.runnables.extraEnv": [
+    {
+        "platform": "win32", // windows only
+        "env": {
+                "APP_DATA": "windows specific data"
+        }
+    },
+    {
+        "platform": ["linux"],
+        "env": {
+                "APP_DATA": "linux data",
+        }
+    },
+    { // for all platforms
+        "env": {
+                "APP_COMMON_DATA": "xxx",
+        }
+    }
+]
+```
+
+### Compiler feedback from external commands
+
+Instead of relying on the built-in `cargo check`, you can configure Code
+to run a command in the background and use the `$rustc-watch` problem
+matcher to generate inline error markers from its output.
+
+To do this you need to create a new [VS Code
+Task](https://code.visualstudio.com/docs/editor/tasks) and set
+`"rust-analyzer.checkOnSave": false` in preferences.
+
+For example, if you want to run
+[`cargo watch`](https://crates.io/crates/cargo-watch) instead, you might
+add the following to `.vscode/tasks.json`:
+
+```json
+{
+    "label": "Watch",
+    "group": "build",
+    "type": "shell",
+    "command": "cargo watch",
+    "problemMatcher": "$rustc-watch",
+    "isBackground": true
+}
+```
+
+### Live Share
+
+VS Code Live Share has partial support for rust-analyzer.
+
+Live Share *requires* the official Microsoft build of VS Code, OSS
+builds will not work correctly.
+
+The host’s rust-analyzer instance will be shared with all guests joining
+the session. The guests do not have to have the rust-analyzer extension
+installed for this to work.
+
+If you are joining a Live Share session and *do* have rust-analyzer
+installed locally, commands from the command palette will not work
+correctly since they will attempt to communicate with the local server.
diff --git a/src/tools/rust-analyzer/docs/book/src/features.md b/src/tools/rust-analyzer/docs/book/src/features.md
new file mode 100644
index 00000000000..0829a0213b7
--- /dev/null
+++ b/src/tools/rust-analyzer/docs/book/src/features.md
@@ -0,0 +1,3 @@
+# Features
+
+{{#include features_generated.md:2:}}
diff --git a/src/tools/rust-analyzer/docs/book/src/features_generated.md b/src/tools/rust-analyzer/docs/book/src/features_generated.md
new file mode 100644
index 00000000000..2c5829b1f54
--- /dev/null
+++ b/src/tools/rust-analyzer/docs/book/src/features_generated.md
@@ -0,0 +1,940 @@
+//! Generated by `cargo xtask codegen feature-docs`, do not edit by hand.
+
+### Annotations
+**Source:**  [annotations.rs](crates/ide/src/annotations.rs#19) 
+
+Provides user with annotations above items for looking up references or impl blocks
+and running/debugging binaries.
+
+![Annotations](https://user-images.githubusercontent.com/48062697/113020672-b7c34f00-917a-11eb-8f6e-858735660a0e.png)
+
+
+### Auto Import
+**Source:**  [auto_import.rs](crates/ide-assists/src/handlers/auto_import.rs#15) 
+
+Using the `auto-import` assist it is possible to insert missing imports for unresolved items.
+When inserting an import it will do so in a structured manner by keeping imports grouped,
+separated by a newline in the following order:
+
+- `std` and `core`
+- External Crates
+- Current Crate, paths prefixed by `crate`
+- Current Module, paths prefixed by `self`
+- Super Module, paths prefixed by `super`
+
+Example:
+```rust
+use std::fs::File;
+
+use itertools::Itertools;
+use syntax::ast;
+
+use crate::utils::insert_use;
+
+use self::auto_import;
+
+use super::AssistContext;
+```
+
+#### Import Granularity
+
+It is possible to configure how use-trees are merged with the `imports.granularity.group` setting.
+It has the following configurations:
+
+- `crate`: Merge imports from the same crate into a single use statement. This kind of
+ nesting is only supported in Rust versions later than 1.24.
+- `module`: Merge imports from the same module into a single use statement.
+- `item`: Don't merge imports at all, creating one import per item.
+- `preserve`: Do not change the granularity of any imports. For auto-import this has the same
+ effect as `item`.
+- `one`: Merge all imports into a single use statement as long as they have the same visibility
+ and attributes.
+
+In `VS Code` the configuration for this is `rust-analyzer.imports.granularity.group`.
+
+#### Import Prefix
+
+The style of imports in the same crate is configurable through the `imports.prefix` setting.
+It has the following configurations:
+
+- `crate`: This setting will force paths to be always absolute, starting with the `crate`
+ prefix, unless the item is defined outside of the current crate.
+- `self`: This setting will force paths that are relative to the current module to always
+ start with `self`. This will result in paths that always start with either `crate`, `self`,
+ `super` or an extern crate identifier.
+- `plain`: This setting does not impose any restrictions in imports.
+
+In `VS Code` the configuration for this is `rust-analyzer.imports.prefix`.
+
+![Auto Import](https://user-images.githubusercontent.com/48062697/113020673-b85be580-917a-11eb-9022-59585f35d4f8.gif)
+
+
+### Completion With Autoimport
+**Source:**  [flyimport.rs](crates/ide-completion/src/completions/flyimport.rs#20) 
+
+When completing names in the current scope, proposes additional imports from other modules or crates,
+if they can be qualified in the scope, and their name contains all symbols from the completion input.
+
+To be considered applicable, the name must contain all input symbols in the given order, not necessarily adjacent.
+If any input symbol is not lowercased, the name must contain all symbols in exact case; otherwise the containing is checked case-insensitively.
+
+```
+fn main() {
+    pda$0
+}
+# pub mod std { pub mod marker { pub struct PhantomData { } } }
+```
+->
+```
+use std::marker::PhantomData;
+
+fn main() {
+    PhantomData
+}
+# pub mod std { pub mod marker { pub struct PhantomData { } } }
+```
+
+Also completes associated items, that require trait imports.
+If any unresolved and/or partially-qualified path precedes the input, it will be taken into account.
+Currently, only the imports with their import path ending with the whole qualifier will be proposed
+(no fuzzy matching for qualifier).
+
+```
+mod foo {
+    pub mod bar {
+        pub struct Item;
+
+        impl Item {
+            pub const TEST_ASSOC: usize = 3;
+        }
+    }
+}
+
+fn main() {
+    bar::Item::TEST_A$0
+}
+```
+->
+```
+use foo::bar;
+
+mod foo {
+    pub mod bar {
+        pub struct Item;
+
+        impl Item {
+            pub const TEST_ASSOC: usize = 3;
+        }
+    }
+}
+
+fn main() {
+    bar::Item::TEST_ASSOC
+}
+```
+
+NOTE: currently, if an assoc item comes from a trait that's not currently imported, and it also has an unresolved and/or partially-qualified path,
+no imports will be proposed.
+
+#### Fuzzy search details
+
+To avoid an excessive amount of the results returned, completion input is checked for inclusion in the names only
+(i.e. in `HashMap` in the `std::collections::HashMap` path).
+For the same reasons, avoids searching for any path imports for inputs with their length less than 2 symbols
+(but shows all associated items for any input length).
+
+#### Import configuration
+
+It is possible to configure how use-trees are merged with the `imports.granularity.group` setting.
+Mimics the corresponding behavior of the `Auto Import` feature.
+
+#### LSP and performance implications
+
+The feature is enabled only if the LSP client supports LSP protocol version 3.16+ and reports the `additionalTextEdits`
+(case-sensitive) resolve client capability in its client capabilities.
+This way the server is able to defer the costly computations, doing them for a selected completion item only.
+For clients with no such support, all edits have to be calculated on the completion request, including the fuzzy search completion ones,
+which might be slow ergo the feature is automatically disabled.
+
+#### Feature toggle
+
+The feature can be forcefully turned off in the settings with the `rust-analyzer.completion.autoimport.enable` flag.
+Note that having this flag set to `true` does not guarantee that the feature is enabled: your client needs to have the corresponding
+capability enabled.
+
+
+### Debug ItemTree
+**Source:**  [view_item_tree.rs](crates/ide/src/view_item_tree.rs#5) 
+
+Displays the ItemTree of the currently open file, for debugging.
+
+| Editor  | Action Name |
+|---------|-------------|
+| VS Code | **rust-analyzer: Debug ItemTree** |
+
+
+### Expand Macro Recursively
+**Source:**  [expand_macro.rs](crates/ide/src/expand_macro.rs#18) 
+
+Shows the full macro expansion of the macro at the current caret position.
+
+| Editor  | Action Name |
+|---------|-------------|
+| VS Code | **rust-analyzer: Expand macro recursively at caret** |
+
+![Expand Macro Recursively](https://user-images.githubusercontent.com/48062697/113020648-b3973180-917a-11eb-84a9-ecb921293dc5.gif)
+
+
+### Expand and Shrink Selection
+**Source:**  [extend_selection.rs](crates/ide/src/extend_selection.rs#15) 
+
+Extends or shrinks the current selection to the encompassing syntactic construct
+(expression, statement, item, module, etc). It works with multiple cursors.
+
+| Editor  | Shortcut |
+|---------|----------|
+| VS Code | <kbd>Alt+Shift+→</kbd>, <kbd>Alt+Shift+←</kbd> |
+
+![Expand and Shrink Selection](https://user-images.githubusercontent.com/48062697/113020651-b42fc800-917a-11eb-8a4f-cf1a07859fac.gif)
+
+
+### File Structure
+**Source:**  [file_structure.rs](crates/ide/src/file_structure.rs#26) 
+
+Provides a tree of the symbols defined in the file. Can be used to
+
+* fuzzy search symbol in a file (super useful)
+* draw breadcrumbs to describe the context around the cursor
+* draw outline of the file
+
+| Editor  | Shortcut |
+|---------|----------|
+| VS Code | <kbd>Ctrl+Shift+O</kbd> |
+
+![File Structure](https://user-images.githubusercontent.com/48062697/113020654-b42fc800-917a-11eb-8388-e7dc4d92b02e.gif)
+
+
+### Find All References
+**Source:**  [references.rs](crates/ide/src/references.rs#42) 
+
+Shows all references of the item at the cursor location
+
+| Editor  | Shortcut |
+|---------|----------|
+| VS Code | <kbd>Shift+Alt+F12</kbd> |
+
+![Find All References](https://user-images.githubusercontent.com/48062697/113020670-b7c34f00-917a-11eb-8003-370ac5f2b3cb.gif)
+
+
+### Folding
+**Source:**  [folding_ranges.rs](crates/ide/src/folding_ranges.rs#36) 
+
+Defines folding regions for curly braced blocks, runs of consecutive use, mod, const or static
+items, and `region` / `endregion` comment markers.
+
+
+### Format String Completion
+**Source:**  [format_like.rs](crates/ide-completion/src/completions/postfix/format_like.rs#0) 
+
+`"Result {result} is {2 + 2}"` is expanded to the `"Result {} is {}", result, 2 + 2`.
+
+The following postfix snippets are available:
+
+* `format` -> `format!(...)`
+* `panic` -> `panic!(...)`
+* `println` -> `println!(...)`
+* `log`:
+** `logd` -> `log::debug!(...)`
+** `logt` -> `log::trace!(...)`
+** `logi` -> `log::info!(...)`
+** `logw` -> `log::warn!(...)`
+** `loge` -> `log::error!(...)`
+
+![Format String Completion](https://user-images.githubusercontent.com/48062697/113020656-b560f500-917a-11eb-87de-02991f61beb8.gif)
+
+
+### Go to Declaration
+**Source:**  [goto_declaration.rs](crates/ide/src/goto_declaration.rs#13) 
+
+Navigates to the declaration of an identifier.
+
+This is the same as `Go to Definition` with the following exceptions:
+- outline modules will navigate to the `mod name;` item declaration
+- trait assoc items will navigate to the assoc item of the trait declaration as opposed to the trait impl
+- fields in patterns will navigate to the field declaration of the struct, union or variant
+
+
+### Go to Definition
+**Source:**  [goto_definition.rs](crates/ide/src/goto_definition.rs#28) 
+
+Navigates to the definition of an identifier.
+
+For outline modules, this will navigate to the source file of the module.
+
+| Editor  | Shortcut |
+|---------|----------|
+| VS Code | <kbd>F12</kbd> |
+
+![Go to Definition](https://user-images.githubusercontent.com/48062697/113065563-025fbe00-91b1-11eb-83e4-a5a703610b23.gif)
+
+
+### Go to Implementation
+**Source:**  [goto_implementation.rs](crates/ide/src/goto_implementation.rs#11) 
+
+Navigates to the impl items of types.
+
+| Editor  | Shortcut |
+|---------|----------|
+| VS Code | <kbd>Ctrl+F12</kbd>
+
+![Go to Implementation](https://user-images.githubusercontent.com/48062697/113065566-02f85480-91b1-11eb-9288-aaad8abd8841.gif)
+
+
+### Go to Type Definition
+**Source:**  [goto_type_definition.rs](crates/ide/src/goto_type_definition.rs#7) 
+
+Navigates to the type of an identifier.
+
+| Editor  | Action Name |
+|---------|-------------|
+| VS Code | **Go to Type Definition** |
+
+![Go to Type Definition](https://user-images.githubusercontent.com/48062697/113020657-b560f500-917a-11eb-9007-0f809733a338.gif)
+
+
+### Highlight Related
+**Source:**  [highlight_related.rs](crates/ide/src/highlight_related.rs#42) 
+
+Highlights constructs related to the thing under the cursor:
+
+1. if on an identifier, highlights all references to that identifier in the current file
+     * additionally, if the identifier is a trait in a where clause, type parameter trait bound or use item, highlights all references to that trait's assoc items in the corresponding scope
+1. if on an `async` or `await` token, highlights all yield points for that async context
+1. if on a `return` or `fn` keyword, `?` character or `->` return type arrow, highlights all exit points for that context
+1. if on a `break`, `loop`, `while` or `for` token, highlights all break points for that loop or block context
+1. if on a `move` or `|` token that belongs to a closure, highlights all captures of the closure.
+
+Note: `?`, `|` and `->` do not currently trigger this behavior in the VSCode editor.
+
+
+### Hover
+**Source:**  [hover.rs](crates/ide/src/hover.rs#116) 
+
+Shows additional information, like the type of an expression or the documentation for a definition when "focusing" code.
+Focusing is usually hovering with a mouse, but can also be triggered with a shortcut.
+
+![Hover](https://user-images.githubusercontent.com/48062697/113020658-b5f98b80-917a-11eb-9f88-3dbc27320c95.gif)
+
+
+### Inlay Hints
+**Source:**  [inlay_hints.rs](crates/ide/src/inlay_hints.rs#41) 
+
+rust-analyzer shows additional information inline with the source code.
+Editors usually render this using read-only virtual text snippets interspersed with code.
+
+rust-analyzer by default shows hints for
+
+* types of local variables
+* names of function arguments
+* names of const generic parameters
+* types of chained expressions
+
+Optionally, one can enable additional hints for
+
+* return types of closure expressions
+* elided lifetimes
+* compiler inserted reborrows
+* names of generic type and lifetime parameters
+
+Note: inlay hints for function argument names are heuristically omitted to reduce noise and will not appear if
+any of the
+[following criteria](https://github.com/rust-lang/rust-analyzer/blob/6b8b8ff4c56118ddee6c531cde06add1aad4a6af/crates/ide/src/inlay_hints/param_name.rs#L92-L99)
+are met:
+
+* the parameter name is a suffix of the function's name
+* the argument is a qualified constructing or call expression where the qualifier is an ADT
+* exact argument<->parameter match(ignoring leading underscore) or parameter is a prefix/suffix
+  of argument with _ splitting it off
+* the parameter name starts with `ra_fixture`
+* the parameter name is a
+[well known name](https://github.com/rust-lang/rust-analyzer/blob/6b8b8ff4c56118ddee6c531cde06add1aad4a6af/crates/ide/src/inlay_hints/param_name.rs#L200)
+in a unary function
+* the parameter name is a
+[single character](https://github.com/rust-lang/rust-analyzer/blob/6b8b8ff4c56118ddee6c531cde06add1aad4a6af/crates/ide/src/inlay_hints/param_name.rs#L201)
+in a unary function
+
+![Inlay hints](https://user-images.githubusercontent.com/48062697/113020660-b5f98b80-917a-11eb-8d70-3be3fd558cdd.png)
+
+
+### Interpret A Function, Static Or Const.
+**Source:**  [interpret.rs](crates/ide/src/interpret.rs#8) 
+
+| Editor  | Action Name |
+|---------|-------------|
+| VS Code | **rust-analyzer: Interpret** |
+
+
+### Join Lines
+**Source:**  [join_lines.rs](crates/ide/src/join_lines.rs#20) 
+
+Join selected lines into one, smartly fixing up whitespace, trailing commas, and braces.
+
+See [this gif](https://user-images.githubusercontent.com/1711539/124515923-4504e800-dde9-11eb-8d58-d97945a1a785.gif) for the cases handled specially by joined lines.
+
+| Editor  | Action Name |
+|---------|-------------|
+| VS Code | **rust-analyzer: Join lines** |
+
+![Join Lines](https://user-images.githubusercontent.com/48062697/113020661-b6922200-917a-11eb-87c4-b75acc028f11.gif)
+
+
+### Magic Completions
+**Source:**  [lib.rs](crates/ide-completion/src/lib.rs#78) 
+
+In addition to usual reference completion, rust-analyzer provides some ✨magic✨
+completions as well:
+
+Keywords like `if`, `else` `while`, `loop` are completed with braces, and cursor
+is placed at the appropriate position. Even though `if` is easy to type, you
+still want to complete it, to get ` { }` for free! `return` is inserted with a
+space or `;` depending on the return type of the function.
+
+When completing a function call, `()` are automatically inserted. If a function
+takes arguments, the cursor is positioned inside the parenthesis.
+
+There are postfix completions, which can be triggered by typing something like
+`foo().if`. The word after `.` determines postfix completion. Possible variants are:
+
+- `expr.if` -> `if expr {}` or `if let ... {}` for `Option` or `Result`
+- `expr.match` -> `match expr {}`
+- `expr.while` -> `while expr {}` or `while let ... {}` for `Option` or `Result`
+- `expr.ref` -> `&expr`
+- `expr.refm` -> `&mut expr`
+- `expr.let` -> `let $0 = expr;`
+- `expr.lete` -> `let $1 = expr else { $0 };`
+- `expr.letm` -> `let mut $0 = expr;`
+- `expr.not` -> `!expr`
+- `expr.dbg` -> `dbg!(expr)`
+- `expr.dbgr` -> `dbg!(&expr)`
+- `expr.call` -> `(expr)`
+
+There also snippet completions:
+
+#### Expressions
+
+- `pd` -> `eprintln!(" = {:?}", );`
+- `ppd` -> `eprintln!(" = {:#?}", );`
+
+#### Items
+
+- `tfn` -> `#[test] fn feature(){}`
+- `tmod` ->
+```rust
+#[cfg(test)]
+mod tests {
+    use super::*;
+
+    #[test]
+    fn test_name() {}
+}
+```
+
+And the auto import completions, enabled with the `rust-analyzer.completion.autoimport.enable` setting and the corresponding LSP client capabilities.
+Those are the additional completion options with automatic `use` import and options from all project importable items,
+fuzzy matched against the completion input.
+
+![Magic Completions](https://user-images.githubusercontent.com/48062697/113020667-b72ab880-917a-11eb-8778-716cf26a0eb3.gif)
+
+
+### Matching Brace
+**Source:**  [matching_brace.rs](crates/ide/src/matching_brace.rs#6) 
+
+If the cursor is on any brace (`<>(){}[]||`) which is a part of a brace-pair,
+moves cursor to the matching brace. It uses the actual parser to determine
+braces, so it won't confuse generics with comparisons.
+
+| Editor  | Action Name |
+|---------|-------------|
+| VS Code | **rust-analyzer: Find matching brace** |
+
+![Matching Brace](https://user-images.githubusercontent.com/48062697/113065573-04298180-91b1-11eb-8dec-d4e2a202f304.gif)
+
+
+### Memory Usage
+**Source:**  [apply_change.rs](crates/ide-db/src/apply_change.rs#43) 
+
+Clears rust-analyzer's internal database and prints memory usage statistics.
+
+| Editor  | Action Name |
+|---------|-------------|
+| VS Code | **rust-analyzer: Memory Usage (Clears Database)**
+
+
+### Move Item
+**Source:**  [move_item.rs](crates/ide/src/move_item.rs#16) 
+
+Move item under cursor or selection up and down.
+
+| Editor  | Action Name |
+|---------|-------------|
+| VS Code | **rust-analyzer: Move item up**
+| VS Code | **rust-analyzer: Move item down**
+
+![Move Item](https://user-images.githubusercontent.com/48062697/113065576-04298180-91b1-11eb-91ce-4505e99ed598.gif)
+
+
+### On Enter
+**Source:**  [on_enter.rs](crates/ide/src/typing/on_enter.rs#17) 
+
+rust-analyzer can override <kbd>Enter</kbd> key to make it smarter:
+
+- <kbd>Enter</kbd> inside triple-slash comments automatically inserts `///`
+- <kbd>Enter</kbd> in the middle or after a trailing space in `//` inserts `//`
+- <kbd>Enter</kbd> inside `//!` doc comments automatically inserts `//!`
+- <kbd>Enter</kbd> after `{` indents contents and closing `}` of single-line block
+
+This action needs to be assigned to shortcut explicitly.
+
+Note that, depending on the other installed extensions, this feature can visibly slow down typing.
+Similarly, if rust-analyzer crashes or stops responding, `Enter` might not work.
+In that case, you can still press `Shift-Enter` to insert a newline.
+
+#### VS Code
+
+Add the following to `keybindings.json`:
+```json
+{
+  "key": "Enter",
+  "command": "rust-analyzer.onEnter",
+  "when": "editorTextFocus && !suggestWidgetVisible && editorLangId == rust"
+}
+````
+
+When using the Vim plugin:
+```json
+{
+  "key": "Enter",
+  "command": "rust-analyzer.onEnter",
+  "when": "editorTextFocus && !suggestWidgetVisible && editorLangId == rust && vim.mode == 'Insert'"
+}
+````
+
+![On Enter](https://user-images.githubusercontent.com/48062697/113065578-04c21800-91b1-11eb-82b8-22b8c481e645.gif)
+
+
+### On Typing Assists
+**Source:**  [typing.rs](crates/ide/src/typing.rs#42) 
+
+Some features trigger on typing certain characters:
+
+- typing `let =` tries to smartly add `;` if `=` is followed by an existing expression
+- typing `=` between two expressions adds `;` when in statement position
+- typing `=` to turn an assignment into an equality comparison removes `;` when in expression position
+- typing `.` in a chain method call auto-indents
+- typing `{` or `(` in front of an expression inserts a closing `}` or `)` after the expression
+- typing `{` in a use item adds a closing `}` in the right place
+- typing `>` to complete a return type `->` will insert a whitespace after it
+
+#### VS Code
+
+Add the following to `settings.json`:
+```json
+"editor.formatOnType": true,
+```
+
+![On Typing Assists](https://user-images.githubusercontent.com/48062697/113166163-69758500-923a-11eb-81ee-eb33ec380399.gif)
+![On Typing Assists](https://user-images.githubusercontent.com/48062697/113171066-105c2000-923f-11eb-87ab-f4a263346567.gif)
+
+
+### Open Docs
+**Source:**  [doc_links.rs](crates/ide/src/doc_links.rs#118) 
+
+Retrieve a links to documentation for the given symbol.
+
+The simplest way to use this feature is via the context menu. Right-click on
+the selected item. The context menu opens. Select **Open Docs**.
+
+| Editor  | Action Name |
+|---------|-------------|
+| VS Code | **rust-analyzer: Open Docs** |
+
+
+### Parent Module
+**Source:**  [parent_module.rs](crates/ide/src/parent_module.rs#14) 
+
+Navigates to the parent module of the current module.
+
+| Editor  | Action Name |
+|---------|-------------|
+| VS Code | **rust-analyzer: Locate parent module** |
+
+![Parent Module](https://user-images.githubusercontent.com/48062697/113065580-04c21800-91b1-11eb-9a32-00086161c0bd.gif)
+
+
+### Related Tests
+**Source:**  [runnables.rs](crates/ide/src/runnables.rs#202) 
+
+Provides a sneak peek of all tests where the current item is used.
+
+The simplest way to use this feature is via the context menu. Right-click on
+the selected item. The context menu opens. Select **Peek Related Tests**.
+
+| Editor  | Action Name |
+|---------|-------------|
+| VS Code | **rust-analyzer: Peek Related Tests** |
+
+
+### Rename
+**Source:**  [rename.rs](crates/ide/src/rename.rs#70) 
+
+Renames the item below the cursor and all of its references
+
+| Editor  | Shortcut |
+|---------|----------|
+| VS Code | <kbd>F2</kbd> |
+
+![Rename](https://user-images.githubusercontent.com/48062697/113065582-055aae80-91b1-11eb-8ade-2b58e6d81883.gif)
+
+
+### Run
+**Source:**  [runnables.rs](crates/ide/src/runnables.rs#116) 
+
+Shows a popup suggesting to run a test/benchmark/binary **at the current cursor
+location**. Super useful for repeatedly running just a single test. Do bind this
+to a shortcut!
+
+| Editor  | Action Name |
+|---------|-------------|
+| VS Code | **rust-analyzer: Run** |
+
+![Run](https://user-images.githubusercontent.com/48062697/113065583-055aae80-91b1-11eb-958f-d67efcaf6a2f.gif)
+
+
+### Semantic Syntax Highlighting
+**Source:**  [syntax_highlighting.rs](crates/ide/src/syntax_highlighting.rs#68) 
+
+rust-analyzer highlights the code semantically.
+For example, `Bar` in `foo::Bar` might be colored differently depending on whether `Bar` is an enum or a trait.
+rust-analyzer does not specify colors directly, instead it assigns a tag (like `struct`) and a set of modifiers (like `declaration`) to each token.
+It's up to the client to map those to specific colors.
+
+The general rule is that a reference to an entity gets colored the same way as the entity itself.
+We also give special modifier for `mut` and `&mut` local variables.
+
+
+#### Token Tags
+
+Rust-analyzer currently emits the following token tags:
+
+- For items:
+
+|           |                                |
+|-----------|--------------------------------|
+| attribute |  Emitted for attribute macros. |
+|enum| Emitted for enums. |
+|function| Emitted for free-standing functions. |
+|derive| Emitted for derive macros. |
+|macro| Emitted for function-like macros. |
+|method| Emitted for associated functions, also knowns as methods. |
+|namespace| Emitted for modules. |
+|struct| Emitted for structs.|
+|trait| Emitted for traits.|
+|typeAlias| Emitted for type aliases and `Self` in `impl`s.|
+|union| Emitted for unions.|
+
+- For literals:
+
+|           |                                |
+|-----------|--------------------------------|
+| boolean|  Emitted for the boolean literals `true` and `false`.|
+| character| Emitted for character literals.|
+| number| Emitted for numeric literals.|
+| string| Emitted for string literals.|
+| escapeSequence| Emitted for escaped sequences inside strings like `\n`.|
+| formatSpecifier| Emitted for format specifiers `{:?}` in `format!`-like macros.|
+
+- For operators:
+
+|           |                                |
+|-----------|--------------------------------|
+|operator| Emitted for general operators.|
+|arithmetic| Emitted for the arithmetic operators `+`, `-`, `*`, `/`, `+=`, `-=`, `*=`, `/=`.|
+|bitwise| Emitted for the bitwise operators `|`, `&`, `!`, `^`, `|=`, `&=`, `^=`.|
+|comparison| Emitted for the comparison oerators `>`, `<`, `==`, `>=`, `<=`, `!=`.|
+|logical| Emitted for the logical operatos `||`, `&&`, `!`.|
+
+- For punctuation:
+
+|           |                                |
+|-----------|--------------------------------|
+|punctuation| Emitted for general punctuation.|
+|attributeBracket| Emitted for attribute invocation brackets, that is the `#[` and `]` tokens.|
+|angle| Emitted for `<>` angle brackets.|
+|brace| Emitted for `{}` braces.|
+|bracket| Emitted for `[]` brackets.|
+|parenthesis| Emitted for `()` parentheses.|
+|colon| Emitted for the `:` token.|
+|comma| Emitted for the `,` token.|
+|dot| Emitted for the `.` token.|
+|semi| Emitted for the `;` token.|
+|macroBang| Emitted for the `!` token in macro calls.|
+
+-
+
+|           |                                |
+|-----------|--------------------------------|
+|builtinAttribute| Emitted for names to builtin attributes in attribute path, the `repr` in `#[repr(u8)]` for example.|
+|builtinType| Emitted for builtin types like `u32`, `str` and `f32`.|
+|comment| Emitted for comments.|
+|constParameter| Emitted for const parameters.|
+|deriveHelper| Emitted for derive helper attributes.|
+|enumMember| Emitted for enum variants.|
+|generic| Emitted for generic tokens that have no mapping.|
+|keyword| Emitted for keywords.|
+|label| Emitted for labels.|
+|lifetime| Emitted for lifetimes.|
+|parameter| Emitted for non-self function parameters.|
+|property| Emitted for struct and union fields.|
+|selfKeyword| Emitted for the self function parameter and self path-specifier.|
+|selfTypeKeyword| Emitted for the Self type parameter.|
+|toolModule| Emitted for tool modules.|
+|typeParameter| Emitted for type parameters.|
+|unresolvedReference| Emitted for unresolved references, names that rust-analyzer can't find the definition of.|
+|variable| Emitted for locals, constants and statics.|
+
+
+#### Token Modifiers
+
+Token modifiers allow to style some elements in the source code more precisely.
+
+Rust-analyzer currently emits the following token modifiers:
+
+|           |                                |
+|-----------|--------------------------------|
+|async| Emitted for async functions and the `async` and `await` keywords.|
+|attribute| Emitted for tokens inside attributes.|
+|callable| Emitted for locals whose types implements one of the `Fn*` traits.|
+|constant| Emitted for const.|
+|consuming| Emitted for locals that are being consumed when use in a function call.|
+|controlFlow| Emitted for control-flow related tokens, this includes th `?` operator.|
+|crateRoot| Emitted for crate names, like `serde` and `crate.|
+|declaration| Emitted for names of definitions, like `foo` in `fn foo(){}`.|
+|defaultLibrary| Emitted for items from built-in crates (std, core, allc, test and proc_macro).|
+|documentation| Emitted for documentation comment.|
+|injected| Emitted for doc-string injected highlighting like rust source blocks in documentation.|
+|intraDocLink| Emitted for intra doc links in doc-string.|
+|library| Emitted for items that are defined outside of the current crae.|
+|macro|  Emitted for tokens inside macro call.|
+|mutable| Emitted for mutable locals and statics as well as functions taking `&mut self`.|
+|public| Emitted for items that are from the current crate and are `pub.|
+|reference| Emitted for locals behind a reference and functions taking self` by reference.|
+|static| Emitted for "static" functions, also known as functions that d not take a `self` param, as well as statics and consts.|
+|trait| Emitted for associated trait item.|
+|unsafe| Emitted for unsafe operations, like unsafe function calls, as ell as the `unsafe` token.|
+
+![Semantic Syntax Highlighting](https://user-images.githubusercontent.com/48062697/113164457-06cfb980-9239-11eb-819b-0f93e646acf8.png)
+![Semantic Syntax Highlighting](https://user-images.githubusercontent.com/48062697/113187625-f7f50100-9250-11eb-825e-91c58f236071.png)
+
+
+### Show Dependency Tree
+**Source:**  [fetch_crates.rs](crates/ide/src/fetch_crates.rs#13) 
+
+Shows a view tree with all the dependencies of this project
+
+| Editor  | Panel Name |
+|---------|------------|
+| VS Code | **Rust Dependencies** |
+
+![Show Dependency Tree](https://user-images.githubusercontent.com/5748995/229394139-2625beab-f4c9-484b-84ed-ad5dee0b1e1a.png)
+
+
+### Show Syntax Tree
+**Source:**  [view_syntax_tree.rs](crates/ide/src/view_syntax_tree.rs#14) 
+
+Shows a tree view with the syntax tree of the current file
+
+| Editor  | Panel Name |
+|---------|-------------|
+| VS Code | **Rust Syntax Tree** |
+
+
+### Status
+**Source:**  [status.rs](crates/ide/src/status.rs#28) 
+
+Shows internal statistic about memory usage of rust-analyzer.
+
+| Editor  | Action Name |
+|---------|-------------|
+| VS Code | **rust-analyzer: Status** |
+
+![Status](https://user-images.githubusercontent.com/48062697/113065584-05f34500-91b1-11eb-98cc-5c196f76be7f.gif)
+
+
+### Structural Search and Replace
+**Source:**  [lib.rs](crates/ide-ssr/src/lib.rs#6) 
+
+Search and replace with named wildcards that will match any expression, type, path, pattern or item.
+The syntax for a structural search replace command is `<search_pattern> ==>> <replace_pattern>`.
+A `$<name>` placeholder in the search pattern will match any AST node and `$<name>` will reference it in the replacement.
+Within a macro call, a placeholder will match up until whatever token follows the placeholder.
+
+All paths in both the search pattern and the replacement template must resolve in the context
+in which this command is invoked. Paths in the search pattern will then match the code if they
+resolve to the same item, even if they're written differently. For example if we invoke the
+command in the module `foo` with a pattern of `Bar`, then code in the parent module that refers
+to `foo::Bar` will match.
+
+Paths in the replacement template will be rendered appropriately for the context in which the
+replacement occurs. For example if our replacement template is `foo::Bar` and we match some
+code in the `foo` module, we'll insert just `Bar`.
+
+Inherent method calls should generally be written in UFCS form. e.g. `foo::Bar::baz($s, $a)` will
+match `$s.baz($a)`, provided the method call `baz` resolves to the method `foo::Bar::baz`. When a
+placeholder is the receiver of a method call in the search pattern (e.g. `$s.foo()`), but not in
+the replacement template (e.g. `bar($s)`), then *, & and &mut will be added as needed to mirror
+whatever autoderef and autoref was happening implicitly in the matched code.
+
+The scope of the search / replace will be restricted to the current selection if any, otherwise
+it will apply to the whole workspace.
+
+Placeholders may be given constraints by writing them as `${<name>:<constraint1>:<constraint2>...}`.
+
+Supported constraints:
+
+| Constraint    | Restricts placeholder |
+|---------------|------------------------|
+| kind(literal) | Is a literal (e.g. `42` or `"forty two"`) |
+| not(a)        | Negates the constraint `a` |
+
+Available via the command `rust-analyzer.ssr`.
+
+```rust
+// Using structural search replace command [foo($a, $b) ==>> ($a).foo($b)]
+
+// BEFORE
+String::from(foo(y + 5, z))
+
+// AFTER
+String::from((y + 5).foo(z))
+```
+
+| Editor  | Action Name |
+|---------|--------------|
+| VS Code | **rust-analyzer: Structural Search Replace** |
+
+Also available as an assist, by writing a comment containing the structural
+search and replace rule. You will only see the assist if the comment can
+be parsed as a valid structural search and replace rule.
+
+```rust
+// Place the cursor on the line below to see the assist 💡.
+// foo($a, $b) ==>> ($a).foo($b)
+```
+
+
+### User Snippet Completions
+**Source:**  [snippet.rs](crates/ide-completion/src/snippet.rs#5) 
+
+rust-analyzer allows the user to define custom (postfix)-snippets that may depend on items to be accessible for the current scope to be applicable.
+
+A custom snippet can be defined by adding it to the `rust-analyzer.completion.snippets.custom` object respectively.
+
+```json
+{
+  "rust-analyzer.completion.snippets.custom": {
+    "thread spawn": {
+      "prefix": ["spawn", "tspawn"],
+      "body": [
+        "thread::spawn(move || {",
+        "\t$0",
+        "});",
+      ],
+      "description": "Insert a thread::spawn call",
+      "requires": "std::thread",
+      "scope": "expr",
+    }
+  }
+}
+```
+
+In the example above:
+
+* `"thread spawn"` is the name of the snippet.
+
+* `prefix` defines one or more trigger words that will trigger the snippets completion.
+Using `postfix` will instead create a postfix snippet.
+
+* `body` is one or more lines of content joined via newlines for the final output.
+
+* `description` is an optional description of the snippet, if unset the snippet name will be used.
+
+* `requires` is an optional list of item paths that have to be resolvable in the current crate where the completion is rendered.
+
+
+### View Crate Graph
+**Source:**  [view_crate_graph.rs](crates/ide/src/view_crate_graph.rs#8) 
+
+Renders the currently loaded crate graph as an SVG graphic. Requires the `dot` tool, which
+is part of graphviz, to be installed.
+
+Only workspace crates are included, no crates.io dependencies or sysroot crates.
+
+| Editor  | Action Name |
+|---------|-------------|
+| VS Code | **rust-analyzer: View Crate Graph** |
+
+
+### View Hir
+**Source:**  [view_hir.rs](crates/ide/src/view_hir.rs#5) 
+
+| Editor  | Action Name |
+|---------|--------------|
+| VS Code | **rust-analyzer: View Hir**
+
+![View Hir](https://user-images.githubusercontent.com/48062697/113065588-068bdb80-91b1-11eb-9a78-0b4ef1e972fb.gif)
+
+
+### View Memory Layout
+**Source:**  [view_memory_layout.rs](crates/ide/src/view_memory_layout.rs#74) 
+
+Displays the recursive memory layout of a datatype.
+
+| Editor  | Action Name |
+|---------|-------------|
+| VS Code | **rust-analyzer: View Memory Layout** |
+
+
+### View Mir
+**Source:**  [view_mir.rs](crates/ide/src/view_mir.rs#5) 
+
+| Editor  | Action Name |
+|---------|-------------|
+| VS Code | **rust-analyzer: View Mir**
+
+
+### Workspace Symbol
+**Source:**  [symbol_index.rs](crates/ide-db/src/symbol_index.rs#174) 
+
+Uses fuzzy-search to find types, modules and functions by name across your
+project and dependencies. This is **the** most useful feature, which improves code
+navigation tremendously. It mostly works on top of the built-in LSP
+functionality, however `#` and `*` symbols can be used to narrow down the
+search. Specifically,
+
+- `Foo` searches for `Foo` type in the current workspace
+- `foo#` searches for `foo` function in the current workspace
+- `Foo*` searches for `Foo` type among dependencies, including `stdlib`
+- `foo#*` searches for `foo` function among dependencies
+
+That is, `#` switches from "types" to all symbols, `*` switches from the current
+workspace to dependencies.
+
+Note that filtering does not currently work in VSCode due to the editor never
+sending the special symbols to the language server. Instead, you can configure
+the filtering via the `rust-analyzer.workspace.symbol.search.scope` and
+`rust-analyzer.workspace.symbol.search.kind` settings. Symbols prefixed
+with `__` are hidden from the search results unless configured otherwise.
+
+| Editor  | Shortcut |
+|---------|-----------|
+| VS Code | <kbd>Ctrl+T</kbd>
diff --git a/src/tools/rust-analyzer/docs/book/src/installation.md b/src/tools/rust-analyzer/docs/book/src/installation.md
new file mode 100644
index 00000000000..3a4c0cf2277
--- /dev/null
+++ b/src/tools/rust-analyzer/docs/book/src/installation.md
@@ -0,0 +1,40 @@
+# Installation
+
+To use rust-analyzer, you need a `rust-analyzer` binary, a text editor
+that supports LSP, and the source code of the Rust standard library.
+
+If you're [using VS Code](./vs_code.html), the extension bundles a
+copy of the `rust-analyzer` binary. For other editors, you'll need to
+[install the binary](./rust_analyzer_binary.html) and [configure your
+editor](./other_editors.html).
+
+## Rust Standard Library
+
+rust-analyzer will attempt to install the standard library source code
+automatically. You can also install it manually with `rustup`.
+
+    $ rustup component add rust-src
+
+Only the latest stable standard library source is officially supported
+for use with rust-analyzer. If you are using an older toolchain or have
+an override set, rust-analyzer may fail to understand the Rust source.
+You will either need to update your toolchain or use an older version of
+rust-analyzer that is compatible with your toolchain.
+
+If you are using an override in your project, you can still force
+rust-analyzer to use the stable toolchain via the environment variable
+`RUSTUP_TOOLCHAIN`. For example, with VS Code or coc-rust-analyzer:
+
+```json
+{ "rust-analyzer.server.extraEnv": { "RUSTUP_TOOLCHAIN": "stable" } }
+```
+
+## Crates
+
+There is a package named `ra_ap_rust_analyzer` available on
+[crates.io](https://crates.io/crates/ra_ap_rust-analyzer), for people
+who want to use rust-analyzer programmatically.
+
+For more details, see [the publish
+workflow](https://github.com/rust-lang/rust-analyzer/blob/master/.github/workflows/autopublish.yaml).
+
diff --git a/src/tools/rust-analyzer/docs/book/src/non_cargo_based_projects.md b/src/tools/rust-analyzer/docs/book/src/non_cargo_based_projects.md
new file mode 100644
index 00000000000..151f8758a17
--- /dev/null
+++ b/src/tools/rust-analyzer/docs/book/src/non_cargo_based_projects.md
@@ -0,0 +1,246 @@
+# Non-Cargo Based Projects
+
+rust-analyzer does not require Cargo. However, if you use some other
+build system, you’ll have to describe the structure of your project for
+rust-analyzer in the `rust-project.json` format:
+
+```typescript
+interface JsonProject {
+    /// Path to the sysroot directory.
+    ///
+    /// The sysroot is where rustc looks for the
+    /// crates that are built-in to rust, such as
+    /// std.
+    ///
+    /// https://doc.rust-lang.org/rustc/command-line-arguments.html#--sysroot-override-the-system-root
+    ///
+    /// To see the current value of sysroot, you
+    /// can query rustc:
+    ///
+    /// ```
+    /// $ rustc --print sysroot
+    /// /Users/yourname/.rustup/toolchains/stable-x86_64-apple-darwin
+    /// ```
+    sysroot?: string;
+    /// Path to the directory with *source code* of
+    /// sysroot crates.
+    ///
+    /// By default, this is `lib/rustlib/src/rust/library`
+    /// relative to the sysroot.
+    ///
+    /// It should point to the directory where std,
+    /// core, and friends can be found:
+    ///
+    /// https://github.com/rust-lang/rust/tree/master/library.
+    ///
+    /// If provided, rust-analyzer automatically adds
+    /// dependencies on sysroot crates. Conversely,
+    /// if you omit this path, you can specify sysroot
+    /// dependencies yourself and, for example, have
+    /// several different "sysroots" in one graph of
+    /// crates.
+    sysroot_src?: string;
+    /// List of groups of common cfg values, to allow
+    /// sharing them between crates.
+    ///
+    /// Maps from group name to its cfgs. Cfg follow
+    /// the same format as `Crate.cfg`.
+    cfg_groups?: { [key: string]: string[]; };
+    /// The set of crates comprising the current
+    /// project. Must include all transitive
+    /// dependencies as well as sysroot crate (libstd,
+    /// libcore and such).
+    crates: Crate[];
+    /// Configuration for CLI commands.
+    ///
+    /// These are used for running and debugging binaries
+    /// and tests without encoding build system-specific
+    /// knowledge into rust-analyzer.
+    ///
+    /// # Example
+    ///
+    /// Below is an example of a test runnable. `{label}` and `{test_id}`
+    /// are explained in `Runnable::args`'s documentation below.
+    ///
+    /// ```json
+    /// {
+    ///     "program": "buck",
+    ///     "args": [
+    ///         "test",
+    ///          "{label}",
+    ///          "--",
+    ///          "{test_id}",
+    ///          "--print-passing-details"
+    ///     ],
+    ///     "cwd": "/home/user/repo-root/",
+    ///     "kind": "testOne"
+    /// }
+    /// ```
+    runnables?: Runnable[];
+}
+
+interface Crate {
+    /// Optional crate name used for display purposes,
+    /// without affecting semantics. See the `deps`
+    /// key for semantically-significant crate names.
+    display_name?: string;
+    /// Path to the root module of the crate.
+    root_module: string;
+    /// Edition of the crate.
+    edition: '2015' | '2018' | '2021' | '2024';
+    /// The version of the crate. Used for calculating
+    /// the correct docs.rs URL.
+    version?: string;
+    /// Dependencies
+    deps: Dep[];
+    /// Should this crate be treated as a member of
+    /// current "workspace".
+    ///
+    /// By default, inferred from the `root_module`
+    /// (members are the crates which reside inside
+    /// the directory opened in the editor).
+    ///
+    /// Set this to `false` for things like standard
+    /// library and 3rd party crates to enable
+    /// performance optimizations (rust-analyzer
+    /// assumes that non-member crates don't change).
+    is_workspace_member?: boolean;
+    /// Optionally specify the (super)set of `.rs`
+    /// files comprising this crate.
+    ///
+    /// By default, rust-analyzer assumes that only
+    /// files under `root_module.parent` can belong
+    /// to a crate. `include_dirs` are included
+    /// recursively, unless a subdirectory is in
+    /// `exclude_dirs`.
+    ///
+    /// Different crates can share the same `source`.
+    ///
+    /// If two crates share an `.rs` file in common,
+    /// they *must* have the same `source`.
+    /// rust-analyzer assumes that files from one
+    /// source can't refer to files in another source.
+    source?: {
+        include_dirs: string[];
+        exclude_dirs: string[];
+    };
+    /// List of cfg groups this crate inherits.
+    ///
+    /// All cfg in these groups will be concatenated to
+    /// `cfg`. It is impossible to replace a value from
+    /// the groups.
+    cfg_groups?: string[];
+    /// The set of cfgs activated for a given crate, like
+    /// `["unix", "feature=\"foo\"", "feature=\"bar\""]`.
+    cfg: string[];
+    /// Target tuple for this Crate.
+    ///
+    /// Used when running `rustc --print cfg`
+    /// to get target-specific cfgs.
+    target?: string;
+    /// Environment variables, used for
+    /// the `env!` macro
+    env: { [key: string]: string; };
+
+    /// Whether the crate is a proc-macro crate.
+    is_proc_macro: boolean;
+    /// For proc-macro crates, path to compiled
+    /// proc-macro (.so file).
+    proc_macro_dylib_path?: string;
+
+    /// Repository, matching the URL that would be used
+    /// in Cargo.toml.
+    repository?: string;
+
+    /// Build-specific data about this crate.
+    build?: BuildInfo;
+}
+
+interface Dep {
+    /// Index of a crate in the `crates` array.
+    crate: number;
+    /// Name as should appear in the (implicit)
+    /// `extern crate name` declaration.
+    name: string;
+}
+
+interface BuildInfo {
+    /// The name associated with this crate.
+    ///
+    /// This is determined by the build system that produced
+    /// the `rust-project.json` in question. For instance, if buck were used,
+    /// the label might be something like `//ide/rust/rust-analyzer:rust-analyzer`.
+    ///
+    /// Do not attempt to parse the contents of this string; it is a build system-specific
+    /// identifier similar to `Crate::display_name`.
+    label: string;
+    /// Path corresponding to the build system-specific file defining the crate.
+    build_file: string;
+    /// The kind of target.
+    ///
+    /// This information is used to determine what sort
+    /// of runnable codelens to provide, if any.
+    target_kind: 'bin' | 'lib' | 'test';
+}
+
+interface Runnable {
+    /// The program invoked by the runnable.
+    ///
+    /// For example, this might be `cargo`, `buck`, or `bazel`.
+    program: string;
+    /// The arguments passed to `program`.
+    args: string[];
+    /// The current working directory of the runnable.
+    cwd: string;
+    /// Used to decide what code lens to offer.
+    ///
+    /// `testOne`: This runnable will be used when the user clicks the 'Run Test'
+    /// CodeLens above a test.
+    ///
+    /// The args for testOne can contain two template strings:
+    /// `{label}` and `{test_id}`. `{label}` will be replaced
+    /// with the `Build::label` and `{test_id}` will be replaced
+    /// with the test name.
+    kind: 'testOne' | string;
+}
+```
+
+This format is provisional and subject to change. Specifically, the
+`roots` setup will be different eventually.
+
+There are three ways to feed `rust-project.json` to rust-analyzer:
+
+-   Place `rust-project.json` file at the root of the project, and
+    rust-analyzer will discover it.
+
+-   Specify
+    `"rust-analyzer.linkedProjects": [ "path/to/rust-project.json" ]` in
+    the settings (and make sure that your LSP client sends settings as a
+    part of initialize request).
+
+-   Specify
+    `"rust-analyzer.linkedProjects": [ { "roots": […​], "crates": […​] }]`
+    inline.
+
+Relative paths are interpreted relative to `rust-project.json` file
+location or (for inline JSON) relative to `rootUri`.
+
+You can set the `RA_LOG` environment variable to `rust_analyzer=info` to
+inspect how rust-analyzer handles config and project loading.
+
+Note that calls to `cargo check` are disabled when using
+`rust-project.json` by default, so compilation errors and warnings will
+no longer be sent to your LSP client. To enable these compilation errors
+you will need to specify explicitly what command rust-analyzer should
+run to perform the checks using the
+`rust-analyzer.check.overrideCommand` configuration. As an example, the
+following configuration explicitly sets `cargo check` as the `check`
+command.
+
+    { "rust-analyzer.check.overrideCommand": ["cargo", "check", "--message-format=json"] }
+
+`check.overrideCommand` requires the command specified to output json
+error messages for rust-analyzer to consume. The `--message-format=json`
+flag does this for `cargo check` so whichever command you use must also
+output errors in this format. See the [Configuration](#_configuration)
+section for more information.
diff --git a/src/tools/rust-analyzer/docs/book/src/other_editors.md b/src/tools/rust-analyzer/docs/book/src/other_editors.md
new file mode 100644
index 00000000000..1eac7dd2c25
--- /dev/null
+++ b/src/tools/rust-analyzer/docs/book/src/other_editors.md
@@ -0,0 +1,425 @@
+# Other Editors
+
+rust-analyzer works with any editor that supports the [Language Server
+Protocol](https://microsoft.github.io/language-server-protocol/).
+
+This page assumes that you have already [installed the rust-analyzer
+binary](./rust_analyzer_binary.html).
+
+<!-- toc -->
+
+## Emacs
+
+To use `rust-analyzer`, you need to install and enable one of the two
+popular LSP client implementations for Emacs,
+[Eglot](https://github.com/joaotavora/eglot) or [LSP
+Mode](https://github.com/emacs-lsp/lsp-mode). Both enable
+`rust-analyzer` by default in Rust buffers if it is available.
+
+### Eglot
+
+Eglot is the more minimalistic and lightweight LSP client for Emacs,
+integrates well with existing Emacs functionality and is built into
+Emacs starting from release 29.
+
+After installing Eglot, e.g. via `M-x package-install` (not needed from
+Emacs 29), you can enable it via the `M-x eglot` command or load it
+automatically in `rust-mode` via
+
+```
+(add-hook 'rust-mode-hook 'eglot-ensure)
+```
+
+To enable clippy, you will need to configure the initialization options
+to pass the `check.command` setting.
+
+```
+(add-to-list 'eglot-server-programs
+             '((rust-ts-mode rust-mode) .
+               ("rust-analyzer" :initializationOptions (:check (:command "clippy")))))
+```
+
+For more detailed instructions and options see the [Eglot
+manual](https://joaotavora.github.io/eglot) (also available from Emacs
+via `M-x info`) and the [Eglot
+readme](https://github.com/joaotavora/eglot/blob/master/README.md).
+
+Eglot does not support the rust-analyzer extensions to the
+language-server protocol and does not aim to do so in the future. The
+[eglot-x](https://github.com/nemethf/eglot-x#rust-analyzer-extensions)
+package adds experimental support for those LSP extensions.
+
+### LSP Mode
+
+LSP-mode is the original LSP-client for emacs. Compared to Eglot it has
+a larger codebase and supports more features, like LSP protocol
+extensions. With extension packages like [LSP
+UI](https://github.com/emacs-lsp/lsp-mode) it offers a lot of visual
+eyecandy. Further it integrates well with [DAP
+mode](https://github.com/emacs-lsp/dap-mode) for support of the Debug
+Adapter Protocol.
+
+You can install LSP-mode via `M-x package-install` and then run it via
+the `M-x lsp` command or load it automatically in rust buffers with
+
+```
+(add-hook 'rust-mode-hook 'lsp-deferred)
+```
+
+For more information on how to set up LSP mode and its extension package
+see the instructions in the [LSP mode
+manual](https://emacs-lsp.github.io/lsp-mode/page/installation). Also
+see the [rust-analyzer
+section](https://emacs-lsp.github.io/lsp-mode/page/lsp-rust-analyzer/)
+for `rust-analyzer` specific options and commands, which you can
+optionally bind to keys.
+
+Note the excellent
+[guide](https://robert.kra.hn/posts/2021-02-07_rust-with-emacs/) from
+[@rksm](https://github.com/rksm) on how to set-up Emacs for Rust
+development with LSP mode and several other packages.
+
+## Vim/Neovim
+
+There are several LSP client implementations for Vim or Neovim:
+
+### coc-rust-analyzer
+
+1.  Install coc.nvim by following the instructions at
+    [coc.nvim](https://github.com/neoclide/coc.nvim) (Node.js required)
+
+2.  Run `:CocInstall coc-rust-analyzer` to install
+    [coc-rust-analyzer](https://github.com/fannheyward/coc-rust-analyzer),
+    this extension implements *most* of the features supported in the
+    VSCode extension:
+
+    -   automatically install and upgrade stable/nightly releases
+
+    -   same configurations as VSCode extension,
+        `rust-analyzer.server.path`, `rust-analyzer.cargo.features` etc.
+
+    -   same commands too, `rust-analyzer.analyzerStatus`,
+        `rust-analyzer.ssr` etc.
+
+    -   inlay hints for variables and method chaining, *Neovim Only*
+
+Note: coc-rust-analyzer is capable of installing or updating the
+rust-analyzer binary on its own.
+
+Note: for code actions, use `coc-codeaction-cursor` and
+`coc-codeaction-selected`; `coc-codeaction` and `coc-codeaction-line`
+are unlikely to be useful.
+
+### LanguageClient-neovim
+
+1.  Install LanguageClient-neovim by following the instructions
+    [here](https://github.com/autozimu/LanguageClient-neovim)
+
+    -   The GitHub project wiki has extra tips on configuration
+
+2.  Configure by adding this to your Vim/Neovim config file (replacing
+    the existing Rust-specific line if it exists):
+
+        let g:LanguageClient_serverCommands = {
+        \ 'rust': ['rust-analyzer'],
+        \ }
+
+### YouCompleteMe
+
+Install YouCompleteMe by following the instructions
+[here](https://github.com/ycm-core/YouCompleteMe#installation).
+
+rust-analyzer is the default in ycm, it should work out of the box.
+
+### ALE
+
+To use the LSP server in [ale](https://github.com/dense-analysis/ale):
+
+    let g:ale_linters = {'rust': ['analyzer']}
+
+### nvim-lsp
+
+Neovim 0.5 has built-in language server support. For a quick start
+configuration of rust-analyzer, use
+[neovim/nvim-lspconfig](https://github.com/neovim/nvim-lspconfig#rust_analyzer).
+Once `neovim/nvim-lspconfig` is installed, use
+`lua require'lspconfig'.rust_analyzer.setup({})` in your `init.vim`.
+
+You can also pass LSP settings to the server:
+
+```lua
+lua << EOF
+local lspconfig = require'lspconfig'
+
+local on_attach = function(client)
+    require'completion'.on_attach(client)
+end
+
+lspconfig.rust_analyzer.setup({
+    on_attach = on_attach,
+    settings = {
+        ["rust-analyzer"] = {
+            imports = {
+                granularity = {
+                    group = "module",
+                },
+                prefix = "self",
+            },
+            cargo = {
+                buildScripts = {
+                    enable = true,
+                },
+            },
+            procMacro = {
+                enable = true
+            },
+        }
+    }
+})
+EOF
+```
+
+If you're running Neovim 0.10 or later, you can enable inlay hints via `on_attach`:
+
+```lua
+lspconfig.rust_analyzer.setup({
+    on_attach = function(client, bufnr)
+        vim.lsp.inlay_hint.enable(true, { bufnr = bufnr })
+    end
+})
+```
+
+Note that the hints are only visible after `rust-analyzer` has finished loading **and** you have to
+edit the file to trigger a re-render.
+
+See <https://sharksforarms.dev/posts/neovim-rust/> for more tips on
+getting started.
+
+Check out <https://github.com/mrcjkb/rustaceanvim> for a batteries
+included rust-analyzer setup for Neovim.
+
+### vim-lsp
+
+vim-lsp is installed by following [the plugin
+instructions](https://github.com/prabirshrestha/vim-lsp). It can be as
+simple as adding this line to your `.vimrc`:
+
+    Plug 'prabirshrestha/vim-lsp'
+
+Next you need to register the `rust-analyzer` binary. If it is avim.lspvailable
+in `$PATH`, you may want to add this to your `.vimrc`:
+
+    if executable('rust-analyzer')
+      au User lsp_setup call lsp#register_server({
+            \   'name': 'Rust Language Server',
+            \   'cmd': {server_info->['rust-analyzer']},
+            \   'whitelist': ['rust'],
+            \ })
+    endif
+
+There is no dedicated UI for the server configuration, so you would need
+to send any options as a value of the `initialization_options` field, as
+described in the [Configuration](#configuration) section. Here is an
+example of how to enable the proc-macro support:
+
+    if executable('rust-analyzer')
+      au User lsp_setup call lsp#register_server({
+            \   'name': 'Rust Language Server',
+            \   'cmd': {server_info->['rust-analyzer']},
+            \   'whitelist': ['rust'],
+            \   'initialization_options': {
+            \     'cargo': {
+            \       'buildScripts': {
+            \         'enable': v:true,
+            \       },
+            \     },
+            \     'procMacro': {
+            \       'enable': v:true,
+            \     },
+            \   },
+            \ })
+    endif
+
+## Sublime Text
+
+### Sublime Text 4:
+
+-   Follow the instructions in
+    [LSP-rust-analyzer](https://github.com/sublimelsp/LSP-rust-analyzer).
+
+Install
+[LSP-file-watcher-chokidar](https://packagecontrol.io/packages/LSP-file-watcher-chokidar)
+to enable file watching (`workspace/didChangeWatchedFiles`).
+
+### Sublime Text 3:
+
+-   Install the [LSP package](https://packagecontrol.io/packages/LSP).
+
+-   From the command palette, run `LSP: Enable Language Server Globally`
+    and select `rust-analyzer`.
+
+If it worked, you should see "rust-analyzer, Line X, Column Y" on the
+left side of the status bar, and after waiting a bit, functionalities
+like tooltips on hovering over variables should become available.
+
+If you get an error saying `No such file or directory: 'rust-analyzer'`,
+see the [rust-analyzer binary installation](./rust_analyzer_binary.html) section.
+
+## GNOME Builder
+
+GNOME Builder 3.37.1 and newer has native `rust-analyzer` support. If
+the LSP binary is not available, GNOME Builder can install it when
+opening a Rust file.
+
+## Eclipse IDE
+
+Support for Rust development in the Eclipse IDE is provided by [Eclipse
+Corrosion](https://github.com/eclipse/corrosion). If available in PATH
+or in some standard location, `rust-analyzer` is detected and powers
+editing of Rust files without further configuration. If `rust-analyzer`
+is not detected, Corrosion will prompt you for configuration of your
+Rust toolchain and language server with a link to the *Window &gt;
+Preferences &gt; Rust* preference page; from here a button allows to
+download and configure `rust-analyzer`, but you can also reference
+another installation. You’ll need to close and reopen all .rs and Cargo
+files, or to restart the IDE, for this change to take effect.
+
+## Kate Text Editor
+
+Support for the language server protocol is built into Kate through the
+LSP plugin, which is included by default. It is preconfigured to use
+rust-analyzer for Rust sources since Kate 21.12.
+
+To change rust-analyzer config options, start from the following example
+and put it into Kate’s "User Server Settings" tab (located under the LSP
+Client settings):
+
+```json
+{
+    "servers": {
+        "rust": {
+            "initializationOptions": {
+                "cachePriming": {
+                    "enable": false
+                },
+                "check": {
+                    "allTargets": false
+                },
+                "checkOnSave": false
+            }
+        }
+    }
+}
+```
+
+Then click on apply, and restart the LSP server for your rust project.
+
+## juCi++
+
+[juCi++](https://gitlab.com/cppit/jucipp) has built-in support for the
+language server protocol, and since version 1.7.0 offers installation of
+both Rust and rust-analyzer when opening a Rust file.
+
+## Kakoune
+
+[Kakoune](https://kakoune.org/) supports LSP with the help of
+[`kak-lsp`](https://github.com/kak-lsp/kak-lsp). Follow the
+[instructions](https://github.com/kak-lsp/kak-lsp#installation) to
+install `kak-lsp`. To configure `kak-lsp`, refer to the [configuration
+section](https://github.com/kak-lsp/kak-lsp#configuring-kak-lsp) which
+is basically about copying the [configuration
+file](https://github.com/kak-lsp/kak-lsp/blob/master/kak-lsp.toml) in
+the right place (latest versions should use `rust-analyzer` by default).
+
+Finally, you need to configure Kakoune to talk to `kak-lsp` (see [Usage
+section](https://github.com/kak-lsp/kak-lsp#usage)). A basic
+configuration will only get you LSP but you can also activate inlay
+diagnostics and auto-formatting on save. The following might help you
+get all of this.
+
+    eval %sh{kak-lsp --kakoune -s $kak_session}  # Not needed if you load it with plug.kak.
+    hook global WinSetOption filetype=rust %{
+        # Enable LSP
+        lsp-enable-window
+
+        # Auto-formatting on save
+        hook window BufWritePre .* lsp-formatting-sync
+
+        # Configure inlay hints (only on save)
+        hook window -group rust-inlay-hints BufWritePost .* rust-analyzer-inlay-hints
+        hook -once -always window WinSetOption filetype=.* %{
+            remove-hooks window rust-inlay-hints
+        }
+    }
+
+## Helix
+
+[Helix](https://docs.helix-editor.com/) supports LSP by default.
+However, it won’t install `rust-analyzer` automatically. You can follow
+instructions for [installing the rust-analyzer
+binary](./rust_analyzer_binary.html).
+
+## Visual Studio 2022
+
+There are multiple rust-analyzer extensions for Visual Studio 2022 on
+Windows:
+
+### rust-analyzer.vs
+
+(License: Creative Commons Attribution-NonCommercial-ShareAlike 4.0
+International)
+
+[Visual Studio
+Marketplace](https://marketplace.visualstudio.com/items?itemName=kitamstudios.RustAnalyzer)
+
+[GitHub](https://github.com/kitamstudios/rust-analyzer/)
+
+Support for Rust development in the Visual Studio IDE is enabled by the
+[rust-analyzer](https://marketplace.visualstudio.com/items?itemName=kitamstudios.RustAnalyzer)
+package. Either click on the download link or install from IDE’s
+extension manager. For now [Visual Studio
+2022](https://visualstudio.microsoft.com/downloads/) is required. All
+editions are supported viz. Community, Professional & Enterprise. The
+package aims to provide 0-friction installation and therefore comes
+loaded with most things required including rust-analyzer binary. If
+anything it needs is missing, appropriate errors / warnings will guide
+the user. E.g. cargo.exe needs to be in path and the package will tell
+you as much. This package is under rapid active development. So if you
+encounter any issues please file it at
+[rust-analyzer.vs](https://github.com/kitamstudios/rust-analyzer/).
+
+### VS RustAnalyzer
+
+(License: GPL)
+
+[Visual Studio
+Marketplace](https://marketplace.visualstudio.com/items?itemName=cchharris.vsrustanalyzer)
+
+[GitHub](https://github.com/cchharris/VS-RustAnalyzer)
+
+### SourceGear Rust
+
+(License: closed source)
+
+[Visual Studio
+Marketplace](https://marketplace.visualstudio.com/items?itemName=SourceGear.SourceGearRust)
+
+[GitHub (docs, issues,
+discussions)](https://github.com/sourcegear/rust-vs-extension)
+
+-   Free (no-cost)
+
+-   Supports all editions of Visual Studio 2022 on Windows: Community,
+    Professional, or Enterprise
+
+## Lapce
+
+[Lapce](https://lapce.dev/) has a Rust plugin which you can install
+directly. Unfortunately, it downloads an old version of `rust-analyzer`,
+but you can set the server path under Settings.
+
+## Zed
+
+[Zed](https://zed.dev) has native `rust-analyzer` support. If the
+rust-analyzer binary is not available, Zed can install it when opening
+a Rust file.
diff --git a/src/tools/rust-analyzer/docs/book/src/privacy.md b/src/tools/rust-analyzer/docs/book/src/privacy.md
new file mode 100644
index 00000000000..602c68d6f67
--- /dev/null
+++ b/src/tools/rust-analyzer/docs/book/src/privacy.md
@@ -0,0 +1,15 @@
+# Privacy
+
+The LSP server performs no network access in itself, but runs
+`cargo metadata` which will update or download the crate registry and
+the source code of the project dependencies. If enabled (the default),
+build scripts and procedural macros can do anything.
+
+The Code extension does not access the network.
+
+Any other editor plugins are not under the control of the
+`rust-analyzer` developers. For any privacy concerns, you should check
+with their respective developers.
+
+For `rust-analyzer` developers, `cargo xtask release` uses the GitHub
+API to put together the release notes.
diff --git a/src/tools/rust-analyzer/docs/book/src/rust_analyzer_binary.md b/src/tools/rust-analyzer/docs/book/src/rust_analyzer_binary.md
new file mode 100644
index 00000000000..c7ac3087ced
--- /dev/null
+++ b/src/tools/rust-analyzer/docs/book/src/rust_analyzer_binary.md
@@ -0,0 +1,74 @@
+# rust-analyzer Binary
+
+Text editors require the `rust-analyzer` binary to be in
+`$PATH`. You can download pre-built binaries from the
+[releases](https://github.com/rust-lang/rust-analyzer/releases) page.
+You will need to uncompress and rename the binary for your platform,
+e.g. from `rust-analyzer-aarch64-apple-darwin.gz` on Mac OS to
+`rust-analyzer`, make it executable, then move it into a directory in
+your `$PATH`.
+
+On Linux to install the `rust-analyzer` binary into `~/.local/bin`,
+these commands should work:
+
+    $ mkdir -p ~/.local/bin
+    $ curl -L https://github.com/rust-lang/rust-analyzer/releases/latest/download/rust-analyzer-x86_64-unknown-linux-gnu.gz | gunzip -c - > ~/.local/bin/rust-analyzer
+    $ chmod +x ~/.local/bin/rust-analyzer
+
+Make sure that `~/.local/bin` is listed in the `$PATH` variable and use
+the appropriate URL if you’re not on a `x86-64` system.
+
+You don’t have to use `~/.local/bin`, any other path like `~/.cargo/bin`
+or `/usr/local/bin` will work just as well.
+
+Alternatively, you can install it from source using the command below.
+You’ll need the latest stable version of the Rust toolchain.
+
+    $ git clone https://github.com/rust-lang/rust-analyzer.git && cd rust-analyzer
+    $ cargo xtask install --server
+
+If your editor can’t find the binary even though the binary is on your
+`$PATH`, the likely explanation is that it doesn’t see the same `$PATH`
+as the shell, see [this
+issue](https://github.com/rust-lang/rust-analyzer/issues/1811). On Unix,
+running the editor from a shell or changing the `.desktop` file to set
+the environment should help.
+
+### rustup
+
+`rust-analyzer` is available in `rustup`:
+
+    $ rustup component add rust-analyzer
+
+### Arch Linux
+
+The `rust-analyzer` binary can be installed from the repos or AUR (Arch
+User Repository):
+
+-   [`rust-analyzer`](https://www.archlinux.org/packages/extra/x86_64/rust-analyzer/)
+    (built from latest tagged source)
+
+-   [`rust-analyzer-git`](https://aur.archlinux.org/packages/rust-analyzer-git)
+    (latest Git version)
+
+Install it with pacman, for example:
+
+    $ pacman -S rust-analyzer
+
+### Gentoo Linux
+
+`rust-analyzer` is installed when the `rust-analyzer` use flag is set for dev-lang/rust or dev-lang/rust-bin. You also need to set the `rust-src` use flag.
+
+### macOS
+
+The `rust-analyzer` binary can be installed via
+[Homebrew](https://brew.sh/).
+
+    $ brew install rust-analyzer
+
+### Windows
+
+It is recommended to install the latest Microsoft Visual C++ Redistributable prior to installation.
+Download links can be found
+[here](https://learn.microsoft.com/en-us/cpp/windows/latest-supported-vc-redist).
+
diff --git a/src/tools/rust-analyzer/docs/book/src/security.md b/src/tools/rust-analyzer/docs/book/src/security.md
new file mode 100644
index 00000000000..1444af03248
--- /dev/null
+++ b/src/tools/rust-analyzer/docs/book/src/security.md
@@ -0,0 +1,19 @@
+# Security
+
+At the moment, rust-analyzer assumes that all code is trusted. Here is a
+**non-exhaustive** list of ways to make rust-analyzer execute arbitrary
+code:
+
+-   proc macros and build scripts are executed by default
+
+-   `.cargo/config` can override `rustc` with an arbitrary executable
+
+-   `rust-toolchain.toml` can override `rustc` with an arbitrary
+    executable
+
+-   VS Code plugin reads configuration from project directory, and that
+    can be used to override paths to various executables, like `rustfmt`
+    or `rust-analyzer` itself.
+
+-   rust-analyzer’s syntax trees library uses a lot of `unsafe` and
+    hasn’t been properly audited for memory safety.
\ No newline at end of file
diff --git a/src/tools/rust-analyzer/docs/book/src/troubleshooting.md b/src/tools/rust-analyzer/docs/book/src/troubleshooting.md
new file mode 100644
index 00000000000..4092b9de990
--- /dev/null
+++ b/src/tools/rust-analyzer/docs/book/src/troubleshooting.md
@@ -0,0 +1,50 @@
+# Troubleshooting
+
+Start with looking at the rust-analyzer version. Try **rust-analyzer:
+Show RA Version** in VS Code (using **Command Palette** feature
+typically activated by Ctrl+Shift+P) or `rust-analyzer --version` in the
+command line. If the date is more than a week ago, it’s better to update
+rust-analyzer version.
+
+The next thing to check would be panic messages in rust-analyzer’s log.
+Log messages are printed to stderr, in VS Code you can see them in the
+`Output > Rust Analyzer Language Server` tab of the panel. To see more
+logs, set the `RA_LOG=info` environment variable, this can be done
+either by setting the environment variable manually or by using
+`rust-analyzer.server.extraEnv`, note that both of these approaches
+require the server to be restarted.
+
+To fully capture LSP messages between the editor and the server, run
+the `rust-analyzer: Toggle LSP Logs` command and check `Output > Rust
+Analyzer Language Server Trace`.
+
+The root cause for many "nothing works" problems is that rust-analyzer
+fails to understand the project structure. To debug that, first note the
+`rust-analyzer` section in the status bar. If it has an error icon and
+red, that’s the problem (hover will have somewhat helpful error
+message). **rust-analyzer: Status** prints dependency information for
+the current file. Finally, `RA_LOG=project_model=debug` enables verbose
+logs during project loading.
+
+If rust-analyzer outright crashes, try running
+`rust-analyzer analysis-stats /path/to/project/directory/` on the
+command line. This command type checks the whole project in batch mode
+bypassing LSP machinery.
+
+When filing issues, it is useful (but not necessary) to try to minimize
+examples. An ideal bug reproduction looks like this:
+
+```shell
+$ git clone https://github.com/username/repo.git && cd repo && git switch --detach commit-hash
+$ rust-analyzer --version
+rust-analyzer dd12184e4 2021-05-08 dev
+$ rust-analyzer analysis-stats .
+💀 💀 💀
+```
+
+It is especially useful when the `repo` doesn’t use external crates or
+the standard library.
+
+If you want to go as far as to modify the source code to debug the
+problem, be sure to take a look at the [dev
+docs](https://github.com/rust-lang/rust-analyzer/tree/master/docs/dev)!
diff --git a/src/tools/rust-analyzer/docs/book/src/vs_code.md b/src/tools/rust-analyzer/docs/book/src/vs_code.md
new file mode 100644
index 00000000000..233b862d2c6
--- /dev/null
+++ b/src/tools/rust-analyzer/docs/book/src/vs_code.md
@@ -0,0 +1,121 @@
+# VS Code
+
+This is the best supported editor at the moment. The rust-analyzer
+plugin for VS Code is maintained [in
+tree](https://github.com/rust-lang/rust-analyzer/tree/master/editors/code).
+
+You can install the latest release of the plugin from [the
+marketplace](https://marketplace.visualstudio.com/items?itemName=rust-lang.rust-analyzer).
+
+Note that the plugin may cause conflicts with the [previous official
+Rust
+plugin](https://marketplace.visualstudio.com/items?itemName=rust-lang.rust).
+The latter is no longer maintained and should be uninstalled.
+
+The server binary is stored in the extension install directory, which
+starts with `rust-lang.rust-analyzer-` and is located under:
+
+-   Linux: `~/.vscode/extensions`
+
+-   Linux (Remote, such as WSL): `~/.vscode-server/extensions`
+
+-   macOS: `~/.vscode/extensions`
+
+-   Windows: `%USERPROFILE%\.vscode\extensions`
+
+As an exception, on NixOS, the extension makes a copy of the server and
+stores it under
+`~/.config/Code/User/globalStorage/rust-lang.rust-analyzer`.
+
+Note that we only support the two most recent versions of VS Code.
+
+### Updates
+
+The extension will be updated automatically as new versions become
+available. It will ask your permission to download the matching language
+server version binary if needed.
+
+#### Nightly
+
+We ship nightly releases for VS Code. To help us out by testing the
+newest code, you can enable pre-release versions in the Code extension
+page.
+
+### Manual installation
+
+Alternatively, download a VSIX corresponding to your platform from the
+[releases](https://github.com/rust-lang/rust-analyzer/releases) page.
+
+Install the extension with the `Extensions: Install from VSIX` command
+within VS Code, or from the command line via:
+
+    $ code --install-extension /path/to/rust-analyzer.vsix
+
+If you are running an unsupported platform, you can install
+`rust-analyzer-no-server.vsix` and compile or obtain a server binary.
+Copy the server anywhere, then add the path to your settings.json, for
+example:
+
+```json
+{ "rust-analyzer.server.path": "~/.local/bin/rust-analyzer-linux" }
+```
+
+### Building From Source
+
+Both the server and the Code plugin can be installed from source:
+
+    $ git clone https://github.com/rust-lang/rust-analyzer.git && cd rust-analyzer
+    $ cargo xtask install
+
+You’ll need Cargo, nodejs (matching a supported version of VS Code) and
+npm for this.
+
+Note that installing via `xtask install` does not work for VS Code
+Remote, instead you’ll need to install the `.vsix` manually.
+
+If you’re not using Code, you can compile and install only the LSP
+server:
+
+    $ cargo xtask install --server
+
+Make sure that `.cargo/bin` is in `$PATH` and precedes paths where
+`rust-analyzer` may also be installed. Specifically, `rustup` includes a
+proxy called `rust-analyzer`, which can cause problems if you’re
+planning to use a source build or even a downloaded binary.
+
+## VS Code or VSCodium in Flatpak
+
+Setting up `rust-analyzer` with a Flatpak version of Code is not trivial
+because of the Flatpak sandbox. While the sandbox can be disabled for
+some directories, `/usr/bin` will always be mounted under
+`/run/host/usr/bin`. This prevents access to the system’s C compiler, a
+system-wide installation of Rust, or any other libraries you might want
+to link to. Some compilers and libraries can be acquired as Flatpak
+SDKs, such as `org.freedesktop.Sdk.Extension.rust-stable` or
+`org.freedesktop.Sdk.Extension.llvm15`.
+
+If you use a Flatpak SDK for Rust, it must be in your `PATH`:
+
+ * install the SDK extensions with `flatpak install org.freedesktop.Sdk.Extension.{llvm15,rust-stable}//23.08`
+ * enable SDK extensions in the editor with the environment variable `FLATPAK_ENABLE_SDK_EXT=llvm15,rust-stable` (this can be done using flatseal or `flatpak override`)
+
+If you want to use Flatpak in combination with `rustup`, the following
+steps might help:
+
+-   both Rust and `rustup` have to be installed using
+    <https://rustup.rs>. Distro packages *will not* work.
+
+-   you need to launch Code, open a terminal and run `echo $PATH`
+
+-   using
+    [Flatseal](https://flathub.org/apps/details/com.github.tchx84.Flatseal),
+    you must add an environment variable called `PATH`. Set its value to
+    the output from above, appending `:~/.cargo/bin`, where `~` is the
+    path to your home directory. You must replace `~`, as it won’t be
+    expanded otherwise.
+
+-   while Flatseal is open, you must enable access to "All user files"
+
+A C compiler should already be available via `org.freedesktop.Sdk`. Any
+other tools or libraries you will need to acquire from Flatpak.
+
diff --git a/src/tools/rust-analyzer/docs/user/.gitignore b/src/tools/rust-analyzer/docs/user/.gitignore
deleted file mode 100644
index c32b1bcec2e..00000000000
--- a/src/tools/rust-analyzer/docs/user/.gitignore
+++ /dev/null
@@ -1 +0,0 @@
-manual.html
diff --git a/src/tools/rust-analyzer/docs/user/generated_config.adoc b/src/tools/rust-analyzer/docs/user/generated_config.adoc
deleted file mode 100644
index b33de1956b8..00000000000
--- a/src/tools/rust-analyzer/docs/user/generated_config.adoc
+++ /dev/null
@@ -1,1197 +0,0 @@
-[[rust-analyzer.assist.emitMustUse]]rust-analyzer.assist.emitMustUse (default: `false`)::
-+
---
-Whether to insert #[must_use] when generating `as_` methods
-for enum variants.
---
-[[rust-analyzer.assist.expressionFillDefault]]rust-analyzer.assist.expressionFillDefault (default: `"todo"`)::
-+
---
-Placeholder expression to use for missing expressions in assists.
---
-[[rust-analyzer.assist.termSearch.borrowcheck]]rust-analyzer.assist.termSearch.borrowcheck (default: `true`)::
-+
---
-Enable borrow checking for term search code assists. If set to false, also there will be more suggestions, but some of them may not borrow-check.
---
-[[rust-analyzer.assist.termSearch.fuel]]rust-analyzer.assist.termSearch.fuel (default: `1800`)::
-+
---
-Term search fuel in "units of work" for assists (Defaults to 1800).
---
-[[rust-analyzer.cachePriming.enable]]rust-analyzer.cachePriming.enable (default: `true`)::
-+
---
-Warm up caches on project load.
---
-[[rust-analyzer.cachePriming.numThreads]]rust-analyzer.cachePriming.numThreads (default: `"physical"`)::
-+
---
-How many worker threads to handle priming caches. The default `0` means to pick automatically.
---
-[[rust-analyzer.cargo.allTargets]]rust-analyzer.cargo.allTargets (default: `true`)::
-+
---
-Pass `--all-targets` to cargo invocation.
---
-[[rust-analyzer.cargo.autoreload]]rust-analyzer.cargo.autoreload (default: `true`)::
-+
---
-Automatically refresh project info via `cargo metadata` on
-`Cargo.toml` or `.cargo/config.toml` changes.
---
-[[rust-analyzer.cargo.buildScripts.enable]]rust-analyzer.cargo.buildScripts.enable (default: `true`)::
-+
---
-Run build scripts (`build.rs`) for more precise code analysis.
---
-[[rust-analyzer.cargo.buildScripts.invocationStrategy]]rust-analyzer.cargo.buildScripts.invocationStrategy (default: `"per_workspace"`)::
-+
---
-Specifies the invocation strategy to use when running the build scripts command.
-If `per_workspace` is set, the command will be executed for each Rust workspace with the
-workspace as the working directory.
-If `once` is set, the command will be executed once with the opened project as the
-working directory.
-This config only has an effect when `#rust-analyzer.cargo.buildScripts.overrideCommand#`
-is set.
---
-[[rust-analyzer.cargo.buildScripts.overrideCommand]]rust-analyzer.cargo.buildScripts.overrideCommand (default: `null`)::
-+
---
-Override the command rust-analyzer uses to run build scripts and
-build procedural macros. The command is required to output json
-and should therefore include `--message-format=json` or a similar
-option.
-
-If there are multiple linked projects/workspaces, this command is invoked for
-each of them, with the working directory being the workspace root
-(i.e., the folder containing the `Cargo.toml`). This can be overwritten
-by changing `#rust-analyzer.cargo.buildScripts.invocationStrategy#`.
-
-By default, a cargo invocation will be constructed for the configured
-targets and features, with the following base command line:
-
-```bash
-cargo check --quiet --workspace --message-format=json --all-targets --keep-going
-```
-.
---
-[[rust-analyzer.cargo.buildScripts.rebuildOnSave]]rust-analyzer.cargo.buildScripts.rebuildOnSave (default: `true`)::
-+
---
-Rerun proc-macros building/build-scripts running when proc-macro
-or build-script sources change and are saved.
---
-[[rust-analyzer.cargo.buildScripts.useRustcWrapper]]rust-analyzer.cargo.buildScripts.useRustcWrapper (default: `true`)::
-+
---
-Use `RUSTC_WRAPPER=rust-analyzer` when running build scripts to
-avoid checking unnecessary things.
---
-[[rust-analyzer.cargo.cfgs]]rust-analyzer.cargo.cfgs::
-+
---
-Default:
-----
-[
-  "debug_assertions",
-  "miri"
-]
-----
-List of cfg options to enable with the given values.
-
---
-[[rust-analyzer.cargo.extraArgs]]rust-analyzer.cargo.extraArgs (default: `[]`)::
-+
---
-Extra arguments that are passed to every cargo invocation.
---
-[[rust-analyzer.cargo.extraEnv]]rust-analyzer.cargo.extraEnv (default: `{}`)::
-+
---
-Extra environment variables that will be set when running cargo, rustc
-or other commands within the workspace. Useful for setting RUSTFLAGS.
---
-[[rust-analyzer.cargo.features]]rust-analyzer.cargo.features (default: `[]`)::
-+
---
-List of features to activate.
-
-Set this to `"all"` to pass `--all-features` to cargo.
---
-[[rust-analyzer.cargo.noDefaultFeatures]]rust-analyzer.cargo.noDefaultFeatures (default: `false`)::
-+
---
-Whether to pass `--no-default-features` to cargo.
---
-[[rust-analyzer.cargo.sysroot]]rust-analyzer.cargo.sysroot (default: `"discover"`)::
-+
---
-Relative path to the sysroot, or "discover" to try to automatically find it via
-"rustc --print sysroot".
-
-Unsetting this disables sysroot loading.
-
-This option does not take effect until rust-analyzer is restarted.
---
-[[rust-analyzer.cargo.sysrootSrc]]rust-analyzer.cargo.sysrootSrc (default: `null`)::
-+
---
-Relative path to the sysroot library sources. If left unset, this will default to
-`{cargo.sysroot}/lib/rustlib/src/rust/library`.
-
-This option does not take effect until rust-analyzer is restarted.
---
-[[rust-analyzer.cargo.target]]rust-analyzer.cargo.target (default: `null`)::
-+
---
-Compilation target override (target tuple).
---
-[[rust-analyzer.cargo.targetDir]]rust-analyzer.cargo.targetDir (default: `null`)::
-+
---
-Optional path to a rust-analyzer specific target directory.
-This prevents rust-analyzer's `cargo check` and initial build-script and proc-macro
-building from locking the `Cargo.lock` at the expense of duplicating build artifacts.
-
-Set to `true` to use a subdirectory of the existing target directory or
-set to a path relative to the workspace to use that path.
---
-[[rust-analyzer.cfg.setTest]]rust-analyzer.cfg.setTest (default: `true`)::
-+
---
-Set `cfg(test)` for local crates. Defaults to true.
---
-[[rust-analyzer.checkOnSave]]rust-analyzer.checkOnSave (default: `true`)::
-+
---
-Run the check command for diagnostics on save.
---
-[[rust-analyzer.check.allTargets]]rust-analyzer.check.allTargets (default: `null`)::
-+
---
-Check all targets and tests (`--all-targets`). Defaults to
-`#rust-analyzer.cargo.allTargets#`.
---
-[[rust-analyzer.check.command]]rust-analyzer.check.command (default: `"check"`)::
-+
---
-Cargo command to use for `cargo check`.
---
-[[rust-analyzer.check.extraArgs]]rust-analyzer.check.extraArgs (default: `[]`)::
-+
---
-Extra arguments for `cargo check`.
---
-[[rust-analyzer.check.extraEnv]]rust-analyzer.check.extraEnv (default: `{}`)::
-+
---
-Extra environment variables that will be set when running `cargo check`.
-Extends `#rust-analyzer.cargo.extraEnv#`.
---
-[[rust-analyzer.check.features]]rust-analyzer.check.features (default: `null`)::
-+
---
-List of features to activate. Defaults to
-`#rust-analyzer.cargo.features#`.
-
-Set to `"all"` to pass `--all-features` to Cargo.
---
-[[rust-analyzer.check.ignore]]rust-analyzer.check.ignore (default: `[]`)::
-+
---
-List of `cargo check` (or other command specified in `check.command`) diagnostics to ignore.
-
-For example for `cargo check`: `dead_code`, `unused_imports`, `unused_variables`,...
---
-[[rust-analyzer.check.invocationStrategy]]rust-analyzer.check.invocationStrategy (default: `"per_workspace"`)::
-+
---
-Specifies the invocation strategy to use when running the check command.
-If `per_workspace` is set, the command will be executed for each workspace.
-If `once` is set, the command will be executed once.
-This config only has an effect when `#rust-analyzer.check.overrideCommand#`
-is set.
---
-[[rust-analyzer.check.noDefaultFeatures]]rust-analyzer.check.noDefaultFeatures (default: `null`)::
-+
---
-Whether to pass `--no-default-features` to Cargo. Defaults to
-`#rust-analyzer.cargo.noDefaultFeatures#`.
---
-[[rust-analyzer.check.overrideCommand]]rust-analyzer.check.overrideCommand (default: `null`)::
-+
---
-Override the command rust-analyzer uses instead of `cargo check` for
-diagnostics on save. The command is required to output json and
-should therefore include `--message-format=json` or a similar option
-(if your client supports the `colorDiagnosticOutput` experimental
-capability, you can use `--message-format=json-diagnostic-rendered-ansi`).
-
-If you're changing this because you're using some tool wrapping
-Cargo, you might also want to change
-`#rust-analyzer.cargo.buildScripts.overrideCommand#`.
-
-If there are multiple linked projects/workspaces, this command is invoked for
-each of them, with the working directory being the workspace root
-(i.e., the folder containing the `Cargo.toml`). This can be overwritten
-by changing `#rust-analyzer.check.invocationStrategy#`.
-
-If `$saved_file` is part of the command, rust-analyzer will pass
-the absolute path of the saved file to the provided command. This is
-intended to be used with non-Cargo build systems.
-Note that `$saved_file` is experimental and may be removed in the future.
-
-An example command would be:
-
-```bash
-cargo check --workspace --message-format=json --all-targets
-```
-.
---
-[[rust-analyzer.check.targets]]rust-analyzer.check.targets (default: `null`)::
-+
---
-Check for specific targets. Defaults to `#rust-analyzer.cargo.target#` if empty.
-
-Can be a single target, e.g. `"x86_64-unknown-linux-gnu"` or a list of targets, e.g.
-`["aarch64-apple-darwin", "x86_64-apple-darwin"]`.
-
-Aliased as `"checkOnSave.targets"`.
---
-[[rust-analyzer.check.workspace]]rust-analyzer.check.workspace (default: `true`)::
-+
---
-Whether `--workspace` should be passed to `cargo check`.
-If false, `-p <package>` will be passed instead if applicable. In case it is not, no
-check will be performed.
---
-[[rust-analyzer.completion.addSemicolonToUnit]]rust-analyzer.completion.addSemicolonToUnit (default: `true`)::
-+
---
-Whether to automatically add a semicolon when completing unit-returning functions.
-
-In `match` arms it completes a comma instead.
---
-[[rust-analyzer.completion.autoAwait.enable]]rust-analyzer.completion.autoAwait.enable (default: `true`)::
-+
---
-Toggles the additional completions that automatically show method calls and field accesses with `await` prefixed to them when completing on a future.
---
-[[rust-analyzer.completion.autoIter.enable]]rust-analyzer.completion.autoIter.enable (default: `true`)::
-+
---
-Toggles the additional completions that automatically show method calls with `iter()` or `into_iter()` prefixed to them when completing on a type that has them.
---
-[[rust-analyzer.completion.autoimport.enable]]rust-analyzer.completion.autoimport.enable (default: `true`)::
-+
---
-Toggles the additional completions that automatically add imports when completed.
-Note that your client must specify the `additionalTextEdits` LSP client capability to truly have this feature enabled.
---
-[[rust-analyzer.completion.autoimport.exclude]]rust-analyzer.completion.autoimport.exclude::
-+
---
-Default:
-----
-[
-  {
-    "path": "core::borrow::Borrow",
-    "type": "methods"
-  },
-  {
-    "path": "core::borrow::BorrowMut",
-    "type": "methods"
-  }
-]
-----
-A list of full paths to items to exclude from auto-importing completions.
-
-Traits in this list won't have their methods suggested in completions unless the trait
-is in scope.
-
-You can either specify a string path which defaults to type "always" or use the more verbose
-form `{ "path": "path::to::item", type: "always" }`.
-
-For traits the type "methods" can be used to only exclude the methods but not the trait itself.
-
-This setting also inherits `#rust-analyzer.completion.excludeTraits#`.
-
---
-[[rust-analyzer.completion.autoself.enable]]rust-analyzer.completion.autoself.enable (default: `true`)::
-+
---
-Toggles the additional completions that automatically show method calls and field accesses
-with `self` prefixed to them when inside a method.
---
-[[rust-analyzer.completion.callable.snippets]]rust-analyzer.completion.callable.snippets (default: `"fill_arguments"`)::
-+
---
-Whether to add parenthesis and argument snippets when completing function.
---
-[[rust-analyzer.completion.excludeTraits]]rust-analyzer.completion.excludeTraits (default: `[]`)::
-+
---
-A list of full paths to traits whose methods to exclude from completion.
-
-Methods from these traits won't be completed, even if the trait is in scope. However, they will still be suggested on expressions whose type is `dyn Trait`, `impl Trait` or `T where T: Trait`.
-
-Note that the trait themselves can still be completed.
---
-[[rust-analyzer.completion.fullFunctionSignatures.enable]]rust-analyzer.completion.fullFunctionSignatures.enable (default: `false`)::
-+
---
-Whether to show full function/method signatures in completion docs.
---
-[[rust-analyzer.completion.hideDeprecated]]rust-analyzer.completion.hideDeprecated (default: `false`)::
-+
---
-Whether to omit deprecated items from autocompletion. By default they are marked as deprecated but not hidden.
---
-[[rust-analyzer.completion.limit]]rust-analyzer.completion.limit (default: `null`)::
-+
---
-Maximum number of completions to return. If `None`, the limit is infinite.
---
-[[rust-analyzer.completion.postfix.enable]]rust-analyzer.completion.postfix.enable (default: `true`)::
-+
---
-Whether to show postfix snippets like `dbg`, `if`, `not`, etc.
---
-[[rust-analyzer.completion.privateEditable.enable]]rust-analyzer.completion.privateEditable.enable (default: `false`)::
-+
---
-Enables completions of private items and fields that are defined in the current workspace even if they are not visible at the current position.
---
-[[rust-analyzer.completion.snippets.custom]]rust-analyzer.completion.snippets.custom::
-+
---
-Default:
-----
-{
-  "Ok": {
-    "postfix": "ok",
-    "body": "Ok(${receiver})",
-    "description": "Wrap the expression in a `Result::Ok`",
-    "scope": "expr"
-  },
-  "Box::pin": {
-    "postfix": "pinbox",
-    "body": "Box::pin(${receiver})",
-    "requires": "std::boxed::Box",
-    "description": "Put the expression into a pinned `Box`",
-    "scope": "expr"
-  },
-  "Arc::new": {
-    "postfix": "arc",
-    "body": "Arc::new(${receiver})",
-    "requires": "std::sync::Arc",
-    "description": "Put the expression into an `Arc`",
-    "scope": "expr"
-  },
-  "Some": {
-    "postfix": "some",
-    "body": "Some(${receiver})",
-    "description": "Wrap the expression in an `Option::Some`",
-    "scope": "expr"
-  },
-  "Err": {
-    "postfix": "err",
-    "body": "Err(${receiver})",
-    "description": "Wrap the expression in a `Result::Err`",
-    "scope": "expr"
-  },
-  "Rc::new": {
-    "postfix": "rc",
-    "body": "Rc::new(${receiver})",
-    "requires": "std::rc::Rc",
-    "description": "Put the expression into an `Rc`",
-    "scope": "expr"
-  }
-}
-----
-Custom completion snippets.
-
---
-[[rust-analyzer.completion.termSearch.enable]]rust-analyzer.completion.termSearch.enable (default: `false`)::
-+
---
-Whether to enable term search based snippets like `Some(foo.bar().baz())`.
---
-[[rust-analyzer.completion.termSearch.fuel]]rust-analyzer.completion.termSearch.fuel (default: `1000`)::
-+
---
-Term search fuel in "units of work" for autocompletion (Defaults to 1000).
---
-[[rust-analyzer.diagnostics.disabled]]rust-analyzer.diagnostics.disabled (default: `[]`)::
-+
---
-List of rust-analyzer diagnostics to disable.
---
-[[rust-analyzer.diagnostics.enable]]rust-analyzer.diagnostics.enable (default: `true`)::
-+
---
-Whether to show native rust-analyzer diagnostics.
---
-[[rust-analyzer.diagnostics.experimental.enable]]rust-analyzer.diagnostics.experimental.enable (default: `false`)::
-+
---
-Whether to show experimental rust-analyzer diagnostics that might
-have more false positives than usual.
---
-[[rust-analyzer.diagnostics.remapPrefix]]rust-analyzer.diagnostics.remapPrefix (default: `{}`)::
-+
---
-Map of prefixes to be substituted when parsing diagnostic file paths.
-This should be the reverse mapping of what is passed to `rustc` as `--remap-path-prefix`.
---
-[[rust-analyzer.diagnostics.styleLints.enable]]rust-analyzer.diagnostics.styleLints.enable (default: `false`)::
-+
---
-Whether to run additional style lints.
---
-[[rust-analyzer.diagnostics.warningsAsHint]]rust-analyzer.diagnostics.warningsAsHint (default: `[]`)::
-+
---
-List of warnings that should be displayed with hint severity.
-
-The warnings will be indicated by faded text or three dots in code
-and will not show up in the `Problems Panel`.
---
-[[rust-analyzer.diagnostics.warningsAsInfo]]rust-analyzer.diagnostics.warningsAsInfo (default: `[]`)::
-+
---
-List of warnings that should be displayed with info severity.
-
-The warnings will be indicated by a blue squiggly underline in code
-and a blue icon in the `Problems Panel`.
---
-[[rust-analyzer.files.excludeDirs]]rust-analyzer.files.excludeDirs (default: `[]`)::
-+
---
-These directories will be ignored by rust-analyzer. They are
-relative to the workspace root, and globs are not supported. You may
-also need to add the folders to Code's `files.watcherExclude`.
---
-[[rust-analyzer.files.watcher]]rust-analyzer.files.watcher (default: `"client"`)::
-+
---
-Controls file watching implementation.
---
-[[rust-analyzer.highlightRelated.breakPoints.enable]]rust-analyzer.highlightRelated.breakPoints.enable (default: `true`)::
-+
---
-Enables highlighting of related references while the cursor is on `break`, `loop`, `while`, or `for` keywords.
---
-[[rust-analyzer.highlightRelated.closureCaptures.enable]]rust-analyzer.highlightRelated.closureCaptures.enable (default: `true`)::
-+
---
-Enables highlighting of all captures of a closure while the cursor is on the `|` or move keyword of a closure.
---
-[[rust-analyzer.highlightRelated.exitPoints.enable]]rust-analyzer.highlightRelated.exitPoints.enable (default: `true`)::
-+
---
-Enables highlighting of all exit points while the cursor is on any `return`, `?`, `fn`, or return type arrow (`->`).
---
-[[rust-analyzer.highlightRelated.references.enable]]rust-analyzer.highlightRelated.references.enable (default: `true`)::
-+
---
-Enables highlighting of related references while the cursor is on any identifier.
---
-[[rust-analyzer.highlightRelated.yieldPoints.enable]]rust-analyzer.highlightRelated.yieldPoints.enable (default: `true`)::
-+
---
-Enables highlighting of all break points for a loop or block context while the cursor is on any `async` or `await` keywords.
---
-[[rust-analyzer.hover.actions.debug.enable]]rust-analyzer.hover.actions.debug.enable (default: `true`)::
-+
---
-Whether to show `Debug` action. Only applies when
-`#rust-analyzer.hover.actions.enable#` is set.
---
-[[rust-analyzer.hover.actions.enable]]rust-analyzer.hover.actions.enable (default: `true`)::
-+
---
-Whether to show HoverActions in Rust files.
---
-[[rust-analyzer.hover.actions.gotoTypeDef.enable]]rust-analyzer.hover.actions.gotoTypeDef.enable (default: `true`)::
-+
---
-Whether to show `Go to Type Definition` action. Only applies when
-`#rust-analyzer.hover.actions.enable#` is set.
---
-[[rust-analyzer.hover.actions.implementations.enable]]rust-analyzer.hover.actions.implementations.enable (default: `true`)::
-+
---
-Whether to show `Implementations` action. Only applies when
-`#rust-analyzer.hover.actions.enable#` is set.
---
-[[rust-analyzer.hover.actions.references.enable]]rust-analyzer.hover.actions.references.enable (default: `false`)::
-+
---
-Whether to show `References` action. Only applies when
-`#rust-analyzer.hover.actions.enable#` is set.
---
-[[rust-analyzer.hover.actions.run.enable]]rust-analyzer.hover.actions.run.enable (default: `true`)::
-+
---
-Whether to show `Run` action. Only applies when
-`#rust-analyzer.hover.actions.enable#` is set.
---
-[[rust-analyzer.hover.actions.updateTest.enable]]rust-analyzer.hover.actions.updateTest.enable (default: `true`)::
-+
---
-Whether to show `Update Test` action. Only applies when
-`#rust-analyzer.hover.actions.enable#` and `#rust-analyzer.hover.actions.run.enable#` are set.
---
-[[rust-analyzer.hover.documentation.enable]]rust-analyzer.hover.documentation.enable (default: `true`)::
-+
---
-Whether to show documentation on hover.
---
-[[rust-analyzer.hover.documentation.keywords.enable]]rust-analyzer.hover.documentation.keywords.enable (default: `true`)::
-+
---
-Whether to show keyword hover popups. Only applies when
-`#rust-analyzer.hover.documentation.enable#` is set.
---
-[[rust-analyzer.hover.links.enable]]rust-analyzer.hover.links.enable (default: `true`)::
-+
---
-Use markdown syntax for links on hover.
---
-[[rust-analyzer.hover.maxSubstitutionLength]]rust-analyzer.hover.maxSubstitutionLength (default: `20`)::
-+
---
-Whether to show what types are used as generic arguments in calls etc. on hover, and what is their max length to show such types, beyond it they will be shown with ellipsis.
-
-This can take three values: `null` means "unlimited", the string `"hide"` means to not show generic substitutions at all, and a number means to limit them to X characters.
-
-The default is 20 characters.
---
-[[rust-analyzer.hover.memoryLayout.alignment]]rust-analyzer.hover.memoryLayout.alignment (default: `"hexadecimal"`)::
-+
---
-How to render the align information in a memory layout hover.
---
-[[rust-analyzer.hover.memoryLayout.enable]]rust-analyzer.hover.memoryLayout.enable (default: `true`)::
-+
---
-Whether to show memory layout data on hover.
---
-[[rust-analyzer.hover.memoryLayout.niches]]rust-analyzer.hover.memoryLayout.niches (default: `false`)::
-+
---
-How to render the niche information in a memory layout hover.
---
-[[rust-analyzer.hover.memoryLayout.offset]]rust-analyzer.hover.memoryLayout.offset (default: `"hexadecimal"`)::
-+
---
-How to render the offset information in a memory layout hover.
---
-[[rust-analyzer.hover.memoryLayout.size]]rust-analyzer.hover.memoryLayout.size (default: `"both"`)::
-+
---
-How to render the size information in a memory layout hover.
---
-[[rust-analyzer.hover.show.enumVariants]]rust-analyzer.hover.show.enumVariants (default: `5`)::
-+
---
-How many variants of an enum to display when hovering on. Show none if empty.
---
-[[rust-analyzer.hover.show.fields]]rust-analyzer.hover.show.fields (default: `5`)::
-+
---
-How many fields of a struct, variant or union to display when hovering on. Show none if empty.
---
-[[rust-analyzer.hover.show.traitAssocItems]]rust-analyzer.hover.show.traitAssocItems (default: `null`)::
-+
---
-How many associated items of a trait to display when hovering a trait.
---
-[[rust-analyzer.imports.granularity.enforce]]rust-analyzer.imports.granularity.enforce (default: `false`)::
-+
---
-Whether to enforce the import granularity setting for all files. If set to false rust-analyzer will try to keep import styles consistent per file.
---
-[[rust-analyzer.imports.granularity.group]]rust-analyzer.imports.granularity.group (default: `"crate"`)::
-+
---
-How imports should be grouped into use statements.
---
-[[rust-analyzer.imports.group.enable]]rust-analyzer.imports.group.enable (default: `true`)::
-+
---
-Group inserted imports by the https://rust-analyzer.github.io/manual.html#auto-import[following order]. Groups are separated by newlines.
---
-[[rust-analyzer.imports.merge.glob]]rust-analyzer.imports.merge.glob (default: `true`)::
-+
---
-Whether to allow import insertion to merge new imports into single path glob imports like `use std::fmt::*;`.
---
-[[rust-analyzer.imports.preferNoStd]]rust-analyzer.imports.preferNoStd (default: `false`)::
-+
---
-Prefer to unconditionally use imports of the core and alloc crate, over the std crate.
---
-[[rust-analyzer.imports.preferPrelude]]rust-analyzer.imports.preferPrelude (default: `false`)::
-+
---
-Whether to prefer import paths containing a `prelude` module.
---
-[[rust-analyzer.imports.prefix]]rust-analyzer.imports.prefix (default: `"plain"`)::
-+
---
-The path structure for newly inserted paths to use.
---
-[[rust-analyzer.imports.prefixExternPrelude]]rust-analyzer.imports.prefixExternPrelude (default: `false`)::
-+
---
-Whether to prefix external (including std, core) crate imports with `::`. e.g. "use ::std::io::Read;".
---
-[[rust-analyzer.inlayHints.bindingModeHints.enable]]rust-analyzer.inlayHints.bindingModeHints.enable (default: `false`)::
-+
---
-Whether to show inlay type hints for binding modes.
---
-[[rust-analyzer.inlayHints.chainingHints.enable]]rust-analyzer.inlayHints.chainingHints.enable (default: `true`)::
-+
---
-Whether to show inlay type hints for method chains.
---
-[[rust-analyzer.inlayHints.closingBraceHints.enable]]rust-analyzer.inlayHints.closingBraceHints.enable (default: `true`)::
-+
---
-Whether to show inlay hints after a closing `}` to indicate what item it belongs to.
---
-[[rust-analyzer.inlayHints.closingBraceHints.minLines]]rust-analyzer.inlayHints.closingBraceHints.minLines (default: `25`)::
-+
---
-Minimum number of lines required before the `}` until the hint is shown (set to 0 or 1
-to always show them).
---
-[[rust-analyzer.inlayHints.closureCaptureHints.enable]]rust-analyzer.inlayHints.closureCaptureHints.enable (default: `false`)::
-+
---
-Whether to show inlay hints for closure captures.
---
-[[rust-analyzer.inlayHints.closureReturnTypeHints.enable]]rust-analyzer.inlayHints.closureReturnTypeHints.enable (default: `"never"`)::
-+
---
-Whether to show inlay type hints for return types of closures.
---
-[[rust-analyzer.inlayHints.closureStyle]]rust-analyzer.inlayHints.closureStyle (default: `"impl_fn"`)::
-+
---
-Closure notation in type and chaining inlay hints.
---
-[[rust-analyzer.inlayHints.discriminantHints.enable]]rust-analyzer.inlayHints.discriminantHints.enable (default: `"never"`)::
-+
---
-Whether to show enum variant discriminant hints.
---
-[[rust-analyzer.inlayHints.expressionAdjustmentHints.enable]]rust-analyzer.inlayHints.expressionAdjustmentHints.enable (default: `"never"`)::
-+
---
-Whether to show inlay hints for type adjustments.
---
-[[rust-analyzer.inlayHints.expressionAdjustmentHints.hideOutsideUnsafe]]rust-analyzer.inlayHints.expressionAdjustmentHints.hideOutsideUnsafe (default: `false`)::
-+
---
-Whether to hide inlay hints for type adjustments outside of `unsafe` blocks.
---
-[[rust-analyzer.inlayHints.expressionAdjustmentHints.mode]]rust-analyzer.inlayHints.expressionAdjustmentHints.mode (default: `"prefix"`)::
-+
---
-Whether to show inlay hints as postfix ops (`.*` instead of `*`, etc).
---
-[[rust-analyzer.inlayHints.genericParameterHints.const.enable]]rust-analyzer.inlayHints.genericParameterHints.const.enable (default: `true`)::
-+
---
-Whether to show const generic parameter name inlay hints.
---
-[[rust-analyzer.inlayHints.genericParameterHints.lifetime.enable]]rust-analyzer.inlayHints.genericParameterHints.lifetime.enable (default: `false`)::
-+
---
-Whether to show generic lifetime parameter name inlay hints.
---
-[[rust-analyzer.inlayHints.genericParameterHints.type.enable]]rust-analyzer.inlayHints.genericParameterHints.type.enable (default: `false`)::
-+
---
-Whether to show generic type parameter name inlay hints.
---
-[[rust-analyzer.inlayHints.implicitDrops.enable]]rust-analyzer.inlayHints.implicitDrops.enable (default: `false`)::
-+
---
-Whether to show implicit drop hints.
---
-[[rust-analyzer.inlayHints.implicitSizedBoundHints.enable]]rust-analyzer.inlayHints.implicitSizedBoundHints.enable (default: `false`)::
-+
---
-Whether to show inlay hints for the implied type parameter `Sized` bound.
---
-[[rust-analyzer.inlayHints.lifetimeElisionHints.enable]]rust-analyzer.inlayHints.lifetimeElisionHints.enable (default: `"never"`)::
-+
---
-Whether to show inlay type hints for elided lifetimes in function signatures.
---
-[[rust-analyzer.inlayHints.lifetimeElisionHints.useParameterNames]]rust-analyzer.inlayHints.lifetimeElisionHints.useParameterNames (default: `false`)::
-+
---
-Whether to prefer using parameter names as the name for elided lifetime hints if possible.
---
-[[rust-analyzer.inlayHints.maxLength]]rust-analyzer.inlayHints.maxLength (default: `25`)::
-+
---
-Maximum length for inlay hints. Set to null to have an unlimited length.
---
-[[rust-analyzer.inlayHints.parameterHints.enable]]rust-analyzer.inlayHints.parameterHints.enable (default: `true`)::
-+
---
-Whether to show function parameter name inlay hints at the call
-site.
---
-[[rust-analyzer.inlayHints.rangeExclusiveHints.enable]]rust-analyzer.inlayHints.rangeExclusiveHints.enable (default: `false`)::
-+
---
-Whether to show exclusive range inlay hints.
---
-[[rust-analyzer.inlayHints.reborrowHints.enable]]rust-analyzer.inlayHints.reborrowHints.enable (default: `"never"`)::
-+
---
-Whether to show inlay hints for compiler inserted reborrows.
-This setting is deprecated in favor of #rust-analyzer.inlayHints.expressionAdjustmentHints.enable#.
---
-[[rust-analyzer.inlayHints.renderColons]]rust-analyzer.inlayHints.renderColons (default: `true`)::
-+
---
-Whether to render leading colons for type hints, and trailing colons for parameter hints.
---
-[[rust-analyzer.inlayHints.typeHints.enable]]rust-analyzer.inlayHints.typeHints.enable (default: `true`)::
-+
---
-Whether to show inlay type hints for variables.
---
-[[rust-analyzer.inlayHints.typeHints.hideClosureInitialization]]rust-analyzer.inlayHints.typeHints.hideClosureInitialization (default: `false`)::
-+
---
-Whether to hide inlay type hints for `let` statements that initialize to a closure.
-Only applies to closures with blocks, same as `#rust-analyzer.inlayHints.closureReturnTypeHints.enable#`.
---
-[[rust-analyzer.inlayHints.typeHints.hideNamedConstructor]]rust-analyzer.inlayHints.typeHints.hideNamedConstructor (default: `false`)::
-+
---
-Whether to hide inlay type hints for constructors.
---
-[[rust-analyzer.interpret.tests]]rust-analyzer.interpret.tests (default: `false`)::
-+
---
-Enables the experimental support for interpreting tests.
---
-[[rust-analyzer.joinLines.joinAssignments]]rust-analyzer.joinLines.joinAssignments (default: `true`)::
-+
---
-Join lines merges consecutive declaration and initialization of an assignment.
---
-[[rust-analyzer.joinLines.joinElseIf]]rust-analyzer.joinLines.joinElseIf (default: `true`)::
-+
---
-Join lines inserts else between consecutive ifs.
---
-[[rust-analyzer.joinLines.removeTrailingComma]]rust-analyzer.joinLines.removeTrailingComma (default: `true`)::
-+
---
-Join lines removes trailing commas.
---
-[[rust-analyzer.joinLines.unwrapTrivialBlock]]rust-analyzer.joinLines.unwrapTrivialBlock (default: `true`)::
-+
---
-Join lines unwraps trivial blocks.
---
-[[rust-analyzer.lens.debug.enable]]rust-analyzer.lens.debug.enable (default: `true`)::
-+
---
-Whether to show `Debug` lens. Only applies when
-`#rust-analyzer.lens.enable#` is set.
---
-[[rust-analyzer.lens.enable]]rust-analyzer.lens.enable (default: `true`)::
-+
---
-Whether to show CodeLens in Rust files.
---
-[[rust-analyzer.lens.implementations.enable]]rust-analyzer.lens.implementations.enable (default: `true`)::
-+
---
-Whether to show `Implementations` lens. Only applies when
-`#rust-analyzer.lens.enable#` is set.
---
-[[rust-analyzer.lens.location]]rust-analyzer.lens.location (default: `"above_name"`)::
-+
---
-Where to render annotations.
---
-[[rust-analyzer.lens.references.adt.enable]]rust-analyzer.lens.references.adt.enable (default: `false`)::
-+
---
-Whether to show `References` lens for Struct, Enum, and Union.
-Only applies when `#rust-analyzer.lens.enable#` is set.
---
-[[rust-analyzer.lens.references.enumVariant.enable]]rust-analyzer.lens.references.enumVariant.enable (default: `false`)::
-+
---
-Whether to show `References` lens for Enum Variants.
-Only applies when `#rust-analyzer.lens.enable#` is set.
---
-[[rust-analyzer.lens.references.method.enable]]rust-analyzer.lens.references.method.enable (default: `false`)::
-+
---
-Whether to show `Method References` lens. Only applies when
-`#rust-analyzer.lens.enable#` is set.
---
-[[rust-analyzer.lens.references.trait.enable]]rust-analyzer.lens.references.trait.enable (default: `false`)::
-+
---
-Whether to show `References` lens for Trait.
-Only applies when `#rust-analyzer.lens.enable#` is set.
---
-[[rust-analyzer.lens.run.enable]]rust-analyzer.lens.run.enable (default: `true`)::
-+
---
-Whether to show `Run` lens. Only applies when
-`#rust-analyzer.lens.enable#` is set.
---
-[[rust-analyzer.lens.updateTest.enable]]rust-analyzer.lens.updateTest.enable (default: `true`)::
-+
---
-Whether to show `Update Test` lens. Only applies when
-`#rust-analyzer.lens.enable#` and `#rust-analyzer.lens.run.enable#` are set.
---
-[[rust-analyzer.linkedProjects]]rust-analyzer.linkedProjects (default: `[]`)::
-+
---
-Disable project auto-discovery in favor of explicitly specified set
-of projects.
-
-Elements must be paths pointing to `Cargo.toml`,
-`rust-project.json`, `.rs` files (which will be treated as standalone files) or JSON
-objects in `rust-project.json` format.
---
-[[rust-analyzer.lru.capacity]]rust-analyzer.lru.capacity (default: `null`)::
-+
---
-Number of syntax trees rust-analyzer keeps in memory. Defaults to 128.
---
-[[rust-analyzer.lru.query.capacities]]rust-analyzer.lru.query.capacities (default: `{}`)::
-+
---
-Sets the LRU capacity of the specified queries.
---
-[[rust-analyzer.notifications.cargoTomlNotFound]]rust-analyzer.notifications.cargoTomlNotFound (default: `true`)::
-+
---
-Whether to show `can't find Cargo.toml` error message.
---
-[[rust-analyzer.numThreads]]rust-analyzer.numThreads (default: `null`)::
-+
---
-How many worker threads in the main loop. The default `null` means to pick automatically.
---
-[[rust-analyzer.procMacro.attributes.enable]]rust-analyzer.procMacro.attributes.enable (default: `true`)::
-+
---
-Expand attribute macros. Requires `#rust-analyzer.procMacro.enable#` to be set.
---
-[[rust-analyzer.procMacro.enable]]rust-analyzer.procMacro.enable (default: `true`)::
-+
---
-Enable support for procedural macros, implies `#rust-analyzer.cargo.buildScripts.enable#`.
---
-[[rust-analyzer.procMacro.ignored]]rust-analyzer.procMacro.ignored (default: `{}`)::
-+
---
-These proc-macros will be ignored when trying to expand them.
-
-This config takes a map of crate names with the exported proc-macro names to ignore as values.
---
-[[rust-analyzer.procMacro.server]]rust-analyzer.procMacro.server (default: `null`)::
-+
---
-Internal config, path to proc-macro server executable.
---
-[[rust-analyzer.references.excludeImports]]rust-analyzer.references.excludeImports (default: `false`)::
-+
---
-Exclude imports from find-all-references.
---
-[[rust-analyzer.references.excludeTests]]rust-analyzer.references.excludeTests (default: `false`)::
-+
---
-Exclude tests from find-all-references and call-hierarchy.
---
-[[rust-analyzer.runnables.command]]rust-analyzer.runnables.command (default: `null`)::
-+
---
-Command to be executed instead of 'cargo' for runnables.
---
-[[rust-analyzer.runnables.extraArgs]]rust-analyzer.runnables.extraArgs (default: `[]`)::
-+
---
-Additional arguments to be passed to cargo for runnables such as
-tests or binaries. For example, it may be `--release`.
---
-[[rust-analyzer.runnables.extraTestBinaryArgs]]rust-analyzer.runnables.extraTestBinaryArgs::
-+
---
-Default:
-----
-[
-  "--show-output"
-]
-----
-Additional arguments to be passed through Cargo to launched tests, benchmarks, or
-doc-tests.
-
-Unless the launched target uses a
-[custom test harness](https://doc.rust-lang.org/cargo/reference/cargo-targets.html#the-harness-field),
-they will end up being interpreted as options to
-[`rustc`’s built-in test harness (“libtest”)](https://doc.rust-lang.org/rustc/tests/index.html#cli-arguments).
-
---
-[[rust-analyzer.rustc.source]]rust-analyzer.rustc.source (default: `null`)::
-+
---
-Path to the Cargo.toml of the rust compiler workspace, for usage in rustc_private
-projects, or "discover" to try to automatically find it if the `rustc-dev` component
-is installed.
-
-Any project which uses rust-analyzer with the rustcPrivate
-crates must set `[package.metadata.rust-analyzer] rustc_private=true` to use it.
-
-This option does not take effect until rust-analyzer is restarted.
---
-[[rust-analyzer.rustfmt.extraArgs]]rust-analyzer.rustfmt.extraArgs (default: `[]`)::
-+
---
-Additional arguments to `rustfmt`.
---
-[[rust-analyzer.rustfmt.overrideCommand]]rust-analyzer.rustfmt.overrideCommand (default: `null`)::
-+
---
-Advanced option, fully override the command rust-analyzer uses for
-formatting. This should be the equivalent of `rustfmt` here, and
-not that of `cargo fmt`. The file contents will be passed on the
-standard input and the formatted result will be read from the
-standard output.
---
-[[rust-analyzer.rustfmt.rangeFormatting.enable]]rust-analyzer.rustfmt.rangeFormatting.enable (default: `false`)::
-+
---
-Enables the use of rustfmt's unstable range formatting command for the
-`textDocument/rangeFormatting` request. The rustfmt option is unstable and only
-available on a nightly build.
---
-[[rust-analyzer.semanticHighlighting.doc.comment.inject.enable]]rust-analyzer.semanticHighlighting.doc.comment.inject.enable (default: `true`)::
-+
---
-Inject additional highlighting into doc comments.
-
-When enabled, rust-analyzer will highlight rust source in doc comments as well as intra
-doc links.
---
-[[rust-analyzer.semanticHighlighting.nonStandardTokens]]rust-analyzer.semanticHighlighting.nonStandardTokens (default: `true`)::
-+
---
-Whether the server is allowed to emit non-standard tokens and modifiers.
---
-[[rust-analyzer.semanticHighlighting.operator.enable]]rust-analyzer.semanticHighlighting.operator.enable (default: `true`)::
-+
---
-Use semantic tokens for operators.
-
-When disabled, rust-analyzer will emit semantic tokens only for operator tokens when
-they are tagged with modifiers.
---
-[[rust-analyzer.semanticHighlighting.operator.specialization.enable]]rust-analyzer.semanticHighlighting.operator.specialization.enable (default: `false`)::
-+
---
-Use specialized semantic tokens for operators.
-
-When enabled, rust-analyzer will emit special token types for operator tokens instead
-of the generic `operator` token type.
---
-[[rust-analyzer.semanticHighlighting.punctuation.enable]]rust-analyzer.semanticHighlighting.punctuation.enable (default: `false`)::
-+
---
-Use semantic tokens for punctuation.
-
-When disabled, rust-analyzer will emit semantic tokens only for punctuation tokens when
-they are tagged with modifiers or have a special role.
---
-[[rust-analyzer.semanticHighlighting.punctuation.separate.macro.bang]]rust-analyzer.semanticHighlighting.punctuation.separate.macro.bang (default: `false`)::
-+
---
-When enabled, rust-analyzer will emit a punctuation semantic token for the `!` of macro
-calls.
---
-[[rust-analyzer.semanticHighlighting.punctuation.specialization.enable]]rust-analyzer.semanticHighlighting.punctuation.specialization.enable (default: `false`)::
-+
---
-Use specialized semantic tokens for punctuation.
-
-When enabled, rust-analyzer will emit special token types for punctuation tokens instead
-of the generic `punctuation` token type.
---
-[[rust-analyzer.semanticHighlighting.strings.enable]]rust-analyzer.semanticHighlighting.strings.enable (default: `true`)::
-+
---
-Use semantic tokens for strings.
-
-In some editors (e.g. vscode) semantic tokens override other highlighting grammars.
-By disabling semantic tokens for strings, other grammars can be used to highlight
-their contents.
---
-[[rust-analyzer.signatureInfo.detail]]rust-analyzer.signatureInfo.detail (default: `"full"`)::
-+
---
-Show full signature of the callable. Only shows parameters if disabled.
---
-[[rust-analyzer.signatureInfo.documentation.enable]]rust-analyzer.signatureInfo.documentation.enable (default: `true`)::
-+
---
-Show documentation.
---
-[[rust-analyzer.typing.triggerChars]]rust-analyzer.typing.triggerChars (default: `"=."`)::
-+
---
-Specify the characters allowed to invoke special on typing triggers.
-- typing `=` after `let` tries to smartly add `;` if `=` is followed by an existing expression
-- typing `=` between two expressions adds `;` when in statement position
-- typing `=` to turn an assignment into an equality comparison removes `;` when in expression position
-- typing `.` in a chain method call auto-indents
-- typing `{` or `(` in front of an expression inserts a closing `}` or `)` after the expression
-- typing `{` in a use item adds a closing `}` in the right place
-- typing `>` to complete a return type `->` will insert a whitespace after it
-- typing `<` in a path or type position inserts a closing `>` after the path or type.
---
-[[rust-analyzer.vfs.extraIncludes]]rust-analyzer.vfs.extraIncludes (default: `[]`)::
-+
---
-Additional paths to include in the VFS. Generally for code that is
-generated or otherwise managed by a build system outside of Cargo,
-though Cargo might be the eventual consumer.
---
-[[rust-analyzer.workspace.discoverConfig]]rust-analyzer.workspace.discoverConfig (default: `null`)::
-+
---
-Enables automatic discovery of projects using [`DiscoverWorkspaceConfig::command`].
-
-[`DiscoverWorkspaceConfig`] also requires setting `progress_label` and `files_to_watch`.
-`progress_label` is used for the title in progress indicators, whereas `files_to_watch`
-is used to determine which build system-specific files should be watched in order to
-reload rust-analyzer.
-
-Below is an example of a valid configuration:
-```json
-"rust-analyzer.workspace.discoverConfig": {
-    "command": [
-        "rust-project",
-        "develop-json"
-    ],
-    "progressLabel": "rust-analyzer",
-    "filesToWatch": [
-        "BUCK"
-    ]
-}
-```
-
-## On `DiscoverWorkspaceConfig::command`
-
-**Warning**: This format is provisional and subject to change.
-
-[`DiscoverWorkspaceConfig::command`] *must* return a JSON object
-corresponding to `DiscoverProjectData::Finished`:
-
-```norun
-#[derive(Debug, Clone, Deserialize, Serialize)]
-#[serde(tag = "kind")]
-#[serde(rename_all = "snake_case")]
-enum DiscoverProjectData {
-    Finished { buildfile: Utf8PathBuf, project: ProjectJsonData },
-    Error { error: String, source: Option<String> },
-    Progress { message: String },
-}
-```
-
-As JSON, `DiscoverProjectData::Finished` is:
-
-```json
-{
-    // the internally-tagged representation of the enum.
-    "kind": "finished",
-    // the file used by a non-Cargo build system to define
-    // a package or target.
-    "buildfile": "rust-analyzer/BUILD",
-    // the contents of a rust-project.json, elided for brevity
-    "project": {
-        "sysroot": "foo",
-        "crates": []
-    }
-}
-```
-
-It is encouraged, but not required, to use the other variants on
-`DiscoverProjectData` to provide a more polished end-user experience.
-
-`DiscoverWorkspaceConfig::command` may *optionally* include an `{arg}`,
-which will be substituted with the JSON-serialized form of the following
-enum:
-
-```norun
-#[derive(PartialEq, Clone, Debug, Serialize)]
-#[serde(rename_all = "camelCase")]
-pub enum DiscoverArgument {
-   Path(AbsPathBuf),
-   Buildfile(AbsPathBuf),
-}
-```
-
-The JSON representation of `DiscoverArgument::Path` is:
-
-```json
-{
-    "path": "src/main.rs"
-}
-```
-
-Similarly, the JSON representation of `DiscoverArgument::Buildfile` is:
-
-```
-{
-    "buildfile": "BUILD"
-}
-```
-
-`DiscoverArgument::Path` is used to find and generate a `rust-project.json`,
-and therefore, a workspace, whereas `DiscoverArgument::buildfile` is used to
-to update an existing workspace. As a reference for implementors,
-buck2's `rust-project` will likely be useful:
-https://github.com/facebook/buck2/tree/main/integrations/rust-project.
---
-[[rust-analyzer.workspace.symbol.search.kind]]rust-analyzer.workspace.symbol.search.kind (default: `"only_types"`)::
-+
---
-Workspace symbol search kind.
---
-[[rust-analyzer.workspace.symbol.search.limit]]rust-analyzer.workspace.symbol.search.limit (default: `128`)::
-+
---
-Limits the number of items returned from a workspace symbol search (Defaults to 128).
-Some clients like vs-code issue new searches on result filtering and don't require all results to be returned in the initial search.
-Other clients requires all results upfront and might require a higher limit.
---
-[[rust-analyzer.workspace.symbol.search.scope]]rust-analyzer.workspace.symbol.search.scope (default: `"workspace"`)::
-+
---
-Workspace symbol search scope.
---
diff --git a/src/tools/rust-analyzer/docs/user/manual.adoc b/src/tools/rust-analyzer/docs/user/manual.adoc
deleted file mode 100644
index 4a2a6f2e368..00000000000
--- a/src/tools/rust-analyzer/docs/user/manual.adoc
+++ /dev/null
@@ -1,1121 +0,0 @@
-= User Manual
-:toc: preamble
-:sectanchors:
-:page-layout: post
-:icons: font
-:source-highlighter: rouge
-:experimental:
-
-////
-IMPORTANT: the master copy of this document lives in the https://github.com/rust-lang/rust-analyzer repository
-////
-
-At its core, rust-analyzer is a *library* for semantic analysis of Rust code as it changes over time.
-This manual focuses on a specific usage of the library -- running it as part of a server that implements the
-https://microsoft.github.io/language-server-protocol/[Language Server Protocol] (LSP).
-The LSP allows various code editors, like VS Code, Emacs or Vim, to implement semantic features like completion or goto definition by talking to an external language server process.
-
-[TIP]
-====
-[.lead]
-To improve this document, send a pull request: +
-https://github.com/rust-lang/rust-analyzer/blob/master/docs/user/manual.adoc[https://github.com/rust-analyzer/.../manual.adoc]
-
-The manual is written in https://asciidoc.org[AsciiDoc] and includes some extra files which are generated from the source code. Run `cargo test` and `cargo xtask codegen` to create these and then `asciidoctor manual.adoc` to create an HTML copy.
-====
-
-If you have questions about using rust-analyzer, please ask them in the https://users.rust-lang.org/c/ide/14["`IDEs and Editors`"] topic of Rust users forum.
-
-== Installation
-
-In theory, one should be able to just install the <<rust-analyzer-language-server-binary,`rust-analyzer` binary>> and have it automatically work with any editor.
-We are not there yet, so some editor specific setup is required.
-
-Additionally, rust-analyzer needs the sources of the standard library.
-If the source code is not present, rust-analyzer will attempt to install it automatically.
-
-To add the sources manually, run the following command:
-
-```bash
-$ rustup component add rust-src
-```
-
-=== Toolchain
-
-Only the latest stable standard library source is officially supported for use with rust-analyzer.
-If you are using an older toolchain or have an override set, rust-analyzer may fail to understand the Rust source.
-You will either need to update your toolchain or use an older version of rust-analyzer that is compatible with your toolchain.
-
-If you are using an override in your project, you can still force rust-analyzer to use the stable toolchain via the environment variable `RUSTUP_TOOLCHAIN`.
-For example, with VS Code or coc-rust-analyzer:
-
-[source,json]
-----
-{ "rust-analyzer.server.extraEnv": { "RUSTUP_TOOLCHAIN": "stable" } }
-----
-
-=== VS Code
-
-This is the best supported editor at the moment.
-The rust-analyzer plugin for VS Code is maintained
-https://github.com/rust-lang/rust-analyzer/tree/master/editors/code[in tree].
-
-You can install the latest release of the plugin from
-https://marketplace.visualstudio.com/items?itemName=rust-lang.rust-analyzer[the marketplace].
-
-Note that the plugin may cause conflicts with the
-https://marketplace.visualstudio.com/items?itemName=rust-lang.rust[previous official Rust plugin].
-The latter is no longer maintained and should be uninstalled.
-
-The server binary is stored in the extension install directory, which starts with `rust-lang.rust-analyzer-` and is located under:
-
-* Linux: `~/.vscode/extensions`
-* Linux (Remote, such as WSL): `~/.vscode-server/extensions`
-* macOS: `~/.vscode/extensions`
-* Windows: `%USERPROFILE%\.vscode\extensions`
-
-As an exception, on NixOS, the extension makes a copy of the server and stores it under `~/.config/Code/User/globalStorage/rust-lang.rust-analyzer`.
-
-Note that we only support the two most recent versions of VS Code.
-
-==== Updates
-
-The extension will be updated automatically as new versions become available.
-It will ask your permission to download the matching language server version binary if needed.
-
-===== Nightly
-
-We ship nightly releases for VS Code.
-To help us out by testing the newest code, you can enable pre-release versions in the Code extension page.
-
-==== Manual installation
-
-Alternatively, download a VSIX corresponding to your platform from the
-https://github.com/rust-lang/rust-analyzer/releases[releases] page.
-
-Install the extension with the `Extensions: Install from VSIX` command within VS Code, or from the command line via:
-[source]
-----
-$ code --install-extension /path/to/rust-analyzer.vsix
-----
-
-If you are running an unsupported platform, you can install `rust-analyzer-no-server.vsix` and compile or obtain a server binary.
-Copy the server anywhere, then add the path to your settings.json, for example:
-[source,json]
-----
-{ "rust-analyzer.server.path": "~/.local/bin/rust-analyzer-linux" }
-----
-
-==== Building From Source
-
-Both the server and the Code plugin can be installed from source:
-
-[source]
-----
-$ git clone https://github.com/rust-lang/rust-analyzer.git && cd rust-analyzer
-$ cargo xtask install
-----
-
-You'll need Cargo, nodejs (matching a supported version of VS Code) and npm for this.
-
-Note that installing via `xtask install` does not work for VS Code Remote, instead you'll need to install the `.vsix` manually.
-
-If you're not using Code, you can compile and install only the LSP server:
-
-[source]
-----
-$ cargo xtask install --server
-----
-
-Make sure that `.cargo/bin` is in `$PATH` and precedes paths where `rust-analyzer` may also be installed.
-Specifically, `rustup` includes a proxy called `rust-analyzer`, which can cause problems if you're planning to use a source build or even a downloaded binary.
-
-=== rust-analyzer Language Server Binary
-
-Other editors generally require the `rust-analyzer` binary to be in `$PATH`.
-You can download pre-built binaries from the https://github.com/rust-lang/rust-analyzer/releases[releases] page.
-You will need to uncompress and rename the binary for your platform, e.g. from `rust-analyzer-aarch64-apple-darwin.gz` on Mac OS to `rust-analyzer`, make it executable, then move it into a directory in your `$PATH`.
-
-On Linux to install the `rust-analyzer` binary into `~/.local/bin`, these commands should work:
-
-[source,bash]
-----
-$ mkdir -p ~/.local/bin
-$ curl -L https://github.com/rust-lang/rust-analyzer/releases/latest/download/rust-analyzer-x86_64-unknown-linux-gnu.gz | gunzip -c - > ~/.local/bin/rust-analyzer
-$ chmod +x ~/.local/bin/rust-analyzer
-----
-
-Make sure that `~/.local/bin` is listed in the `$PATH` variable and use the appropriate URL if you're not on a `x86-64` system.
-
-You don't have to use `~/.local/bin`, any other path like `~/.cargo/bin` or `/usr/local/bin` will work just as well.
-
-Alternatively, you can install it from source using the command below.
-You'll need the latest stable version of the Rust toolchain.
-
-[source,bash]
-----
-$ git clone https://github.com/rust-lang/rust-analyzer.git && cd rust-analyzer
-$ cargo xtask install --server
-----
-
-If your editor can't find the binary even though the binary is on your `$PATH`, the likely explanation is that it doesn't see the same `$PATH` as the shell, see https://github.com/rust-lang/rust-analyzer/issues/1811[this issue].
-On Unix, running the editor from a shell or changing the `.desktop` file to set the environment should help.
-
-==== rustup
-
-`rust-analyzer` is available in `rustup`:
-
-[source,bash]
-----
-$ rustup component add rust-analyzer
-----
-
-==== Arch Linux
-
-The `rust-analyzer` binary can be installed from the repos or AUR (Arch User Repository):
-
-- https://www.archlinux.org/packages/extra/x86_64/rust-analyzer/[`rust-analyzer`] (built from latest tagged source)
-- https://aur.archlinux.org/packages/rust-analyzer-git[`rust-analyzer-git`] (latest Git version)
-
-Install it with pacman, for example:
-
-[source,bash]
-----
-$ pacman -S rust-analyzer
-----
-
-==== Gentoo Linux
-
-There are two ways to install `rust-analyzer` under Gentoo:
-
-- when installing `dev-lang/rust` or `dev-lang/rust-bin`, enable the `rust-analyzer` and `rust-src` USE flags
-- use the `rust-analyzer` component in `rustup` (see instructions above)
-
-Note that in both cases, the version installed lags for a couple of months behind the official releases on GitHub.
-To obtain a newer one, you can download a binary from GitHub Releases or building from source.
-
-==== macOS
-
-The `rust-analyzer` binary can be installed via https://brew.sh/[Homebrew].
-
-[source,bash]
-----
-$ brew install rust-analyzer
-----
-
-==== Windows
-
-It is recommended to install the latest Microsoft Visual C++ Redistributable prior to installation.
-Download links can be found
-https://learn.microsoft.com/en-us/cpp/windows/latest-supported-vc-redist[here].
-
-=== VS Code or VSCodium in Flatpak
-
-Setting up `rust-analyzer` with a Flatpak version of Code is not trivial because of the Flatpak sandbox.
-While the sandbox can be disabled for some directories, `/usr/bin` will always be mounted under `/run/host/usr/bin`.
-This prevents access to the system's C compiler, a system-wide installation of Rust, or any other libraries you might want to link to.
-Some compilers and libraries can be acquired as Flatpak SDKs, such as `org.freedesktop.Sdk.Extension.rust-stable` or `org.freedesktop.Sdk.Extension.llvm15`.
-
-If you use a Flatpak SDK for Rust, it must be in your `PATH`:
-
- * install the SDK extensions with `flatpak install org.freedesktop.Sdk.Extension.{llvm15,rust-stable}//23.08`
- * enable SDK extensions in the editor with the environment variable `FLATPAK_ENABLE_SDK_EXT=llvm15,rust-stable` (this can be done using flatseal or `flatpak override`)
-
-If you want to use Flatpak in combination with `rustup`, the following steps might help:
-
- - both Rust and `rustup` have to be installed using https://rustup.rs. Distro packages _will not_ work.
- - you need to launch Code, open a terminal and run `echo $PATH`
- - using https://flathub.org/apps/details/com.github.tchx84.Flatseal[Flatseal], you must add an environment variable called `PATH`.
-   Set its value to the output from above, appending `:~/.cargo/bin`, where `~` is the path to your home directory.
-   You must replace `~`, as it won't be expanded otherwise.
- - while Flatseal is open, you must enable access to "All user files"
-
-A C compiler should already be available via `org.freedesktop.Sdk`.
-Any other tools or libraries you will need to acquire  from Flatpak.
-
-=== Emacs
-
-Prerequisites: You have installed the <<rust-analyzer-language-server-binary,`rust-analyzer` binary>>.
-
-To use `rust-analyzer`, you need to install and enable one of the two popular LSP client implementations for Emacs, https://github.com/joaotavora/eglot[Eglot] or https://github.com/emacs-lsp/lsp-mode[LSP Mode]. Both enable `rust-analyzer` by default in rust buffers if it is available.
-
-==== Eglot
-
-Eglot is the more minimalistic and lightweight LSP client for Emacs, integrates well with existing Emacs functionality and is built into Emacs starting from release 29.
-
-After installing Eglot, e.g. via `M-x package-install` (not needed from Emacs 29), you can enable it via the `M-x eglot` command or load it automatically in `rust-mode` via
-
-[source,emacs-lisp]
-----
-(add-hook 'rust-mode-hook 'eglot-ensure)
-----
-
-To enable clippy, you will need to configure the initialization options to pass the `check.command` setting.
-
-[source,emacs-lisp]
-----
-(add-to-list 'eglot-server-programs
-             '((rust-ts-mode rust-mode) .
-               ("rust-analyzer" :initializationOptions (:check (:command "clippy")))))
-----
-
-For more detailed instructions and options see the https://joaotavora.github.io/eglot[Eglot manual] (also available from Emacs via `M-x info`) and the
-https://github.com/joaotavora/eglot/blob/master/README.md[Eglot readme].
-
-Eglot does not support the rust-analyzer extensions to the language-server protocol and does not aim to do so in the future. The https://github.com/nemethf/eglot-x#rust-analyzer-extensions[eglot-x] package adds experimental support for those LSP extensions.
-
-==== LSP Mode
-
-LSP-mode is the original LSP-client for emacs. Compared to Eglot it has a larger codebase and supports more features, like LSP protocol extensions.
-With extension packages like https://github.com/emacs-lsp/lsp-mode[LSP UI] it offers a lot of visual eyecandy.
-Further it integrates well with https://github.com/emacs-lsp/dap-mode[DAP mode] for support of the Debug Adapter Protocol.
-
-You can install LSP-mode via `M-x package-install` and then run it via the `M-x lsp` command or load it automatically in rust buffers with
-
-[source,emacs-lisp]
-----
-(add-hook 'rust-mode-hook 'lsp-deferred)
-----
-
-For more information on how to set up LSP mode and its extension package see the instructions in the https://emacs-lsp.github.io/lsp-mode/page/installation[LSP mode manual].
-Also see the https://emacs-lsp.github.io/lsp-mode/page/lsp-rust-analyzer/[rust-analyzer section] for `rust-analyzer` specific options and commands, which you can optionally bind to keys.
-
-Note the excellent https://robert.kra.hn/posts/2021-02-07_rust-with-emacs/[guide] from https://github.com/rksm[@rksm] on how to set-up Emacs for Rust development with LSP mode and several other packages.
-
-=== Vim/Neovim
-
-Prerequisites: You have installed the <<rust-analyzer-language-server-binary,`rust-analyzer` binary>>.
-Not needed if the extension can install/update it on its own, coc-rust-analyzer is one example.
-
-There are several LSP client implementations for Vim or Neovim:
-
-==== coc-rust-analyzer
-
-1. Install coc.nvim by following the instructions at
-   https://github.com/neoclide/coc.nvim[coc.nvim]
-   (Node.js required)
-2. Run `:CocInstall coc-rust-analyzer` to install
-   https://github.com/fannheyward/coc-rust-analyzer[coc-rust-analyzer],
-   this extension implements _most_ of the features supported in the VSCode extension:
-   * automatically install and upgrade stable/nightly releases
-   * same configurations as VSCode extension, `rust-analyzer.server.path`, `rust-analyzer.cargo.features` etc.
-   * same commands too, `rust-analyzer.analyzerStatus`, `rust-analyzer.ssr` etc.
-   * inlay hints for variables and method chaining, _Neovim Only_
-
-Note: for code actions, use `coc-codeaction-cursor` and `coc-codeaction-selected`; `coc-codeaction` and `coc-codeaction-line` are unlikely to be useful.
-
-==== LanguageClient-neovim
-
-1. Install LanguageClient-neovim by following the instructions
-   https://github.com/autozimu/LanguageClient-neovim[here]
-   * The GitHub project wiki has extra tips on configuration
-
-2. Configure by adding this to your Vim/Neovim config file (replacing the existing Rust-specific line if it exists):
-+
-[source,vim]
-----
-let g:LanguageClient_serverCommands = {
-\ 'rust': ['rust-analyzer'],
-\ }
-----
-
-==== YouCompleteMe
-
-Install YouCompleteMe by following the instructions
-  https://github.com/ycm-core/YouCompleteMe#installation[here].
-
-rust-analyzer is the default in ycm, it should work out of the box.
-
-==== ALE
-
-To use the LSP server in https://github.com/dense-analysis/ale[ale]:
-
-[source,vim]
-----
-let g:ale_linters = {'rust': ['analyzer']}
-----
-
-==== nvim-lsp
-
-Neovim 0.5 has built-in language server support.
-For a quick start configuration of rust-analyzer, use https://github.com/neovim/nvim-lspconfig#rust_analyzer[neovim/nvim-lspconfig].
-Once `neovim/nvim-lspconfig` is installed, use `+lua require'lspconfig'.rust_analyzer.setup({})+` in your `init.vim`.
-
-You can also pass LSP settings to the server:
-
-[source,vim]
-----
-lua << EOF
-local lspconfig = require'lspconfig'
-
-local on_attach = function(client)
-    require'completion'.on_attach(client)
-end
-
-lspconfig.rust_analyzer.setup({
-    on_attach = on_attach,
-    settings = {
-        ["rust-analyzer"] = {
-            imports = {
-                granularity = {
-                    group = "module",
-                },
-                prefix = "self",
-            },
-            cargo = {
-                buildScripts = {
-                    enable = true,
-                },
-            },
-            procMacro = {
-                enable = true
-            },
-        }
-    }
-})
-EOF
-----
-
-If you're running Neovim 0.10 or later, you can enable inlay hints via `on_attach`:
-
-[source,vim]
-----
-lspconfig.rust_analyzer.setup({
-    on_attach = function(client, bufnr)
-        vim.lsp.inlay_hint.enable(true, { bufnr = bufnr })
-    end
-})
-----
-
-Note that the hints are only visible after `rust-analyzer` has finished loading **and** you have to edit the file to trigger a re-render.
-
-See https://sharksforarms.dev/posts/neovim-rust/ for more tips on getting started.
-
-Check out https://github.com/mrcjkb/rustaceanvim for a batteries included rust-analyzer setup for Neovim.
-
-==== vim-lsp
-
-vim-lsp is installed by following https://github.com/prabirshrestha/vim-lsp[the plugin instructions].
-It can be as simple as adding this line to your `.vimrc`:
-
-[source,vim]
-----
-Plug 'prabirshrestha/vim-lsp'
-----
-
-Next you need to register the `rust-analyzer` binary.
-If it is available in `$PATH`, you may want to add this to your `.vimrc`:
-
-[source,vim]
-----
-if executable('rust-analyzer')
-  au User lsp_setup call lsp#register_server({
-        \   'name': 'Rust Language Server',
-        \   'cmd': {server_info->['rust-analyzer']},
-        \   'whitelist': ['rust'],
-        \ })
-endif
-----
-
-There is no dedicated UI for the server configuration, so you would need to send any options as a value of the `initialization_options` field, as described in the <<configuration,Configuration>> section.
-Here is an example of how to enable the proc-macro support:
-
-[source,vim]
-----
-if executable('rust-analyzer')
-  au User lsp_setup call lsp#register_server({
-        \   'name': 'Rust Language Server',
-        \   'cmd': {server_info->['rust-analyzer']},
-        \   'whitelist': ['rust'],
-        \   'initialization_options': {
-        \     'cargo': {
-        \       'buildScripts': {
-        \         'enable': v:true,
-        \       },
-        \     },
-        \     'procMacro': {
-        \       'enable': v:true,
-        \     },
-        \   },
-        \ })
-endif
-----
-
-=== Sublime Text
-
-==== Sublime Text 4:
-* Follow the instructions in link:https://github.com/sublimelsp/LSP-rust-analyzer[LSP-rust-analyzer].
-
-NOTE: Install link:https://packagecontrol.io/packages/LSP-file-watcher-chokidar[LSP-file-watcher-chokidar] to enable file watching (`workspace/didChangeWatchedFiles`).
-
-==== Sublime Text 3:
-* Install the <<rust-analyzer-language-server-binary,`rust-analyzer` binary>>.
-* Install the link:https://packagecontrol.io/packages/LSP[LSP package].
-* From the command palette, run `LSP: Enable Language Server Globally` and select `rust-analyzer`.
-
-If it worked, you should see "rust-analyzer, Line X, Column Y" on the left side of the status bar, and after waiting a bit, functionalities like tooltips on hovering over variables should become available.
-
-If you get an error saying `No such file or directory: 'rust-analyzer'`, see the <<rust-analyzer-language-server-binary,`rust-analyzer` binary>> section on installing the language server binary.
-
-=== GNOME Builder
-
-GNOME Builder 3.37.1 and newer has native `rust-analyzer` support.
-If the LSP binary is not available, GNOME Builder can install it when opening a Rust file.
-
-
-=== Eclipse IDE
-
-Support for Rust development in the Eclipse IDE is provided by link:https://github.com/eclipse/corrosion[Eclipse Corrosion].
-If available in PATH or in some standard location, `rust-analyzer` is detected and powers editing of Rust files without further configuration.
-If `rust-analyzer` is not detected, Corrosion will prompt you for configuration of your Rust toolchain and language server with a link to the __Window > Preferences > Rust__ preference page; from here a button allows to download and configure `rust-analyzer`, but you can also reference another installation.
-You'll need to close and reopen all .rs and Cargo files, or to restart the IDE, for this change to take effect.
-
-=== Kate Text Editor
-
-Support for the language server protocol is built into Kate through the LSP plugin, which is included by default.
-It is preconfigured to use rust-analyzer for Rust sources since Kate 21.12.
-
-To change rust-analyzer config options, start from the following example and put it into Kate's "User Server Settings" tab (located under the LSP Client settings):
-[source,json]
-----
-{
-    "servers": {
-        "rust": {
-            "initializationOptions": {
-                "cachePriming": {
-                    "enable": false
-                },
-                "check": {
-                    "allTargets": false
-                },
-                "checkOnSave": false
-            }
-        }
-    }
-}
-----
-Then click on apply, and restart the LSP server for your rust project.
-
-=== juCi++
-
-https://gitlab.com/cppit/jucipp[juCi++] has built-in support for the language server protocol, and since version 1.7.0 offers installation of both Rust and rust-analyzer when opening a Rust file.
-
-=== Kakoune
-
-https://kakoune.org/[Kakoune] supports LSP with the help of https://github.com/kak-lsp/kak-lsp[`kak-lsp`].
-Follow the https://github.com/kak-lsp/kak-lsp#installation[instructions] to install `kak-lsp`.
-To configure `kak-lsp`, refer to the https://github.com/kak-lsp/kak-lsp#configuring-kak-lsp[configuration section] which is basically about copying the https://github.com/kak-lsp/kak-lsp/blob/master/kak-lsp.toml[configuration file] in the right place (latest versions should use `rust-analyzer` by default).
-
-Finally, you need to configure Kakoune to talk to `kak-lsp` (see https://github.com/kak-lsp/kak-lsp#usage[Usage section]).
-A basic configuration will only get you LSP but you can also activate inlay diagnostics and auto-formatting on save.
-The following might help you get all of this.
-
-[source,txt]
-----
-eval %sh{kak-lsp --kakoune -s $kak_session}  # Not needed if you load it with plug.kak.
-hook global WinSetOption filetype=rust %{
-    # Enable LSP
-    lsp-enable-window
-
-    # Auto-formatting on save
-    hook window BufWritePre .* lsp-formatting-sync
-
-    # Configure inlay hints (only on save)
-    hook window -group rust-inlay-hints BufWritePost .* rust-analyzer-inlay-hints
-    hook -once -always window WinSetOption filetype=.* %{
-        remove-hooks window rust-inlay-hints
-    }
-}
-----
-
-=== Helix
-
-https://docs.helix-editor.com/[Helix] supports LSP by default.
-However, it won't install `rust-analyzer` automatically.
-You can follow instructions for installing <<rust-analyzer-language-server-binary,`rust-analyzer` binary>>.
-
-[#visual-studio]
-=== [[visual-studio-2022]]Visual Studio 2022
-
-There are multiple rust-analyzer extensions for Visual Studio 2022 on Windows:
-
-==== rust-analyzer.vs
-
-(License: Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International)
-
-https://marketplace.visualstudio.com/items?itemName=kitamstudios.RustAnalyzer[Visual Studio Marketplace]
-
-https://github.com/kitamstudios/rust-analyzer/[GitHub]
-
-Support for Rust development in the Visual Studio IDE is enabled by the link:https://marketplace.visualstudio.com/items?itemName=kitamstudios.RustAnalyzer[rust-analyzer] package. Either click on the download link or install from IDE's extension manager.
-For now link:https://visualstudio.microsoft.com/downloads/[Visual Studio 2022] is required. All editions are supported viz. Community, Professional & Enterprise.
-The package aims to provide 0-friction installation and therefore comes loaded with most things required including rust-analyzer binary. If anything it needs is missing, appropriate errors / warnings will guide the user. E.g. cargo.exe needs to be in path and the package will tell you as much.
-This package is under rapid active development. So if you encounter any issues please file it at link:https://github.com/kitamstudios/rust-analyzer/[rust-analyzer.vs].
-
-==== VS_RustAnalyzer
-
-(License: GPL)
-
-https://marketplace.visualstudio.com/items?itemName=cchharris.vsrustanalyzer[Visual Studio Marketplace]
-
-https://github.com/cchharris/VS-RustAnalyzer[GitHub]
-
-==== SourceGear Rust
-
-(License: closed source)
-
-https://marketplace.visualstudio.com/items?itemName=SourceGear.SourceGearRust[Visual Studio Marketplace]
-
-https://github.com/sourcegear/rust-vs-extension[GitHub (docs, issues, discussions)]
-
-* Free (no-cost)
-* Supports all editions of Visual Studio 2022 on Windows: Community, Professional, or Enterprise
-
-=== Lapce
-
-https://lapce.dev/[Lapce] has a Rust plugin which you can install directly.
-Unfortunately, it downloads an old version of `rust-analyzer`, but you can set the server path under Settings.
-
-=== Crates
-
-There is a package named `ra_ap_rust_analyzer` available on https://crates.io/crates/ra_ap_rust-analyzer[crates.io], for someone who wants to use it programmatically.
-
-For more details, see https://github.com/rust-lang/rust-analyzer/blob/master/.github/workflows/autopublish.yaml[the publish workflow].
-
-=== Zed
-
-https://zed.dev[Zed] has native `rust-analyzer` support.
-If the LSP binary is not available, Zed can install it when opening a Rust file.
-
-== Troubleshooting
-
-Start with looking at the rust-analyzer version.
-Try **rust-analyzer: Show RA Version** in VS Code (using **Command Palette** feature typically activated by Ctrl+Shift+P) or `rust-analyzer --version` in the command line.
-If the date is more than a week ago, it's better to update rust-analyzer version.
-
-The next thing to check would be panic messages in rust-analyzer's log.
-Log messages are printed to stderr, in VS Code you can see them in the `Output > Rust Analyzer Language Server` tab of the panel.
-To see more logs, set the `RA_LOG=info` environment variable, this can be done either by setting the environment variable manually or by using `rust-analyzer.server.extraEnv`, note that both of these approaches require the server to be restarted.
-
-To fully capture LSP messages between the editor and the server, run the `rust-analyzer: Toggle LSP Logs` command and check
-`Output > Rust Analyzer Language Server Trace`.
-
-The root cause for many "`nothing works`" problems is that rust-analyzer fails to understand the project structure.
-To debug that, first note the `rust-analyzer` section in the status bar.
-If it has an error icon and red, that's the problem (hover will have somewhat helpful error message).
-**rust-analyzer: Status** prints dependency information for the current file.
-Finally, `RA_LOG=project_model=debug` enables verbose logs during project loading.
-
-If rust-analyzer outright crashes, try running `rust-analyzer analysis-stats /path/to/project/directory/` on the command line.
-This command type checks the whole project in batch mode bypassing LSP machinery.
-
-When filing issues, it is useful (but not necessary) to try to minimize examples.
-An ideal bug reproduction looks like this:
-
-```bash
-$ git clone https://github.com/username/repo.git && cd repo && git switch --detach commit-hash
-$ rust-analyzer --version
-rust-analyzer dd12184e4 2021-05-08 dev
-$ rust-analyzer analysis-stats .
-💀 💀 💀
-```
-
-It is especially useful when the `repo` doesn't use external crates or the standard library.
-
-If you want to go as far as to modify the source code to debug the problem, be sure to take a look at the
-https://github.com/rust-lang/rust-analyzer/tree/master/docs/dev[dev docs]!
-
-== Configuration
-
-**Source:** https://github.com/rust-lang/rust-analyzer/blob/master/crates/rust-analyzer/src/config.rs[config.rs]
-
-The <<installation,Installation>> section contains details on configuration for some of the editors.
-In general `rust-analyzer` is configured via LSP messages, which means that it's up to the editor to decide on the exact format and location of configuration files.
-
-Some clients, such as <<vs-code,VS Code>> or <<coc-rust-analyzer,COC plugin in Vim>> provide `rust-analyzer` specific configuration UIs. Others may require you to know a bit more about the interaction with `rust-analyzer`.
-
-For the later category, it might help to know that the initial configuration is specified as a value of the `initializationOptions` field of the https://microsoft.github.io/language-server-protocol/specifications/specification-current/#initialize[`InitializeParams` message, in the LSP protocol].
-The spec says that the field type is `any?`, but `rust-analyzer` is looking for a JSON object that is constructed using settings from the list below.
-Name of the setting, ignoring the `rust-analyzer.` prefix, is used as a path, and value of the setting becomes the JSON property value.
-
-For example, a very common configuration is to enable proc-macro support, can be achieved by sending this JSON:
-
-[source,json]
-----
-{
-  "cargo": {
-    "buildScripts": {
-      "enable": true,
-    },
-  },
-  "procMacro": {
-    "enable": true,
-  }
-}
-----
-
-Please consult your editor's documentation to learn more about how to configure https://microsoft.github.io/language-server-protocol/[LSP servers].
-
-To verify which configuration is actually used by `rust-analyzer`, set `RA_LOG` environment variable to `rust_analyzer=info` and look for config-related messages.
-Logs should show both the JSON that `rust-analyzer` sees as well as the updated config.
-
-This is the list of config options `rust-analyzer` supports:
-
-include::./generated_config.adoc[]
-
-== Non-Cargo Based Projects
-
-rust-analyzer does not require Cargo.
-However, if you use some other build system, you'll have to describe the structure of your project for rust-analyzer in the `rust-project.json` format:
-
-[source,TypeScript]
-----
-interface JsonProject {
-    /// Path to the sysroot directory.
-    ///
-    /// The sysroot is where rustc looks for the
-    /// crates that are built-in to rust, such as
-    /// std.
-    ///
-    /// https://doc.rust-lang.org/rustc/command-line-arguments.html#--sysroot-override-the-system-root
-    ///
-    /// To see the current value of sysroot, you
-    /// can query rustc:
-    ///
-    /// ```
-    /// $ rustc --print sysroot
-    /// /Users/yourname/.rustup/toolchains/stable-x86_64-apple-darwin
-    /// ```
-    sysroot?: string;
-    /// Path to the directory with *source code* of
-    /// sysroot crates.
-    ///
-    /// By default, this is `lib/rustlib/src/rust/library`
-    /// relative to the sysroot.
-    ///
-    /// It should point to the directory where std,
-    /// core, and friends can be found:
-    ///
-    /// https://github.com/rust-lang/rust/tree/master/library.
-    ///
-    /// If provided, rust-analyzer automatically adds
-    /// dependencies on sysroot crates. Conversely,
-    /// if you omit this path, you can specify sysroot
-    /// dependencies yourself and, for example, have
-    /// several different "sysroots" in one graph of
-    /// crates.
-    sysroot_src?: string;
-    /// List of groups of common cfg values, to allow
-    /// sharing them between crates.
-    ///
-    /// Maps from group name to its cfgs. Cfg follow
-    /// the same format as `Crate.cfg`.
-    cfg_groups?: { [key: string]: string[]; };
-    /// The set of crates comprising the current
-    /// project. Must include all transitive
-    /// dependencies as well as sysroot crate (libstd,
-    /// libcore and such).
-    crates: Crate[];
-    /// Configuration for CLI commands.
-    ///
-    /// These are used for running and debugging binaries
-    /// and tests without encoding build system-specific
-    /// knowledge into rust-analyzer.
-    ///
-    /// # Example
-    ///
-    /// Below is an example of a test runnable. `{label}` and `{test_id}`
-    /// are explained in `Runnable::args`'s documentation below.
-    ///
-    /// ```json
-    /// {
-    ///     "program": "buck",
-    ///     "args": [
-    ///         "test",
-    ///          "{label}",
-    ///          "--",
-    ///          "{test_id}",
-    ///          "--print-passing-details"
-    ///     ],
-    ///     "cwd": "/home/user/repo-root/",
-    ///     "kind": "testOne"
-    /// }
-    /// ```
-    runnables?: Runnable[];
-}
-
-interface Crate {
-    /// Optional crate name used for display purposes,
-    /// without affecting semantics. See the `deps`
-    /// key for semantically-significant crate names.
-    display_name?: string;
-    /// Path to the root module of the crate.
-    root_module: string;
-    /// Edition of the crate.
-    edition: '2015' | '2018' | '2021' | '2024';
-    /// The version of the crate. Used for calculating
-    /// the correct docs.rs URL.
-    version?: string;
-    /// Dependencies
-    deps: Dep[];
-    /// Should this crate be treated as a member of
-    /// current "workspace".
-    ///
-    /// By default, inferred from the `root_module`
-    /// (members are the crates which reside inside
-    /// the directory opened in the editor).
-    ///
-    /// Set this to `false` for things like standard
-    /// library and 3rd party crates to enable
-    /// performance optimizations (rust-analyzer
-    /// assumes that non-member crates don't change).
-    is_workspace_member?: boolean;
-    /// Optionally specify the (super)set of `.rs`
-    /// files comprising this crate.
-    ///
-    /// By default, rust-analyzer assumes that only
-    /// files under `root_module.parent` can belong
-    /// to a crate. `include_dirs` are included
-    /// recursively, unless a subdirectory is in
-    /// `exclude_dirs`.
-    ///
-    /// Different crates can share the same `source`.
-    ///
-    /// If two crates share an `.rs` file in common,
-    /// they *must* have the same `source`.
-    /// rust-analyzer assumes that files from one
-    /// source can't refer to files in another source.
-    source?: {
-        include_dirs: string[];
-        exclude_dirs: string[];
-    };
-    /// List of cfg groups this crate inherits.
-    ///
-    /// All cfg in these groups will be concatenated to
-    /// `cfg`. It is impossible to replace a value from
-    /// the groups.
-    cfg_groups?: string[];
-    /// The set of cfgs activated for a given crate, like
-    /// `["unix", "feature=\"foo\"", "feature=\"bar\""]`.
-    cfg: string[];
-    /// Target tuple for this Crate.
-    ///
-    /// Used when running `rustc --print cfg`
-    /// to get target-specific cfgs.
-    target?: string;
-    /// Environment variables, used for
-    /// the `env!` macro
-    env: { [key: string]: string; };
-
-    /// Whether the crate is a proc-macro crate.
-    is_proc_macro: boolean;
-    /// For proc-macro crates, path to compiled
-    /// proc-macro (.so file).
-    proc_macro_dylib_path?: string;
-
-    /// Repository, matching the URL that would be used
-    /// in Cargo.toml.
-    repository?: string;
-
-    /// Build-specific data about this crate.
-    build?: BuildInfo;
-}
-
-interface Dep {
-    /// Index of a crate in the `crates` array.
-    crate: number;
-    /// Name as should appear in the (implicit)
-    /// `extern crate name` declaration.
-    name: string;
-}
-
-interface BuildInfo {
-    /// The name associated with this crate.
-    ///
-    /// This is determined by the build system that produced
-    /// the `rust-project.json` in question. For instance, if buck were used,
-    /// the label might be something like `//ide/rust/rust-analyzer:rust-analyzer`.
-    ///
-    /// Do not attempt to parse the contents of this string; it is a build system-specific
-    /// identifier similar to `Crate::display_name`.
-    label: string;
-    /// Path corresponding to the build system-specific file defining the crate.
-    build_file: string;
-    /// The kind of target.
-    ///
-    /// This information is used to determine what sort
-    /// of runnable codelens to provide, if any.
-    target_kind: 'bin' | 'lib' | 'test';
-}
-
-interface Runnable {
-    /// The program invoked by the runnable.
-    ///
-    /// For example, this might be `cargo`, `buck`, or `bazel`.
-    program: string;
-    /// The arguments passed to `program`.
-    args: string[];
-    /// The current working directory of the runnable.
-    cwd: string;
-    /// Used to decide what code lens to offer.
-    ///
-    /// `testOne`: This runnable will be used when the user clicks the 'Run Test'
-    /// CodeLens above a test.
-    ///
-    /// The args for testOne can contain two template strings:
-    /// `{label}` and `{test_id}`. `{label}` will be replaced
-    /// with the `Build::label` and `{test_id}` will be replaced
-    /// with the test name.
-    kind: 'testOne' | string;
-}
-----
-
-This format is provisional and subject to change.
-Specifically, the `roots` setup will be different eventually.
-
-There are three ways to feed `rust-project.json` to rust-analyzer:
-
-* Place `rust-project.json` file at the root of the project, and rust-analyzer will discover it.
-* Specify `"rust-analyzer.linkedProjects": [ "path/to/rust-project.json" ]` in the settings (and make sure that your LSP client sends settings as a part of initialize request).
-* Specify `"rust-analyzer.linkedProjects": [ { "roots": [...], "crates": [...] }]` inline.
-
-Relative paths are interpreted relative to `rust-project.json` file location or (for inline JSON) relative to `rootUri`.
-
-You can set the `RA_LOG` environment variable to `rust_analyzer=info` to inspect how rust-analyzer handles config and project loading.
-
-Note that calls to `cargo check` are disabled when using `rust-project.json` by default, so compilation errors and warnings will no longer be sent to your LSP client.
-To enable these compilation errors you will need to specify explicitly what command rust-analyzer should run to perform the checks using the `rust-analyzer.check.overrideCommand` configuration.
-As an example, the following configuration explicitly sets `cargo check` as the `check` command.
-
-[source,json]
-----
-{ "rust-analyzer.check.overrideCommand": ["cargo", "check", "--message-format=json"] }
-----
-
-`check.overrideCommand` requires the command specified to output json error messages for rust-analyzer to consume.
-The `--message-format=json` flag does this for `cargo check` so whichever command you use must also output errors in this format.
-See the <<Configuration>> section for more information.
-
-== Security
-
-At the moment, rust-analyzer assumes that all code is trusted.
-Here is a **non-exhaustive** list of ways to make rust-analyzer execute arbitrary code:
-
-* proc macros and build scripts are executed by default
-* `.cargo/config` can override `rustc` with an arbitrary executable
-* `rust-toolchain.toml` can override `rustc` with an arbitrary executable
-* VS Code plugin reads configuration from project directory, and that can be used to override paths to various executables, like `rustfmt` or `rust-analyzer` itself.
-* rust-analyzer's syntax trees library uses a lot of `unsafe` and hasn't been properly audited for memory safety.
-
-== Privacy
-
-The LSP server performs no network access in itself, but runs `cargo metadata` which will update or download the crate registry and the source code of the project dependencies.
-If enabled (the default), build scripts and procedural macros can do anything.
-
-The Code extension does not access the network.
-
-Any other editor plugins are not under the control of the `rust-analyzer` developers. For any privacy concerns, you should check with their respective developers.
-
-For `rust-analyzer` developers, `cargo xtask release` uses the GitHub API to put together the release notes.
-
-== Features
-
-include::./generated_features.adoc[]
-
-== Assists (Code Actions)
-
-Assists, or code actions, are small local refactorings, available in a particular context.
-They are usually triggered by a shortcut or by clicking a light bulb icon in the editor.
-Cursor position or selection is signified by `┃` character.
-
-include::./generated_assists.adoc[]
-
-== Diagnostics
-
-While most errors and warnings provided by rust-analyzer come from the `cargo check` integration, there's a growing number of diagnostics implemented using rust-analyzer's own analysis.
-Some of these diagnostics don't respect `\#[allow]` or `\#[deny]` attributes yet, but can be turned off using the `rust-analyzer.diagnostics.enable`, `rust-analyzer.diagnostics.experimental.enable` or `rust-analyzer.diagnostics.disabled` settings.
-
-=== Clippy
-
-To run `cargo clippy` instead of `cargo check`, you can set `"rust-analyzer.check.command": "clippy"`.
-
-include::./generated_diagnostic.adoc[]
-
-== Editor Features
-=== VS Code
-
-==== Color configurations
-
-It is possible to change the foreground/background color and font family/size of inlay hints.
-Just add this to your `settings.json`:
-
-[source,jsonc]
-----
-{
-  "editor.inlayHints.fontFamily": "Courier New",
-  "editor.inlayHints.fontSize": 11,
-
-  "workbench.colorCustomizations": {
-    // Name of the theme you are currently using
-    "[Default Dark+]": {
-      "editorInlayHint.foreground": "#868686f0",
-      "editorInlayHint.background": "#3d3d3d48",
-
-      // Overrides for specific kinds of inlay hints
-      "editorInlayHint.typeForeground": "#fdb6fdf0",
-      "editorInlayHint.parameterForeground": "#fdb6fdf0",
-    }
-  }
-}
-----
-
-==== Semantic style customizations
-
-You can customize the look of different semantic elements in the source code.
-For example, mutable bindings are underlined by default and you can override this behavior by adding the following section to your `settings.json`:
-
-[source,jsonc]
-----
-{
-  "editor.semanticTokenColorCustomizations": {
-    "rules": {
-      "*.mutable": {
-        "fontStyle": "", // underline is the default
-      },
-    }
-  },
-}
-----
-
-Most themes doesn't support styling unsafe operations differently yet. You can fix this by adding overrides for the rules `operator.unsafe`, `function.unsafe`, and `method.unsafe`:
-
-[source,jsonc]
-----
-{
-   "editor.semanticTokenColorCustomizations": {
-         "rules": {
-             "operator.unsafe": "#ff6600",
-             "function.unsafe": "#ff6600",
-             "method.unsafe": "#ff6600"
-         }
-    },
-}
-----
-
-In addition to the top-level rules you can specify overrides for specific themes. For example, if you wanted to use a darker text color on a specific light theme, you might write:
-
-[source,jsonc]
-----
-{
-   "editor.semanticTokenColorCustomizations": {
-         "rules": {
-             "operator.unsafe": "#ff6600"
-         },
-         "[Ayu Light]": {
-            "rules": {
-               "operator.unsafe": "#572300"
-            }
-         }
-    },
-}
-----
-
-Make sure you include the brackets around the theme name. For example, use `"[Ayu Light]"` to customize the theme Ayu Light.
-
-==== Special `when` clause context for keybindings.
-You may use `inRustProject` context to configure keybindings for rust projects only.
-For example:
-
-[source,json]
-----
-{
-  "key": "ctrl+alt+d",
-  "command": "rust-analyzer.openDocs",
-  "when": "inRustProject"
-}
-----
-More about `when` clause contexts https://code.visualstudio.com/docs/getstarted/keybindings#_when-clause-contexts[here].
-
-==== Setting runnable environment variables
-You can use "rust-analyzer.runnables.extraEnv" setting to define runnable environment-specific substitution variables.
-The simplest way for all runnables in a bunch:
-```jsonc
-"rust-analyzer.runnables.extraEnv": {
-    "RUN_SLOW_TESTS": "1"
-}
-```
-
-Or it is possible to specify vars more granularly:
-```jsonc
-"rust-analyzer.runnables.extraEnv": [
-    {
-        // "mask": null, // null mask means that this rule will be applied for all runnables
-        "env": {
-             "APP_ID": "1",
-             "APP_DATA": "asdf"
-        }
-    },
-    {
-        "mask": "test_name",
-        "env": {
-             "APP_ID": "2", // overwrites only APP_ID
-        }
-    }
-]
-```
-
-You can use any valid regular expression as a mask.
-Also note that a full runnable name is something like *run bin_or_example_name*, *test some::mod::test_name* or *test-mod some::mod*, so it is possible to distinguish binaries, single tests, and test modules with this masks: `"^run"`, `"^test "` (the trailing space matters!), and `"^test-mod"` respectively.
-
-If needed, you can set different values for different platforms:
-```jsonc
-"rust-analyzer.runnables.extraEnv": [
-    {
-        "platform": "win32", // windows only
-        "env": {
-             "APP_DATA": "windows specific data"
-        }
-    },
-    {
-        "platform": ["linux"],
-        "env": {
-             "APP_DATA": "linux data",
-        }
-    },
-    { // for all platforms
-        "env": {
-             "APP_COMMON_DATA": "xxx",
-        }
-    }
-]
-```
-
-==== Compiler feedback from external commands
-
-Instead of relying on the built-in `cargo check`, you can configure Code to run a command in the background and use the `$rustc-watch` problem matcher to generate inline error markers from its output.
-
-To do this you need to create a new https://code.visualstudio.com/docs/editor/tasks[VS Code Task] and set `"rust-analyzer.checkOnSave": false` in preferences.
-
-For example, if you want to run https://crates.io/crates/cargo-watch[`cargo watch`] instead, you might add the following to `.vscode/tasks.json`:
-
-```json
-{
-    "label": "Watch",
-    "group": "build",
-    "type": "shell",
-    "command": "cargo watch",
-    "problemMatcher": "$rustc-watch",
-    "isBackground": true
-}
-```
-
-==== Live Share
-
-VS Code Live Share has partial support for rust-analyzer.
-
-Live Share _requires_ the official Microsoft build of VS Code, OSS builds will not work correctly.
-
-The host's rust-analyzer instance will be shared with all guests joining the session.
-The guests do not have to have the rust-analyzer extension installed for this to work.
-
-If you are joining a Live Share session and _do_ have rust-analyzer installed locally, commands from the command palette will not work correctly since they will attempt to communicate with the local server.
diff --git a/src/tools/rust-analyzer/editors/code/package-lock.json b/src/tools/rust-analyzer/editors/code/package-lock.json
index 6027f813311..86a066454a5 100644
--- a/src/tools/rust-analyzer/editors/code/package-lock.json
+++ b/src/tools/rust-analyzer/editors/code/package-lock.json
@@ -23,7 +23,7 @@
                 "@typescript-eslint/parser": "^6.0.0",
                 "@vscode/test-electron": "^2.3.8",
                 "@vscode/vsce": "^3.0.0",
-                "esbuild": "^0.18.12",
+                "esbuild": "^0.25.0",
                 "eslint": "^8.44.0",
                 "eslint-config-prettier": "^8.8.0",
                 "ovsx": "^0.8.2",
@@ -256,356 +256,429 @@
                 "node": ">=16"
             }
         },
+        "node_modules/@esbuild/aix-ppc64": {
+            "version": "0.25.0",
+            "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.0.tgz",
+            "integrity": "sha512-O7vun9Sf8DFjH2UtqK8Ku3LkquL9SZL8OLY1T5NZkA34+wG3OQF7cl4Ql8vdNzM6fzBbYfLaiRLIOZ+2FOCgBQ==",
+            "cpu": [
+                "ppc64"
+            ],
+            "dev": true,
+            "license": "MIT",
+            "optional": true,
+            "os": [
+                "aix"
+            ],
+            "engines": {
+                "node": ">=18"
+            }
+        },
         "node_modules/@esbuild/android-arm": {
-            "version": "0.18.12",
-            "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.18.12.tgz",
-            "integrity": "sha512-LIxaNIQfkFZbTLb4+cX7dozHlAbAshhFE5PKdro0l+FnCpx1GDJaQ2WMcqm+ToXKMt8p8Uojk/MFRuGyz3V5Sw==",
+            "version": "0.25.0",
+            "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.0.tgz",
+            "integrity": "sha512-PTyWCYYiU0+1eJKmw21lWtC+d08JDZPQ5g+kFyxP0V+es6VPPSUhM6zk8iImp2jbV6GwjX4pap0JFbUQN65X1g==",
             "cpu": [
                 "arm"
             ],
             "dev": true,
+            "license": "MIT",
             "optional": true,
             "os": [
                 "android"
             ],
             "engines": {
-                "node": ">=12"
+                "node": ">=18"
             }
         },
         "node_modules/@esbuild/android-arm64": {
-            "version": "0.18.12",
-            "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.18.12.tgz",
-            "integrity": "sha512-BMAlczRqC/LUt2P97E4apTBbkvS9JTJnp2DKFbCwpZ8vBvXVbNdqmvzW/OsdtI/+mGr+apkkpqGM8WecLkPgrA==",
+            "version": "0.25.0",
+            "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.0.tgz",
+            "integrity": "sha512-grvv8WncGjDSyUBjN9yHXNt+cq0snxXbDxy5pJtzMKGmmpPxeAmAhWxXI+01lU5rwZomDgD3kJwulEnhTRUd6g==",
             "cpu": [
                 "arm64"
             ],
             "dev": true,
+            "license": "MIT",
             "optional": true,
             "os": [
                 "android"
             ],
             "engines": {
-                "node": ">=12"
+                "node": ">=18"
             }
         },
         "node_modules/@esbuild/android-x64": {
-            "version": "0.18.12",
-            "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.18.12.tgz",
-            "integrity": "sha512-zU5MyluNsykf5cOJ0LZZZjgAHbhPJ1cWfdH1ZXVMXxVMhEV0VZiZXQdwBBVvmvbF28EizeK7obG9fs+fpmS0eQ==",
+            "version": "0.25.0",
+            "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.0.tgz",
+            "integrity": "sha512-m/ix7SfKG5buCnxasr52+LI78SQ+wgdENi9CqyCXwjVR2X4Jkz+BpC3le3AoBPYTC9NHklwngVXvbJ9/Akhrfg==",
             "cpu": [
                 "x64"
             ],
             "dev": true,
+            "license": "MIT",
             "optional": true,
             "os": [
                 "android"
             ],
             "engines": {
-                "node": ">=12"
+                "node": ">=18"
             }
         },
         "node_modules/@esbuild/darwin-arm64": {
-            "version": "0.18.12",
-            "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.18.12.tgz",
-            "integrity": "sha512-zUZMep7YONnp6954QOOwEBwFX9svlKd3ov6PkxKd53LGTHsp/gy7vHaPGhhjBmEpqXEXShi6dddjIkmd+NgMsA==",
+            "version": "0.25.0",
+            "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.0.tgz",
+            "integrity": "sha512-mVwdUb5SRkPayVadIOI78K7aAnPamoeFR2bT5nszFUZ9P8UpK4ratOdYbZZXYSqPKMHfS1wdHCJk1P1EZpRdvw==",
             "cpu": [
                 "arm64"
             ],
             "dev": true,
+            "license": "MIT",
             "optional": true,
             "os": [
                 "darwin"
             ],
             "engines": {
-                "node": ">=12"
+                "node": ">=18"
             }
         },
         "node_modules/@esbuild/darwin-x64": {
-            "version": "0.18.12",
-            "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.18.12.tgz",
-            "integrity": "sha512-ohqLPc7i67yunArPj1+/FeeJ7AgwAjHqKZ512ADk3WsE3FHU9l+m5aa7NdxXr0HmN1bjDlUslBjWNbFlD9y12Q==",
+            "version": "0.25.0",
+            "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.0.tgz",
+            "integrity": "sha512-DgDaYsPWFTS4S3nWpFcMn/33ZZwAAeAFKNHNa1QN0rI4pUjgqf0f7ONmXf6d22tqTY+H9FNdgeaAa+YIFUn2Rg==",
             "cpu": [
                 "x64"
             ],
             "dev": true,
+            "license": "MIT",
             "optional": true,
             "os": [
                 "darwin"
             ],
             "engines": {
-                "node": ">=12"
+                "node": ">=18"
             }
         },
         "node_modules/@esbuild/freebsd-arm64": {
-            "version": "0.18.12",
-            "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.18.12.tgz",
-            "integrity": "sha512-GIIHtQXqgeOOqdG16a/A9N28GpkvjJnjYMhOnXVbn3EDJcoItdR58v/pGN31CHjyXDc8uCcRnFWmqaJt24AYJg==",
+            "version": "0.25.0",
+            "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.0.tgz",
+            "integrity": "sha512-VN4ocxy6dxefN1MepBx/iD1dH5K8qNtNe227I0mnTRjry8tj5MRk4zprLEdG8WPyAPb93/e4pSgi1SoHdgOa4w==",
             "cpu": [
                 "arm64"
             ],
             "dev": true,
+            "license": "MIT",
             "optional": true,
             "os": [
                 "freebsd"
             ],
             "engines": {
-                "node": ">=12"
+                "node": ">=18"
             }
         },
         "node_modules/@esbuild/freebsd-x64": {
-            "version": "0.18.12",
-            "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.18.12.tgz",
-            "integrity": "sha512-zK0b9a1/0wZY+6FdOS3BpZcPc1kcx2G5yxxfEJtEUzVxI6n/FrC2Phsxj/YblPuBchhBZ/1wwn7AyEBUyNSa6g==",
+            "version": "0.25.0",
+            "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.0.tgz",
+            "integrity": "sha512-mrSgt7lCh07FY+hDD1TxiTyIHyttn6vnjesnPoVDNmDfOmggTLXRv8Id5fNZey1gl/V2dyVK1VXXqVsQIiAk+A==",
             "cpu": [
                 "x64"
             ],
             "dev": true,
+            "license": "MIT",
             "optional": true,
             "os": [
                 "freebsd"
             ],
             "engines": {
-                "node": ">=12"
+                "node": ">=18"
             }
         },
         "node_modules/@esbuild/linux-arm": {
-            "version": "0.18.12",
-            "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.18.12.tgz",
-            "integrity": "sha512-y75OijvrBE/1XRrXq1jtrJfG26eHeMoqLJ2dwQNwviwTuTtHGCojsDO6BJNF8gU+3jTn1KzJEMETytwsFSvc+Q==",
+            "version": "0.25.0",
+            "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.0.tgz",
+            "integrity": "sha512-vkB3IYj2IDo3g9xX7HqhPYxVkNQe8qTK55fraQyTzTX/fxaDtXiEnavv9geOsonh2Fd2RMB+i5cbhu2zMNWJwg==",
             "cpu": [
                 "arm"
             ],
             "dev": true,
+            "license": "MIT",
             "optional": true,
             "os": [
                 "linux"
             ],
             "engines": {
-                "node": ">=12"
+                "node": ">=18"
             }
         },
         "node_modules/@esbuild/linux-arm64": {
-            "version": "0.18.12",
-            "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.18.12.tgz",
-            "integrity": "sha512-JKgG8Q/LL/9sw/iHHxQyVMoQYu3rU3+a5Z87DxC+wAu3engz+EmctIrV+FGOgI6gWG1z1+5nDDbXiRMGQZXqiw==",
+            "version": "0.25.0",
+            "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.0.tgz",
+            "integrity": "sha512-9QAQjTWNDM/Vk2bgBl17yWuZxZNQIF0OUUuPZRKoDtqF2k4EtYbpyiG5/Dk7nqeK6kIJWPYldkOcBqjXjrUlmg==",
             "cpu": [
                 "arm64"
             ],
             "dev": true,
+            "license": "MIT",
             "optional": true,
             "os": [
                 "linux"
             ],
             "engines": {
-                "node": ">=12"
+                "node": ">=18"
             }
         },
         "node_modules/@esbuild/linux-ia32": {
-            "version": "0.18.12",
-            "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.18.12.tgz",
-            "integrity": "sha512-yoRIAqc0B4lDIAAEFEIu9ttTRFV84iuAl0KNCN6MhKLxNPfzwCBvEMgwco2f71GxmpBcTtn7KdErueZaM2rEvw==",
+            "version": "0.25.0",
+            "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.0.tgz",
+            "integrity": "sha512-43ET5bHbphBegyeqLb7I1eYn2P/JYGNmzzdidq/w0T8E2SsYL1U6un2NFROFRg1JZLTzdCoRomg8Rvf9M6W6Gg==",
             "cpu": [
                 "ia32"
             ],
             "dev": true,
+            "license": "MIT",
             "optional": true,
             "os": [
                 "linux"
             ],
             "engines": {
-                "node": ">=12"
+                "node": ">=18"
             }
         },
         "node_modules/@esbuild/linux-loong64": {
-            "version": "0.18.12",
-            "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.18.12.tgz",
-            "integrity": "sha512-qYgt3dHPVvf/MgbIBpJ4Sup/yb9DAopZ3a2JgMpNKIHUpOdnJ2eHBo/aQdnd8dJ21X/+sS58wxHtA9lEazYtXQ==",
+            "version": "0.25.0",
+            "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.0.tgz",
+            "integrity": "sha512-fC95c/xyNFueMhClxJmeRIj2yrSMdDfmqJnyOY4ZqsALkDrrKJfIg5NTMSzVBr5YW1jf+l7/cndBfP3MSDpoHw==",
             "cpu": [
                 "loong64"
             ],
             "dev": true,
+            "license": "MIT",
             "optional": true,
             "os": [
                 "linux"
             ],
             "engines": {
-                "node": ">=12"
+                "node": ">=18"
             }
         },
         "node_modules/@esbuild/linux-mips64el": {
-            "version": "0.18.12",
-            "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.18.12.tgz",
-            "integrity": "sha512-wHphlMLK4ufNOONqukELfVIbnGQJrHJ/mxZMMrP2jYrPgCRZhOtf0kC4yAXBwnfmULimV1qt5UJJOw4Kh13Yfg==",
+            "version": "0.25.0",
+            "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.0.tgz",
+            "integrity": "sha512-nkAMFju7KDW73T1DdH7glcyIptm95a7Le8irTQNO/qtkoyypZAnjchQgooFUDQhNAy4iu08N79W4T4pMBwhPwQ==",
             "cpu": [
                 "mips64el"
             ],
             "dev": true,
+            "license": "MIT",
             "optional": true,
             "os": [
                 "linux"
             ],
             "engines": {
-                "node": ">=12"
+                "node": ">=18"
             }
         },
         "node_modules/@esbuild/linux-ppc64": {
-            "version": "0.18.12",
-            "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.18.12.tgz",
-            "integrity": "sha512-TeN//1Ft20ZZW41+zDSdOI/Os1bEq5dbvBvYkberB7PHABbRcsteeoNVZFlI0YLpGdlBqohEpjrn06kv8heCJg==",
+            "version": "0.25.0",
+            "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.0.tgz",
+            "integrity": "sha512-NhyOejdhRGS8Iwv+KKR2zTq2PpysF9XqY+Zk77vQHqNbo/PwZCzB5/h7VGuREZm1fixhs4Q/qWRSi5zmAiO4Fw==",
             "cpu": [
                 "ppc64"
             ],
             "dev": true,
+            "license": "MIT",
             "optional": true,
             "os": [
                 "linux"
             ],
             "engines": {
-                "node": ">=12"
+                "node": ">=18"
             }
         },
         "node_modules/@esbuild/linux-riscv64": {
-            "version": "0.18.12",
-            "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.18.12.tgz",
-            "integrity": "sha512-AgUebVS4DoAblBgiB2ACQ/8l4eGE5aWBb8ZXtkXHiET9mbj7GuWt3OnsIW/zX+XHJt2RYJZctbQ2S/mDjbp0UA==",
+            "version": "0.25.0",
+            "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.0.tgz",
+            "integrity": "sha512-5S/rbP5OY+GHLC5qXp1y/Mx//e92L1YDqkiBbO9TQOvuFXM+iDqUNG5XopAnXoRH3FjIUDkeGcY1cgNvnXp/kA==",
             "cpu": [
                 "riscv64"
             ],
             "dev": true,
+            "license": "MIT",
             "optional": true,
             "os": [
                 "linux"
             ],
             "engines": {
-                "node": ">=12"
+                "node": ">=18"
             }
         },
         "node_modules/@esbuild/linux-s390x": {
-            "version": "0.18.12",
-            "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.18.12.tgz",
-            "integrity": "sha512-dJ3Rb3Ei2u/ysSXd6pzleGtfDdc2MuzKt8qc6ls8vreP1G3B7HInX3i7gXS4BGeVd24pp0yqyS7bJ5NHaI9ing==",
+            "version": "0.25.0",
+            "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.0.tgz",
+            "integrity": "sha512-XM2BFsEBz0Fw37V0zU4CXfcfuACMrppsMFKdYY2WuTS3yi8O1nFOhil/xhKTmE1nPmVyvQJjJivgDT+xh8pXJA==",
             "cpu": [
                 "s390x"
             ],
             "dev": true,
+            "license": "MIT",
             "optional": true,
             "os": [
                 "linux"
             ],
             "engines": {
-                "node": ">=12"
+                "node": ">=18"
             }
         },
         "node_modules/@esbuild/linux-x64": {
-            "version": "0.18.12",
-            "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.18.12.tgz",
-            "integrity": "sha512-OrNJMGQbPaVyHHcDF8ybNSwu7TDOfX8NGpXCbetwOSP6txOJiWlgQnRymfC9ocR1S0Y5PW0Wb1mV6pUddqmvmQ==",
+            "version": "0.25.0",
+            "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.0.tgz",
+            "integrity": "sha512-9yl91rHw/cpwMCNytUDxwj2XjFpxML0y9HAOH9pNVQDpQrBxHy01Dx+vaMu0N1CKa/RzBD2hB4u//nfc+Sd3Cw==",
             "cpu": [
                 "x64"
             ],
             "dev": true,
+            "license": "MIT",
             "optional": true,
             "os": [
                 "linux"
             ],
             "engines": {
-                "node": ">=12"
+                "node": ">=18"
+            }
+        },
+        "node_modules/@esbuild/netbsd-arm64": {
+            "version": "0.25.0",
+            "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.0.tgz",
+            "integrity": "sha512-RuG4PSMPFfrkH6UwCAqBzauBWTygTvb1nxWasEJooGSJ/NwRw7b2HOwyRTQIU97Hq37l3npXoZGYMy3b3xYvPw==",
+            "cpu": [
+                "arm64"
+            ],
+            "dev": true,
+            "license": "MIT",
+            "optional": true,
+            "os": [
+                "netbsd"
+            ],
+            "engines": {
+                "node": ">=18"
             }
         },
         "node_modules/@esbuild/netbsd-x64": {
-            "version": "0.18.12",
-            "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.18.12.tgz",
-            "integrity": "sha512-55FzVCAiwE9FK8wWeCRuvjazNRJ1QqLCYGZVB6E8RuQuTeStSwotpSW4xoRGwp3a1wUsaVCdYcj5LGCASVJmMg==",
+            "version": "0.25.0",
+            "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.0.tgz",
+            "integrity": "sha512-jl+qisSB5jk01N5f7sPCsBENCOlPiS/xptD5yxOx2oqQfyourJwIKLRA2yqWdifj3owQZCL2sn6o08dBzZGQzA==",
             "cpu": [
                 "x64"
             ],
             "dev": true,
+            "license": "MIT",
             "optional": true,
             "os": [
                 "netbsd"
             ],
             "engines": {
-                "node": ">=12"
+                "node": ">=18"
+            }
+        },
+        "node_modules/@esbuild/openbsd-arm64": {
+            "version": "0.25.0",
+            "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.0.tgz",
+            "integrity": "sha512-21sUNbq2r84YE+SJDfaQRvdgznTD8Xc0oc3p3iW/a1EVWeNj/SdUCbm5U0itZPQYRuRTW20fPMWMpcrciH2EJw==",
+            "cpu": [
+                "arm64"
+            ],
+            "dev": true,
+            "license": "MIT",
+            "optional": true,
+            "os": [
+                "openbsd"
+            ],
+            "engines": {
+                "node": ">=18"
             }
         },
         "node_modules/@esbuild/openbsd-x64": {
-            "version": "0.18.12",
-            "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.18.12.tgz",
-            "integrity": "sha512-qnluf8rfb6Y5Lw2tirfK2quZOBbVqmwxut7GPCIJsM8lc4AEUj9L8y0YPdLaPK0TECt4IdyBdBD/KRFKorlK3g==",
+            "version": "0.25.0",
+            "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.0.tgz",
+            "integrity": "sha512-2gwwriSMPcCFRlPlKx3zLQhfN/2WjJ2NSlg5TKLQOJdV0mSxIcYNTMhk3H3ulL/cak+Xj0lY1Ym9ysDV1igceg==",
             "cpu": [
                 "x64"
             ],
             "dev": true,
+            "license": "MIT",
             "optional": true,
             "os": [
                 "openbsd"
             ],
             "engines": {
-                "node": ">=12"
+                "node": ">=18"
             }
         },
         "node_modules/@esbuild/sunos-x64": {
-            "version": "0.18.12",
-            "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.18.12.tgz",
-            "integrity": "sha512-+RkKpVQR7bICjTOPUpkTBTaJ4TFqQBX5Ywyd/HSdDkQGn65VPkTsR/pL4AMvuMWy+wnXgIl4EY6q4mVpJal8Kg==",
+            "version": "0.25.0",
+            "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.0.tgz",
+            "integrity": "sha512-bxI7ThgLzPrPz484/S9jLlvUAHYMzy6I0XiU1ZMeAEOBcS0VePBFxh1JjTQt3Xiat5b6Oh4x7UC7IwKQKIJRIg==",
             "cpu": [
                 "x64"
             ],
             "dev": true,
+            "license": "MIT",
             "optional": true,
             "os": [
                 "sunos"
             ],
             "engines": {
-                "node": ">=12"
+                "node": ">=18"
             }
         },
         "node_modules/@esbuild/win32-arm64": {
-            "version": "0.18.12",
-            "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.18.12.tgz",
-            "integrity": "sha512-GNHuciv0mFM7ouzsU0+AwY+7eV4Mgo5WnbhfDCQGtpvOtD1vbOiRjPYG6dhmMoFyBjj+pNqQu2X+7DKn0KQ/Gw==",
+            "version": "0.25.0",
+            "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.0.tgz",
+            "integrity": "sha512-ZUAc2YK6JW89xTbXvftxdnYy3m4iHIkDtK3CLce8wg8M2L+YZhIvO1DKpxrd0Yr59AeNNkTiic9YLf6FTtXWMw==",
             "cpu": [
                 "arm64"
             ],
             "dev": true,
+            "license": "MIT",
             "optional": true,
             "os": [
                 "win32"
             ],
             "engines": {
-                "node": ">=12"
+                "node": ">=18"
             }
         },
         "node_modules/@esbuild/win32-ia32": {
-            "version": "0.18.12",
-            "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.18.12.tgz",
-            "integrity": "sha512-kR8cezhYipbbypGkaqCTWIeu4zID17gamC8YTPXYtcN3E5BhhtTnwKBn9I0PJur/T6UVwIEGYzkffNL0lFvxEw==",
+            "version": "0.25.0",
+            "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.0.tgz",
+            "integrity": "sha512-eSNxISBu8XweVEWG31/JzjkIGbGIJN/TrRoiSVZwZ6pkC6VX4Im/WV2cz559/TXLcYbcrDN8JtKgd9DJVIo8GA==",
             "cpu": [
                 "ia32"
             ],
             "dev": true,
+            "license": "MIT",
             "optional": true,
             "os": [
                 "win32"
             ],
             "engines": {
-                "node": ">=12"
+                "node": ">=18"
             }
         },
         "node_modules/@esbuild/win32-x64": {
-            "version": "0.18.12",
-            "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.18.12.tgz",
-            "integrity": "sha512-O0UYQVkvfM/jO8a4OwoV0mAKSJw+mjWTAd1MJd/1FCX6uiMdLmMRPK/w6e9OQ0ob2WGxzIm9va/KG0Ja4zIOgg==",
+            "version": "0.25.0",
+            "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.0.tgz",
+            "integrity": "sha512-ZENoHJBxA20C2zFzh6AI4fT6RraMzjYw4xKWemRTRmRVtN9c5DcH9r/f2ihEkMjOW5eGgrwCslG/+Y/3bL+DHQ==",
             "cpu": [
                 "x64"
             ],
             "dev": true,
+            "license": "MIT",
             "optional": true,
             "os": [
                 "win32"
             ],
             "engines": {
-                "node": ">=12"
+                "node": ">=18"
             }
         },
         "node_modules/@eslint-community/eslint-utils": {
@@ -2521,40 +2594,44 @@
             }
         },
         "node_modules/esbuild": {
-            "version": "0.18.12",
-            "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.18.12.tgz",
-            "integrity": "sha512-XuOVLDdtsDslXStStduT41op21Ytmf4/BDS46aa3xPJ7X5h2eMWBF1oAe3QjUH3bDksocNXgzGUZ7XHIBya6Tg==",
+            "version": "0.25.0",
+            "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.0.tgz",
+            "integrity": "sha512-BXq5mqc8ltbaN34cDqWuYKyNhX8D/Z0J1xdtdQ8UcIIIyJyz+ZMKUt58tF3SrZ85jcfN/PZYhjR5uDQAYNVbuw==",
             "dev": true,
             "hasInstallScript": true,
+            "license": "MIT",
             "bin": {
                 "esbuild": "bin/esbuild"
             },
             "engines": {
-                "node": ">=12"
+                "node": ">=18"
             },
             "optionalDependencies": {
-                "@esbuild/android-arm": "0.18.12",
-                "@esbuild/android-arm64": "0.18.12",
-                "@esbuild/android-x64": "0.18.12",
-                "@esbuild/darwin-arm64": "0.18.12",
-                "@esbuild/darwin-x64": "0.18.12",
-                "@esbuild/freebsd-arm64": "0.18.12",
-                "@esbuild/freebsd-x64": "0.18.12",
-                "@esbuild/linux-arm": "0.18.12",
-                "@esbuild/linux-arm64": "0.18.12",
-                "@esbuild/linux-ia32": "0.18.12",
-                "@esbuild/linux-loong64": "0.18.12",
-                "@esbuild/linux-mips64el": "0.18.12",
-                "@esbuild/linux-ppc64": "0.18.12",
-                "@esbuild/linux-riscv64": "0.18.12",
-                "@esbuild/linux-s390x": "0.18.12",
-                "@esbuild/linux-x64": "0.18.12",
-                "@esbuild/netbsd-x64": "0.18.12",
-                "@esbuild/openbsd-x64": "0.18.12",
-                "@esbuild/sunos-x64": "0.18.12",
-                "@esbuild/win32-arm64": "0.18.12",
-                "@esbuild/win32-ia32": "0.18.12",
-                "@esbuild/win32-x64": "0.18.12"
+                "@esbuild/aix-ppc64": "0.25.0",
+                "@esbuild/android-arm": "0.25.0",
+                "@esbuild/android-arm64": "0.25.0",
+                "@esbuild/android-x64": "0.25.0",
+                "@esbuild/darwin-arm64": "0.25.0",
+                "@esbuild/darwin-x64": "0.25.0",
+                "@esbuild/freebsd-arm64": "0.25.0",
+                "@esbuild/freebsd-x64": "0.25.0",
+                "@esbuild/linux-arm": "0.25.0",
+                "@esbuild/linux-arm64": "0.25.0",
+                "@esbuild/linux-ia32": "0.25.0",
+                "@esbuild/linux-loong64": "0.25.0",
+                "@esbuild/linux-mips64el": "0.25.0",
+                "@esbuild/linux-ppc64": "0.25.0",
+                "@esbuild/linux-riscv64": "0.25.0",
+                "@esbuild/linux-s390x": "0.25.0",
+                "@esbuild/linux-x64": "0.25.0",
+                "@esbuild/netbsd-arm64": "0.25.0",
+                "@esbuild/netbsd-x64": "0.25.0",
+                "@esbuild/openbsd-arm64": "0.25.0",
+                "@esbuild/openbsd-x64": "0.25.0",
+                "@esbuild/sunos-x64": "0.25.0",
+                "@esbuild/win32-arm64": "0.25.0",
+                "@esbuild/win32-ia32": "0.25.0",
+                "@esbuild/win32-x64": "0.25.0"
             }
         },
         "node_modules/escalade": {
diff --git a/src/tools/rust-analyzer/editors/code/package.json b/src/tools/rust-analyzer/editors/code/package.json
index f148041ac3e..3f09033051b 100644
--- a/src/tools/rust-analyzer/editors/code/package.json
+++ b/src/tools/rust-analyzer/editors/code/package.json
@@ -59,7 +59,7 @@
         "@typescript-eslint/parser": "^6.0.0",
         "@vscode/test-electron": "^2.3.8",
         "@vscode/vsce": "^3.0.0",
-        "esbuild": "^0.18.12",
+        "esbuild": "^0.25.0",
         "eslint": "^8.44.0",
         "eslint-config-prettier": "^8.8.0",
         "ovsx": "^0.8.2",
@@ -501,6 +501,10 @@
                                 },
                                 {
                                     "pattern": "**/Cargo.lock"
+                                },
+                                {
+                                    "scheme": "output",
+                                    "pattern": "extension-output-rust-lang.rust-analyzer*"
                                 }
                             ]
                         }
@@ -1469,8 +1473,8 @@
             {
                 "title": "files",
                 "properties": {
-                    "rust-analyzer.files.excludeDirs": {
-                        "markdownDescription": "These directories will be ignored by rust-analyzer. They are\nrelative to the workspace root, and globs are not supported. You may\nalso need to add the folders to Code's `files.watcherExclude`.",
+                    "rust-analyzer.files.exclude": {
+                        "markdownDescription": "These paths (file/directories) will be ignored by rust-analyzer. They are\nrelative to the workspace root, and globs are not supported. You may\nalso need to add the folders to Code's `files.watcherExclude`.",
                         "default": [],
                         "type": "array",
                         "items": {
@@ -2252,6 +2256,16 @@
             {
                 "title": "inlayHints",
                 "properties": {
+                    "rust-analyzer.inlayHints.typeHints.hideClosureParameter": {
+                        "markdownDescription": "Whether to hide inlay parameter type hints for closures.",
+                        "default": false,
+                        "type": "boolean"
+                    }
+                }
+            },
+            {
+                "title": "inlayHints",
+                "properties": {
                     "rust-analyzer.inlayHints.typeHints.hideNamedConstructor": {
                         "markdownDescription": "Whether to hide inlay type hints for constructors.",
                         "default": false,
diff --git a/src/tools/rust-analyzer/editors/code/src/commands.ts b/src/tools/rust-analyzer/editors/code/src/commands.ts
index b3aa04af7ed..eee623ecae9 100644
--- a/src/tools/rust-analyzer/editors/code/src/commands.ts
+++ b/src/tools/rust-analyzer/editors/code/src/commands.ts
@@ -361,10 +361,7 @@ export function syntaxTreeReveal(): Cmd {
         const activeEditor = vscode.window.activeTextEditor;
 
         if (activeEditor !== undefined) {
-            const start = activeEditor.document.positionAt(element.start);
-            const end = activeEditor.document.positionAt(element.end);
-
-            const newSelection = new vscode.Selection(start, end);
+            const newSelection = new vscode.Selection(element.range.start, element.range.end);
 
             activeEditor.selection = newSelection;
             activeEditor.revealRange(newSelection);
@@ -378,15 +375,12 @@ function elementToString(
     depth: number = 0,
 ): string {
     let result = "  ".repeat(depth);
-    const start = element.istart ?? element.start;
-    const end = element.iend ?? element.end;
+    const offsets = element.inner?.offsets ?? element.offsets;
 
-    result += `${element.kind}@${start}..${end}`;
+    result += `${element.kind}@${offsets.start}..${offsets.end}`;
 
     if (element.type === "Token") {
-        const startPosition = activeDocument.positionAt(element.start);
-        const endPosition = activeDocument.positionAt(element.end);
-        const text = activeDocument.getText(new vscode.Range(startPosition, endPosition));
+        const text = activeDocument.getText(element.range).replaceAll("\r\n", "\n");
         // JSON.stringify quotes and escapes the string for us.
         result += ` ${JSON.stringify(text)}\n`;
     } else {
diff --git a/src/tools/rust-analyzer/editors/code/src/ctx.ts b/src/tools/rust-analyzer/editors/code/src/ctx.ts
index 96dc4f19b82..4248305d5cc 100644
--- a/src/tools/rust-analyzer/editors/code/src/ctx.ts
+++ b/src/tools/rust-analyzer/editors/code/src/ctx.ts
@@ -384,9 +384,7 @@ export class Ctx implements RustAnalyzerExtensionApi {
                 return;
             }
 
-            const start = e.textEditor.document.offsetAt(selection.start);
-            const end = e.textEditor.document.offsetAt(selection.end);
-            const result = this.syntaxTreeProvider?.getElementByRange(start, end);
+            const result = this.syntaxTreeProvider?.getElementByRange(selection);
             if (result !== undefined) {
                 await this.syntaxTreeView?.reveal(result);
             }
diff --git a/src/tools/rust-analyzer/editors/code/src/syntax_tree_provider.ts b/src/tools/rust-analyzer/editors/code/src/syntax_tree_provider.ts
index c7e8007e838..3f7e30f13a3 100644
--- a/src/tools/rust-analyzer/editors/code/src/syntax_tree_provider.ts
+++ b/src/tools/rust-analyzer/editors/code/src/syntax_tree_provider.ts
@@ -37,11 +37,7 @@ export class SyntaxTreeProvider implements vscode.TreeDataProvider<SyntaxElement
         const editor = vscode.window.activeTextEditor;
 
         if (editor !== undefined) {
-            const start = editor.document.positionAt(element.start);
-            const end = editor.document.positionAt(element.end);
-            const range = new vscode.Range(start, end);
-
-            const text = editor.document.getText(range);
+            const text = editor.document.getText(element.range);
             item.tooltip = new vscode.MarkdownString().appendCodeblock(text, "rust");
         }
 
@@ -74,14 +70,61 @@ export class SyntaxTreeProvider implements vscode.TreeDataProvider<SyntaxElement
         if (editor && isRustEditor(editor)) {
             const params = { textDocument: { uri: editor.document.uri.toString() }, range: null };
             const fileText = await this.ctx.client.sendRequest(ra.viewSyntaxTree, params);
-            this.root = JSON.parse(fileText, (_key, value: SyntaxElement) => {
+            this.root = JSON.parse(fileText, (_key, value: RawElement): SyntaxElement => {
+                if (value.type !== "Node" && value.type !== "Token") {
+                    // This is something other than a RawElement.
+                    return value;
+                }
+                const [startOffset, startLine, startCol] = value.start;
+                const [endOffset, endLine, endCol] = value.end;
+                const range = new vscode.Range(startLine, startCol, endLine, endCol);
+                const offsets = {
+                    start: startOffset,
+                    end: endOffset,
+                };
+
+                let inner;
+                if (value.istart && value.iend) {
+                    const [istartOffset, istartLine, istartCol] = value.istart;
+                    const [iendOffset, iendLine, iendCol] = value.iend;
+
+                    inner = {
+                        offsets: {
+                            start: istartOffset,
+                            end: iendOffset,
+                        },
+                        range: new vscode.Range(istartLine, istartCol, iendLine, iendCol),
+                    };
+                }
+
                 if (value.type === "Node") {
-                    for (const child of value.children) {
-                        child.parent = value;
+                    const result = {
+                        type: value.type,
+                        kind: value.kind,
+                        offsets,
+                        range,
+                        inner,
+                        children: value.children,
+                        parent: undefined,
+                        document: editor.document,
+                    };
+
+                    for (const child of result.children) {
+                        child.parent = result;
                     }
-                }
 
-                return value;
+                    return result;
+                } else {
+                    return {
+                        type: value.type,
+                        kind: value.kind,
+                        offsets,
+                        range,
+                        inner,
+                        parent: undefined,
+                        document: editor.document,
+                    };
+                }
             });
         } else {
             this.root = undefined;
@@ -90,14 +133,14 @@ export class SyntaxTreeProvider implements vscode.TreeDataProvider<SyntaxElement
         this._onDidChangeTreeData.fire();
     }
 
-    getElementByRange(start: number, end: number): SyntaxElement | undefined {
+    getElementByRange(target: vscode.Range): SyntaxElement | undefined {
         if (this.root === undefined) {
             return undefined;
         }
 
         let result: SyntaxElement = this.root;
 
-        if (this.root.start === start && this.root.end === end) {
+        if (this.root.range.isEqual(target)) {
             return result;
         }
 
@@ -105,9 +148,9 @@ export class SyntaxTreeProvider implements vscode.TreeDataProvider<SyntaxElement
 
         outer: while (true) {
             for (const child of children) {
-                if (child.start <= start && child.end >= end) {
+                if (child.range.contains(target)) {
                     result = child;
-                    if (start === end && start === child.end) {
+                    if (target.isEmpty && target.start === child.range.end) {
                         // When the cursor is on the very end of a token,
                         // we assume the user wants the next token instead.
                         continue;
@@ -136,31 +179,72 @@ export class SyntaxTreeProvider implements vscode.TreeDataProvider<SyntaxElement
 export type SyntaxNode = {
     type: "Node";
     kind: string;
-    start: number;
-    end: number;
-    istart?: number;
-    iend?: number;
+    range: vscode.Range;
+    offsets: {
+        start: number;
+        end: number;
+    };
+    /** This element's position within a Rust string literal, if it's inside of one. */
+    inner?: {
+        range: vscode.Range;
+        offsets: {
+            start: number;
+            end: number;
+        };
+    };
     children: SyntaxElement[];
     parent?: SyntaxElement;
+    document: vscode.TextDocument;
 };
 
 type SyntaxToken = {
     type: "Token";
     kind: string;
-    start: number;
-    end: number;
-    istart?: number;
-    iend?: number;
+    range: vscode.Range;
+    offsets: {
+        start: number;
+        end: number;
+    };
+    /** This element's position within a Rust string literal, if it's inside of one. */
+    inner?: {
+        range: vscode.Range;
+        offsets: {
+            start: number;
+            end: number;
+        };
+    };
     parent?: SyntaxElement;
+    document: vscode.TextDocument;
 };
 
 export type SyntaxElement = SyntaxNode | SyntaxToken;
 
+type RawNode = {
+    type: "Node";
+    kind: string;
+    start: [number, number, number];
+    end: [number, number, number];
+    istart?: [number, number, number];
+    iend?: [number, number, number];
+    children: SyntaxElement[];
+};
+
+type RawToken = {
+    type: "Token";
+    kind: string;
+    start: [number, number, number];
+    end: [number, number, number];
+    istart?: [number, number, number];
+    iend?: [number, number, number];
+};
+
+type RawElement = RawNode | RawToken;
+
 export class SyntaxTreeItem extends vscode.TreeItem {
     constructor(private readonly element: SyntaxElement) {
         super(element.kind);
-        const icon = getIcon(element.kind);
-        if (element.type === "Node") {
+        const icon = getIcon(this.element.kind);
+        if (this.element.type === "Node") {
             this.contextValue = "syntaxNode";
             this.iconPath = icon ?? new vscode.ThemeIcon("list-tree");
             this.collapsibleState = vscode.TreeItemCollapsibleState.Expanded;
@@ -170,11 +254,9 @@ export class SyntaxTreeItem extends vscode.TreeItem {
             this.collapsibleState = vscode.TreeItemCollapsibleState.None;
         }
 
-        if (element.istart !== undefined && element.iend !== undefined) {
-            this.description = `${this.element.istart}..${this.element.iend}`;
-        } else {
-            this.description = `${this.element.start}..${this.element.end}`;
-        }
+        const offsets = this.element.inner?.offsets ?? this.element.offsets;
+
+        this.description = `${offsets.start}..${offsets.end}`;
     }
 }
 
diff --git a/src/tools/rust-analyzer/editors/code/walkthrough-setup-tips.md b/src/tools/rust-analyzer/editors/code/walkthrough-setup-tips.md
index fda4ac80023..aabe0dd662b 100644
--- a/src/tools/rust-analyzer/editors/code/walkthrough-setup-tips.md
+++ b/src/tools/rust-analyzer/editors/code/walkthrough-setup-tips.md
@@ -5,6 +5,7 @@ Add the following to settings.json to mark Rust library sources as read-only:
 ```json
 "files.readonlyInclude": {
   "**/.cargo/registry/src/**/*.rs": true,
+  "**/.cargo/git/checkouts/**/*.rs": true,
   "**/lib/rustlib/src/rust/library/**/*.rs": true,
 },
 ```
diff --git a/src/tools/rust-analyzer/lib/line-index/src/lib.rs b/src/tools/rust-analyzer/lib/line-index/src/lib.rs
index 6f0455ee98b..bc87ada3eb5 100644
--- a/src/tools/rust-analyzer/lib/line-index/src/lib.rs
+++ b/src/tools/rust-analyzer/lib/line-index/src/lib.rs
@@ -235,7 +235,7 @@ fn analyze_source_file_dispatch(
     }
 }
 
-#[cfg(target_arch = "aarch64")]
+#[cfg(all(target_arch = "aarch64", target_endian = "little"))]
 fn analyze_source_file_dispatch(
     src: &str,
     lines: &mut Vec<TextSize>,
@@ -347,7 +347,7 @@ unsafe fn analyze_source_file_sse2(
 }
 
 #[target_feature(enable = "neon")]
-#[cfg(target_arch = "aarch64")]
+#[cfg(all(target_arch = "aarch64", target_endian = "little"))]
 #[inline]
 // See https://community.arm.com/arm-community-blogs/b/infrastructure-solutions-blog/posts/porting-x86-vector-bitmask-optimizations-to-arm-neon
 //
@@ -362,7 +362,7 @@ unsafe fn move_mask(v: std::arch::aarch64::uint8x16_t) -> u64 {
 }
 
 #[target_feature(enable = "neon")]
-#[cfg(target_arch = "aarch64")]
+#[cfg(all(target_arch = "aarch64", target_endian = "little"))]
 unsafe fn analyze_source_file_neon(
     src: &str,
     lines: &mut Vec<TextSize>,
@@ -441,7 +441,11 @@ unsafe fn analyze_source_file_neon(
     }
 }
 
-#[cfg(not(any(target_arch = "x86", target_arch = "x86_64", target_arch = "aarch64")))]
+#[cfg(not(any(
+    target_arch = "x86",
+    target_arch = "x86_64",
+    all(target_arch = "aarch64", target_endian = "little")
+)))]
 // The target (or compiler version) does not support SSE2 ...
 fn analyze_source_file_dispatch(
     src: &str,
diff --git a/src/tools/rust-analyzer/rust-version b/src/tools/rust-analyzer/rust-version
index 2d9a927c638..6cd39fabeee 100644
--- a/src/tools/rust-analyzer/rust-version
+++ b/src/tools/rust-analyzer/rust-version
@@ -1 +1 @@
-9a1d156f38c51441ee51e5a068f1d0caf4bb0f27
+e0be1a02626abef2878cb7f4aaef7ae409477112
diff --git a/src/tools/rust-analyzer/xtask/Cargo.toml b/src/tools/rust-analyzer/xtask/Cargo.toml
index b505ee835b2..ebd8903ad8a 100644
--- a/src/tools/rust-analyzer/xtask/Cargo.toml
+++ b/src/tools/rust-analyzer/xtask/Cargo.toml
@@ -16,8 +16,7 @@ xflags = "0.3.0"
 time = { version = "0.3", default-features = false }
 zip = { version = "0.6", default-features = false, features = ["deflate", "time"] }
 stdx.workspace = true
-# https://github.com/dtolnay/proc-macro2/issues/475
-proc-macro2 = "=1.0.86"
+proc-macro2 = "1.0.93"
 quote = "1.0.20"
 ungrammar = "1.16.1"
 either.workspace = true
diff --git a/src/tools/rust-analyzer/xtask/src/codegen.rs b/src/tools/rust-analyzer/xtask/src/codegen.rs
index bc04b9474f2..8165a2a12b0 100644
--- a/src/tools/rust-analyzer/xtask/src/codegen.rs
+++ b/src/tools/rust-analyzer/xtask/src/codegen.rs
@@ -24,6 +24,7 @@ impl flags::Codegen {
                 diagnostics_docs::generate(self.check);
                 assists_doc_tests::generate(self.check);
                 parser_inline_tests::generate(self.check);
+                feature_docs::generate(self.check)
                 // diagnostics_docs::generate(self.check) doesn't generate any tests
                 // lints::generate(self.check) Updating clones the rust repo, so don't run it unless
                 // explicitly asked for
@@ -118,10 +119,10 @@ impl fmt::Display for Location {
         let name = self.file.file_name().unwrap();
         write!(
             f,
-            "https://github.com/rust-lang/rust-analyzer/blob/master/{}#L{}[{}]",
+            " [{}](https://github.com/rust-lang/rust-analyzer/blob/master/{}#L{}) ",
+            name.to_str().unwrap(),
             path,
-            self.line,
-            name.to_str().unwrap()
+            self.line
         )
     }
 }
@@ -162,7 +163,7 @@ fn reformat(text: String) -> String {
 }
 
 fn add_preamble(cg: CodegenType, mut text: String) -> String {
-    let preamble = format!("//! Generated by `cargo codegen {cg}`, do not edit by hand.\n\n");
+    let preamble = format!("//! Generated by `cargo xtask codegen {cg}`, do not edit by hand.\n\n");
     text.insert_str(0, &preamble);
     text
 }
@@ -186,7 +187,7 @@ fn ensure_file_contents(cg: CodegenType, file: &Path, contents: &str, check: boo
             file.display(),
             if std::env::var("CI").is_ok() {
                 format!(
-                    "\n    NOTE: run `cargo codegen {cg}` locally and commit the updated files\n"
+                    "\n    NOTE: run `cargo xtask codegen {cg}` locally and commit the updated files\n"
                 )
             } else {
                 "".to_owned()
diff --git a/src/tools/rust-analyzer/xtask/src/codegen/assists_doc_tests.rs b/src/tools/rust-analyzer/xtask/src/codegen/assists_doc_tests.rs
index d06c9d65df3..0bb18c73cfc 100644
--- a/src/tools/rust-analyzer/xtask/src/codegen/assists_doc_tests.rs
+++ b/src/tools/rust-analyzer/xtask/src/codegen/assists_doc_tests.rs
@@ -1,4 +1,4 @@
-//! Generates `assists.md` documentation.
+//! Generates `assists_generated.md` documentation.
 
 use std::{fmt, fs, path::Path};
 
@@ -62,7 +62,7 @@ r#####"
             crate::flags::CodegenType::AssistsDocTests,
             assists.into_iter().map(|it| it.to_string()).collect::<Vec<_>>().join("\n\n"),
         );
-        let dst = project_root().join("docs/user/generated_assists.adoc");
+        let dst = project_root().join("docs/book/src/assists_generated.md");
         fs::write(dst, contents).unwrap();
     }
 }
@@ -146,7 +146,7 @@ impl fmt::Display for Assist {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
         let _ = writeln!(
             f,
-            "[discrete]\n=== `{}`
+            "### `{}`
 **Source:** {}",
             self.id, self.location,
         );
@@ -159,11 +159,11 @@ impl fmt::Display for Assist {
                 "
 {}
 
-.Before
+#### Before
 ```rust
 {}```
 
-.After
+#### After
 ```rust
 {}```",
                 section.doc,
diff --git a/src/tools/rust-analyzer/xtask/src/codegen/diagnostics_docs.rs b/src/tools/rust-analyzer/xtask/src/codegen/diagnostics_docs.rs
index 4cb8f3f259d..cf8f97be009 100644
--- a/src/tools/rust-analyzer/xtask/src/codegen/diagnostics_docs.rs
+++ b/src/tools/rust-analyzer/xtask/src/codegen/diagnostics_docs.rs
@@ -1,4 +1,4 @@
-//! Generates `assists.md` documentation.
+//! Generates `diagnostics_generated.md` documentation.
 
 use std::{fmt, fs, io, path::PathBuf};
 
@@ -14,7 +14,7 @@ pub(crate) fn generate(check: bool) {
         let contents =
             diagnostics.into_iter().map(|it| it.to_string()).collect::<Vec<_>>().join("\n\n");
         let contents = add_preamble(crate::flags::CodegenType::DiagnosticsDocs, contents);
-        let dst = project_root().join("docs/user/generated_diagnostic.adoc");
+        let dst = project_root().join("docs/book/src/diagnostics_generated.md");
         fs::write(dst, contents).unwrap();
     }
 }
@@ -73,6 +73,6 @@ fn is_valid_diagnostic_name(diagnostic: &str) -> Result<(), String> {
 
 impl fmt::Display for Diagnostic {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        writeln!(f, "=== {}\n**Source:** {}\n{}", self.id, self.location, self.doc)
+        writeln!(f, "#### {}\n\nSource: {}\n\n{}\n\n", self.id, self.location, self.doc)
     }
 }
diff --git a/src/tools/rust-analyzer/xtask/src/codegen/feature_docs.rs b/src/tools/rust-analyzer/xtask/src/codegen/feature_docs.rs
index c6451d888b0..51ff13aba81 100644
--- a/src/tools/rust-analyzer/xtask/src/codegen/feature_docs.rs
+++ b/src/tools/rust-analyzer/xtask/src/codegen/feature_docs.rs
@@ -1,9 +1,9 @@
-//! Generates `assists.md` documentation.
+//! Generates `features_generated.md` documentation.
 
 use std::{fmt, fs, io, path::PathBuf};
 
 use crate::{
-    codegen::{CommentBlock, Location},
+    codegen::{add_preamble, CommentBlock, Location},
     project_root,
     util::list_rust_files,
 };
@@ -11,14 +11,8 @@ use crate::{
 pub(crate) fn generate(_check: bool) {
     let features = Feature::collect().unwrap();
     let contents = features.into_iter().map(|it| it.to_string()).collect::<Vec<_>>().join("\n\n");
-    let contents = format!(
-        "
-// Generated file, do not edit by hand, see `sourcegen_feature_docs`.
-{}
-",
-        contents.trim()
-    );
-    let dst = project_root().join("docs/user/generated_features.adoc");
+    let contents = add_preamble(crate::flags::CodegenType::FeatureDocs, contents);
+    let dst = project_root().join("docs/book/src/features_generated.md");
     fs::write(dst, contents).unwrap();
 }
 
@@ -80,6 +74,6 @@ fn is_valid_feature_name(feature: &str) -> Result<(), String> {
 
 impl fmt::Display for Feature {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        writeln!(f, "=== {}\n**Source:** {}\n{}", self.id, self.location, self.doc)
+        writeln!(f, "### {}\n**Source:** {}\n{}", self.id, self.location, self.doc)
     }
 }
diff --git a/src/tools/rust-analyzer/xtask/src/codegen/grammar.rs b/src/tools/rust-analyzer/xtask/src/codegen/grammar.rs
index d5fe3236789..e20dda7fec3 100644
--- a/src/tools/rust-analyzer/xtask/src/codegen/grammar.rs
+++ b/src/tools/rust-analyzer/xtask/src/codegen/grammar.rs
@@ -397,6 +397,9 @@ fn generate_syntax_kinds(grammar: KindsSrc) -> String {
         if "{}[]()".contains(token) {
             let c = token.chars().next().unwrap();
             quote! { #c }
+            // underscore is an identifier in the proc-macro api
+        } else if *token == "_" {
+            quote! { _ }
         } else {
             let cs = token.chars().map(|c| Punct::new(c, Spacing::Joint));
             quote! { #(#cs)* }
diff --git a/src/tools/rust-analyzer/xtask/src/codegen/parser_inline_tests.rs b/src/tools/rust-analyzer/xtask/src/codegen/parser_inline_tests.rs
index f9f73df8eb7..88732cebe72 100644
--- a/src/tools/rust-analyzer/xtask/src/codegen/parser_inline_tests.rs
+++ b/src/tools/rust-analyzer/xtask/src/codegen/parser_inline_tests.rs
@@ -18,92 +18,87 @@ use crate::{
     util::list_rust_files,
 };
 
-const PARSER_CRATE_ROOT: &str = "crates/parser";
-const PARSER_TEST_DATA: &str = "crates/parser/test_data";
-const PARSER_TEST_DATA_INLINE: &str = "crates/parser/test_data/parser/inline";
-
 pub(crate) fn generate(check: bool) {
-    let tests = tests_from_dir(
-        &project_root().join(Path::new(&format!("{PARSER_CRATE_ROOT}/src/grammar"))),
-    );
+    let parser_crate_root = project_root().join("crates/parser");
+    let parser_test_data = parser_crate_root.join("test_data");
+    let parser_test_data_inline = parser_test_data.join("parser/inline");
+
+    let tests = tests_from_dir(&parser_crate_root.join("src/grammar"));
 
     let mut some_file_was_updated = false;
     some_file_was_updated |=
-        install_tests(&tests.ok, &format!("{PARSER_TEST_DATA_INLINE}/ok"), check).unwrap();
+        install_tests(&tests.ok, parser_test_data_inline.join("ok"), check).unwrap();
     some_file_was_updated |=
-        install_tests(&tests.err, &format!("{PARSER_TEST_DATA_INLINE}/err"), check).unwrap();
+        install_tests(&tests.err, parser_test_data_inline.join("err"), check).unwrap();
 
     if some_file_was_updated {
-        let _ = fs::File::open(format!("{PARSER_CRATE_ROOT}/src/tests.rs"))
+        let _ = fs::File::open(parser_crate_root.join("src/tests.rs"))
             .unwrap()
             .set_modified(SystemTime::now());
+    }
 
-        let ok_tests = tests.ok.values().sorted_by(|a, b| a.name.cmp(&b.name)).map(|test| {
-            let test_name = quote::format_ident!("{}", test.name);
-            let test_file = format!("test_data/parser/inline/ok/{test_name}.rs");
-            let (test_func, args) = match &test.edition {
-                Some(edition) => {
-                    let edition = quote::format_ident!("Edition{edition}");
-                    (
-                        quote::format_ident!("run_and_expect_no_errors_with_edition"),
-                        quote::quote! {#test_file, crate::Edition::#edition},
-                    )
-                }
-                None => {
-                    (quote::format_ident!("run_and_expect_no_errors"), quote::quote! {#test_file})
-                }
-            };
-            quote::quote! {
-                #[test]
-                fn #test_name() {
-                    #test_func(#args);
-                }
-            }
-        });
-        let err_tests = tests.err.values().sorted_by(|a, b| a.name.cmp(&b.name)).map(|test| {
-            let test_name = quote::format_ident!("{}", test.name);
-            let test_file = format!("test_data/parser/inline/err/{test_name}.rs");
-            let (test_func, args) = match &test.edition {
-                Some(edition) => {
-                    let edition = quote::format_ident!("Edition{edition}");
-                    (
-                        quote::format_ident!("run_and_expect_errors_with_edition"),
-                        quote::quote! {#test_file, crate::Edition::#edition},
-                    )
-                }
-                None => (quote::format_ident!("run_and_expect_errors"), quote::quote! {#test_file}),
-            };
-            quote::quote! {
-                #[test]
-                fn #test_name() {
-                    #test_func(#args);
-                }
+    let ok_tests = tests.ok.values().sorted_by(|a, b| a.name.cmp(&b.name)).map(|test| {
+        let test_name = quote::format_ident!("{}", test.name);
+        let test_file = format!("test_data/parser/inline/ok/{test_name}.rs");
+        let (test_func, args) = match &test.edition {
+            Some(edition) => {
+                let edition = quote::format_ident!("Edition{edition}");
+                (
+                    quote::format_ident!("run_and_expect_no_errors_with_edition"),
+                    quote::quote! {#test_file, crate::Edition::#edition},
+                )
             }
-        });
-
-        let output = quote::quote! {
-            mod ok {
-                use crate::tests::*;
-                #(#ok_tests)*
+            None => (quote::format_ident!("run_and_expect_no_errors"), quote::quote! {#test_file}),
+        };
+        quote::quote! {
+            #[test]
+            fn #test_name() {
+                #test_func(#args);
             }
-            mod err {
-                use crate::tests::*;
-                #(#err_tests)*
+        }
+    });
+    let err_tests = tests.err.values().sorted_by(|a, b| a.name.cmp(&b.name)).map(|test| {
+        let test_name = quote::format_ident!("{}", test.name);
+        let test_file = format!("test_data/parser/inline/err/{test_name}.rs");
+        let (test_func, args) = match &test.edition {
+            Some(edition) => {
+                let edition = quote::format_ident!("Edition{edition}");
+                (
+                    quote::format_ident!("run_and_expect_errors_with_edition"),
+                    quote::quote! {#test_file, crate::Edition::#edition},
+                )
             }
+            None => (quote::format_ident!("run_and_expect_errors"), quote::quote! {#test_file}),
         };
+        quote::quote! {
+            #[test]
+            fn #test_name() {
+                #test_func(#args);
+            }
+        }
+    });
 
-        let pretty = reformat(output.to_string());
-        ensure_file_contents(
-            crate::flags::CodegenType::ParserTests,
-            format!("{PARSER_TEST_DATA}/generated/runner.rs").as_ref(),
-            &pretty,
-            check,
-        );
-    }
+    let output = quote::quote! {
+        mod ok {
+            use crate::tests::*;
+            #(#ok_tests)*
+        }
+        mod err {
+            use crate::tests::*;
+            #(#err_tests)*
+        }
+    };
+
+    let pretty = reformat(output.to_string());
+    ensure_file_contents(
+        crate::flags::CodegenType::ParserTests,
+        parser_test_data.join("generated/runner.rs").as_ref(),
+        &pretty,
+        check,
+    );
 }
 
-fn install_tests(tests: &HashMap<String, Test>, into: &str, check: bool) -> Result<bool> {
-    let tests_dir = project_root().join(into);
+fn install_tests(tests: &HashMap<String, Test>, tests_dir: PathBuf, check: bool) -> Result<bool> {
     if !tests_dir.is_dir() {
         fs::create_dir_all(&tests_dir)?;
     }
diff --git a/src/tools/rust-analyzer/xtask/src/publish/notes.rs b/src/tools/rust-analyzer/xtask/src/publish/notes.rs
index c30267295bf..7245ce24311 100644
--- a/src/tools/rust-analyzer/xtask/src/publish/notes.rs
+++ b/src/tools/rust-analyzer/xtask/src/publish/notes.rs
@@ -549,18 +549,18 @@ impl Macro {
             }
             "pr" => {
                 let pr = &self.target;
-                let url = format!("https://github.com/rust-analyzer/rust-analyzer/pull/{pr}");
+                let url = format!("https://github.com/rust-lang/rust-analyzer/pull/{pr}");
                 format!("[`#{pr}`]({url})")
             }
             "commit" => {
                 let hash = &self.target;
                 let short = &hash[0..7];
-                let url = format!("https://github.com/rust-analyzer/rust-analyzer/commit/{hash}");
+                let url = format!("https://github.com/rust-lang/rust-analyzer/commit/{hash}");
                 format!("[`{short}`]({url})")
             }
             "release" => {
                 let date = &self.target;
-                let url = format!("https://github.com/rust-analyzer/rust-analyzer/releases/{date}");
+                let url = format!("https://github.com/rust-lang/rust-analyzer/releases/{date}");
                 format!("[`{date}`]({url})")
             }
             _ => bail!("macro not supported: {name}"),
diff --git a/src/tools/rust-analyzer/xtask/src/release.rs b/src/tools/rust-analyzer/xtask/src/release.rs
index 1a1364c7d10..8e56ce439c5 100644
--- a/src/tools/rust-analyzer/xtask/src/release.rs
+++ b/src/tools/rust-analyzer/xtask/src/release.rs
@@ -9,7 +9,7 @@ use directories::ProjectDirs;
 use stdx::JodChild;
 use xshell::{cmd, Shell};
 
-use crate::{codegen, date_iso, flags, is_release_tag, project_root};
+use crate::{date_iso, flags, is_release_tag, project_root};
 
 impl flags::Release {
     pub(crate) fn run(self, sh: &Shell) -> anyhow::Result<()> {
@@ -29,11 +29,6 @@ impl flags::Release {
             cmd!(sh, "git push --force").run()?;
         }
 
-        // Generates bits of manual.adoc.
-        codegen::diagnostics_docs::generate(false);
-        codegen::assists_doc_tests::generate(false);
-        codegen::feature_docs::generate(false);
-
         let website_root = project_root().join("../rust-analyzer.github.io");
         {
             let _dir = sh.push_dir(&website_root);
@@ -54,20 +49,6 @@ impl flags::Release {
             .max()
             .unwrap_or_default();
 
-        for adoc in [
-            "manual.adoc",
-            "generated_assists.adoc",
-            "generated_config.adoc",
-            "generated_diagnostic.adoc",
-            "generated_features.adoc",
-        ] {
-            let src = project_root().join("./docs/user/").join(adoc);
-            let dst = website_root.join(adoc);
-
-            let contents = sh.read_file(src)?;
-            sh.write_file(dst, contents)?;
-        }
-
         let tags = cmd!(sh, "git tag --list").read()?;
         let prev_tag = tags.lines().filter(|line| is_release_tag(line)).last().unwrap();
 
diff --git a/src/tools/rust-analyzer/xtask/src/tidy.rs b/src/tools/rust-analyzer/xtask/src/tidy.rs
index 35412be8764..b500b251ed3 100644
--- a/src/tools/rust-analyzer/xtask/src/tidy.rs
+++ b/src/tools/rust-analyzer/xtask/src/tidy.rs
@@ -27,8 +27,9 @@ fn check_lsp_extensions_docs(sh: &Shell) {
     };
 
     let actual_hash = {
-        let lsp_extensions_md =
-            sh.read_file(project_root().join("docs/dev/lsp-extensions.md")).unwrap();
+        let lsp_extensions_md = sh
+            .read_file(project_root().join("docs/book/src/contributing/lsp-extensions.md"))
+            .unwrap();
         let text = lsp_extensions_md
             .lines()
             .find_map(|line| line.strip_prefix("lsp/ext.rs hash:"))
@@ -185,7 +186,7 @@ Zlib OR Apache-2.0 OR MIT
 
 fn check_test_attrs(path: &Path, text: &str) {
     let panic_rule =
-        "https://github.com/rust-lang/rust-analyzer/blob/master/docs/dev/style.md#should_panic";
+        "https://github.com/rust-lang/rust-analyzer/blob/master/docs/book/src/contributing/style.md#should_panic";
     let need_panic: &[&str] = &[
         // This file.
         "slow-tests/tidy.rs",
diff --git a/src/tools/rust-analyzer/xtask/test_data/expected.md b/src/tools/rust-analyzer/xtask/test_data/expected.md
index 19c940c67bd..301837b5c21 100644
--- a/src/tools/rust-analyzer/xtask/test_data/expected.md
+++ b/src/tools/rust-analyzer/xtask/test_data/expected.md
@@ -2,12 +2,12 @@
 
 Hello!
 
-Commit: [`0123456`](https://github.com/rust-analyzer/rust-analyzer/commit/0123456789abcdef0123456789abcdef01234567) \
-Release: [`2022-01-01`](https://github.com/rust-analyzer/rust-analyzer/releases/2022-01-01)
+Commit: [`0123456`](https://github.com/rust-lang/rust-analyzer/commit/0123456789abcdef0123456789abcdef01234567) \
+Release: [`2022-01-01`](https://github.com/rust-lang/rust-analyzer/releases/2022-01-01)
 
 ## New Features
 
-- **BREAKING** [`#1111`](https://github.com/rust-analyzer/rust-analyzer/pull/1111) shortcut <kbd>ctrl</kbd>+<kbd>r</kbd>
+- **BREAKING** [`#1111`](https://github.com/rust-lang/rust-analyzer/pull/1111) shortcut <kbd>ctrl</kbd>+<kbd>r</kbd>
   - hyphen-prefixed list item
 - nested list item
   - `foo` -> `foofoo`
@@ -65,7 +65,7 @@ Release: [`2022-01-01`](https://github.com/rust-analyzer/rust-analyzer/releases/
 - list item with an inline image
   ![](https://example.com/animation.gif)
 
-The highlight of the month is probably [`#1111`](https://github.com/rust-analyzer/rust-analyzer/pull/1111).
+The highlight of the month is probably [`#1111`](https://github.com/rust-lang/rust-analyzer/pull/1111).
 See [online manual](https://example.com/manual) for more information.
 
 ```bash
diff --git a/src/tools/rustbook/Cargo.lock b/src/tools/rustbook/Cargo.lock
index b31bf61a6fb..ddcf315a267 100644
--- a/src/tools/rustbook/Cargo.lock
+++ b/src/tools/rustbook/Cargo.lock
@@ -447,6 +447,13 @@ dependencies = [
 ]
 
 [[package]]
+name = "error_index_generator"
+version = "0.0.0"
+dependencies = [
+ "mdbook",
+]
+
+[[package]]
 name = "fastrand"
 version = "2.3.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -867,9 +874,9 @@ dependencies = [
 
 [[package]]
 name = "mdbook"
-version = "0.4.44"
+version = "0.4.45"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f9da1e54401fe5d45a664c57e112e70f18e8c5a73e268c179305b932ee864574"
+checksum = "b07d36d96ffe1b5b16ddf2bc80b3b26bb7a498b2a6591061250bf0af8e8095ad"
 dependencies = [
  "ammonia",
  "anyhow",
diff --git a/src/tools/rustbook/Cargo.toml b/src/tools/rustbook/Cargo.toml
index 9f9846cdee0..6aec0bec0fa 100644
--- a/src/tools/rustbook/Cargo.toml
+++ b/src/tools/rustbook/Cargo.toml
@@ -1,4 +1,5 @@
 [workspace]
+members = ["../error_index_generator"]
 
 [package]
 name = "rustbook"
@@ -14,6 +15,6 @@ mdbook-i18n-helpers = "0.3.3"
 mdbook-spec = { path = "../../doc/reference/mdbook-spec" }
 
 [dependencies.mdbook]
-version = "0.4.44"
+version = "0.4.45"
 default-features = false
 features = ["search"]
diff --git a/src/tools/rustfmt/src/patterns.rs b/src/tools/rustfmt/src/patterns.rs
index 1d88726d945..bafed41e39f 100644
--- a/src/tools/rustfmt/src/patterns.rs
+++ b/src/tools/rustfmt/src/patterns.rs
@@ -75,12 +75,12 @@ fn is_short_pattern_inner(context: &RewriteContext<'_>, pat: &ast::Pat) -> bool
     }
 }
 
-pub(crate) struct RangeOperand<'a> {
-    operand: &'a Option<ptr::P<ast::Expr>>,
-    pub(crate) span: Span,
+pub(crate) struct RangeOperand<'a, T> {
+    pub operand: &'a Option<ptr::P<T>>,
+    pub span: Span,
 }
 
-impl<'a> Rewrite for RangeOperand<'a> {
+impl<'a, T: Rewrite> Rewrite for RangeOperand<'a, T> {
     fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option<String> {
         self.rewrite_result(context, shape).ok()
     }
@@ -259,40 +259,7 @@ impl Rewrite for Pat {
             }
             PatKind::Never => Err(RewriteError::Unknown),
             PatKind::Range(ref lhs, ref rhs, ref end_kind) => {
-                let infix = match end_kind.node {
-                    RangeEnd::Included(RangeSyntax::DotDotDot) => "...",
-                    RangeEnd::Included(RangeSyntax::DotDotEq) => "..=",
-                    RangeEnd::Excluded => "..",
-                };
-                let infix = if context.config.spaces_around_ranges() {
-                    let lhs_spacing = match lhs {
-                        None => "",
-                        Some(_) => " ",
-                    };
-                    let rhs_spacing = match rhs {
-                        None => "",
-                        Some(_) => " ",
-                    };
-                    format!("{lhs_spacing}{infix}{rhs_spacing}")
-                } else {
-                    infix.to_owned()
-                };
-                let lspan = self.span.with_hi(end_kind.span.lo());
-                let rspan = self.span.with_lo(end_kind.span.hi());
-                rewrite_pair(
-                    &RangeOperand {
-                        operand: lhs,
-                        span: lspan,
-                    },
-                    &RangeOperand {
-                        operand: rhs,
-                        span: rspan,
-                    },
-                    PairParts::infix(&infix),
-                    context,
-                    shape,
-                    SeparatorPlace::Front,
-                )
+                rewrite_range_pat(context, shape, lhs, rhs, end_kind, self.span)
             }
             PatKind::Ref(ref pat, mutability) => {
                 let prefix = format!("&{}", format_mutability(mutability));
@@ -359,6 +326,50 @@ impl Rewrite for Pat {
     }
 }
 
+pub fn rewrite_range_pat<T: Rewrite>(
+    context: &RewriteContext<'_>,
+    shape: Shape,
+    lhs: &Option<ptr::P<T>>,
+    rhs: &Option<ptr::P<T>>,
+    end_kind: &rustc_span::source_map::Spanned<RangeEnd>,
+    span: Span,
+) -> RewriteResult {
+    let infix = match end_kind.node {
+        RangeEnd::Included(RangeSyntax::DotDotDot) => "...",
+        RangeEnd::Included(RangeSyntax::DotDotEq) => "..=",
+        RangeEnd::Excluded => "..",
+    };
+    let infix = if context.config.spaces_around_ranges() {
+        let lhs_spacing = match lhs {
+            None => "",
+            Some(_) => " ",
+        };
+        let rhs_spacing = match rhs {
+            None => "",
+            Some(_) => " ",
+        };
+        format!("{lhs_spacing}{infix}{rhs_spacing}")
+    } else {
+        infix.to_owned()
+    };
+    let lspan = span.with_hi(end_kind.span.lo());
+    let rspan = span.with_lo(end_kind.span.hi());
+    rewrite_pair(
+        &RangeOperand {
+            operand: lhs,
+            span: lspan,
+        },
+        &RangeOperand {
+            operand: rhs,
+            span: rspan,
+        },
+        PairParts::infix(&infix),
+        context,
+        shape,
+        SeparatorPlace::Front,
+    )
+}
+
 fn rewrite_struct_pat(
     qself: &Option<ptr::P<ast::QSelf>>,
     path: &ast::Path,
diff --git a/src/tools/rustfmt/src/spanned.rs b/src/tools/rustfmt/src/spanned.rs
index 6b3e40b9115..e93eb53cd87 100644
--- a/src/tools/rustfmt/src/spanned.rs
+++ b/src/tools/rustfmt/src/spanned.rs
@@ -211,7 +211,7 @@ impl Spanned for ast::PreciseCapturingArg {
     }
 }
 
-impl<'a> Spanned for RangeOperand<'a> {
+impl<'a, T> Spanned for RangeOperand<'a, T> {
     fn span(&self) -> Span {
         self.span
     }
diff --git a/src/tools/rustfmt/src/types.rs b/src/tools/rustfmt/src/types.rs
index f8b713117f4..0009490e86f 100644
--- a/src/tools/rustfmt/src/types.rs
+++ b/src/tools/rustfmt/src/types.rs
@@ -18,6 +18,7 @@ use crate::lists::{
 use crate::macros::{MacroPosition, rewrite_macro};
 use crate::overflow;
 use crate::pairs::{PairParts, rewrite_pair};
+use crate::patterns::rewrite_range_pat;
 use crate::rewrite::{Rewrite, RewriteContext, RewriteError, RewriteErrorExt, RewriteResult};
 use crate::shape::Shape;
 use crate::source_map::SpanUtils;
@@ -1045,6 +1046,21 @@ impl Rewrite for ast::Ty {
     }
 }
 
+impl Rewrite for ast::TyPat {
+    fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option<String> {
+        self.rewrite_result(context, shape).ok()
+    }
+
+    fn rewrite_result(&self, context: &RewriteContext<'_>, shape: Shape) -> RewriteResult {
+        match self.kind {
+            ast::TyPatKind::Range(ref lhs, ref rhs, ref end_kind) => {
+                rewrite_range_pat(context, shape, lhs, rhs, end_kind, self.span)
+            }
+            ast::TyPatKind::Err(_) => Err(RewriteError::Unknown),
+        }
+    }
+}
+
 fn rewrite_bare_fn(
     bare_fn: &ast::BareFnTy,
     span: Span,
diff --git a/src/tools/tidy/src/deps.rs b/src/tools/tidy/src/deps.rs
index faa0db27b2b..51e58b4e4fc 100644
--- a/src/tools/tidy/src/deps.rs
+++ b/src/tools/tidy/src/deps.rs
@@ -99,7 +99,6 @@ const EXCEPTIONS: ExceptionList = &[
     ("dissimilar", "Apache-2.0"),                            // rustdoc, rustc_lexer (few tests) via expect-test, (dev deps)
     ("fluent-langneg", "Apache-2.0"),                        // rustc (fluent translations)
     ("foldhash", "Zlib"),                                    // rustc
-    ("mdbook", "MPL-2.0"),                                   // mdbook
     ("option-ext", "MPL-2.0"),                               // cargo-miri (via `directories`)
     ("rustc_apfloat", "Apache-2.0 WITH LLVM-exception"),     // rustc (license is the same as LLVM uses)
     ("ryu", "Apache-2.0 OR BSL-1.0"), // BSL is not acceptble, but we use it under Apache-2.0                       // cargo/... (because of serde)
@@ -132,6 +131,7 @@ const EXCEPTIONS_CARGO: ExceptionList = &[
     ("dunce", "CC0-1.0 OR MIT-0 OR Apache-2.0"),
     ("encoding_rs", "(Apache-2.0 OR MIT) AND BSD-3-Clause"),
     ("fiat-crypto", "MIT OR Apache-2.0 OR BSD-1-Clause"),
+    ("foldhash", "Zlib"),
     ("im-rc", "MPL-2.0+"),
     ("normalize-line-endings", "Apache-2.0"),
     ("openssl", "Apache-2.0"),
@@ -285,7 +285,6 @@ const PERMITTED_RUSTC_DEPENDENCIES: &[&str] = &[
     "expect-test",
     "fallible-iterator", // dependency of `thorin`
     "fastrand",
-    "field-offset",
     "flate2",
     "fluent-bundle",
     "fluent-langneg",
@@ -327,7 +326,6 @@ const PERMITTED_RUSTC_DEPENDENCIES: &[&str] = &[
     "measureme",
     "memchr",
     "memmap2",
-    "memoffset",
     "miniz_oxide",
     "nix",
     "nu-ansi-term",
@@ -367,14 +365,12 @@ const PERMITTED_RUSTC_DEPENDENCIES: &[&str] = &[
     "rustc-rayon-core",
     "rustc-stable-hash",
     "rustc_apfloat",
-    "rustc_version",
     "rustix",
     "ruzstd", // via object in thorin-dwp
     "ryu",
     "scoped-tls",
     "scopeguard",
     "self_cell",
-    "semver",
     "serde",
     "serde_derive",
     "serde_json",
@@ -480,6 +476,8 @@ const PERMITTED_STDLIB_DEPENDENCIES: &[&str] = &[
     "memchr",
     "miniz_oxide",
     "object",
+    "proc-macro2",
+    "quote",
     "r-efi",
     "r-efi-alloc",
     "rand",
@@ -487,6 +485,8 @@ const PERMITTED_STDLIB_DEPENDENCIES: &[&str] = &[
     "rand_xorshift",
     "rustc-demangle",
     "shlex",
+    "syn",
+    "unicode-ident",
     "unicode-width",
     "unwinding",
     "wasi",
@@ -500,6 +500,8 @@ const PERMITTED_STDLIB_DEPENDENCIES: &[&str] = &[
     "windows_x86_64_gnu",
     "windows_x86_64_gnullvm",
     "windows_x86_64_msvc",
+    "zerocopy",
+    "zerocopy-derive",
     // tidy-alphabetical-end
 ];
 
@@ -664,7 +666,7 @@ pub static CRATES: &[&str] = &[
         for extra in expected.difference(&proc_macro_deps) {
             tidy_error!(
                 bad,
-                "`{extra}` is not registered in `src/bootstrap/src/utils/proc_macro_deps.rs`, but is not a proc-macro crate dependency",
+                "`{extra}` is registered in `src/bootstrap/src/utils/proc_macro_deps.rs`, but is not a proc-macro crate dependency",
             );
         }
         if *bad != old_bad {
diff --git a/src/tools/tidy/src/issues.txt b/src/tools/tidy/src/issues.txt
index 39c9a148e9e..253e13375c7 100644
--- a/src/tools/tidy/src/issues.txt
+++ b/src/tools/tidy/src/issues.txt
@@ -3619,7 +3619,6 @@ ui/resolve/issue-21221-1.rs
 ui/resolve/issue-21221-2.rs
 ui/resolve/issue-21221-3.rs
 ui/resolve/issue-21221-4.rs
-ui/resolve/issue-22692.rs
 ui/resolve/issue-2330.rs
 ui/resolve/issue-23305.rs
 ui/resolve/issue-2356.rs
@@ -3828,7 +3827,6 @@ ui/suggestions/issue-103646.rs
 ui/suggestions/issue-104086-suggest-let.rs
 ui/suggestions/issue-104287.rs
 ui/suggestions/issue-104327.rs
-ui/suggestions/issue-104328.rs
 ui/suggestions/issue-104961.rs
 ui/suggestions/issue-105226.rs
 ui/suggestions/issue-105494.rs
diff --git a/src/version b/src/version
index b7844a6ffdc..f6342716723 100644
--- a/src/version
+++ b/src/version
@@ -1 +1 @@
-1.86.0
+1.87.0