about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--.github/workflows/post-merge.yml10
-rw-r--r--Cargo.toml14
-rw-r--r--compiler/rustc/Cargo.toml3
-rw-r--r--compiler/rustc_abi/Cargo.toml3
-rw-r--r--compiler/rustc_abi/src/lib.rs1
-rw-r--r--compiler/rustc_arena/Cargo.toml3
-rw-r--r--compiler/rustc_arena/src/lib.rs1
-rw-r--r--compiler/rustc_ast/Cargo.toml3
-rw-r--r--compiler/rustc_ast/src/lib.rs1
-rw-r--r--compiler/rustc_ast_ir/Cargo.toml3
-rw-r--r--compiler/rustc_ast_ir/src/lib.rs1
-rw-r--r--compiler/rustc_ast_lowering/Cargo.toml3
-rw-r--r--compiler/rustc_ast_lowering/src/delegation.rs30
-rw-r--r--compiler/rustc_ast_lowering/src/item.rs4
-rw-r--r--compiler/rustc_ast_lowering/src/lib.rs1
-rw-r--r--compiler/rustc_ast_passes/Cargo.toml3
-rw-r--r--compiler/rustc_ast_passes/src/lib.rs1
-rw-r--r--compiler/rustc_ast_pretty/Cargo.toml3
-rw-r--r--compiler/rustc_ast_pretty/src/lib.rs1
-rw-r--r--compiler/rustc_attr_data_structures/Cargo.toml3
-rw-r--r--compiler/rustc_attr_data_structures/src/lib.rs1
-rw-r--r--compiler/rustc_attr_parsing/Cargo.toml3
-rw-r--r--compiler/rustc_attr_parsing/src/lib.rs1
-rw-r--r--compiler/rustc_baked_icu_data/Cargo.toml3
-rw-r--r--compiler/rustc_baked_icu_data/src/lib.rs2
-rw-r--r--compiler/rustc_borrowck/Cargo.toml3
-rw-r--r--compiler/rustc_borrowck/src/lib.rs1
-rw-r--r--compiler/rustc_builtin_macros/Cargo.toml7
-rw-r--r--compiler/rustc_builtin_macros/src/lib.rs1
-rw-r--r--compiler/rustc_codegen_llvm/Cargo.toml3
-rw-r--r--compiler/rustc_codegen_llvm/src/lib.rs1
-rw-r--r--compiler/rustc_codegen_ssa/Cargo.toml3
-rw-r--r--compiler/rustc_codegen_ssa/src/lib.rs1
-rw-r--r--compiler/rustc_const_eval/Cargo.toml3
-rw-r--r--compiler/rustc_const_eval/src/lib.rs1
-rw-r--r--compiler/rustc_data_structures/Cargo.toml3
-rw-r--r--compiler/rustc_data_structures/src/graph/tests.rs4
-rw-r--r--compiler/rustc_data_structures/src/obligation_forest/mod.rs2
-rw-r--r--compiler/rustc_data_structures/src/sync.rs2
-rw-r--r--compiler/rustc_data_structures/src/sync/parallel.rs2
-rw-r--r--compiler/rustc_data_structures/src/tagged_ptr/tests.rs2
-rw-r--r--compiler/rustc_driver/Cargo.toml3
-rw-r--r--compiler/rustc_driver_impl/Cargo.toml3
-rw-r--r--compiler/rustc_driver_impl/src/lib.rs1
-rw-r--r--compiler/rustc_error_codes/Cargo.toml3
-rw-r--r--compiler/rustc_error_codes/src/lib.rs1
-rw-r--r--compiler/rustc_error_messages/Cargo.toml3
-rw-r--r--compiler/rustc_error_messages/src/lib.rs1
-rw-r--r--compiler/rustc_errors/Cargo.toml3
-rw-r--r--compiler/rustc_errors/src/lib.rs1
-rw-r--r--compiler/rustc_expand/Cargo.toml3
-rw-r--r--compiler/rustc_expand/src/lib.rs1
-rw-r--r--compiler/rustc_feature/Cargo.toml3
-rw-r--r--compiler/rustc_feature/src/lib.rs1
-rw-r--r--compiler/rustc_fluent_macro/Cargo.toml3
-rw-r--r--compiler/rustc_fluent_macro/src/lib.rs1
-rw-r--r--compiler/rustc_fs_util/Cargo.toml3
-rw-r--r--compiler/rustc_graphviz/Cargo.toml3
-rw-r--r--compiler/rustc_graphviz/src/lib.rs1
-rw-r--r--compiler/rustc_hashes/Cargo.toml3
-rw-r--r--compiler/rustc_hir/Cargo.toml3
-rw-r--r--compiler/rustc_hir/src/lib.rs1
-rw-r--r--compiler/rustc_hir_analysis/Cargo.toml3
-rw-r--r--compiler/rustc_hir_analysis/src/check/intrinsicck.rs110
-rw-r--r--compiler/rustc_hir_analysis/src/lib.rs1
-rw-r--r--compiler/rustc_hir_pretty/Cargo.toml3
-rw-r--r--compiler/rustc_hir_pretty/src/lib.rs1
-rw-r--r--compiler/rustc_hir_typeck/Cargo.toml3
-rw-r--r--compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs15
-rw-r--r--compiler/rustc_hir_typeck/src/lib.rs1
-rw-r--r--compiler/rustc_incremental/Cargo.toml3
-rw-r--r--compiler/rustc_incremental/src/lib.rs1
-rw-r--r--compiler/rustc_index/Cargo.toml3
-rw-r--r--compiler/rustc_index/src/lib.rs1
-rw-r--r--compiler/rustc_index_macros/Cargo.toml3
-rw-r--r--compiler/rustc_index_macros/src/newtype.rs2
-rw-r--r--compiler/rustc_infer/Cargo.toml3
-rw-r--r--compiler/rustc_infer/src/lib.rs1
-rw-r--r--compiler/rustc_interface/Cargo.toml3
-rw-r--r--compiler/rustc_interface/src/lib.rs1
-rw-r--r--compiler/rustc_lexer/Cargo.toml3
-rw-r--r--compiler/rustc_lexer/src/lib.rs1
-rw-r--r--compiler/rustc_lint/Cargo.toml3
-rw-r--r--compiler/rustc_lint/src/lib.rs1
-rw-r--r--compiler/rustc_lint_defs/Cargo.toml3
-rw-r--r--compiler/rustc_lint_defs/src/lib.rs4
-rw-r--r--compiler/rustc_llvm/Cargo.toml3
-rw-r--r--compiler/rustc_llvm/src/lib.rs1
-rw-r--r--compiler/rustc_log/Cargo.toml3
-rw-r--r--compiler/rustc_macros/Cargo.toml3
-rw-r--r--compiler/rustc_macros/src/lib.rs1
-rw-r--r--compiler/rustc_metadata/Cargo.toml3
-rw-r--r--compiler/rustc_metadata/src/lib.rs1
-rw-r--r--compiler/rustc_middle/Cargo.toml3
-rw-r--r--compiler/rustc_middle/src/lib.rs1
-rw-r--r--compiler/rustc_mir_build/Cargo.toml3
-rw-r--r--compiler/rustc_mir_build/src/lib.rs1
-rw-r--r--compiler/rustc_mir_dataflow/Cargo.toml3
-rw-r--r--compiler/rustc_mir_dataflow/src/lib.rs1
-rw-r--r--compiler/rustc_mir_transform/Cargo.toml3
-rw-r--r--compiler/rustc_mir_transform/src/lib.rs1
-rw-r--r--compiler/rustc_monomorphize/Cargo.toml3
-rw-r--r--compiler/rustc_monomorphize/src/lib.rs1
-rw-r--r--compiler/rustc_next_trait_solver/Cargo.toml3
-rw-r--r--compiler/rustc_next_trait_solver/src/lib.rs1
-rw-r--r--compiler/rustc_parse/Cargo.toml2
-rw-r--r--compiler/rustc_parse/src/lib.rs1
-rw-r--r--compiler/rustc_parse_format/Cargo.toml3
-rw-r--r--compiler/rustc_parse_format/src/lib.rs1
-rw-r--r--compiler/rustc_passes/Cargo.toml3
-rw-r--r--compiler/rustc_passes/src/lib.rs1
-rw-r--r--compiler/rustc_pattern_analysis/Cargo.toml3
-rw-r--r--compiler/rustc_pattern_analysis/src/lib.rs1
-rw-r--r--compiler/rustc_pattern_analysis/tests/common/mod.rs14
-rw-r--r--compiler/rustc_privacy/Cargo.toml3
-rw-r--r--compiler/rustc_privacy/src/lib.rs1
-rw-r--r--compiler/rustc_query_impl/Cargo.toml3
-rw-r--r--compiler/rustc_query_impl/src/lib.rs1
-rw-r--r--compiler/rustc_query_system/Cargo.toml3
-rw-r--r--compiler/rustc_query_system/src/lib.rs1
-rw-r--r--compiler/rustc_resolve/Cargo.toml3
-rw-r--r--compiler/rustc_resolve/src/lib.rs1
-rw-r--r--compiler/rustc_sanitizers/Cargo.toml3
-rw-r--r--compiler/rustc_sanitizers/src/lib.rs1
-rw-r--r--compiler/rustc_serialize/Cargo.toml3
-rw-r--r--compiler/rustc_serialize/src/lib.rs1
-rw-r--r--compiler/rustc_session/Cargo.toml3
-rw-r--r--compiler/rustc_session/src/lib.rs1
-rw-r--r--compiler/rustc_smir/Cargo.toml3
-rw-r--r--compiler/rustc_smir/src/lib.rs1
-rw-r--r--compiler/rustc_span/Cargo.toml3
-rw-r--r--compiler/rustc_span/src/lib.rs1
-rw-r--r--compiler/rustc_symbol_mangling/Cargo.toml3
-rw-r--r--compiler/rustc_symbol_mangling/src/lib.rs1
-rw-r--r--compiler/rustc_target/Cargo.toml3
-rw-r--r--compiler/rustc_target/src/lib.rs1
-rw-r--r--compiler/rustc_trait_selection/Cargo.toml3
-rw-r--r--compiler/rustc_trait_selection/src/lib.rs1
-rw-r--r--compiler/rustc_traits/Cargo.toml3
-rw-r--r--compiler/rustc_traits/src/lib.rs1
-rw-r--r--compiler/rustc_transmute/Cargo.toml3
-rw-r--r--compiler/rustc_transmute/src/lib.rs1
-rw-r--r--compiler/rustc_ty_utils/Cargo.toml3
-rw-r--r--compiler/rustc_ty_utils/src/lib.rs1
-rw-r--r--compiler/rustc_type_ir/Cargo.toml4
-rw-r--r--compiler/rustc_type_ir/src/lib.rs1
-rw-r--r--compiler/rustc_type_ir_macros/Cargo.toml3
-rw-r--r--compiler/stable_mir/Cargo.toml3
-rw-r--r--src/bootstrap/src/core/builder/cargo.rs13
-rw-r--r--src/ci/citool/src/metrics.rs10
-rw-r--r--src/doc/unstable-book/src/language-features/rustc-private.md30
-rw-r--r--src/librustdoc/doctest/runner.rs19
-rw-r--r--src/tools/rust-analyzer/.editorconfig3
-rw-r--r--src/tools/rust-analyzer/.github/workflows/ci.yaml73
-rw-r--r--src/tools/rust-analyzer/Cargo.lock45
-rw-r--r--src/tools/rust-analyzer/Cargo.toml20
-rw-r--r--src/tools/rust-analyzer/crates/cfg/src/cfg_expr.rs19
-rw-r--r--src/tools/rust-analyzer/crates/cfg/src/dnf.rs19
-rw-r--r--src/tools/rust-analyzer/crates/cfg/src/lib.rs20
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/hir/type_ref.rs2
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mbe/tt_conversion.rs8
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/path.rs16
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/resolver.rs166
-rw-r--r--src/tools/rust-analyzer/crates/hir-expand/src/fixup.rs68
-rw-r--r--src/tools/rust-analyzer/crates/hir-expand/src/lib.rs2
-rw-r--r--src/tools/rust-analyzer/crates/hir-expand/src/mod_path.rs4
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/Cargo.toml1
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/chalk_db.rs8
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/consteval.rs17
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/consteval/tests.rs21
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/diagnostics.rs5
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/expr.rs15
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/unsafe_check.rs42
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/display.rs109
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/layout.rs81
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/layout/tests.rs2
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/lib.rs13
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/mir.rs4
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/mir/borrowck.rs6
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/mir/eval.rs31
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/mir/eval/shim.rs4
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/mir/eval/tests.rs5
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/mir/lower.rs57
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/mir/lower/pattern_matching.rs7
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/mir/pretty.rs63
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/tests.rs47
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/tests/closure_captures.rs8
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/tests/simple.rs39
-rw-r--r--src/tools/rust-analyzer/crates/hir/src/has_source.rs12
-rw-r--r--src/tools/rust-analyzer/crates/hir/src/lib.rs74
-rw-r--r--src/tools/rust-analyzer/crates/hir/src/semantics.rs214
-rw-r--r--src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs4
-rw-r--r--src/tools/rust-analyzer/crates/hir/src/symbols.rs14
-rw-r--r--src/tools/rust-analyzer/crates/hir/src/term_search/expr.rs50
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_explicit_enum_discriminant.rs (renamed from src/tools/rust-analyzer/crates/ide-assists/src/handlers/explicit_enum_discriminant.rs)23
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/handlers/apply_demorgan.rs14
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_bool_to_enum.rs (renamed from src/tools/rust-analyzer/crates/ide-assists/src/handlers/bool_to_enum.rs)96
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/handlers/expand_rest_pattern.rs (renamed from src/tools/rust-analyzer/crates/ide-assists/src/handlers/fill_record_pattern_fields.rs)205
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_function.rs5
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_function.rs11
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_getter_or_setter.rs2
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/handlers/inline_const_as_literal.rs2
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/handlers/inline_local_variable.rs118
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/handlers/introduce_named_type_parameter.rs (renamed from src/tools/rust-analyzer/crates/ide-assists/src/handlers/introduce_named_generic.rs)36
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/handlers/term_search.rs9
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/lib.rs60
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/tests.rs30
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/tests/generated.rs169
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/utils.rs44
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/completions/expr.rs3
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/completions/keyword.rs30
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/context.rs7
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/context/tests.rs2
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/item.rs8
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/render.rs119
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/render/const_.rs2
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/render/function.rs32
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/render/literal.rs14
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/render/macro_.rs2
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/render/type_alias.rs2
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/render/union_literal.rs2
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/render/variant.rs22
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/tests/expression.rs15
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/tests/item.rs1
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/tests/special.rs4
-rw-r--r--src/tools/rust-analyzer/crates/ide-db/src/defs.rs93
-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/path_transform.rs4
-rw-r--r--src/tools/rust-analyzer/crates/ide-db/src/rename.rs36
-rw-r--r--src/tools/rust-analyzer/crates/ide-db/src/source_change.rs45
-rw-r--r--src/tools/rust-analyzer/crates/ide-db/src/symbol_index.rs3
-rw-r--r--src/tools/rust-analyzer/crates/ide-db/src/syntax_helpers/node_ext.rs22
-rw-r--r--src/tools/rust-analyzer/crates/ide-db/src/text_edit.rs15
-rw-r--r--src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/expected_function.rs2
-rw-r--r--src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/invalid_cast.rs2
-rw-r--r--src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/moved_out_of_ref.rs2
-rw-r--r--src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/trait_impl_redundant_assoc_item.rs4
-rw-r--r--src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/type_mismatch.rs8
-rw-r--r--src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/typed_hole.rs4
-rw-r--r--src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unresolved_field.rs2
-rw-r--r--src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unresolved_method.rs4
-rw-r--r--src/tools/rust-analyzer/crates/ide-diagnostics/src/lib.rs19
-rw-r--r--src/tools/rust-analyzer/crates/ide-ssr/src/matching.rs20
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/hover.rs54
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/hover/render.rs124
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/hover/tests.rs39
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/inlay_hints.rs28
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/inlay_hints/adjustment.rs10
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/inlay_hints/bind_pat.rs7
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/inlay_hints/chaining.rs6
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/inlay_hints/closing_brace.rs7
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/inlay_hints/closure_ret.rs6
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/interpret.rs15
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/moniker.rs14
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/navigation_target.rs61
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/rename.rs81
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/runnables.rs14
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/signature_help.rs55
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/ssr.rs7
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/static_index.rs12
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/status.rs3
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/syntax_highlighting.rs138
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/format.rs2
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/highlight.rs353
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/html.rs7
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_asm.html33
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_assoc_functions.html7
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_attributes.html7
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_block_mod_items.html9
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_const.html21
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_crate_root.html7
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_default_library.html7
-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_extern_crate.html7
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_general.html9
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_injection.html7
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_issue_18089.html7
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_keywords_2015.html7
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_keywords_2018.html7
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_keywords_2021.html7
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_keywords_2024.html7
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_keywords_macros.html7
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_lifetimes.html7
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_macros.html7
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_module_docs_inline.html7
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_module_docs_outline.html7
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_operators.html7
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_rainbow.html7
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_strings.html11
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_unsafe.html94
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/tests.rs75
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/view_memory_layout.rs20
-rw-r--r--src/tools/rust-analyzer/crates/intern/src/symbol.rs31
-rw-r--r--src/tools/rust-analyzer/crates/intern/src/symbol/symbols.rs1
-rw-r--r--src/tools/rust-analyzer/crates/load-cargo/src/lib.rs8
-rw-r--r--src/tools/rust-analyzer/crates/mbe/src/tests.rs17
-rw-r--r--src/tools/rust-analyzer/crates/parser/src/syntax_kind/generated.rs2
-rw-r--r--src/tools/rust-analyzer/crates/profile/src/memory_usage.rs2
-rw-r--r--src/tools/rust-analyzer/crates/project-model/src/cargo_workspace.rs18
-rw-r--r--src/tools/rust-analyzer/crates/project-model/src/project_json.rs2
-rw-r--r--src/tools/rust-analyzer/crates/project-model/src/sysroot.rs137
-rw-r--r--src/tools/rust-analyzer/crates/project-model/src/tests.rs4
-rw-r--r--src/tools/rust-analyzer/crates/project-model/src/workspace.rs64
-rw-r--r--src/tools/rust-analyzer/crates/project-model/test_data/output/rust_project_cfg_groups.txt501
-rw-r--r--src/tools/rust-analyzer/crates/project-model/test_data/output/rust_project_hello_world_project_model.txt454
-rw-r--r--src/tools/rust-analyzer/crates/rust-analyzer/src/cli/analysis_stats.rs43
-rw-r--r--src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs56
-rw-r--r--src/tools/rust-analyzer/crates/rust-analyzer/src/flycheck.rs26
-rw-r--r--src/tools/rust-analyzer/crates/rust-analyzer/src/global_state.rs19
-rw-r--r--src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/request.rs23
-rw-r--r--src/tools/rust-analyzer/crates/rust-analyzer/src/lib.rs121
-rw-r--r--src/tools/rust-analyzer/crates/rust-analyzer/src/lsp.rs113
-rw-r--r--src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/to_proto.rs56
-rw-r--r--src/tools/rust-analyzer/crates/rust-analyzer/src/reload.rs10
-rw-r--r--src/tools/rust-analyzer/crates/syntax-bridge/src/lib.rs70
-rw-r--r--src/tools/rust-analyzer/crates/syntax/rust.ungram5
-rw-r--r--src/tools/rust-analyzer/crates/syntax/src/ast/generated/nodes.rs50
-rw-r--r--src/tools/rust-analyzer/crates/syntax/src/ast/prec.rs63
-rw-r--r--src/tools/rust-analyzer/crates/syntax/src/ast/syntax_factory/constructors.rs21
-rw-r--r--src/tools/rust-analyzer/crates/syntax/src/validation.rs36
-rw-r--r--src/tools/rust-analyzer/crates/syntax/test_data/parser/validation/0224_dangling_dyn.rast25
-rw-r--r--src/tools/rust-analyzer/crates/syntax/test_data/parser/validation/0224_dangling_dyn.rs1
-rw-r--r--src/tools/rust-analyzer/crates/syntax/test_data/parser/validation/dangling_impl.rast23
-rw-r--r--src/tools/rust-analyzer/crates/syntax/test_data/parser/validation/dangling_impl.rs1
-rw-r--r--src/tools/rust-analyzer/crates/syntax/test_data/parser/validation/dangling_impl_reference.rast25
-rw-r--r--src/tools/rust-analyzer/crates/syntax/test_data/parser/validation/dangling_impl_reference.rs1
-rw-r--r--src/tools/rust-analyzer/crates/test-utils/src/minicore.rs7
-rw-r--r--src/tools/rust-analyzer/crates/tt/src/lib.rs30
-rw-r--r--src/tools/rust-analyzer/docs/book/src/assists_generated.md206
-rw-r--r--src/tools/rust-analyzer/docs/book/src/configuration_generated.md4
-rw-r--r--src/tools/rust-analyzer/editors/code/package.json2
-rw-r--r--src/tools/rust-analyzer/editors/code/src/config.ts1
-rw-r--r--src/tools/rust-analyzer/editors/code/src/ctx.ts6
-rw-r--r--src/tools/rust-analyzer/editors/code/src/run.ts19
-rw-r--r--src/tools/rust-analyzer/editors/code/src/util.ts4
-rw-r--r--src/tools/rust-analyzer/rust-version2
-rw-r--r--tests/crashes/127916.rs16
-rw-r--r--tests/crashes/128119.rs15
-rw-r--r--tests/crashes/128190.rs7
-rw-r--r--tests/ui/asm/invalid-const-operand.rs8
-rw-r--r--tests/ui/asm/invalid-const-operand.stderr8
-rw-r--r--tests/ui/asm/tainting-on-error.rs13
-rw-r--r--tests/ui/asm/tainting-on-error.stderr10
-rw-r--r--tests/ui/asm/x86_64/type-check-2.rs8
-rw-r--r--tests/ui/asm/x86_64/type-check-2.stderr16
-rw-r--r--tests/ui/delegation/ice-isssue-128190.rs9
-rw-r--r--tests/ui/delegation/ice-isssue-128190.stderr10
347 files changed, 4967 insertions, 2279 deletions
diff --git a/.github/workflows/post-merge.yml b/.github/workflows/post-merge.yml
index d3f42c5a905..2bc06d83c5b 100644
--- a/.github/workflows/post-merge.yml
+++ b/.github/workflows/post-merge.yml
@@ -16,15 +16,21 @@ jobs:
       pull-requests: write
     steps:
       - uses: actions/checkout@v4
+        with:
+          # Make sure that we have enough commits to find the parent merge commit.
+          # Since all merges should be through merge commits, fetching two commits
+          # should be enough to get the parent bors merge commit.
+          fetch-depth: 2
       - name: Perform analysis and send PR
+        env:
+          GH_TOKEN: ${{ github.token }}
         run: |
           # Get closest bors merge commit
           PARENT_COMMIT=`git rev-list --author='bors <bors@rust-lang.org>' -n1 --first-parent HEAD^1`
+          echo "Parent: ${PARENT_COMMIT}"
 
           # Find PR for the current commit
           HEAD_PR=`gh pr list --search "${{ github.sha }}" --state merged --json number --jq '.[0].number'`
-
-          echo "Parent: ${PARENT_COMMIT}"
           echo "HEAD: ${{ github.sha }} (#${HEAD_PR})"
 
           cd src/ci/citool
diff --git a/Cargo.toml b/Cargo.toml
index e2d032e0806..20a43aaaeeb 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -63,20 +63,6 @@ exclude = [
   "src/tools/x",
 ]
 
-# These lints are applied to many crates in the workspace. In practice, this is
-# all crates under `compiler/`.
-#
-# NOTE: rustc-specific lints (e.g. `rustc::internal`) aren't supported by
-# Cargo. (Support for them is possibly blocked by #44690 (attributes for
-# tools).) Those lints are instead specified for `compiler/` crates in
-# `src/bootstrap/src/core/builder/cargo.rs`.
-[workspace.lints.rust]
-# FIXME(edition_2024): Change this to `-Wrust_2024_idioms` when all of the
-# individual lints are satisfied.
-keyword_idents_2024 = "warn"
-unreachable_pub = "warn"
-unsafe_op_in_unsafe_fn = "warn"
-
 [profile.release.package.rustc-rayon-core]
 # The rustc fork of Rayon has deadlock detection code which intermittently
 # causes overflows in the CI (see https://github.com/rust-lang/rust/issues/90227)
diff --git a/compiler/rustc/Cargo.toml b/compiler/rustc/Cargo.toml
index 7af0b34d2da..f4caa3ef769 100644
--- a/compiler/rustc/Cargo.toml
+++ b/compiler/rustc/Cargo.toml
@@ -32,6 +32,3 @@ llvm = ['rustc_driver_impl/llvm']
 max_level_info = ['rustc_driver_impl/max_level_info']
 rustc_randomized_layouts = ['rustc_driver_impl/rustc_randomized_layouts']
 # tidy-alphabetical-end
-
-[lints]
-workspace = true
diff --git a/compiler/rustc_abi/Cargo.toml b/compiler/rustc_abi/Cargo.toml
index 4713b3474bd..86dc84e2016 100644
--- a/compiler/rustc_abi/Cargo.toml
+++ b/compiler/rustc_abi/Cargo.toml
@@ -31,6 +31,3 @@ nightly = [
 ]
 randomize = ["dep:rand", "dep:rand_xoshiro", "nightly"]
 # tidy-alphabetical-end
-
-[lints]
-workspace = true
diff --git a/compiler/rustc_abi/src/lib.rs b/compiler/rustc_abi/src/lib.rs
index 078b676e40e..1b737582009 100644
--- a/compiler/rustc_abi/src/lib.rs
+++ b/compiler/rustc_abi/src/lib.rs
@@ -5,6 +5,7 @@
 #![cfg_attr(feature = "nightly", feature(rustc_attrs))]
 #![cfg_attr(feature = "nightly", feature(rustdoc_internals))]
 #![cfg_attr(feature = "nightly", feature(step_trait))]
+#![warn(unreachable_pub)]
 // tidy-alphabetical-end
 
 /*! ABI handling for rustc
diff --git a/compiler/rustc_arena/Cargo.toml b/compiler/rustc_arena/Cargo.toml
index 1a600f0ee3f..bbcd8ea6d38 100644
--- a/compiler/rustc_arena/Cargo.toml
+++ b/compiler/rustc_arena/Cargo.toml
@@ -7,6 +7,3 @@ edition = "2024"
 # tidy-alphabetical-start
 smallvec = { version = "1.8.1", features = ["union", "may_dangle"] }
 # tidy-alphabetical-end
-
-[lints]
-workspace = true
diff --git a/compiler/rustc_arena/src/lib.rs b/compiler/rustc_arena/src/lib.rs
index 6aaac072e4b..3b44e996358 100644
--- a/compiler/rustc_arena/src/lib.rs
+++ b/compiler/rustc_arena/src/lib.rs
@@ -23,6 +23,7 @@
 #![feature(maybe_uninit_slice)]
 #![feature(rustc_attrs)]
 #![feature(rustdoc_internals)]
+#![warn(unreachable_pub)]
 // tidy-alphabetical-end
 
 use std::alloc::Layout;
diff --git a/compiler/rustc_ast/Cargo.toml b/compiler/rustc_ast/Cargo.toml
index 259b51689e4..902287d0328 100644
--- a/compiler/rustc_ast/Cargo.toml
+++ b/compiler/rustc_ast/Cargo.toml
@@ -18,6 +18,3 @@ smallvec = { version = "1.8.1", features = ["union", "may_dangle"] }
 thin-vec = "0.2.12"
 tracing = "0.1"
 # tidy-alphabetical-end
-
-[lints]
-workspace = true
diff --git a/compiler/rustc_ast/src/lib.rs b/compiler/rustc_ast/src/lib.rs
index 294c6c9ba7a..6372c66050e 100644
--- a/compiler/rustc_ast/src/lib.rs
+++ b/compiler/rustc_ast/src/lib.rs
@@ -19,6 +19,7 @@
 #![feature(never_type)]
 #![feature(rustdoc_internals)]
 #![feature(stmt_expr_attributes)]
+#![warn(unreachable_pub)]
 // tidy-alphabetical-end
 
 pub mod util {
diff --git a/compiler/rustc_ast_ir/Cargo.toml b/compiler/rustc_ast_ir/Cargo.toml
index 668c45438d6..f54e9687d8c 100644
--- a/compiler/rustc_ast_ir/Cargo.toml
+++ b/compiler/rustc_ast_ir/Cargo.toml
@@ -19,6 +19,3 @@ nightly = [
     "dep:rustc_macros",
     "dep:rustc_span",
 ]
-
-[lints]
-workspace = true
diff --git a/compiler/rustc_ast_ir/src/lib.rs b/compiler/rustc_ast_ir/src/lib.rs
index 6d05cd18cec..9884e191ea7 100644
--- a/compiler/rustc_ast_ir/src/lib.rs
+++ b/compiler/rustc_ast_ir/src/lib.rs
@@ -9,6 +9,7 @@
 #![cfg_attr(feature = "nightly", allow(internal_features))]
 #![cfg_attr(feature = "nightly", feature(never_type))]
 #![cfg_attr(feature = "nightly", feature(rustc_attrs))]
+#![warn(unreachable_pub)]
 // tidy-alphabetical-end
 
 #[cfg(feature = "nightly")]
diff --git a/compiler/rustc_ast_lowering/Cargo.toml b/compiler/rustc_ast_lowering/Cargo.toml
index 358963c7997..2ec4f4b0555 100644
--- a/compiler/rustc_ast_lowering/Cargo.toml
+++ b/compiler/rustc_ast_lowering/Cargo.toml
@@ -28,6 +28,3 @@ smallvec = { version = "1.8.1", features = ["union", "may_dangle"] }
 thin-vec = "0.2.12"
 tracing = "0.1"
 # tidy-alphabetical-end
-
-[lints]
-workspace = true
diff --git a/compiler/rustc_ast_lowering/src/delegation.rs b/compiler/rustc_ast_lowering/src/delegation.rs
index d8b7cb0c322..571172be4ed 100644
--- a/compiler/rustc_ast_lowering/src/delegation.rs
+++ b/compiler/rustc_ast_lowering/src/delegation.rs
@@ -60,25 +60,27 @@ pub(crate) struct DelegationResults<'hir> {
 }
 
 impl<'hir> LoweringContext<'_, 'hir> {
-    pub(crate) fn delegation_has_self(&self, item_id: NodeId, path_id: NodeId, span: Span) -> bool {
+    /// Defines whether the delegatee is an associated function whose first parameter is `self`.
+    pub(crate) fn delegatee_is_method(&self, item_id: NodeId, path_id: NodeId, span: Span) -> bool {
         let sig_id = self.get_delegation_sig_id(item_id, path_id, span);
         let Ok(sig_id) = sig_id else {
             return false;
         };
-        self.has_self(sig_id, span)
+        self.is_method(sig_id, span)
     }
 
-    fn has_self(&self, def_id: DefId, span: Span) -> bool {
-        if let Some(local_sig_id) = def_id.as_local() {
-            // The value may be missing due to recursive delegation.
-            // Error will be emitted later during HIR ty lowering.
-            self.resolver.delegation_fn_sigs.get(&local_sig_id).is_some_and(|sig| sig.has_self)
-        } else {
-            match self.tcx.def_kind(def_id) {
-                DefKind::Fn => false,
-                DefKind::AssocFn => self.tcx.associated_item(def_id).fn_has_self_parameter,
-                _ => span_bug!(span, "unexpected DefKind for delegation item"),
-            }
+    fn is_method(&self, def_id: DefId, span: Span) -> bool {
+        match self.tcx.def_kind(def_id) {
+            DefKind::Fn => false,
+            DefKind::AssocFn => match def_id.as_local() {
+                Some(local_def_id) => self
+                    .resolver
+                    .delegation_fn_sigs
+                    .get(&local_def_id)
+                    .is_some_and(|sig| sig.has_self),
+                None => self.tcx.associated_item(def_id).fn_has_self_parameter,
+            },
+            _ => span_bug!(span, "unexpected DefKind for delegation item"),
         }
     }
 
@@ -324,7 +326,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
 
         let call = if self
             .get_resolution_id(delegation.id, span)
-            .and_then(|def_id| Ok(self.has_self(def_id, span)))
+            .and_then(|def_id| Ok(self.is_method(def_id, span)))
             .unwrap_or_default()
             && delegation.qself.is_none()
             && !has_generic_args
diff --git a/compiler/rustc_ast_lowering/src/item.rs b/compiler/rustc_ast_lowering/src/item.rs
index 5e89321e6ec..b01ca54fb6f 100644
--- a/compiler/rustc_ast_lowering/src/item.rs
+++ b/compiler/rustc_ast_lowering/src/item.rs
@@ -871,7 +871,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
                 hir::AssocItemKind::Fn { has_self: sig.decl.has_self() }
             }
             AssocItemKind::Delegation(box delegation) => hir::AssocItemKind::Fn {
-                has_self: self.delegation_has_self(i.id, delegation.id, i.span),
+                has_self: self.delegatee_is_method(i.id, delegation.id, i.span),
             },
             AssocItemKind::MacCall(..) | AssocItemKind::DelegationMac(..) => {
                 panic!("macros should have been expanded by now")
@@ -1000,7 +1000,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
                     hir::AssocItemKind::Fn { has_self: sig.decl.has_self() }
                 }
                 AssocItemKind::Delegation(box delegation) => hir::AssocItemKind::Fn {
-                    has_self: self.delegation_has_self(i.id, delegation.id, i.span),
+                    has_self: self.delegatee_is_method(i.id, delegation.id, i.span),
                 },
                 AssocItemKind::MacCall(..) | AssocItemKind::DelegationMac(..) => {
                     panic!("macros should have been expanded by now")
diff --git a/compiler/rustc_ast_lowering/src/lib.rs b/compiler/rustc_ast_lowering/src/lib.rs
index 4cecc56909e..1c4edd8348f 100644
--- a/compiler/rustc_ast_lowering/src/lib.rs
+++ b/compiler/rustc_ast_lowering/src/lib.rs
@@ -38,6 +38,7 @@
 #![feature(if_let_guard)]
 #![feature(let_chains)]
 #![feature(rustdoc_internals)]
+#![warn(unreachable_pub)]
 // tidy-alphabetical-end
 
 use std::sync::Arc;
diff --git a/compiler/rustc_ast_passes/Cargo.toml b/compiler/rustc_ast_passes/Cargo.toml
index 5966308a262..c738cb2aa2f 100644
--- a/compiler/rustc_ast_passes/Cargo.toml
+++ b/compiler/rustc_ast_passes/Cargo.toml
@@ -20,6 +20,3 @@ rustc_session = { path = "../rustc_session" }
 rustc_span = { path = "../rustc_span" }
 thin-vec = "0.2.12"
 # tidy-alphabetical-end
-
-[lints]
-workspace = true
diff --git a/compiler/rustc_ast_passes/src/lib.rs b/compiler/rustc_ast_passes/src/lib.rs
index 093199cf342..b4ed70d83e5 100644
--- a/compiler/rustc_ast_passes/src/lib.rs
+++ b/compiler/rustc_ast_passes/src/lib.rs
@@ -10,6 +10,7 @@
 #![feature(iter_is_partitioned)]
 #![feature(let_chains)]
 #![feature(rustdoc_internals)]
+#![warn(unreachable_pub)]
 // tidy-alphabetical-end
 
 pub mod ast_validation;
diff --git a/compiler/rustc_ast_pretty/Cargo.toml b/compiler/rustc_ast_pretty/Cargo.toml
index b120bdc2f05..2634dd1fdf9 100644
--- a/compiler/rustc_ast_pretty/Cargo.toml
+++ b/compiler/rustc_ast_pretty/Cargo.toml
@@ -12,6 +12,3 @@ rustc_lexer = { path = "../rustc_lexer" }
 rustc_span = { path = "../rustc_span" }
 thin-vec = "0.2.12"
 # tidy-alphabetical-end
-
-[lints]
-workspace = true
diff --git a/compiler/rustc_ast_pretty/src/lib.rs b/compiler/rustc_ast_pretty/src/lib.rs
index 84d9ce278a2..602ab69ee5b 100644
--- a/compiler/rustc_ast_pretty/src/lib.rs
+++ b/compiler/rustc_ast_pretty/src/lib.rs
@@ -3,6 +3,7 @@
 #![doc(rust_logo)]
 #![feature(box_patterns)]
 #![feature(rustdoc_internals)]
+#![warn(unreachable_pub)]
 // tidy-alphabetical-end
 
 mod helpers;
diff --git a/compiler/rustc_attr_data_structures/Cargo.toml b/compiler/rustc_attr_data_structures/Cargo.toml
index 8fbc21f3ba2..b18923c337f 100644
--- a/compiler/rustc_attr_data_structures/Cargo.toml
+++ b/compiler/rustc_attr_data_structures/Cargo.toml
@@ -14,6 +14,3 @@ rustc_serialize = {path = "../rustc_serialize"}
 rustc_span = {path = "../rustc_span"}
 thin-vec = "0.2.12"
 # tidy-alphabetical-end
-
-[lints]
-workspace = true
diff --git a/compiler/rustc_attr_data_structures/src/lib.rs b/compiler/rustc_attr_data_structures/src/lib.rs
index be00d1c10e0..f4986d1d187 100644
--- a/compiler/rustc_attr_data_structures/src/lib.rs
+++ b/compiler/rustc_attr_data_structures/src/lib.rs
@@ -3,6 +3,7 @@
 #![doc(rust_logo)]
 #![feature(let_chains)]
 #![feature(rustdoc_internals)]
+#![warn(unreachable_pub)]
 // tidy-alphabetical-end
 
 mod attributes;
diff --git a/compiler/rustc_attr_parsing/Cargo.toml b/compiler/rustc_attr_parsing/Cargo.toml
index 24b552e2de4..6b4ce957630 100644
--- a/compiler/rustc_attr_parsing/Cargo.toml
+++ b/compiler/rustc_attr_parsing/Cargo.toml
@@ -22,6 +22,3 @@ rustc_session = { path = "../rustc_session" }
 rustc_span = { path = "../rustc_span" }
 thin-vec = "0.2.12"
 # tidy-alphabetical-end
-
-[lints]
-workspace = true
diff --git a/compiler/rustc_attr_parsing/src/lib.rs b/compiler/rustc_attr_parsing/src/lib.rs
index 249e71ef70d..386f2c98447 100644
--- a/compiler/rustc_attr_parsing/src/lib.rs
+++ b/compiler/rustc_attr_parsing/src/lib.rs
@@ -80,6 +80,7 @@
 #![doc(rust_logo)]
 #![feature(let_chains)]
 #![feature(rustdoc_internals)]
+#![warn(unreachable_pub)]
 // tidy-alphabetical-end
 
 #[macro_use]
diff --git a/compiler/rustc_baked_icu_data/Cargo.toml b/compiler/rustc_baked_icu_data/Cargo.toml
index 1480b59f1e0..cb0e145386b 100644
--- a/compiler/rustc_baked_icu_data/Cargo.toml
+++ b/compiler/rustc_baked_icu_data/Cargo.toml
@@ -11,6 +11,3 @@ icu_locid_transform = "1.3.2"
 icu_provider = { version = "1.2", features = ["sync"] }
 zerovec = "0.10.0"
 # tidy-alphabetical-end
-
-[lints]
-workspace = true
diff --git a/compiler/rustc_baked_icu_data/src/lib.rs b/compiler/rustc_baked_icu_data/src/lib.rs
index df9bee0ebf5..f86a9db61c6 100644
--- a/compiler/rustc_baked_icu_data/src/lib.rs
+++ b/compiler/rustc_baked_icu_data/src/lib.rs
@@ -23,9 +23,9 @@
 // tidy-alphabetical-start
 #![allow(elided_lifetimes_in_paths)]
 #![allow(internal_features)]
-#![allow(unreachable_pub)] // because this crate is mostly generated code
 #![doc(rust_logo)]
 #![feature(rustdoc_internals)]
+// #![warn(unreachable_pub)] // don't use because this crate is mostly generated code
 // tidy-alphabetical-end
 
 mod data {
diff --git a/compiler/rustc_borrowck/Cargo.toml b/compiler/rustc_borrowck/Cargo.toml
index 15338eeb37a..9e7d55180a2 100644
--- a/compiler/rustc_borrowck/Cargo.toml
+++ b/compiler/rustc_borrowck/Cargo.toml
@@ -27,6 +27,3 @@ rustc_traits = { path = "../rustc_traits" }
 smallvec = { version = "1.8.1", features = ["union", "may_dangle"] }
 tracing = "0.1"
 # tidy-alphabetical-end
-
-[lints]
-workspace = true
diff --git a/compiler/rustc_borrowck/src/lib.rs b/compiler/rustc_borrowck/src/lib.rs
index 4d3774682ce..64a533e05ff 100644
--- a/compiler/rustc_borrowck/src/lib.rs
+++ b/compiler/rustc_borrowck/src/lib.rs
@@ -13,6 +13,7 @@
 #![feature(rustdoc_internals)]
 #![feature(stmt_expr_attributes)]
 #![feature(try_blocks)]
+#![warn(unreachable_pub)]
 // tidy-alphabetical-end
 
 use std::borrow::Cow;
diff --git a/compiler/rustc_builtin_macros/Cargo.toml b/compiler/rustc_builtin_macros/Cargo.toml
index da3572eebee..b5f4f2efd1f 100644
--- a/compiler/rustc_builtin_macros/Cargo.toml
+++ b/compiler/rustc_builtin_macros/Cargo.toml
@@ -3,6 +3,10 @@ name = "rustc_builtin_macros"
 version = "0.0.0"
 edition = "2024"
 
+
+[lints.rust]
+unexpected_cfgs = { level = "warn", check-cfg = ['cfg(llvm_enzyme)'] }
+
 [lib]
 doctest = false
 
@@ -30,6 +34,3 @@ smallvec = { version = "1.8.1", features = ["union", "may_dangle"] }
 thin-vec = "0.2.12"
 tracing = "0.1"
 # tidy-alphabetical-end
-
-[lints]
-workspace = true
diff --git a/compiler/rustc_builtin_macros/src/lib.rs b/compiler/rustc_builtin_macros/src/lib.rs
index c23ce1e5e4a..ca16583a45d 100644
--- a/compiler/rustc_builtin_macros/src/lib.rs
+++ b/compiler/rustc_builtin_macros/src/lib.rs
@@ -18,6 +18,7 @@
 #![feature(rustdoc_internals)]
 #![feature(string_from_utf8_lossy_owned)]
 #![feature(try_blocks)]
+#![warn(unreachable_pub)]
 // tidy-alphabetical-end
 
 extern crate proc_macro;
diff --git a/compiler/rustc_codegen_llvm/Cargo.toml b/compiler/rustc_codegen_llvm/Cargo.toml
index 1741c3bacc7..d3ce7c5a113 100644
--- a/compiler/rustc_codegen_llvm/Cargo.toml
+++ b/compiler/rustc_codegen_llvm/Cargo.toml
@@ -43,6 +43,3 @@ serde_json = "1"
 smallvec = { version = "1.8.1", features = ["union", "may_dangle"] }
 tracing = "0.1"
 # tidy-alphabetical-end
-
-[lints]
-workspace = true
diff --git a/compiler/rustc_codegen_llvm/src/lib.rs b/compiler/rustc_codegen_llvm/src/lib.rs
index 2f3c8c75b0b..e51d4852db2 100644
--- a/compiler/rustc_codegen_llvm/src/lib.rs
+++ b/compiler/rustc_codegen_llvm/src/lib.rs
@@ -19,6 +19,7 @@
 #![feature(rustdoc_internals)]
 #![feature(slice_as_array)]
 #![feature(try_blocks)]
+#![warn(unreachable_pub)]
 // tidy-alphabetical-end
 
 use std::any::Any;
diff --git a/compiler/rustc_codegen_ssa/Cargo.toml b/compiler/rustc_codegen_ssa/Cargo.toml
index c00be0e9926..1346efcb87c 100644
--- a/compiler/rustc_codegen_ssa/Cargo.toml
+++ b/compiler/rustc_codegen_ssa/Cargo.toml
@@ -63,6 +63,3 @@ features = ["read_core", "elf", "macho", "pe", "xcoff", "unaligned", "archive",
 [target.'cfg(windows)'.dependencies.windows]
 version = "0.59.0"
 features = ["Win32_Globalization"]
-
-[lints]
-workspace = true
diff --git a/compiler/rustc_codegen_ssa/src/lib.rs b/compiler/rustc_codegen_ssa/src/lib.rs
index 8ad04032414..4e758bfdec3 100644
--- a/compiler/rustc_codegen_ssa/src/lib.rs
+++ b/compiler/rustc_codegen_ssa/src/lib.rs
@@ -14,6 +14,7 @@
 #![feature(rustdoc_internals)]
 #![feature(trait_alias)]
 #![feature(try_blocks)]
+#![warn(unreachable_pub)]
 // tidy-alphabetical-end
 
 //! This crate contains codegen code that is used by all codegen backends (LLVM and others).
diff --git a/compiler/rustc_const_eval/Cargo.toml b/compiler/rustc_const_eval/Cargo.toml
index d76238a3992..a0cc2c65e6e 100644
--- a/compiler/rustc_const_eval/Cargo.toml
+++ b/compiler/rustc_const_eval/Cargo.toml
@@ -26,6 +26,3 @@ rustc_trait_selection = { path = "../rustc_trait_selection" }
 rustc_type_ir = { path = "../rustc_type_ir" }
 tracing = "0.1"
 # tidy-alphabetical-end
-
-[lints]
-workspace = true
diff --git a/compiler/rustc_const_eval/src/lib.rs b/compiler/rustc_const_eval/src/lib.rs
index da52d60ae59..ed5489652fb 100644
--- a/compiler/rustc_const_eval/src/lib.rs
+++ b/compiler/rustc_const_eval/src/lib.rs
@@ -16,6 +16,7 @@
 #![feature(unqualified_local_imports)]
 #![feature(yeet_expr)]
 #![warn(unqualified_local_imports)]
+#![warn(unreachable_pub)]
 // tidy-alphabetical-end
 
 pub mod check_consts;
diff --git a/compiler/rustc_data_structures/Cargo.toml b/compiler/rustc_data_structures/Cargo.toml
index b364ab0dde4..bdf5494f210 100644
--- a/compiler/rustc_data_structures/Cargo.toml
+++ b/compiler/rustc_data_structures/Cargo.toml
@@ -54,6 +54,3 @@ memmap2 = "0.2.1"
 
 [target.'cfg(not(target_has_atomic = "64"))'.dependencies]
 portable-atomic = "1.5.1"
-
-[lints]
-workspace = true
diff --git a/compiler/rustc_data_structures/src/graph/tests.rs b/compiler/rustc_data_structures/src/graph/tests.rs
index e48b9686c26..b69b9dbc4a8 100644
--- a/compiler/rustc_data_structures/src/graph/tests.rs
+++ b/compiler/rustc_data_structures/src/graph/tests.rs
@@ -3,7 +3,7 @@ use std::cmp::max;
 use super::*;
 use crate::fx::FxHashMap;
 
-pub(super) struct TestGraph {
+pub struct TestGraph {
     num_nodes: usize,
     start_node: usize,
     successors: FxHashMap<usize, Vec<usize>>,
@@ -11,7 +11,7 @@ pub(super) struct TestGraph {
 }
 
 impl TestGraph {
-    pub(super) fn new(start_node: usize, edges: &[(usize, usize)]) -> Self {
+    pub fn new(start_node: usize, edges: &[(usize, usize)]) -> Self {
         let mut graph = TestGraph {
             num_nodes: start_node + 1,
             start_node,
diff --git a/compiler/rustc_data_structures/src/obligation_forest/mod.rs b/compiler/rustc_data_structures/src/obligation_forest/mod.rs
index 8d19fc5f9cc..78d69a66edc 100644
--- a/compiler/rustc_data_structures/src/obligation_forest/mod.rs
+++ b/compiler/rustc_data_structures/src/obligation_forest/mod.rs
@@ -313,7 +313,7 @@ pub struct Error<O, E> {
 
 mod helper {
     use super::*;
-    pub(super) type ObligationTreeIdGenerator = impl Iterator<Item = ObligationTreeId>;
+    pub type ObligationTreeIdGenerator = impl Iterator<Item = ObligationTreeId>;
     impl<O: ForestObligation> ObligationForest<O> {
         pub fn new() -> ObligationForest<O> {
             ObligationForest {
diff --git a/compiler/rustc_data_structures/src/sync.rs b/compiler/rustc_data_structures/src/sync.rs
index 616a18a72ab..a1cc75c4985 100644
--- a/compiler/rustc_data_structures/src/sync.rs
+++ b/compiler/rustc_data_structures/src/sync.rs
@@ -88,7 +88,7 @@ mod mode {
 
     // Whether thread safety might be enabled.
     #[inline]
-    pub(super) fn might_be_dyn_thread_safe() -> bool {
+    pub fn might_be_dyn_thread_safe() -> bool {
         DYN_THREAD_SAFE_MODE.load(Ordering::Relaxed) != DYN_NOT_THREAD_SAFE
     }
 
diff --git a/compiler/rustc_data_structures/src/sync/parallel.rs b/compiler/rustc_data_structures/src/sync/parallel.rs
index 8ef8a3f3585..1ba631b8623 100644
--- a/compiler/rustc_data_structures/src/sync/parallel.rs
+++ b/compiler/rustc_data_structures/src/sync/parallel.rs
@@ -46,7 +46,7 @@ pub fn parallel_guard<R>(f: impl FnOnce(&ParallelGuard) -> R) -> R {
     ret
 }
 
-fn serial_join<A, B, RA, RB>(oper_a: A, oper_b: B) -> (RA, RB)
+pub fn serial_join<A, B, RA, RB>(oper_a: A, oper_b: B) -> (RA, RB)
 where
     A: FnOnce() -> RA,
     B: FnOnce() -> RB,
diff --git a/compiler/rustc_data_structures/src/tagged_ptr/tests.rs b/compiler/rustc_data_structures/src/tagged_ptr/tests.rs
index 85b21a7c8ec..9c1e4cefa69 100644
--- a/compiler/rustc_data_structures/src/tagged_ptr/tests.rs
+++ b/compiler/rustc_data_structures/src/tagged_ptr/tests.rs
@@ -7,7 +7,7 @@ use crate::stable_hasher::{HashStable, StableHasher};
 
 /// A tag type used in [`TaggedRef`] tests.
 #[derive(Copy, Clone, Debug, PartialEq, Eq)]
-enum Tag2 {
+pub enum Tag2 {
     B00 = 0b00,
     B01 = 0b01,
     B10 = 0b10,
diff --git a/compiler/rustc_driver/Cargo.toml b/compiler/rustc_driver/Cargo.toml
index d27895a5e7a..e3ee8351295 100644
--- a/compiler/rustc_driver/Cargo.toml
+++ b/compiler/rustc_driver/Cargo.toml
@@ -10,6 +10,3 @@ crate-type = ["dylib"]
 # tidy-alphabetical-start
 rustc_driver_impl = { path = "../rustc_driver_impl" }
 # tidy-alphabetical-end
-
-[lints]
-workspace = true
diff --git a/compiler/rustc_driver_impl/Cargo.toml b/compiler/rustc_driver_impl/Cargo.toml
index 4469fa4e1f3..8593d1faba2 100644
--- a/compiler/rustc_driver_impl/Cargo.toml
+++ b/compiler/rustc_driver_impl/Cargo.toml
@@ -79,6 +79,3 @@ rustc_randomized_layouts = [
     'rustc_middle/rustc_randomized_layouts'
 ]
 # tidy-alphabetical-end
-
-[lints]
-workspace = true
diff --git a/compiler/rustc_driver_impl/src/lib.rs b/compiler/rustc_driver_impl/src/lib.rs
index 6e4020c6eca..a2ddff7183e 100644
--- a/compiler/rustc_driver_impl/src/lib.rs
+++ b/compiler/rustc_driver_impl/src/lib.rs
@@ -16,6 +16,7 @@
 #![feature(result_flattening)]
 #![feature(rustdoc_internals)]
 #![feature(try_blocks)]
+#![warn(unreachable_pub)]
 // tidy-alphabetical-end
 
 use std::cmp::max;
diff --git a/compiler/rustc_error_codes/Cargo.toml b/compiler/rustc_error_codes/Cargo.toml
index d89e4526016..55b4e899051 100644
--- a/compiler/rustc_error_codes/Cargo.toml
+++ b/compiler/rustc_error_codes/Cargo.toml
@@ -6,6 +6,3 @@ edition = "2024"
 [dependencies]
 # tidy-alphabetical-start
 # tidy-alphabetical-end
-
-[lints]
-workspace = true
diff --git a/compiler/rustc_error_codes/src/lib.rs b/compiler/rustc_error_codes/src/lib.rs
index dfeef5a957d..d53d5678832 100644
--- a/compiler/rustc_error_codes/src/lib.rs
+++ b/compiler/rustc_error_codes/src/lib.rs
@@ -6,6 +6,7 @@
 #![deny(rustdoc::invalid_codeblock_attributes)]
 #![doc(rust_logo)]
 #![feature(rustdoc_internals)]
+#![warn(unreachable_pub)]
 // tidy-alphabetical-end
 
 // This higher-order macro defines the error codes that are in use. It is used
diff --git a/compiler/rustc_error_messages/Cargo.toml b/compiler/rustc_error_messages/Cargo.toml
index e9047ba16e5..578af7fc51d 100644
--- a/compiler/rustc_error_messages/Cargo.toml
+++ b/compiler/rustc_error_messages/Cargo.toml
@@ -19,6 +19,3 @@ rustc_span = { path = "../rustc_span" }
 tracing = "0.1"
 unic-langid = { version = "0.9.0", features = ["macros"] }
 # tidy-alphabetical-end
-
-[lints]
-workspace = true
diff --git a/compiler/rustc_error_messages/src/lib.rs b/compiler/rustc_error_messages/src/lib.rs
index a6fbbb29ccd..ba1c3e185c2 100644
--- a/compiler/rustc_error_messages/src/lib.rs
+++ b/compiler/rustc_error_messages/src/lib.rs
@@ -4,6 +4,7 @@
 #![feature(rustc_attrs)]
 #![feature(rustdoc_internals)]
 #![feature(type_alias_impl_trait)]
+#![warn(unreachable_pub)]
 // tidy-alphabetical-end
 
 use std::borrow::Cow;
diff --git a/compiler/rustc_errors/Cargo.toml b/compiler/rustc_errors/Cargo.toml
index fc39a726093..b11793c190a 100644
--- a/compiler/rustc_errors/Cargo.toml
+++ b/compiler/rustc_errors/Cargo.toml
@@ -39,6 +39,3 @@ features = [
     "Win32_Security",
     "Win32_System_Threading",
 ]
-
-[lints]
-workspace = true
diff --git a/compiler/rustc_errors/src/lib.rs b/compiler/rustc_errors/src/lib.rs
index 80e43ede445..86959b28e53 100644
--- a/compiler/rustc_errors/src/lib.rs
+++ b/compiler/rustc_errors/src/lib.rs
@@ -25,6 +25,7 @@
 #![feature(trait_alias)]
 #![feature(try_blocks)]
 #![feature(yeet_expr)]
+#![warn(unreachable_pub)]
 // tidy-alphabetical-end
 
 extern crate self as rustc_errors;
diff --git a/compiler/rustc_expand/Cargo.toml b/compiler/rustc_expand/Cargo.toml
index f051ea0c2ac..0ba139ea5cc 100644
--- a/compiler/rustc_expand/Cargo.toml
+++ b/compiler/rustc_expand/Cargo.toml
@@ -29,6 +29,3 @@ smallvec = { version = "1.8.1", features = ["union", "may_dangle"] }
 thin-vec = "0.2.12"
 tracing = "0.1"
 # tidy-alphabetical-end
-
-[lints]
-workspace = true
diff --git a/compiler/rustc_expand/src/lib.rs b/compiler/rustc_expand/src/lib.rs
index 4222c9fe906..777044e3f33 100644
--- a/compiler/rustc_expand/src/lib.rs
+++ b/compiler/rustc_expand/src/lib.rs
@@ -13,6 +13,7 @@
 #![feature(rustdoc_internals)]
 #![feature(try_blocks)]
 #![feature(yeet_expr)]
+#![warn(unreachable_pub)]
 // tidy-alphabetical-end
 
 extern crate proc_macro as pm;
diff --git a/compiler/rustc_feature/Cargo.toml b/compiler/rustc_feature/Cargo.toml
index 1aaace75404..a5ae06473cb 100644
--- a/compiler/rustc_feature/Cargo.toml
+++ b/compiler/rustc_feature/Cargo.toml
@@ -10,6 +10,3 @@ rustc_span = { path = "../rustc_span" }
 serde = { version = "1.0.125", features = [ "derive" ] }
 serde_json = "1.0.59"
 # tidy-alphabetical-end
-
-[lints]
-workspace = true
diff --git a/compiler/rustc_feature/src/lib.rs b/compiler/rustc_feature/src/lib.rs
index 25764755a8f..0b034a2ae10 100644
--- a/compiler/rustc_feature/src/lib.rs
+++ b/compiler/rustc_feature/src/lib.rs
@@ -15,6 +15,7 @@
 #![allow(internal_features)]
 #![doc(rust_logo)]
 #![feature(rustdoc_internals)]
+#![warn(unreachable_pub)]
 // tidy-alphabetical-end
 
 mod accepted;
diff --git a/compiler/rustc_fluent_macro/Cargo.toml b/compiler/rustc_fluent_macro/Cargo.toml
index 695e8b3fd5d..ce76b2745ea 100644
--- a/compiler/rustc_fluent_macro/Cargo.toml
+++ b/compiler/rustc_fluent_macro/Cargo.toml
@@ -16,6 +16,3 @@ quote = "1"
 syn = { version = "2", features = ["full"] }
 unic-langid = { version = "0.9.0", features = ["macros"] }
 # tidy-alphabetical-end
-
-[lints]
-workspace = true
diff --git a/compiler/rustc_fluent_macro/src/lib.rs b/compiler/rustc_fluent_macro/src/lib.rs
index c6e0484b921..3ad51fa1e64 100644
--- a/compiler/rustc_fluent_macro/src/lib.rs
+++ b/compiler/rustc_fluent_macro/src/lib.rs
@@ -7,6 +7,7 @@
 #![feature(proc_macro_span)]
 #![feature(rustdoc_internals)]
 #![feature(track_path)]
+#![warn(unreachable_pub)]
 // tidy-alphabetical-end
 
 use proc_macro::TokenStream;
diff --git a/compiler/rustc_fs_util/Cargo.toml b/compiler/rustc_fs_util/Cargo.toml
index 3518209ea5b..baca3bc7d49 100644
--- a/compiler/rustc_fs_util/Cargo.toml
+++ b/compiler/rustc_fs_util/Cargo.toml
@@ -6,6 +6,3 @@ edition = "2024"
 [dependencies]
 # tidy-alphabetical-start
 # tidy-alphabetical-end
-
-[lints]
-workspace = true
diff --git a/compiler/rustc_graphviz/Cargo.toml b/compiler/rustc_graphviz/Cargo.toml
index 2672a624eab..d84943760ba 100644
--- a/compiler/rustc_graphviz/Cargo.toml
+++ b/compiler/rustc_graphviz/Cargo.toml
@@ -6,6 +6,3 @@ edition = "2024"
 [dependencies]
 # tidy-alphabetical-start
 # tidy-alphabetical-end
-
-[lints]
-workspace = true
diff --git a/compiler/rustc_graphviz/src/lib.rs b/compiler/rustc_graphviz/src/lib.rs
index c8f8fd5be02..b5774f64b66 100644
--- a/compiler/rustc_graphviz/src/lib.rs
+++ b/compiler/rustc_graphviz/src/lib.rs
@@ -277,6 +277,7 @@
 )]
 #![doc(rust_logo)]
 #![feature(rustdoc_internals)]
+#![warn(unreachable_pub)]
 // tidy-alphabetical-end
 
 use std::borrow::Cow;
diff --git a/compiler/rustc_hashes/Cargo.toml b/compiler/rustc_hashes/Cargo.toml
index ff46696c4c0..c7a273cff88 100644
--- a/compiler/rustc_hashes/Cargo.toml
+++ b/compiler/rustc_hashes/Cargo.toml
@@ -7,6 +7,3 @@ edition = "2024"
 # tidy-alphabetical-start
 rustc-stable-hash = { version = "0.1.0" }
 # tidy-alphabetical-end
-
-[lints]
-workspace = true
diff --git a/compiler/rustc_hir/Cargo.toml b/compiler/rustc_hir/Cargo.toml
index e45c49cd7db..7ca8539845a 100644
--- a/compiler/rustc_hir/Cargo.toml
+++ b/compiler/rustc_hir/Cargo.toml
@@ -21,6 +21,3 @@ smallvec = { version = "1.8.1", features = ["union", "may_dangle"] }
 thin-vec = "0.2.12"
 tracing = "0.1"
 # tidy-alphabetical-end
-
-[lints]
-workspace = true
diff --git a/compiler/rustc_hir/src/lib.rs b/compiler/rustc_hir/src/lib.rs
index 84d369f1edd..270d4fbec30 100644
--- a/compiler/rustc_hir/src/lib.rs
+++ b/compiler/rustc_hir/src/lib.rs
@@ -13,6 +13,7 @@
 #![feature(never_type)]
 #![feature(rustc_attrs)]
 #![feature(variant_count)]
+#![warn(unreachable_pub)]
 // tidy-alphabetical-end
 
 extern crate self as rustc_hir;
diff --git a/compiler/rustc_hir_analysis/Cargo.toml b/compiler/rustc_hir_analysis/Cargo.toml
index 99ced5ff0a9..55a816a855a 100644
--- a/compiler/rustc_hir_analysis/Cargo.toml
+++ b/compiler/rustc_hir_analysis/Cargo.toml
@@ -32,6 +32,3 @@ rustc_type_ir = { path = "../rustc_type_ir" }
 smallvec = { version = "1.8.1", features = ["union", "may_dangle"] }
 tracing = "0.1"
 # tidy-alphabetical-end
-
-[lints]
-workspace = true
diff --git a/compiler/rustc_hir_analysis/src/check/intrinsicck.rs b/compiler/rustc_hir_analysis/src/check/intrinsicck.rs
index 590ade516ec..d63165f0f16 100644
--- a/compiler/rustc_hir_analysis/src/check/intrinsicck.rs
+++ b/compiler/rustc_hir_analysis/src/check/intrinsicck.rs
@@ -3,8 +3,11 @@ use rustc_ast::InlineAsmTemplatePiece;
 use rustc_data_structures::fx::FxIndexSet;
 use rustc_hir::def_id::DefId;
 use rustc_hir::{self as hir, LangItem};
+use rustc_infer::infer::InferCtxt;
 use rustc_middle::bug;
-use rustc_middle::ty::{self, Article, FloatTy, IntTy, Ty, TyCtxt, TypeVisitableExt, UintTy};
+use rustc_middle::ty::{
+    self, Article, FloatTy, IntTy, Ty, TyCtxt, TypeVisitableExt, TypeckResults, UintTy,
+};
 use rustc_session::lint;
 use rustc_span::def_id::LocalDefId;
 use rustc_span::{Symbol, sym};
@@ -14,12 +17,11 @@ use rustc_target::asm::{
 
 use crate::errors::RegisterTypeUnstable;
 
-pub struct InlineAsmCtxt<'a, 'tcx: 'a> {
-    tcx: TyCtxt<'tcx>,
+pub struct InlineAsmCtxt<'a, 'tcx> {
     typing_env: ty::TypingEnv<'tcx>,
     target_features: &'tcx FxIndexSet<Symbol>,
-    expr_ty: Box<dyn Fn(&hir::Expr<'tcx>) -> Ty<'tcx> + 'a>,
-    node_ty: Box<dyn Fn(hir::HirId) -> Ty<'tcx> + 'a>,
+    infcx: &'a InferCtxt<'tcx>,
+    typeck_results: &'a TypeckResults<'tcx>,
 }
 
 enum NonAsmTypeReason<'tcx> {
@@ -31,34 +33,38 @@ enum NonAsmTypeReason<'tcx> {
 
 impl<'a, 'tcx> InlineAsmCtxt<'a, 'tcx> {
     pub fn new(
-        tcx: TyCtxt<'tcx>,
         def_id: LocalDefId,
+        infcx: &'a InferCtxt<'tcx>,
         typing_env: ty::TypingEnv<'tcx>,
-        expr_ty: impl Fn(&hir::Expr<'tcx>) -> Ty<'tcx> + 'a,
-        node_ty: impl Fn(hir::HirId) -> Ty<'tcx> + 'a,
+        typeck_results: &'a TypeckResults<'tcx>,
     ) -> Self {
         InlineAsmCtxt {
-            tcx,
             typing_env,
-            target_features: tcx.asm_target_features(def_id),
-            expr_ty: Box::new(expr_ty),
-            node_ty: Box::new(node_ty),
+            target_features: infcx.tcx.asm_target_features(def_id),
+            infcx,
+            typeck_results,
         }
     }
 
-    fn expr_ty(&self, expr: &hir::Expr<'tcx>) -> Ty<'tcx> {
-        (self.expr_ty)(expr)
+    fn tcx(&self) -> TyCtxt<'tcx> {
+        self.infcx.tcx
     }
 
-    fn node_ty(&self, hir_id: hir::HirId) -> Ty<'tcx> {
-        (self.node_ty)(hir_id)
+    fn expr_ty(&self, expr: &hir::Expr<'tcx>) -> Ty<'tcx> {
+        let ty = self.typeck_results.expr_ty_adjusted(expr);
+        let ty = self.infcx.resolve_vars_if_possible(ty);
+        if ty.has_non_region_infer() {
+            Ty::new_misc_error(self.tcx())
+        } else {
+            self.tcx().erase_regions(ty)
+        }
     }
 
     // FIXME(compiler-errors): This could use `<$ty as Pointee>::Metadata == ()`
     fn is_thin_ptr_ty(&self, ty: Ty<'tcx>) -> bool {
         // Type still may have region variables, but `Sized` does not depend
         // on those, so just erase them before querying.
-        if ty.is_sized(self.tcx, self.typing_env) {
+        if ty.is_sized(self.tcx(), self.typing_env) {
             return true;
         }
         if let ty::Foreign(..) = ty.kind() {
@@ -68,7 +74,7 @@ impl<'a, 'tcx> InlineAsmCtxt<'a, 'tcx> {
     }
 
     fn get_asm_ty(&self, ty: Ty<'tcx>) -> Result<InlineAsmType, NonAsmTypeReason<'tcx>> {
-        let asm_ty_isize = match self.tcx.sess.target.pointer_width {
+        let asm_ty_isize = match self.tcx().sess.target.pointer_width {
             16 => InlineAsmType::I16,
             32 => InlineAsmType::I32,
             64 => InlineAsmType::I64,
@@ -97,12 +103,12 @@ impl<'a, 'tcx> InlineAsmCtxt<'a, 'tcx> {
             ty::Adt(adt, args) if adt.repr().simd() => {
                 let fields = &adt.non_enum_variant().fields;
                 let field = &fields[FieldIdx::ZERO];
-                let elem_ty = field.ty(self.tcx, args);
+                let elem_ty = field.ty(self.tcx(), args);
 
                 let (size, ty) = match elem_ty.kind() {
                     ty::Array(ty, len) => {
-                        let len = self.tcx.normalize_erasing_regions(self.typing_env, *len);
-                        if let Some(len) = len.try_to_target_usize(self.tcx) {
+                        let len = self.tcx().normalize_erasing_regions(self.typing_env, *len);
+                        if let Some(len) = len.try_to_target_usize(self.tcx()) {
                             (len, *ty)
                         } else {
                             return Err(NonAsmTypeReason::UnevaluatedSIMDArrayLength(
@@ -122,7 +128,7 @@ impl<'a, 'tcx> InlineAsmCtxt<'a, 'tcx> {
                         Ok(InlineAsmType::VecI128(size))
                     }
                     ty::Int(IntTy::Isize) | ty::Uint(UintTy::Usize) => {
-                        Ok(match self.tcx.sess.target.pointer_width {
+                        Ok(match self.tcx().sess.target.pointer_width {
                             16 => InlineAsmType::VecI16(size),
                             32 => InlineAsmType::VecI32(size),
                             64 => InlineAsmType::VecI64(size),
@@ -159,9 +165,9 @@ impl<'a, 'tcx> InlineAsmCtxt<'a, 'tcx> {
             // `!` is allowed for input but not for output (issue #87802)
             ty::Never if is_input => return None,
             _ if ty.references_error() => return None,
-            ty::Adt(adt, args) if self.tcx.is_lang_item(adt.did(), LangItem::MaybeUninit) => {
+            ty::Adt(adt, args) if self.tcx().is_lang_item(adt.did(), LangItem::MaybeUninit) => {
                 let fields = &adt.non_enum_variant().fields;
-                let ty = fields[FieldIdx::from_u32(1)].ty(self.tcx, args);
+                let ty = fields[FieldIdx::from_u32(1)].ty(self.tcx(), args);
                 // FIXME: Are we just trying to map to the `T` in `MaybeUninit<T>`?
                 // If so, just get it from the args.
                 let ty::Adt(ty, args) = ty.kind() else {
@@ -172,7 +178,7 @@ impl<'a, 'tcx> InlineAsmCtxt<'a, 'tcx> {
                     "expected first field of `MaybeUninit` to be `ManuallyDrop`"
                 );
                 let fields = &ty.non_enum_variant().fields;
-                let ty = fields[FieldIdx::ZERO].ty(self.tcx, args);
+                let ty = fields[FieldIdx::ZERO].ty(self.tcx(), args);
                 self.get_asm_ty(ty)
             }
             _ => self.get_asm_ty(ty),
@@ -183,9 +189,9 @@ impl<'a, 'tcx> InlineAsmCtxt<'a, 'tcx> {
                 match reason {
                     NonAsmTypeReason::UnevaluatedSIMDArrayLength(did, len) => {
                         let msg = format!("cannot evaluate SIMD vector length `{len}`");
-                        self.tcx
+                        self.infcx
                             .dcx()
-                            .struct_span_err(self.tcx.def_span(did), msg)
+                            .struct_span_err(self.tcx().def_span(did), msg)
                             .with_span_note(
                                 expr.span,
                                 "SIMD vector length needs to be known statically for use in `asm!`",
@@ -194,7 +200,7 @@ impl<'a, 'tcx> InlineAsmCtxt<'a, 'tcx> {
                     }
                     NonAsmTypeReason::Invalid(ty) => {
                         let msg = format!("cannot use value of type `{ty}` for inline assembly");
-                        self.tcx.dcx().struct_span_err(expr.span, msg).with_note(
+                        self.infcx.dcx().struct_span_err(expr.span, msg).with_note(
                             "only integers, floats, SIMD vectors, pointers and function pointers \
                             can be used as arguments for inline assembly",
                         ).emit();
@@ -203,7 +209,7 @@ impl<'a, 'tcx> InlineAsmCtxt<'a, 'tcx> {
                         let msg = format!(
                             "cannot use value of unsized pointer type `{ty}` for inline assembly"
                         );
-                        self.tcx
+                        self.infcx
                             .dcx()
                             .struct_span_err(expr.span, msg)
                             .with_note("only sized pointers can be used in inline assembly")
@@ -213,8 +219,8 @@ impl<'a, 'tcx> InlineAsmCtxt<'a, 'tcx> {
                         let msg = format!(
                             "cannot use SIMD vector with element type `{ty}` for inline assembly"
                         );
-                        self.tcx.dcx()
-                        .struct_span_err(self.tcx.def_span(did), msg).with_span_note(
+                        self.infcx.dcx()
+                        .struct_span_err(self.tcx().def_span(did), msg).with_span_note(
                             expr.span,
                             "only integers, floats, SIMD vectors, pointers and function pointers \
                             can be used as arguments for inline assembly",
@@ -227,9 +233,9 @@ impl<'a, 'tcx> InlineAsmCtxt<'a, 'tcx> {
 
         // Check that the type implements Copy. The only case where this can
         // possibly fail is for SIMD types which don't #[derive(Copy)].
-        if !self.tcx.type_is_copy_modulo_regions(self.typing_env, ty) {
+        if !self.tcx().type_is_copy_modulo_regions(self.typing_env, ty) {
             let msg = "arguments for inline assembly must be copyable";
-            self.tcx
+            self.infcx
                 .dcx()
                 .struct_span_err(expr.span, msg)
                 .with_note(format!("`{ty}` does not implement the Copy trait"))
@@ -249,7 +255,7 @@ impl<'a, 'tcx> InlineAsmCtxt<'a, 'tcx> {
             if in_asm_ty != asm_ty {
                 let msg = "incompatible types for asm inout argument";
                 let in_expr_ty = self.expr_ty(in_expr);
-                self.tcx
+                self.infcx
                     .dcx()
                     .struct_span_err(vec![in_expr.span, expr.span], msg)
                     .with_span_label(in_expr.span, format!("type `{in_expr_ty}`"))
@@ -268,21 +274,21 @@ impl<'a, 'tcx> InlineAsmCtxt<'a, 'tcx> {
 
         // Check the type against the list of types supported by the selected
         // register class.
-        let asm_arch = self.tcx.sess.asm_arch.unwrap();
-        let allow_experimental_reg = self.tcx.features().asm_experimental_reg();
+        let asm_arch = self.tcx().sess.asm_arch.unwrap();
+        let allow_experimental_reg = self.tcx().features().asm_experimental_reg();
         let reg_class = reg.reg_class();
         let supported_tys = reg_class.supported_types(asm_arch, allow_experimental_reg);
         let Some((_, feature)) = supported_tys.iter().find(|&&(t, _)| t == asm_ty) else {
             let mut err = if !allow_experimental_reg
                 && reg_class.supported_types(asm_arch, true).iter().any(|&(t, _)| t == asm_ty)
             {
-                self.tcx.sess.create_feature_err(
+                self.tcx().sess.create_feature_err(
                     RegisterTypeUnstable { span: expr.span, ty },
                     sym::asm_experimental_reg,
                 )
             } else {
                 let msg = format!("type `{ty}` cannot be used with this register class");
-                let mut err = self.tcx.dcx().struct_span_err(expr.span, msg);
+                let mut err = self.infcx.dcx().struct_span_err(expr.span, msg);
                 let supported_tys: Vec<_> =
                     supported_tys.iter().map(|(t, _)| t.to_string()).collect();
                 err.note(format!(
@@ -312,7 +318,7 @@ impl<'a, 'tcx> InlineAsmCtxt<'a, 'tcx> {
         if let Some(feature) = feature {
             if !self.target_features.contains(feature) {
                 let msg = format!("`{feature}` target feature is not enabled");
-                self.tcx
+                self.infcx
                     .dcx()
                     .struct_span_err(expr.span, msg)
                     .with_note(format!(
@@ -349,7 +355,7 @@ impl<'a, 'tcx> InlineAsmCtxt<'a, 'tcx> {
                     result: default_result,
                     size: default_size,
                 } = reg_class.default_modifier(asm_arch).unwrap();
-                self.tcx.node_span_lint(
+                self.tcx().node_span_lint(
                     lint::builtin::ASM_SUB_REGISTER,
                     expr.hir_id,
                     spans,
@@ -371,11 +377,11 @@ impl<'a, 'tcx> InlineAsmCtxt<'a, 'tcx> {
     }
 
     pub fn check_asm(&self, asm: &hir::InlineAsm<'tcx>) {
-        let Some(asm_arch) = self.tcx.sess.asm_arch else {
-            self.tcx.dcx().delayed_bug("target architecture does not support asm");
+        let Some(asm_arch) = self.tcx().sess.asm_arch else {
+            self.infcx.dcx().delayed_bug("target architecture does not support asm");
             return;
         };
-        let allow_experimental_reg = self.tcx.features().asm_experimental_reg();
+        let allow_experimental_reg = self.tcx().features().asm_experimental_reg();
         for (idx, &(op, op_sp)) in asm.operands.iter().enumerate() {
             // Validate register classes against currently enabled target
             // features. We check that at least one type is available for
@@ -398,13 +404,13 @@ impl<'a, 'tcx> InlineAsmCtxt<'a, 'tcx> {
                     }
                     if let Err(msg) = reg.validate(
                         asm_arch,
-                        self.tcx.sess.relocation_model(),
+                        self.tcx().sess.relocation_model(),
                         self.target_features,
-                        &self.tcx.sess.target,
+                        &self.tcx().sess.target,
                         op.is_clobber(),
                     ) {
                         let msg = format!("cannot use register `{}`: {}", reg.name(), msg);
-                        self.tcx.dcx().span_err(op_sp, msg);
+                        self.infcx.dcx().span_err(op_sp, msg);
                         continue;
                     }
                 }
@@ -444,7 +450,7 @@ impl<'a, 'tcx> InlineAsmCtxt<'a, 'tcx> {
                                 reg_class.name(),
                                 feature
                             );
-                            self.tcx.dcx().span_err(op_sp, msg);
+                            self.infcx.dcx().span_err(op_sp, msg);
                             // register isn't enabled, don't do more checks
                             continue;
                         }
@@ -458,7 +464,7 @@ impl<'a, 'tcx> InlineAsmCtxt<'a, 'tcx> {
                                     .intersperse(", ")
                                     .collect::<String>(),
                             );
-                            self.tcx.dcx().span_err(op_sp, msg);
+                            self.infcx.dcx().span_err(op_sp, msg);
                             // register isn't enabled, don't do more checks
                             continue;
                         }
@@ -493,16 +499,16 @@ impl<'a, 'tcx> InlineAsmCtxt<'a, 'tcx> {
                     }
                 }
                 hir::InlineAsmOperand::Const { anon_const } => {
-                    let ty = self.node_ty(anon_const.hir_id);
+                    let ty = self.expr_ty(self.tcx().hir_body(anon_const.body).value);
                     match ty.kind() {
                         ty::Error(_) => {}
                         _ if ty.is_integral() => {}
                         _ => {
-                            self.tcx
+                            self.infcx
                                 .dcx()
                                 .struct_span_err(op_sp, "invalid type for `const` operand")
                                 .with_span_label(
-                                    self.tcx.def_span(anon_const.def_id),
+                                    self.tcx().def_span(anon_const.def_id),
                                     format!("is {} `{}`", ty.kind().article(), ty),
                                 )
                                 .with_help("`const` operands must be of an integer type")
@@ -517,7 +523,7 @@ impl<'a, 'tcx> InlineAsmCtxt<'a, 'tcx> {
                         ty::FnDef(..) => {}
                         ty::Error(_) => {}
                         _ => {
-                            self.tcx
+                            self.infcx
                                 .dcx()
                                 .struct_span_err(op_sp, "invalid `sym` operand")
                                 .with_span_label(
diff --git a/compiler/rustc_hir_analysis/src/lib.rs b/compiler/rustc_hir_analysis/src/lib.rs
index 26a20690a95..50b0e32b95e 100644
--- a/compiler/rustc_hir_analysis/src/lib.rs
+++ b/compiler/rustc_hir_analysis/src/lib.rs
@@ -73,6 +73,7 @@ This API is completely unstable and subject to change.
 #![feature(slice_partition_dedup)]
 #![feature(try_blocks)]
 #![feature(unwrap_infallible)]
+#![warn(unreachable_pub)]
 // tidy-alphabetical-end
 
 // These are used by Clippy.
diff --git a/compiler/rustc_hir_pretty/Cargo.toml b/compiler/rustc_hir_pretty/Cargo.toml
index bc6431d0271..91da8cb3fc5 100644
--- a/compiler/rustc_hir_pretty/Cargo.toml
+++ b/compiler/rustc_hir_pretty/Cargo.toml
@@ -12,6 +12,3 @@ rustc_attr_data_structures = { path = "../rustc_attr_data_structures" }
 rustc_hir = { path = "../rustc_hir" }
 rustc_span = { path = "../rustc_span" }
 # tidy-alphabetical-end
-
-[lints]
-workspace = true
diff --git a/compiler/rustc_hir_pretty/src/lib.rs b/compiler/rustc_hir_pretty/src/lib.rs
index 0e3721126fb..2572ff50eb7 100644
--- a/compiler/rustc_hir_pretty/src/lib.rs
+++ b/compiler/rustc_hir_pretty/src/lib.rs
@@ -3,6 +3,7 @@
 
 // tidy-alphabetical-start
 #![recursion_limit = "256"]
+#![warn(unreachable_pub)]
 // tidy-alphabetical-end
 
 use std::cell::Cell;
diff --git a/compiler/rustc_hir_typeck/Cargo.toml b/compiler/rustc_hir_typeck/Cargo.toml
index 1d6486fb7dc..f1afb7b712d 100644
--- a/compiler/rustc_hir_typeck/Cargo.toml
+++ b/compiler/rustc_hir_typeck/Cargo.toml
@@ -27,6 +27,3 @@ rustc_type_ir = { path = "../rustc_type_ir" }
 smallvec = { version = "1.8.1", features = ["union", "may_dangle"] }
 tracing = "0.1"
 # tidy-alphabetical-end
-
-[lints]
-workspace = true
diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs
index db947b6744d..ac911c20222 100644
--- a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs
+++ b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs
@@ -99,22 +99,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         debug!("FnCtxt::check_asm: {} deferred checks", deferred_asm_checks.len());
         for (asm, hir_id) in deferred_asm_checks.drain(..) {
             let enclosing_id = self.tcx.hir_enclosing_body_owner(hir_id);
-            let expr_ty = |expr: &hir::Expr<'tcx>| {
-                let ty = self.typeck_results.borrow().expr_ty_adjusted(expr);
-                let ty = self.resolve_vars_if_possible(ty);
-                if ty.has_non_region_infer() {
-                    Ty::new_misc_error(self.tcx)
-                } else {
-                    self.tcx.erase_regions(ty)
-                }
-            };
-            let node_ty = |hir_id: HirId| self.typeck_results.borrow().node_type(hir_id);
             InlineAsmCtxt::new(
-                self.tcx,
                 enclosing_id,
+                &self.infcx,
                 self.typing_env(self.param_env),
-                expr_ty,
-                node_ty,
+                &*self.typeck_results.borrow(),
             )
             .check_asm(asm);
         }
diff --git a/compiler/rustc_hir_typeck/src/lib.rs b/compiler/rustc_hir_typeck/src/lib.rs
index 263fb84206e..8b9c2b4a6ca 100644
--- a/compiler/rustc_hir_typeck/src/lib.rs
+++ b/compiler/rustc_hir_typeck/src/lib.rs
@@ -8,6 +8,7 @@
 #![feature(let_chains)]
 #![feature(never_type)]
 #![feature(try_blocks)]
+#![warn(unreachable_pub)]
 // tidy-alphabetical-end
 
 mod _match;
diff --git a/compiler/rustc_incremental/Cargo.toml b/compiler/rustc_incremental/Cargo.toml
index 754881309bf..4939bfb3a1c 100644
--- a/compiler/rustc_incremental/Cargo.toml
+++ b/compiler/rustc_incremental/Cargo.toml
@@ -22,6 +22,3 @@ rustc_span = { path = "../rustc_span" }
 thin-vec = "0.2.12"
 tracing = "0.1"
 # tidy-alphabetical-end
-
-[lints]
-workspace = true
diff --git a/compiler/rustc_incremental/src/lib.rs b/compiler/rustc_incremental/src/lib.rs
index 299ee487638..563ed7614c6 100644
--- a/compiler/rustc_incremental/src/lib.rs
+++ b/compiler/rustc_incremental/src/lib.rs
@@ -7,6 +7,7 @@
 #![doc(rust_logo)]
 #![feature(file_buffered)]
 #![feature(rustdoc_internals)]
+#![warn(unreachable_pub)]
 // tidy-alphabetical-end
 
 mod assert_dep_graph;
diff --git a/compiler/rustc_index/Cargo.toml b/compiler/rustc_index/Cargo.toml
index ee6fe11f9a5..3d83a3c98da 100644
--- a/compiler/rustc_index/Cargo.toml
+++ b/compiler/rustc_index/Cargo.toml
@@ -21,6 +21,3 @@ nightly = [
 ]
 rustc_randomized_layouts = []
 # tidy-alphabetical-end
-
-[lints]
-workspace = true
diff --git a/compiler/rustc_index/src/lib.rs b/compiler/rustc_index/src/lib.rs
index cc680838e7e..3441a5f65c7 100644
--- a/compiler/rustc_index/src/lib.rs
+++ b/compiler/rustc_index/src/lib.rs
@@ -4,6 +4,7 @@
 #![cfg_attr(feature = "nightly", feature(extend_one, step_trait, test))]
 #![cfg_attr(feature = "nightly", feature(new_range_api))]
 #![cfg_attr(feature = "nightly", feature(new_zeroed_alloc))]
+#![warn(unreachable_pub)]
 // tidy-alphabetical-end
 
 pub mod bit_set;
diff --git a/compiler/rustc_index_macros/Cargo.toml b/compiler/rustc_index_macros/Cargo.toml
index de100bd0e2c..891e7ded619 100644
--- a/compiler/rustc_index_macros/Cargo.toml
+++ b/compiler/rustc_index_macros/Cargo.toml
@@ -13,6 +13,3 @@ quote = "1"
 
 [features]
 nightly = []
-
-[lints]
-workspace = true
diff --git a/compiler/rustc_index_macros/src/newtype.rs b/compiler/rustc_index_macros/src/newtype.rs
index f0b58eabbff..67ec7761133 100644
--- a/compiler/rustc_index_macros/src/newtype.rs
+++ b/compiler/rustc_index_macros/src/newtype.rs
@@ -305,7 +305,7 @@ impl Parse for Newtype {
     }
 }
 
-pub(crate) fn newtype(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
+pub fn newtype(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
     let input = parse_macro_input!(input as Newtype);
     input.0.into()
 }
diff --git a/compiler/rustc_infer/Cargo.toml b/compiler/rustc_infer/Cargo.toml
index 242886a9248..08c03614884 100644
--- a/compiler/rustc_infer/Cargo.toml
+++ b/compiler/rustc_infer/Cargo.toml
@@ -21,6 +21,3 @@ smallvec = { version = "1.8.1", features = ["union", "may_dangle"] }
 thin-vec = "0.2.12"
 tracing = "0.1"
 # tidy-alphabetical-end
-
-[lints]
-workspace = true
diff --git a/compiler/rustc_infer/src/lib.rs b/compiler/rustc_infer/src/lib.rs
index ece18f4ea64..a04b2bb2b08 100644
--- a/compiler/rustc_infer/src/lib.rs
+++ b/compiler/rustc_infer/src/lib.rs
@@ -24,6 +24,7 @@
 #![feature(let_chains)]
 #![feature(rustdoc_internals)]
 #![recursion_limit = "512"] // For rustdoc
+#![warn(unreachable_pub)]
 // tidy-alphabetical-end
 
 mod errors;
diff --git a/compiler/rustc_interface/Cargo.toml b/compiler/rustc_interface/Cargo.toml
index add8c0e20e6..9c9660cf504 100644
--- a/compiler/rustc_interface/Cargo.toml
+++ b/compiler/rustc_interface/Cargo.toml
@@ -56,6 +56,3 @@ tracing = "0.1"
 # tidy-alphabetical-start
 llvm = ['dep:rustc_codegen_llvm']
 # tidy-alphabetical-end
-
-[lints]
-workspace = true
diff --git a/compiler/rustc_interface/src/lib.rs b/compiler/rustc_interface/src/lib.rs
index 67e0be93523..54cd341698f 100644
--- a/compiler/rustc_interface/src/lib.rs
+++ b/compiler/rustc_interface/src/lib.rs
@@ -4,6 +4,7 @@
 #![feature(iter_intersperse)]
 #![feature(let_chains)]
 #![feature(try_blocks)]
+#![warn(unreachable_pub)]
 // tidy-alphabetical-end
 
 mod callbacks;
diff --git a/compiler/rustc_lexer/Cargo.toml b/compiler/rustc_lexer/Cargo.toml
index b9b16eebc54..448a50faf45 100644
--- a/compiler/rustc_lexer/Cargo.toml
+++ b/compiler/rustc_lexer/Cargo.toml
@@ -24,6 +24,3 @@ features = ["emoji"]
 
 [dev-dependencies]
 expect-test = "1.4.0"
-
-[lints]
-workspace = true
diff --git a/compiler/rustc_lexer/src/lib.rs b/compiler/rustc_lexer/src/lib.rs
index 61638e45253..bf18845a083 100644
--- a/compiler/rustc_lexer/src/lib.rs
+++ b/compiler/rustc_lexer/src/lib.rs
@@ -23,6 +23,7 @@
 // We want to be able to build this crate with a stable compiler,
 // so no `#![feature]` attributes should be added.
 #![deny(unstable_features)]
+#![warn(unreachable_pub)]
 // tidy-alphabetical-end
 
 mod cursor;
diff --git a/compiler/rustc_lint/Cargo.toml b/compiler/rustc_lint/Cargo.toml
index f6c10aa9744..d6014f5006a 100644
--- a/compiler/rustc_lint/Cargo.toml
+++ b/compiler/rustc_lint/Cargo.toml
@@ -28,6 +28,3 @@ smallvec = { version = "1.8.1", features = ["union", "may_dangle"] }
 tracing = "0.1"
 unicode-security = "0.1.0"
 # tidy-alphabetical-end
-
-[lints]
-workspace = true
diff --git a/compiler/rustc_lint/src/lib.rs b/compiler/rustc_lint/src/lib.rs
index 35867d8c9ef..7018774e5c6 100644
--- a/compiler/rustc_lint/src/lib.rs
+++ b/compiler/rustc_lint/src/lib.rs
@@ -32,6 +32,7 @@
 #![feature(rustc_attrs)]
 #![feature(rustdoc_internals)]
 #![feature(try_blocks)]
+#![warn(unreachable_pub)]
 // tidy-alphabetical-end
 
 mod async_closures;
diff --git a/compiler/rustc_lint_defs/Cargo.toml b/compiler/rustc_lint_defs/Cargo.toml
index f9b45a00ec1..9ab350daf69 100644
--- a/compiler/rustc_lint_defs/Cargo.toml
+++ b/compiler/rustc_lint_defs/Cargo.toml
@@ -15,6 +15,3 @@ rustc_serialize = { path = "../rustc_serialize" }
 rustc_span = { path = "../rustc_span" }
 serde = { version = "1.0.125", features = ["derive"] }
 # tidy-alphabetical-end
-
-[lints]
-workspace = true
diff --git a/compiler/rustc_lint_defs/src/lib.rs b/compiler/rustc_lint_defs/src/lib.rs
index 46b4b1d4383..e564235c41a 100644
--- a/compiler/rustc_lint_defs/src/lib.rs
+++ b/compiler/rustc_lint_defs/src/lib.rs
@@ -1,3 +1,7 @@
+// tidy-alphabetical-start
+#![warn(unreachable_pub)]
+// tidy-alphabetical-end
+
 use rustc_abi::ExternAbi;
 use rustc_ast::AttrId;
 use rustc_ast::attr::AttributeExt;
diff --git a/compiler/rustc_llvm/Cargo.toml b/compiler/rustc_llvm/Cargo.toml
index dcfaf9a0282..061562b2ec5 100644
--- a/compiler/rustc_llvm/Cargo.toml
+++ b/compiler/rustc_llvm/Cargo.toml
@@ -14,6 +14,3 @@ libc = "0.2.73"
 # pinned `cc` in `rustc_codegen_ssa` if you update `cc` here.
 cc = "=1.2.16"
 # tidy-alphabetical-end
-
-[lints]
-workspace = true
diff --git a/compiler/rustc_llvm/src/lib.rs b/compiler/rustc_llvm/src/lib.rs
index 68058250a26..eda9b2b1fc0 100644
--- a/compiler/rustc_llvm/src/lib.rs
+++ b/compiler/rustc_llvm/src/lib.rs
@@ -4,6 +4,7 @@
 #![doc(rust_logo)]
 #![feature(extern_types)]
 #![feature(rustdoc_internals)]
+#![warn(unreachable_pub)]
 // tidy-alphabetical-end
 
 use std::cell::RefCell;
diff --git a/compiler/rustc_log/Cargo.toml b/compiler/rustc_log/Cargo.toml
index 665ebd52f47..30f6e9ba805 100644
--- a/compiler/rustc_log/Cargo.toml
+++ b/compiler/rustc_log/Cargo.toml
@@ -20,6 +20,3 @@ rustc_span = { path = "../rustc_span" }
 # tidy-alphabetical-start
 max_level_info = ['tracing/max_level_info']
 # tidy-alphabetical-end
-
-[lints]
-workspace = true
diff --git a/compiler/rustc_macros/Cargo.toml b/compiler/rustc_macros/Cargo.toml
index b937f75e892..f9d3b758359 100644
--- a/compiler/rustc_macros/Cargo.toml
+++ b/compiler/rustc_macros/Cargo.toml
@@ -13,6 +13,3 @@ quote = "1"
 syn = { version = "2.0.9", features = ["full"] }
 synstructure = "0.13.0"
 # tidy-alphabetical-end
-
-[lints]
-workspace = true
diff --git a/compiler/rustc_macros/src/lib.rs b/compiler/rustc_macros/src/lib.rs
index 44ba064dd82..34fc0f00320 100644
--- a/compiler/rustc_macros/src/lib.rs
+++ b/compiler/rustc_macros/src/lib.rs
@@ -6,6 +6,7 @@
 #![feature(proc_macro_diagnostic)]
 #![feature(proc_macro_span)]
 #![feature(proc_macro_tracked_env)]
+#![warn(unreachable_pub)]
 // tidy-alphabetical-end
 
 use proc_macro::TokenStream;
diff --git a/compiler/rustc_metadata/Cargo.toml b/compiler/rustc_metadata/Cargo.toml
index 5fa3047d14e..a8821640f04 100644
--- a/compiler/rustc_metadata/Cargo.toml
+++ b/compiler/rustc_metadata/Cargo.toml
@@ -31,6 +31,3 @@ rustc_type_ir = { path = "../rustc_type_ir" }
 tempfile = "3.2"
 tracing = "0.1"
 # tidy-alphabetical-end
-
-[lints]
-workspace = true
diff --git a/compiler/rustc_metadata/src/lib.rs b/compiler/rustc_metadata/src/lib.rs
index 8898c5824fa..ebcc0efd5a6 100644
--- a/compiler/rustc_metadata/src/lib.rs
+++ b/compiler/rustc_metadata/src/lib.rs
@@ -15,6 +15,7 @@
 #![feature(proc_macro_internals)]
 #![feature(rustdoc_internals)]
 #![feature(trusted_len)]
+#![warn(unreachable_pub)]
 // tidy-alphabetical-end
 
 extern crate proc_macro;
diff --git a/compiler/rustc_middle/Cargo.toml b/compiler/rustc_middle/Cargo.toml
index 3a6216e63a9..43c1af642dd 100644
--- a/compiler/rustc_middle/Cargo.toml
+++ b/compiler/rustc_middle/Cargo.toml
@@ -43,6 +43,3 @@ tracing = "0.1"
 # tidy-alphabetical-start
 rustc_randomized_layouts = []
 # tidy-alphabetical-end
-
-[lints]
-workspace = true
diff --git a/compiler/rustc_middle/src/lib.rs b/compiler/rustc_middle/src/lib.rs
index 8fe2cc7101b..48ea7df5c23 100644
--- a/compiler/rustc_middle/src/lib.rs
+++ b/compiler/rustc_middle/src/lib.rs
@@ -61,6 +61,7 @@
 #![feature(try_trait_v2_yeet)]
 #![feature(type_alias_impl_trait)]
 #![feature(yeet_expr)]
+#![warn(unreachable_pub)]
 // tidy-alphabetical-end
 
 #[cfg(test)]
diff --git a/compiler/rustc_mir_build/Cargo.toml b/compiler/rustc_mir_build/Cargo.toml
index a29c0116266..d70d70a31a4 100644
--- a/compiler/rustc_mir_build/Cargo.toml
+++ b/compiler/rustc_mir_build/Cargo.toml
@@ -28,6 +28,3 @@ rustc_span = { path = "../rustc_span" }
 rustc_trait_selection = { path = "../rustc_trait_selection" }
 tracing = "0.1"
 # tidy-alphabetical-end
-
-[lints]
-workspace = true
diff --git a/compiler/rustc_mir_build/src/lib.rs b/compiler/rustc_mir_build/src/lib.rs
index 8e96d46dac2..fa5db32d913 100644
--- a/compiler/rustc_mir_build/src/lib.rs
+++ b/compiler/rustc_mir_build/src/lib.rs
@@ -8,6 +8,7 @@
 #![feature(if_let_guard)]
 #![feature(let_chains)]
 #![feature(try_blocks)]
+#![warn(unreachable_pub)]
 // tidy-alphabetical-end
 
 // The `builder` module used to be named `build`, but that was causing GitHub's
diff --git a/compiler/rustc_mir_dataflow/Cargo.toml b/compiler/rustc_mir_dataflow/Cargo.toml
index a171f9641bb..293bcbef21b 100644
--- a/compiler/rustc_mir_dataflow/Cargo.toml
+++ b/compiler/rustc_mir_dataflow/Cargo.toml
@@ -21,6 +21,3 @@ rustc_span = { path = "../rustc_span" }
 smallvec = { version = "1.8.1", features = ["union", "may_dangle"] }
 tracing = "0.1"
 # tidy-alphabetical-end
-
-[lints]
-workspace = true
diff --git a/compiler/rustc_mir_dataflow/src/lib.rs b/compiler/rustc_mir_dataflow/src/lib.rs
index a0efc623b8e..a8a56baa1ff 100644
--- a/compiler/rustc_mir_dataflow/src/lib.rs
+++ b/compiler/rustc_mir_dataflow/src/lib.rs
@@ -7,6 +7,7 @@
 #![feature(let_chains)]
 #![feature(never_type)]
 #![feature(try_blocks)]
+#![warn(unreachable_pub)]
 // tidy-alphabetical-end
 
 use rustc_middle::ty;
diff --git a/compiler/rustc_mir_transform/Cargo.toml b/compiler/rustc_mir_transform/Cargo.toml
index 4dc91723f03..fb8d0ac5e74 100644
--- a/compiler/rustc_mir_transform/Cargo.toml
+++ b/compiler/rustc_mir_transform/Cargo.toml
@@ -30,6 +30,3 @@ rustc_type_ir = { path = "../rustc_type_ir" }
 smallvec = { version = "1.8.1", features = ["union", "may_dangle"] }
 tracing = "0.1"
 # tidy-alphabetical-end
-
-[lints]
-workspace = true
diff --git a/compiler/rustc_mir_transform/src/lib.rs b/compiler/rustc_mir_transform/src/lib.rs
index 739cee5d7f4..8aa14d15644 100644
--- a/compiler/rustc_mir_transform/src/lib.rs
+++ b/compiler/rustc_mir_transform/src/lib.rs
@@ -12,6 +12,7 @@
 #![feature(never_type)]
 #![feature(try_blocks)]
 #![feature(yeet_expr)]
+#![warn(unreachable_pub)]
 // tidy-alphabetical-end
 
 use hir::ConstContext;
diff --git a/compiler/rustc_monomorphize/Cargo.toml b/compiler/rustc_monomorphize/Cargo.toml
index 51be8e55fa7..36b76d261de 100644
--- a/compiler/rustc_monomorphize/Cargo.toml
+++ b/compiler/rustc_monomorphize/Cargo.toml
@@ -22,6 +22,3 @@ serde = "1"
 serde_json = "1"
 tracing = "0.1"
 # tidy-alphabetical-end
-
-[lints]
-workspace = true
diff --git a/compiler/rustc_monomorphize/src/lib.rs b/compiler/rustc_monomorphize/src/lib.rs
index 8f6914f3d72..714b64b3a23 100644
--- a/compiler/rustc_monomorphize/src/lib.rs
+++ b/compiler/rustc_monomorphize/src/lib.rs
@@ -4,6 +4,7 @@
 #![feature(if_let_guard)]
 #![feature(impl_trait_in_assoc_type)]
 #![feature(let_chains)]
+#![warn(unreachable_pub)]
 // tidy-alphabetical-end
 
 use rustc_hir::lang_items::LangItem;
diff --git a/compiler/rustc_next_trait_solver/Cargo.toml b/compiler/rustc_next_trait_solver/Cargo.toml
index e119d23d41a..63aa60f2f26 100644
--- a/compiler/rustc_next_trait_solver/Cargo.toml
+++ b/compiler/rustc_next_trait_solver/Cargo.toml
@@ -24,6 +24,3 @@ nightly = [
     "rustc_index/nightly",
     "rustc_type_ir/nightly",
 ]
-
-[lints]
-workspace = true
diff --git a/compiler/rustc_next_trait_solver/src/lib.rs b/compiler/rustc_next_trait_solver/src/lib.rs
index f6963a79067..d67ae2550d9 100644
--- a/compiler/rustc_next_trait_solver/src/lib.rs
+++ b/compiler/rustc_next_trait_solver/src/lib.rs
@@ -6,6 +6,7 @@
 
 // tidy-alphabetical-start
 #![allow(rustc::usage_of_type_ir_inherent)]
+#![warn(unreachable_pub)]
 // tidy-alphabetical-end
 
 pub mod canonicalizer;
diff --git a/compiler/rustc_parse/Cargo.toml b/compiler/rustc_parse/Cargo.toml
index b78ac197dbb..c9dcab0c871 100644
--- a/compiler/rustc_parse/Cargo.toml
+++ b/compiler/rustc_parse/Cargo.toml
@@ -26,5 +26,3 @@ unicode-width = "0.2.0"
 [dev-dependencies]
 termcolor = "1.2"
 
-[lints]
-workspace = true
diff --git a/compiler/rustc_parse/src/lib.rs b/compiler/rustc_parse/src/lib.rs
index 2edc8c83017..1a104ff5e33 100644
--- a/compiler/rustc_parse/src/lib.rs
+++ b/compiler/rustc_parse/src/lib.rs
@@ -12,6 +12,7 @@
 #![feature(iter_intersperse)]
 #![feature(let_chains)]
 #![feature(string_from_utf8_lossy_owned)]
+#![warn(unreachable_pub)]
 // tidy-alphabetical-end
 
 use std::path::{Path, PathBuf};
diff --git a/compiler/rustc_parse_format/Cargo.toml b/compiler/rustc_parse_format/Cargo.toml
index a720903097a..a39cca716d2 100644
--- a/compiler/rustc_parse_format/Cargo.toml
+++ b/compiler/rustc_parse_format/Cargo.toml
@@ -8,6 +8,3 @@ edition = "2024"
 rustc_index = { path = "../rustc_index", default-features = false }
 rustc_lexer = { path = "../rustc_lexer" }
 # tidy-alphabetical-end
-
-[lints]
-workspace = true
diff --git a/compiler/rustc_parse_format/src/lib.rs b/compiler/rustc_parse_format/src/lib.rs
index 287bd8678da..3b985621b57 100644
--- a/compiler/rustc_parse_format/src/lib.rs
+++ b/compiler/rustc_parse_format/src/lib.rs
@@ -13,6 +13,7 @@
     html_playground_url = "https://play.rust-lang.org/",
     test(attr(deny(warnings)))
 )]
+#![warn(unreachable_pub)]
 // tidy-alphabetical-end
 
 pub use Alignment::*;
diff --git a/compiler/rustc_passes/Cargo.toml b/compiler/rustc_passes/Cargo.toml
index 4cd7fde6034..ba81ef3103b 100644
--- a/compiler/rustc_passes/Cargo.toml
+++ b/compiler/rustc_passes/Cargo.toml
@@ -26,6 +26,3 @@ rustc_target = { path = "../rustc_target" }
 rustc_trait_selection = { path = "../rustc_trait_selection" }
 tracing = "0.1"
 # tidy-alphabetical-end
-
-[lints]
-workspace = true
diff --git a/compiler/rustc_passes/src/lib.rs b/compiler/rustc_passes/src/lib.rs
index 93ff0f66d69..1aa077ad2bb 100644
--- a/compiler/rustc_passes/src/lib.rs
+++ b/compiler/rustc_passes/src/lib.rs
@@ -12,6 +12,7 @@
 #![feature(map_try_insert)]
 #![feature(rustdoc_internals)]
 #![feature(try_blocks)]
+#![warn(unreachable_pub)]
 // tidy-alphabetical-end
 
 use rustc_middle::query::Providers;
diff --git a/compiler/rustc_pattern_analysis/Cargo.toml b/compiler/rustc_pattern_analysis/Cargo.toml
index 0624fe96cd9..40d549630ac 100644
--- a/compiler/rustc_pattern_analysis/Cargo.toml
+++ b/compiler/rustc_pattern_analysis/Cargo.toml
@@ -43,6 +43,3 @@ rustc = [
     "smallvec/may_dangle",
     "rustc_index/nightly",
 ]
-
-[lints]
-workspace = true
diff --git a/compiler/rustc_pattern_analysis/src/lib.rs b/compiler/rustc_pattern_analysis/src/lib.rs
index eeea724a29b..a3400ebb799 100644
--- a/compiler/rustc_pattern_analysis/src/lib.rs
+++ b/compiler/rustc_pattern_analysis/src/lib.rs
@@ -6,6 +6,7 @@
 #![allow(rustc::diagnostic_outside_of_impl)]
 #![allow(rustc::untranslatable_diagnostic)]
 #![cfg_attr(feature = "rustc", feature(let_chains))]
+#![warn(unreachable_pub)]
 // tidy-alphabetical-end
 
 pub mod constructor;
diff --git a/compiler/rustc_pattern_analysis/tests/common/mod.rs b/compiler/rustc_pattern_analysis/tests/common/mod.rs
index 8980b644f59..365bc2d863f 100644
--- a/compiler/rustc_pattern_analysis/tests/common/mod.rs
+++ b/compiler/rustc_pattern_analysis/tests/common/mod.rs
@@ -5,7 +5,7 @@ use rustc_pattern_analysis::usefulness::{PlaceValidity, UsefulnessReport};
 use rustc_pattern_analysis::{MatchArm, PatCx, PrivateUninhabitedField};
 
 /// Sets up `tracing` for easier debugging. Tries to look like the `rustc` setup.
-fn init_tracing() {
+pub fn init_tracing() {
     use tracing_subscriber::Layer;
     use tracing_subscriber::layer::SubscriberExt;
     use tracing_subscriber::util::SubscriberInitExt;
@@ -24,7 +24,7 @@ fn init_tracing() {
 /// A simple set of types.
 #[allow(dead_code)]
 #[derive(Debug, Copy, Clone, PartialEq, Eq)]
-pub(super) enum Ty {
+pub enum Ty {
     /// Booleans
     Bool,
     /// 8-bit unsigned integers
@@ -41,7 +41,7 @@ pub(super) enum Ty {
 
 /// The important logic.
 impl Ty {
-    pub(super) fn sub_tys(&self, ctor: &Constructor<Cx>) -> Vec<Self> {
+    pub fn sub_tys(&self, ctor: &Constructor<Cx>) -> Vec<Self> {
         use Constructor::*;
         match (ctor, *self) {
             (Struct, Ty::Tuple(tys)) => tys.iter().copied().collect(),
@@ -63,7 +63,7 @@ impl Ty {
         }
     }
 
-    fn ctor_set(&self) -> ConstructorSet<Cx> {
+    pub fn ctor_set(&self) -> ConstructorSet<Cx> {
         match *self {
             Ty::Bool => ConstructorSet::Bool,
             Ty::U8 => ConstructorSet::Integers {
@@ -104,7 +104,7 @@ impl Ty {
         }
     }
 
-    fn write_variant_name(
+    pub fn write_variant_name(
         &self,
         f: &mut std::fmt::Formatter<'_>,
         ctor: &Constructor<Cx>,
@@ -120,7 +120,7 @@ impl Ty {
 }
 
 /// Compute usefulness in our simple context (and set up tracing for easier debugging).
-pub(super) fn compute_match_usefulness<'p>(
+pub fn compute_match_usefulness<'p>(
     arms: &[MatchArm<'p, Cx>],
     ty: Ty,
     scrut_validity: PlaceValidity,
@@ -137,7 +137,7 @@ pub(super) fn compute_match_usefulness<'p>(
 }
 
 #[derive(Debug)]
-pub(super) struct Cx;
+pub struct Cx;
 
 /// The context for pattern analysis. Forwards anything interesting to `Ty` methods.
 impl PatCx for Cx {
diff --git a/compiler/rustc_privacy/Cargo.toml b/compiler/rustc_privacy/Cargo.toml
index dc00ea8af43..242c67d732a 100644
--- a/compiler/rustc_privacy/Cargo.toml
+++ b/compiler/rustc_privacy/Cargo.toml
@@ -18,6 +18,3 @@ rustc_span = { path = "../rustc_span" }
 rustc_ty_utils = { path = "../rustc_ty_utils" }
 tracing = "0.1"
 # tidy-alphabetical-end
-
-[lints]
-workspace = true
diff --git a/compiler/rustc_privacy/src/lib.rs b/compiler/rustc_privacy/src/lib.rs
index 5e3e8d69b60..5271d03a6f6 100644
--- a/compiler/rustc_privacy/src/lib.rs
+++ b/compiler/rustc_privacy/src/lib.rs
@@ -6,6 +6,7 @@
 #![feature(let_chains)]
 #![feature(rustdoc_internals)]
 #![feature(try_blocks)]
+#![warn(unreachable_pub)]
 // tidy-alphabetical-end
 
 mod errors;
diff --git a/compiler/rustc_query_impl/Cargo.toml b/compiler/rustc_query_impl/Cargo.toml
index 42f78448151..c85156e059e 100644
--- a/compiler/rustc_query_impl/Cargo.toml
+++ b/compiler/rustc_query_impl/Cargo.toml
@@ -20,6 +20,3 @@ rustc_span = { path = "../rustc_span" }
 thin-vec = "0.2.12"
 tracing = "0.1"
 # tidy-alphabetical-end
-
-[lints]
-workspace = true
diff --git a/compiler/rustc_query_impl/src/lib.rs b/compiler/rustc_query_impl/src/lib.rs
index 40da46a027d..73c205fdb17 100644
--- a/compiler/rustc_query_impl/src/lib.rs
+++ b/compiler/rustc_query_impl/src/lib.rs
@@ -8,6 +8,7 @@
 #![feature(min_specialization)]
 #![feature(rustc_attrs)]
 #![feature(rustdoc_internals)]
+#![warn(unreachable_pub)]
 // tidy-alphabetical-end
 
 use rustc_data_structures::stable_hasher::HashStable;
diff --git a/compiler/rustc_query_system/Cargo.toml b/compiler/rustc_query_system/Cargo.toml
index c34d1170f0e..3e8ccb51021 100644
--- a/compiler/rustc_query_system/Cargo.toml
+++ b/compiler/rustc_query_system/Cargo.toml
@@ -25,6 +25,3 @@ smallvec = { version = "1.8.1", features = ["union", "may_dangle"] }
 thin-vec = "0.2.12"
 tracing = "0.1"
 # tidy-alphabetical-end
-
-[lints]
-workspace = true
diff --git a/compiler/rustc_query_system/src/lib.rs b/compiler/rustc_query_system/src/lib.rs
index a546362414c..ee984095ad8 100644
--- a/compiler/rustc_query_system/src/lib.rs
+++ b/compiler/rustc_query_system/src/lib.rs
@@ -6,6 +6,7 @@
 #![feature(hash_raw_entry)]
 #![feature(let_chains)]
 #![feature(min_specialization)]
+#![warn(unreachable_pub)]
 // tidy-alphabetical-end
 
 pub mod cache;
diff --git a/compiler/rustc_resolve/Cargo.toml b/compiler/rustc_resolve/Cargo.toml
index 8958a3ac304..f4771f1af2c 100644
--- a/compiler/rustc_resolve/Cargo.toml
+++ b/compiler/rustc_resolve/Cargo.toml
@@ -28,6 +28,3 @@ smallvec = { version = "1.8.1", features = ["union", "may_dangle"] }
 thin-vec = "0.2.12"
 tracing = "0.1"
 # tidy-alphabetical-end
-
-[lints]
-workspace = true
diff --git a/compiler/rustc_resolve/src/lib.rs b/compiler/rustc_resolve/src/lib.rs
index f724ecf76b3..ccdca855232 100644
--- a/compiler/rustc_resolve/src/lib.rs
+++ b/compiler/rustc_resolve/src/lib.rs
@@ -20,6 +20,7 @@
 #![feature(let_chains)]
 #![feature(rustc_attrs)]
 #![feature(rustdoc_internals)]
+#![warn(unreachable_pub)]
 // tidy-alphabetical-end
 
 use std::cell::{Cell, RefCell};
diff --git a/compiler/rustc_sanitizers/Cargo.toml b/compiler/rustc_sanitizers/Cargo.toml
index e18ed121ca5..900cd4243b1 100644
--- a/compiler/rustc_sanitizers/Cargo.toml
+++ b/compiler/rustc_sanitizers/Cargo.toml
@@ -15,6 +15,3 @@ rustc_middle = { path = "../rustc_middle" }
 rustc_span = { path = "../rustc_span" }
 rustc_target = { path = "../rustc_target" }
 rustc_trait_selection = { path = "../rustc_trait_selection" }
-
-[lints]
-workspace = true
diff --git a/compiler/rustc_sanitizers/src/lib.rs b/compiler/rustc_sanitizers/src/lib.rs
index e4792563e71..55be931bcd6 100644
--- a/compiler/rustc_sanitizers/src/lib.rs
+++ b/compiler/rustc_sanitizers/src/lib.rs
@@ -5,6 +5,7 @@
 
 // tidy-alphabetical-start
 #![feature(let_chains)]
+#![warn(unreachable_pub)]
 // tidy-alphabetical-end
 
 pub mod cfi;
diff --git a/compiler/rustc_serialize/Cargo.toml b/compiler/rustc_serialize/Cargo.toml
index 10bfe14abde..948242352e7 100644
--- a/compiler/rustc_serialize/Cargo.toml
+++ b/compiler/rustc_serialize/Cargo.toml
@@ -16,6 +16,3 @@ thin-vec = "0.2.12"
 rustc_macros = { path = "../rustc_macros" }
 tempfile = "3.2"
 # tidy-alphabetical-end
-
-[lints]
-workspace = true
diff --git a/compiler/rustc_serialize/src/lib.rs b/compiler/rustc_serialize/src/lib.rs
index 13c1a273eb8..9e9b78cfdd5 100644
--- a/compiler/rustc_serialize/src/lib.rs
+++ b/compiler/rustc_serialize/src/lib.rs
@@ -14,6 +14,7 @@
 #![feature(min_specialization)]
 #![feature(never_type)]
 #![feature(rustdoc_internals)]
+#![warn(unreachable_pub)]
 // tidy-alphabetical-end
 
 pub use self::serialize::{Decodable, Decoder, Encodable, Encoder};
diff --git a/compiler/rustc_session/Cargo.toml b/compiler/rustc_session/Cargo.toml
index 4b2b41ba845..a087725d34d 100644
--- a/compiler/rustc_session/Cargo.toml
+++ b/compiler/rustc_session/Cargo.toml
@@ -37,6 +37,3 @@ features = [
     "Win32_Foundation",
     "Win32_System_LibraryLoader",
 ]
-
-[lints]
-workspace = true
diff --git a/compiler/rustc_session/src/lib.rs b/compiler/rustc_session/src/lib.rs
index 0e19b982a13..d432e84fdb2 100644
--- a/compiler/rustc_session/src/lib.rs
+++ b/compiler/rustc_session/src/lib.rs
@@ -7,6 +7,7 @@
 // To generate CodegenOptionsTargetModifiers and UnstableOptionsTargetModifiers enums
 // with macro_rules, it is necessary to use recursive mechanic ("Incremental TT Munchers").
 #![recursion_limit = "256"]
+#![warn(unreachable_pub)]
 // tidy-alphabetical-end
 
 pub mod errors;
diff --git a/compiler/rustc_smir/Cargo.toml b/compiler/rustc_smir/Cargo.toml
index d14bff73b8b..a11df9a9c9b 100644
--- a/compiler/rustc_smir/Cargo.toml
+++ b/compiler/rustc_smir/Cargo.toml
@@ -18,6 +18,3 @@ scoped-tls = "1.0"
 stable_mir = {path = "../stable_mir" }
 tracing = "0.1"
 # tidy-alphabetical-end
-
-[lints]
-workspace = true
diff --git a/compiler/rustc_smir/src/lib.rs b/compiler/rustc_smir/src/lib.rs
index 9f888875306..2215e2f01ad 100644
--- a/compiler/rustc_smir/src/lib.rs
+++ b/compiler/rustc_smir/src/lib.rs
@@ -15,6 +15,7 @@
 )]
 #![doc(rust_logo)]
 #![feature(rustdoc_internals)]
+#![warn(unreachable_pub)]
 // tidy-alphabetical-end
 
 pub mod rustc_internal;
diff --git a/compiler/rustc_span/Cargo.toml b/compiler/rustc_span/Cargo.toml
index a6f86151b81..43a2d692577 100644
--- a/compiler/rustc_span/Cargo.toml
+++ b/compiler/rustc_span/Cargo.toml
@@ -22,6 +22,3 @@ sha2 = "0.10.1"
 tracing = "0.1"
 unicode-width = "0.2.0"
 # tidy-alphabetical-end
-
-[lints]
-workspace = true
diff --git a/compiler/rustc_span/src/lib.rs b/compiler/rustc_span/src/lib.rs
index f19d4d9f362..798e186a94b 100644
--- a/compiler/rustc_span/src/lib.rs
+++ b/compiler/rustc_span/src/lib.rs
@@ -32,6 +32,7 @@
 #![feature(rustc_attrs)]
 #![feature(rustdoc_internals)]
 #![feature(slice_as_chunks)]
+#![warn(unreachable_pub)]
 // tidy-alphabetical-end
 
 // The code produced by the `Encodable`/`Decodable` derive macros refer to
diff --git a/compiler/rustc_symbol_mangling/Cargo.toml b/compiler/rustc_symbol_mangling/Cargo.toml
index 89fe7ef2e40..90ddf4c8a04 100644
--- a/compiler/rustc_symbol_mangling/Cargo.toml
+++ b/compiler/rustc_symbol_mangling/Cargo.toml
@@ -19,6 +19,3 @@ rustc_session = { path = "../rustc_session" }
 rustc_span = { path = "../rustc_span" }
 tracing = "0.1"
 # tidy-alphabetical-end
-
-[lints]
-workspace = true
diff --git a/compiler/rustc_symbol_mangling/src/lib.rs b/compiler/rustc_symbol_mangling/src/lib.rs
index 269401ef3a2..4312c82815c 100644
--- a/compiler/rustc_symbol_mangling/src/lib.rs
+++ b/compiler/rustc_symbol_mangling/src/lib.rs
@@ -93,6 +93,7 @@
 #![doc(rust_logo)]
 #![feature(let_chains)]
 #![feature(rustdoc_internals)]
+#![warn(unreachable_pub)]
 // tidy-alphabetical-end
 
 use rustc_hir::def::DefKind;
diff --git a/compiler/rustc_target/Cargo.toml b/compiler/rustc_target/Cargo.toml
index 6a0268cdee7..189b19b0286 100644
--- a/compiler/rustc_target/Cargo.toml
+++ b/compiler/rustc_target/Cargo.toml
@@ -22,6 +22,3 @@ default-features = false
 features = ["elf", "macho"]
 version = "0.36.2"
 # tidy-alphabetical-end
-
-[lints]
-workspace = true
diff --git a/compiler/rustc_target/src/lib.rs b/compiler/rustc_target/src/lib.rs
index a8d7da5692d..7ebe96960ed 100644
--- a/compiler/rustc_target/src/lib.rs
+++ b/compiler/rustc_target/src/lib.rs
@@ -16,6 +16,7 @@
 #![feature(let_chains)]
 #![feature(rustc_attrs)]
 #![feature(rustdoc_internals)]
+#![warn(unreachable_pub)]
 // tidy-alphabetical-end
 
 use std::path::{Path, PathBuf};
diff --git a/compiler/rustc_trait_selection/Cargo.toml b/compiler/rustc_trait_selection/Cargo.toml
index 5def437bd80..1c61e23362a 100644
--- a/compiler/rustc_trait_selection/Cargo.toml
+++ b/compiler/rustc_trait_selection/Cargo.toml
@@ -26,6 +26,3 @@ smallvec = { version = "1.8.1", features = ["union", "may_dangle"] }
 thin-vec = "0.2"
 tracing = "0.1"
 # tidy-alphabetical-end
-
-[lints]
-workspace = true
diff --git a/compiler/rustc_trait_selection/src/lib.rs b/compiler/rustc_trait_selection/src/lib.rs
index b18fb0fb8fd..b235d0da83c 100644
--- a/compiler/rustc_trait_selection/src/lib.rs
+++ b/compiler/rustc_trait_selection/src/lib.rs
@@ -31,6 +31,7 @@
 #![feature(unwrap_infallible)]
 #![feature(yeet_expr)]
 #![recursion_limit = "512"] // For rustdoc
+#![warn(unreachable_pub)] // For rustdoc
 // tidy-alphabetical-end
 
 pub mod error_reporting;
diff --git a/compiler/rustc_traits/Cargo.toml b/compiler/rustc_traits/Cargo.toml
index 49bcaae8571..04aef4e7b9e 100644
--- a/compiler/rustc_traits/Cargo.toml
+++ b/compiler/rustc_traits/Cargo.toml
@@ -13,6 +13,3 @@ rustc_span = { path = "../rustc_span" }
 rustc_trait_selection = { path = "../rustc_trait_selection" }
 tracing = "0.1"
 # tidy-alphabetical-end
-
-[lints]
-workspace = true
diff --git a/compiler/rustc_traits/src/lib.rs b/compiler/rustc_traits/src/lib.rs
index 697c8391803..d2f979bd6d9 100644
--- a/compiler/rustc_traits/src/lib.rs
+++ b/compiler/rustc_traits/src/lib.rs
@@ -2,6 +2,7 @@
 
 // tidy-alphabetical-start
 #![recursion_limit = "256"]
+#![warn(unreachable_pub)]
 // tidy-alphabetical-end
 
 mod codegen;
diff --git a/compiler/rustc_transmute/Cargo.toml b/compiler/rustc_transmute/Cargo.toml
index ae1beb10728..f0c783b3002 100644
--- a/compiler/rustc_transmute/Cargo.toml
+++ b/compiler/rustc_transmute/Cargo.toml
@@ -25,6 +25,3 @@ rustc = [
 # tidy-alphabetical-start
 itertools = "0.12"
 # tidy-alphabetical-end
-
-[lints]
-workspace = true
diff --git a/compiler/rustc_transmute/src/lib.rs b/compiler/rustc_transmute/src/lib.rs
index 00928137d29..81a11f7cfb2 100644
--- a/compiler/rustc_transmute/src/lib.rs
+++ b/compiler/rustc_transmute/src/lib.rs
@@ -1,5 +1,6 @@
 // tidy-alphabetical-start
 #![feature(never_type)]
+#![warn(unreachable_pub)]
 // tidy-alphabetical-end
 
 pub(crate) use rustc_data_structures::fx::{FxIndexMap as Map, FxIndexSet as Set};
diff --git a/compiler/rustc_ty_utils/Cargo.toml b/compiler/rustc_ty_utils/Cargo.toml
index 61acc12d0eb..4c7a57f2931 100644
--- a/compiler/rustc_ty_utils/Cargo.toml
+++ b/compiler/rustc_ty_utils/Cargo.toml
@@ -23,6 +23,3 @@ rustc_trait_selection = { path = "../rustc_trait_selection" }
 rustc_type_ir = { path = "../rustc_type_ir" }
 tracing = "0.1"
 # tidy-alphabetical-end
-
-[lints]
-workspace = true
diff --git a/compiler/rustc_ty_utils/src/lib.rs b/compiler/rustc_ty_utils/src/lib.rs
index 35cc6f39856..8be1611bb9a 100644
--- a/compiler/rustc_ty_utils/src/lib.rs
+++ b/compiler/rustc_ty_utils/src/lib.rs
@@ -16,6 +16,7 @@
 #![feature(let_chains)]
 #![feature(never_type)]
 #![feature(rustdoc_internals)]
+#![warn(unreachable_pub)]
 // tidy-alphabetical-end
 
 use rustc_middle::query::Providers;
diff --git a/compiler/rustc_type_ir/Cargo.toml b/compiler/rustc_type_ir/Cargo.toml
index 0381797d7e9..7b2593b96e3 100644
--- a/compiler/rustc_type_ir/Cargo.toml
+++ b/compiler/rustc_type_ir/Cargo.toml
@@ -34,5 +34,5 @@ nightly = [
     "rustc_ast_ir/nightly",
 ]
 
-[lints]
-workspace = true
+[lints.rust]
+unexpected_cfgs = { level = "warn", check-cfg = ['cfg(bootstrap)'] }
diff --git a/compiler/rustc_type_ir/src/lib.rs b/compiler/rustc_type_ir/src/lib.rs
index e2dfd9173fa..15ef4e7d6c1 100644
--- a/compiler/rustc_type_ir/src/lib.rs
+++ b/compiler/rustc_type_ir/src/lib.rs
@@ -6,6 +6,7 @@
     feature(associated_type_defaults, never_type, rustc_attrs, negative_impls)
 )]
 #![cfg_attr(feature = "nightly", allow(internal_features))]
+#![warn(unreachable_pub)]
 // tidy-alphabetical-end
 
 extern crate self as rustc_type_ir;
diff --git a/compiler/rustc_type_ir_macros/Cargo.toml b/compiler/rustc_type_ir_macros/Cargo.toml
index a3fa4623855..15a55575099 100644
--- a/compiler/rustc_type_ir_macros/Cargo.toml
+++ b/compiler/rustc_type_ir_macros/Cargo.toml
@@ -13,6 +13,3 @@ quote = "1"
 syn = { version = "2.0.9", features = ["full"] }
 synstructure = "0.13.0"
 # tidy-alphabetical-end
-
-[lints]
-workspace = true
diff --git a/compiler/stable_mir/Cargo.toml b/compiler/stable_mir/Cargo.toml
index 358a3915402..d691a0e4f22 100644
--- a/compiler/stable_mir/Cargo.toml
+++ b/compiler/stable_mir/Cargo.toml
@@ -6,6 +6,3 @@ edition = "2024"
 [dependencies]
 scoped-tls = "1.0"
 serde = { version = "1.0.125", features = [ "derive" ] }
-
-[lints]
-workspace = true
diff --git a/src/bootstrap/src/core/builder/cargo.rs b/src/bootstrap/src/core/builder/cargo.rs
index 2cbf82ccda3..12dd40d14e9 100644
--- a/src/bootstrap/src/core/builder/cargo.rs
+++ b/src/bootstrap/src/core/builder/cargo.rs
@@ -1072,17 +1072,12 @@ impl Builder<'_> {
         }
 
         if mode == Mode::Rustc {
-            // NOTE: rustc-specific lints are specified here. Normal rust lints
-            // are specified in the `[workspace.lints.rust]` section in the
-            // top-level `Cargo.toml`. If/when tool lints are supported by
-            // Cargo, these lints can be move to a `[workspace.lints.rustc]`
-            // section in the top-level `Cargo.toml`.
-            //
-            // NOTE: these flags are added to RUSTFLAGS, which is ignored when
-            // compiling proc macro crates such as `rustc_macros`,
-            // unfortunately.
             rustflags.arg("-Wrustc::internal");
             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");
+            rustflags.arg("-Wunsafe_op_in_unsafe_fn");
         }
 
         if self.config.rust_frame_pointers {
diff --git a/src/ci/citool/src/metrics.rs b/src/ci/citool/src/metrics.rs
index 8548602b31c..83b3d5ceed0 100644
--- a/src/ci/citool/src/metrics.rs
+++ b/src/ci/citool/src/metrics.rs
@@ -67,6 +67,10 @@ fn render_table(suites: BTreeMap<String, TestSuiteRecord>) -> String {
     let mut table = "| Test suite | Passed ✅ | Ignored 🚫 | Failed  ❌ |\n".to_string();
     writeln!(table, "|:------|------:|------:|------:|").unwrap();
 
+    fn compute_pct(value: f64, total: f64) -> f64 {
+        if total == 0.0 { 0.0 } else { value / total }
+    }
+
     fn write_row(
         buffer: &mut String,
         name: &str,
@@ -75,9 +79,9 @@ fn render_table(suites: BTreeMap<String, TestSuiteRecord>) -> String {
     ) -> std::fmt::Result {
         let TestSuiteRecord { passed, ignored, failed } = record;
         let total = (record.passed + record.ignored + record.failed) as f64;
-        let passed_pct = ((*passed as f64) / total) * 100.0;
-        let ignored_pct = ((*ignored as f64) / total) * 100.0;
-        let failed_pct = ((*failed as f64) / total) * 100.0;
+        let passed_pct = compute_pct(*passed as f64, total) * 100.0;
+        let ignored_pct = compute_pct(*ignored as f64, total) * 100.0;
+        let failed_pct = compute_pct(*failed as f64, total) * 100.0;
 
         write!(buffer, "| {surround}{name}{surround} |")?;
         write!(buffer, " {surround}{passed} ({passed_pct:.0}%){surround} |")?;
diff --git a/src/doc/unstable-book/src/language-features/rustc-private.md b/src/doc/unstable-book/src/language-features/rustc-private.md
index 3b83a3cf4df..57ed857cdfe 100644
--- a/src/doc/unstable-book/src/language-features/rustc-private.md
+++ b/src/doc/unstable-book/src/language-features/rustc-private.md
@@ -12,3 +12,33 @@ The presence of this feature changes the way the linkage format for dylibs is ca
 that is necessary for linking against dylibs that statically link `std` (such as `rustc_driver`).
 This makes this feature "viral" in linkage; its use in a given crate makes its use required in
 dependent crates which link to it (including integration tests, which are built as separate crates).
+
+## Common linker failures related to missing LLVM libraries
+
+### When using `rustc-private` with Official Toolchains
+
+When using the `rustc_private` feature with official toolchains distributed via rustup, you'll need to install:
+
+1. The `rustc-dev` component (provides compiler libraries)
+2. The `llvm-tools` component (provides LLVM libraries needed for linking)
+
+You can install these components using `rustup`:
+
+```text
+rustup component add rustc-dev llvm-tools
+```
+
+Without the `llvm-tools` component, you may encounter linking errors like:
+
+```text
+error: linking with `cc` failed: exit status: 1
+  |
+  = note: rust-lld: error: unable to find library -lLLVM-{version}
+```
+
+### When using `rustc-private` with Custom Toolchains
+
+For custom-built toolchains or environments not using rustup, different configuration may be required:
+
+- Ensure LLVM libraries are available in your library search paths
+- You might need to configure library paths explicitly depending on your LLVM installation
diff --git a/src/librustdoc/doctest/runner.rs b/src/librustdoc/doctest/runner.rs
index 58efa35711a..f891505d2a6 100644
--- a/src/librustdoc/doctest/runner.rs
+++ b/src/librustdoc/doctest/runner.rs
@@ -47,11 +47,8 @@ impl DocTestRunner {
                 self.crate_attrs.insert(line.to_string());
             }
         }
-        if !self.ids.is_empty() {
-            self.ids.push(',');
-        }
         self.ids.push_str(&format!(
-            "{}::TEST",
+            "tests.push({}::TEST);\n",
             generate_mergeable_doctest(
                 doctest,
                 scraped_test,
@@ -142,7 +139,11 @@ mod __doctest_mod {{
 
 #[rustc_main]
 fn main() -> std::process::ExitCode {{
-const TESTS: [test::TestDescAndFn; {nb_tests}] = [{ids}];
+let tests = {{
+    let mut tests = Vec::with_capacity({nb_tests});
+    {ids}
+    tests
+}};
 let test_marker = std::ffi::OsStr::new(__doctest_mod::RUN_OPTION);
 let test_args = &[{test_args}];
 const ENV_BIN: &'static str = \"RUSTDOC_DOCTEST_BIN_PATH\";
@@ -150,11 +151,11 @@ const ENV_BIN: &'static str = \"RUSTDOC_DOCTEST_BIN_PATH\";
 if let Ok(binary) = std::env::var(ENV_BIN) {{
     let _ = crate::__doctest_mod::BINARY_PATH.set(binary.into());
     unsafe {{ std::env::remove_var(ENV_BIN); }}
-    return std::process::Termination::report(test::test_main(test_args, Vec::from(TESTS), None));
+    return std::process::Termination::report(test::test_main(test_args, tests, None));
 }} else if let Ok(nb_test) = std::env::var(__doctest_mod::RUN_OPTION) {{
     if let Ok(nb_test) = nb_test.parse::<usize>() {{
-        if let Some(test) = TESTS.get(nb_test) {{
-            if let test::StaticTestFn(f) = test.testfn {{
+        if let Some(test) = tests.get(nb_test) {{
+            if let test::StaticTestFn(f) = &test.testfn {{
                 return std::process::Termination::report(f());
             }}
         }}
@@ -164,7 +165,7 @@ if let Ok(binary) = std::env::var(ENV_BIN) {{
 
 eprintln!(\"WARNING: No rustdoc doctest environment variable provided so doctests will be run in \
 the same process\");
-std::process::Termination::report(test::test_main(test_args, Vec::from(TESTS), None))
+std::process::Termination::report(test::test_main(test_args, tests, None))
 }}",
             nb_tests = self.nb_tests,
             output = self.output_merged_tests,
diff --git a/src/tools/rust-analyzer/.editorconfig b/src/tools/rust-analyzer/.editorconfig
index 6bb743a6736..2d28ebd4766 100644
--- a/src/tools/rust-analyzer/.editorconfig
+++ b/src/tools/rust-analyzer/.editorconfig
@@ -15,3 +15,6 @@ indent_size = 2
 
 [*.{yml,yaml}]
 indent_size = 2
+
+[COMMIT_EDITMSG]
+max_line_length = unset
diff --git a/src/tools/rust-analyzer/.github/workflows/ci.yaml b/src/tools/rust-analyzer/.github/workflows/ci.yaml
index bc770dbe71e..7a6b43a0531 100644
--- a/src/tools/rust-analyzer/.github/workflows/ci.yaml
+++ b/src/tools/rust-analyzer/.github/workflows/ci.yaml
@@ -35,19 +35,48 @@ jobs:
             typescript:
               - 'editors/code/**'
             proc_macros:
+              - 'crates/tt/**'
               - 'crates/proc-macro-api/**'
               - 'crates/proc-macro-srv/**'
               - 'crates/proc-macro-srv-cli/**'
 
-  rust:
+  proc-macro-srv:
     needs: changes
+    if: github.repository == 'rust-lang/rust-analyzer' && needs.changes.outputs.proc_macros == 'true'
+    name: proc-macro-srv
+    runs-on: ubuntu-latest
+    steps:
+      - name: Checkout repository
+        uses: actions/checkout@v4
+        with:
+          ref: ${{ github.event.pull_request.head.sha }}
+
+      - name: Install Rust toolchain
+        run: |
+          rustup update --no-self-update nightly
+          rustup default nightly
+          rustup component add --toolchain nightly rust-src rustfmt
+      # https://github.com/actions-rust-lang/setup-rust-toolchain/blob/main/rust.json
+      - name: Install Rust Problem Matcher
+        if: matrix.os == 'ubuntu-latest'
+        run: echo "::add-matcher::.github/rust.json"
+
+      - name: Cache Dependencies
+        uses: Swatinem/rust-cache@9bdad043e88c75890e36ad3bbc8d27f0090dd609
+
+      - name: Bump opt-level
+        if: matrix.os == 'ubuntu-latest'
+        run: sed -i '/\[profile.dev]/a opt-level=1' Cargo.toml
+
+      - name: Test
+        run: cargo test --features sysroot-abi -p rust-analyzer -p proc-macro-srv -p proc-macro-srv-cli -p proc-macro-api -- --quiet
+
+  rust:
     if: github.repository == 'rust-lang/rust-analyzer'
     name: Rust
     runs-on: ${{ matrix.os }}
     env:
       CC: deny_c
-      RUST_CHANNEL: "${{ needs.changes.outputs.proc_macros == 'true' && 'nightly' || 'stable' }}"
-      USE_SYSROOT_ABI: "${{ needs.changes.outputs.proc_macros == 'true' && '--features sysroot-abi' || '' }}"
 
     strategy:
       fail-fast: false
@@ -62,13 +91,12 @@ jobs:
 
       - name: Install Rust toolchain
         run: |
-          rustup update --no-self-update ${{ env.RUST_CHANNEL }}
-          rustup default ${{ env.RUST_CHANNEL }}
-          rustup component add --toolchain ${{ env.RUST_CHANNEL }} rust-src
+          rustup update --no-self-update stable
+          rustup default stable
+          rustup component add --toolchain stable 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
+          rustup toolchain install nightly --profile minimal --component rustfmt
       # https://github.com/actions-rust-lang/setup-rust-toolchain/blob/main/rust.json
       - name: Install Rust Problem Matcher
         if: matrix.os == 'ubuntu-latest'
@@ -76,8 +104,6 @@ jobs:
 
       - name: Cache Dependencies
         uses: Swatinem/rust-cache@9bdad043e88c75890e36ad3bbc8d27f0090dd609
-        with:
-          key: ${{ env.RUST_CHANNEL }}
 
       - name: Bump opt-level
         if: matrix.os == 'ubuntu-latest'
@@ -87,16 +113,16 @@ jobs:
         run: cargo codegen --check
 
       - name: Compile (tests)
-        run: cargo test --no-run --locked ${{ env.USE_SYSROOT_ABI }}
+        run: cargo test --no-run --locked
 
       # It's faster to `test` before `build` ¯\_(ツ)_/¯
       - name: Compile (rust-analyzer)
         if: matrix.os == 'ubuntu-latest'
-        run: cargo build --quiet ${{ env.USE_SYSROOT_ABI }}
+        run: cargo build --quiet
 
       - name: Test
         if: matrix.os == 'ubuntu-latest' || matrix.os == 'windows-latest' || github.event_name == 'push'
-        run: cargo test ${{ env.USE_SYSROOT_ABI }} -- --nocapture --quiet
+        run: cargo test -- --quiet
 
       - name: Switch to stable toolchain
         run: |
@@ -157,7 +183,7 @@ jobs:
 
   typescript:
     needs: changes
-    if: github.repository == 'rust-lang/rust-analyzer'
+    if: github.repository == 'rust-lang/rust-analyzer' && needs.changes.outputs.typescript == 'true'
     name: TypeScript
     strategy:
       fail-fast: false
@@ -169,21 +195,18 @@ jobs:
     steps:
       - name: Checkout repository
         uses: actions/checkout@v4
-        if: needs.changes.outputs.typescript == 'true'
 
       - name: Install Nodejs
         uses: actions/setup-node@v4
         with:
           node-version: 22
-        if: needs.changes.outputs.typescript == 'true'
 
       - name: Install xvfb
-        if: matrix.os == 'ubuntu-latest' && needs.changes.outputs.typescript == 'true'
+        if: matrix.os == 'ubuntu-latest'
         run: sudo apt-get install -y xvfb
 
       - run: npm ci
         working-directory: ./editors/code
-        if: needs.changes.outputs.typescript == 'true'
 
       #    - run: npm audit || { sleep 10 && npm audit; } || { sleep 30 && npm audit; }
       #      if: runner.os == 'Linux'
@@ -192,27 +215,24 @@ jobs:
       # If this steps fails, your code's type integrity might be wrong at some places at TypeScript level.
       - run: npm run typecheck
         working-directory: ./editors/code
-        if: needs.changes.outputs.typescript == 'true'
 
       # You may fix the code automatically by running `npm run lint:fix` if this steps fails.
       - run: npm run lint
         working-directory: ./editors/code
-        if: needs.changes.outputs.typescript == 'true'
 
       # To fix this steps, please run `npm run format`.
       - run: npm run format:check
         working-directory: ./editors/code
-        if: needs.changes.outputs.typescript == 'true'
 
       - name: Run VS Code tests (Linux)
-        if: matrix.os == 'ubuntu-latest' && needs.changes.outputs.typescript == 'true'
+        if: matrix.os == 'ubuntu-latest'
         env:
           VSCODE_CLI: 1
         run: xvfb-run npm test
         working-directory: ./editors/code
 
       - name: Run VS Code tests (Windows)
-        if: matrix.os == 'windows-latest' && needs.changes.outputs.typescript == 'true'
+        if: matrix.os == 'windows-latest'
         env:
           VSCODE_CLI: 1
         run: npm test
@@ -220,7 +240,6 @@ jobs:
 
       - run: npm run package --scripts-prepend-node-path
         working-directory: ./editors/code
-        if: needs.changes.outputs.typescript == 'true'
 
   typo-check:
     name: Typo Check
@@ -242,7 +261,7 @@ jobs:
         run: typos
 
   conclusion:
-    needs: [rust, rust-cross, typescript, typo-check]
+    needs: [rust, rust-cross, typescript, typo-check, proc-macro-srv]
     # We need to ensure this job does *not* get skipped if its dependencies fail,
     # because a skipped job is considered a success by GitHub. So we have to
     # overwrite `if:`. We use `!cancelled()` to ensure the job does still not get run
@@ -257,5 +276,5 @@ jobs:
         run: |
           # Print the dependent jobs to see them in the CI log
           jq -C <<< '${{ toJson(needs) }}'
-          # Check if all jobs that we depend on (in the needs array) were successful.
-          jq --exit-status 'all(.result == "success")' <<< '${{ toJson(needs) }}'
+          # Check if all jobs that we depend on (in the needs array) were successful (or have been skipped).
+          jq --exit-status 'all(.result == "success" or .result == "skipped")' <<< '${{ toJson(needs) }}'
diff --git a/src/tools/rust-analyzer/Cargo.lock b/src/tools/rust-analyzer/Cargo.lock
index c57953ba654..1e1d68f7782 100644
--- a/src/tools/rust-analyzer/Cargo.lock
+++ b/src/tools/rust-analyzer/Cargo.lock
@@ -191,9 +191,9 @@ checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724"
 
 [[package]]
 name = "chalk-derive"
-version = "0.99.0"
+version = "0.100.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "572583d9b97f9d277e5c7607f8239a30e2e04d3ed3b47c87d1cb2152ae724073"
+checksum = "ab2d131019373f0d0d1f2af0abd4f719739f6583c1b33965112455f643a910af"
 dependencies = [
  "proc-macro2",
  "quote",
@@ -203,9 +203,9 @@ dependencies = [
 
 [[package]]
 name = "chalk-ir"
-version = "0.99.0"
+version = "0.100.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e60e0ef9c81dce1336a9ed3c76f08775f5b623151d96d85ba45f7b10de76d1c7"
+checksum = "4f114996bda14c0213f014a4ef31a7867dcf5f539a3900477fc6b20138e7a17b"
 dependencies = [
  "bitflags 2.7.0",
  "chalk-derive",
@@ -213,9 +213,9 @@ dependencies = [
 
 [[package]]
 name = "chalk-recursive"
-version = "0.99.0"
+version = "0.100.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5a06350d614e22b03a69b8105e3541614450a7ea48bc58ecc6c6bd92731a3995"
+checksum = "551e956e031c09057c7b21f17d48d91de99c9b6b6e34bceaf5e7202d71021268"
 dependencies = [
  "chalk-derive",
  "chalk-ir",
@@ -226,9 +226,9 @@ dependencies = [
 
 [[package]]
 name = "chalk-solve"
-version = "0.99.0"
+version = "0.100.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0e428761e9b55bee516bfe2457caed8b6d1b86353f92ae825bbe438a36ce91e8"
+checksum = "cd7ca50181156ce649efe8e5dd00580f573651554e4dcd11afa4e2ac93f53324"
 dependencies = [
  "chalk-derive",
  "chalk-ir",
@@ -626,7 +626,6 @@ dependencies = [
  "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",
@@ -1504,9 +1503,9 @@ dependencies = [
 
 [[package]]
 name = "ra-ap-rustc_abi"
-version = "0.98.0"
+version = "0.100.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4b42cccfff8091a4c3397736518774dbad619e82f8def6f70d8e46dbbe396007"
+checksum = "f1651b0f7e8c3eb7c27a88f39d277e69c32bfe58e3be174d286c1a24d6a7a4d8"
 dependencies = [
  "bitflags 2.7.0",
  "ra-ap-rustc_hashes",
@@ -1516,18 +1515,18 @@ dependencies = [
 
 [[package]]
 name = "ra-ap-rustc_hashes"
-version = "0.98.0"
+version = "0.100.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "46d8bd34ed6552c8cac1764106ef5adbeef3e5c7700e0ceb4c83a47a631894fe"
+checksum = "2bcd85e93dc0ea850bcfe7957a115957df799ccbc9eea488bdee5ec6780d212b"
 dependencies = [
  "rustc-stable-hash",
 ]
 
 [[package]]
 name = "ra-ap-rustc_index"
-version = "0.98.0"
+version = "0.100.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "93799e4dccbbd47f8b66bc0aa42effc1b7077aaee09d8a40b86b8d659b80c7b7"
+checksum = "62b295fc0640cd9fe0ecab872ee4a17a96f90a3998ec9f0c4765e9b8415c12cc"
 dependencies = [
  "ra-ap-rustc_index_macros",
  "smallvec",
@@ -1535,9 +1534,9 @@ dependencies = [
 
 [[package]]
 name = "ra-ap-rustc_index_macros"
-version = "0.98.0"
+version = "0.100.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "30baa5d00f94ba437a9dcaf7ae074ebe4f367bb05a4c2835e0aa2e7af3463aac"
+checksum = "c675f4257023aa933882906f13802cae287e88cc39ab13cbb96809083db0c801"
 dependencies = [
  "proc-macro2",
  "quote",
@@ -1546,9 +1545,9 @@ dependencies = [
 
 [[package]]
 name = "ra-ap-rustc_lexer"
-version = "0.98.0"
+version = "0.100.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3004d1d1b50afe3e1f9cdd428a282da7ffbf5f26dd8bf04af0d651d44e4873d8"
+checksum = "c8358702c2a510ea84ba5801ddc047d9ad9520902cfb0e6173277610cdce2c9c"
 dependencies = [
  "memchr",
  "unicode-properties",
@@ -1557,9 +1556,9 @@ dependencies = [
 
 [[package]]
 name = "ra-ap-rustc_parse_format"
-version = "0.98.0"
+version = "0.100.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "cb57e5124a64aaaf92c06130fbc1b8e1d547b5a2a96081f1f848e31c211df5d2"
+checksum = "b98f402011d46732c35c47bfd111dec0495747fef2ec900ddee7fe15d78449a7"
 dependencies = [
  "ra-ap-rustc_index",
  "ra-ap-rustc_lexer",
@@ -1567,9 +1566,9 @@ dependencies = [
 
 [[package]]
 name = "ra-ap-rustc_pattern_analysis"
-version = "0.98.0"
+version = "0.100.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e427c3d30e4bdff28abd6b0ef3e6f4dfab44acd9468a4954eeff8717d8df8819"
+checksum = "bef3ff73fa4653252ffe1d1e9177a446f49ef46d97140e4816b7ff2dad59ed53"
 dependencies = [
  "ra-ap-rustc_index",
  "rustc-hash 2.0.0",
diff --git a/src/tools/rust-analyzer/Cargo.toml b/src/tools/rust-analyzer/Cargo.toml
index 1132acb6474..ce2d66000e3 100644
--- a/src/tools/rust-analyzer/Cargo.toml
+++ b/src/tools/rust-analyzer/Cargo.toml
@@ -85,12 +85,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_hashes = { version = "0.98", default-features = false }
-ra-ap-rustc_lexer = { version = "0.98", default-features = false }
-ra-ap-rustc_parse_format = { version = "0.98", default-features = false }
-ra-ap-rustc_index = { version = "0.98", default-features = false }
-ra-ap-rustc_abi = { version = "0.98", default-features = false }
-ra-ap-rustc_pattern_analysis = { version = "0.98", default-features = false }
+ra-ap-rustc_hashes = { version = "0.100", default-features = false }
+ra-ap-rustc_lexer = { version = "0.100", default-features = false }
+ra-ap-rustc_parse_format = { version = "0.100", default-features = false }
+ra-ap-rustc_index = { version = "0.100", default-features = false }
+ra-ap-rustc_abi = { version = "0.100", default-features = false }
+ra-ap-rustc_pattern_analysis = { version = "0.100", default-features = false }
 
 # local crates that aren't published to crates.io. These should not have versions.
 
@@ -105,10 +105,10 @@ arrayvec = "0.7.4"
 bitflags = "2.4.1"
 cargo_metadata = "0.18.1"
 camino = "1.1.6"
-chalk-solve = { version = "0.99.0", default-features = false }
-chalk-ir = "0.99.0"
-chalk-recursive = { version = "0.99.0", default-features = false }
-chalk-derive = "0.99.0"
+chalk-solve = { version = "0.100.0", default-features = false }
+chalk-ir = "0.100.0"
+chalk-recursive = { version = "0.100.0", default-features = false }
+chalk-derive = "0.100.0"
 crossbeam-channel = "0.5.8"
 dissimilar = "1.0.7"
 dot = "0.1.4"
diff --git a/src/tools/rust-analyzer/crates/cfg/src/cfg_expr.rs b/src/tools/rust-analyzer/crates/cfg/src/cfg_expr.rs
index 84b91a527f0..0ec082dfa7f 100644
--- a/src/tools/rust-analyzer/crates/cfg/src/cfg_expr.rs
+++ b/src/tools/rust-analyzer/crates/cfg/src/cfg_expr.rs
@@ -18,6 +18,25 @@ pub enum CfgAtom {
     KeyValue { key: Symbol, value: Symbol },
 }
 
+impl PartialOrd for CfgAtom {
+    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
+        Some(self.cmp(other))
+    }
+}
+
+impl Ord for CfgAtom {
+    fn cmp(&self, other: &Self) -> std::cmp::Ordering {
+        match (self, other) {
+            (CfgAtom::Flag(a), CfgAtom::Flag(b)) => a.as_str().cmp(b.as_str()),
+            (CfgAtom::Flag(_), CfgAtom::KeyValue { .. }) => std::cmp::Ordering::Less,
+            (CfgAtom::KeyValue { .. }, CfgAtom::Flag(_)) => std::cmp::Ordering::Greater,
+            (CfgAtom::KeyValue { key, value }, CfgAtom::KeyValue { key: key2, value: value2 }) => {
+                key.as_str().cmp(key2.as_str()).then(value.as_str().cmp(value2.as_str()))
+            }
+        }
+    }
+}
+
 impl fmt::Display for CfgAtom {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
         match self {
diff --git a/src/tools/rust-analyzer/crates/cfg/src/dnf.rs b/src/tools/rust-analyzer/crates/cfg/src/dnf.rs
index f3ebca04650..424672a275e 100644
--- a/src/tools/rust-analyzer/crates/cfg/src/dnf.rs
+++ b/src/tools/rust-analyzer/crates/cfg/src/dnf.rs
@@ -66,9 +66,9 @@ impl DnfExpr {
             }
         }
 
-        res.enabled.sort_unstable_by(compare);
+        res.enabled.sort_unstable();
         res.enabled.dedup();
-        res.disabled.sort_unstable_by(compare);
+        res.disabled.sort_unstable();
         res.disabled.dedup();
         Some(res)
     }
@@ -114,25 +114,14 @@ impl DnfExpr {
             };
 
             // Undo the FxHashMap randomization for consistent output.
-            diff.enable.sort_unstable_by(compare);
-            diff.disable.sort_unstable_by(compare);
+            diff.enable.sort_unstable();
+            diff.disable.sort_unstable();
 
             Some(diff)
         })
     }
 }
 
-fn compare(a: &CfgAtom, b: &CfgAtom) -> std::cmp::Ordering {
-    match (a, b) {
-        (CfgAtom::Flag(a), CfgAtom::Flag(b)) => a.as_str().cmp(b.as_str()),
-        (CfgAtom::Flag(_), CfgAtom::KeyValue { .. }) => std::cmp::Ordering::Less,
-        (CfgAtom::KeyValue { .. }, CfgAtom::Flag(_)) => std::cmp::Ordering::Greater,
-        (CfgAtom::KeyValue { key, value }, CfgAtom::KeyValue { key: key2, value: value2 }) => {
-            key.as_str().cmp(key2.as_str()).then(value.as_str().cmp(value2.as_str()))
-        }
-    }
-}
-
 impl fmt::Display for DnfExpr {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
         if self.conjunctions.len() != 1 {
diff --git a/src/tools/rust-analyzer/crates/cfg/src/lib.rs b/src/tools/rust-analyzer/crates/cfg/src/lib.rs
index 6a6213a871f..08545b68511 100644
--- a/src/tools/rust-analyzer/crates/cfg/src/lib.rs
+++ b/src/tools/rust-analyzer/crates/cfg/src/lib.rs
@@ -148,16 +148,20 @@ pub struct CfgDiff {
 }
 
 impl CfgDiff {
-    /// Create a new CfgDiff. Will return None if the same item appears more than once in the set
-    /// of both.
-    pub fn new(enable: Vec<CfgAtom>, disable: Vec<CfgAtom>) -> Option<CfgDiff> {
-        let mut occupied = FxHashSet::default();
-        if enable.iter().chain(disable.iter()).any(|item| !occupied.insert(item)) {
-            // was present
-            return None;
+    /// Create a new CfgDiff.
+    pub fn new(mut enable: Vec<CfgAtom>, mut disable: Vec<CfgAtom>) -> CfgDiff {
+        enable.sort();
+        enable.dedup();
+        disable.sort();
+        disable.dedup();
+        for i in (0..enable.len()).rev() {
+            if let Some(j) = disable.iter().position(|atom| *atom == enable[i]) {
+                enable.remove(i);
+                disable.remove(j);
+            }
         }
 
-        Some(CfgDiff { enable, disable })
+        CfgDiff { enable, disable }
     }
 
     /// Returns the total number of atoms changed by this diff.
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/hir/type_ref.rs b/src/tools/rust-analyzer/crates/hir-def/src/hir/type_ref.rs
index 6d4d519cd2b..6de4026dff7 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/hir/type_ref.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/hir/type_ref.rs
@@ -260,7 +260,7 @@ pub enum TypeBound {
 }
 
 #[cfg(target_pointer_width = "64")]
-const _: [(); 24] = [(); ::std::mem::size_of::<TypeBound>()];
+const _: [(); 24] = [(); size_of::<TypeBound>()];
 
 #[derive(Clone, PartialEq, Eq, Hash, Debug)]
 pub enum UseArgRef {
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mbe/tt_conversion.rs b/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mbe/tt_conversion.rs
index c365a603d2a..138db1b498b 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mbe/tt_conversion.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mbe/tt_conversion.rs
@@ -93,15 +93,15 @@ fn broken_parenthesis_sequence() {
 macro_rules! m1 { ($x:ident) => { ($x } }
 macro_rules! m2 { ($x:ident) => {} }
 
-m1!();
-m2!(x
+fn f1() { m1!(x); }
+fn f2() { m2!(x }
 "#,
         expect![[r#"
 macro_rules! m1 { ($x:ident) => { ($x } }
 macro_rules! m2 { ($x:ident) => {} }
 
-/* error: macro definition has parse errors */
-/* error: expected ident */
+fn f1() { (x); }
+fn f2() { /* error: expected ident */ }
 "#]],
     )
 }
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 e6c2504d07a..713e7389736 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/path.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/path.rs
@@ -57,7 +57,7 @@ pub enum Path {
     /// or type anchor, it is `Path::Normal` with the generics filled with `None` even if there are none (practically
     /// this is not a problem since many more paths have generics than a type anchor).
     BarePath(Interned<ModPath>),
-    /// `Path::Normal` may have empty generics and type anchor (but generic args will be filled with `None`).
+    /// `Path::Normal` will always have either generics or type anchor.
     Normal(NormalPath),
     /// A link to a lang item. It is used in desugaring of things like `it?`. We can show these
     /// links via a normal path since they might be private and not accessible in the usage place.
@@ -208,11 +208,15 @@ impl Path {
                     mod_path.segments()[..mod_path.segments().len() - 1].iter().cloned(),
                 ));
                 let qualifier_generic_args = &generic_args[..generic_args.len() - 1];
-                Some(Path::Normal(NormalPath::new(
-                    type_anchor,
-                    qualifier_mod_path,
-                    qualifier_generic_args.iter().cloned(),
-                )))
+                if type_anchor.is_none() && qualifier_generic_args.iter().all(|it| it.is_none()) {
+                    Some(Path::BarePath(qualifier_mod_path))
+                } else {
+                    Some(Path::Normal(NormalPath::new(
+                        type_anchor,
+                        qualifier_mod_path,
+                        qualifier_generic_args.iter().cloned(),
+                    )))
+                }
             }
             Path::LangItem(..) => None,
         }
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 e5774b48044..a2e6e4cc043 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/resolver.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/resolver.rs
@@ -3,10 +3,11 @@ use std::{fmt, iter, mem};
 
 use base_db::CrateId;
 use hir_expand::{name::Name, MacroDefId};
-use intern::sym;
+use intern::{sym, Symbol};
 use itertools::Itertools as _;
 use rustc_hash::FxHashSet;
 use smallvec::{smallvec, SmallVec};
+use span::SyntaxContextId;
 use triomphe::Arc;
 
 use crate::{
@@ -343,15 +344,7 @@ impl Resolver {
         }
 
         if n_segments <= 1 {
-            let mut hygiene_info = if !hygiene_id.is_root() {
-                let ctx = hygiene_id.lookup(db);
-                ctx.outer_expn.map(|expansion| {
-                    let expansion = db.lookup_intern_macro_call(expansion);
-                    (ctx.parent, expansion.def)
-                })
-            } else {
-                None
-            };
+            let mut hygiene_info = hygiene_info(db, hygiene_id);
             for scope in self.scopes() {
                 match scope {
                     Scope::ExprScope(scope) => {
@@ -371,19 +364,7 @@ impl Resolver {
                         }
                     }
                     Scope::MacroDefScope(macro_id) => {
-                        if let Some((parent_ctx, label_macro_id)) = hygiene_info {
-                            if label_macro_id == **macro_id {
-                                // A macro is allowed to refer to variables from before its declaration.
-                                // Therefore, if we got to the rib of its declaration, give up its hygiene
-                                // and use its parent expansion.
-                                let parent_ctx = db.lookup_intern_syntax_context(parent_ctx);
-                                hygiene_id = HygieneId::new(parent_ctx.opaque_and_semitransparent);
-                                hygiene_info = parent_ctx.outer_expn.map(|expansion| {
-                                    let expansion = db.lookup_intern_macro_call(expansion);
-                                    (parent_ctx.parent, expansion.def)
-                                });
-                            }
-                        }
+                        handle_macro_def_scope(db, &mut hygiene_id, &mut hygiene_info, macro_id)
                     }
                     Scope::GenericParams { params, def } => {
                         if let Some(id) = params.find_const_by_name(first_name, *def) {
@@ -730,6 +711,107 @@ impl Resolver {
         })
     }
 
+    /// Checks if we rename `renamed` (currently named `current_name`) to `new_name`, will the meaning of this reference
+    /// (that contains `current_name` path) change from `renamed` to some another variable (returned as `Some`).
+    pub fn rename_will_conflict_with_another_variable(
+        &self,
+        db: &dyn DefDatabase,
+        current_name: &Name,
+        current_name_as_path: &ModPath,
+        mut hygiene_id: HygieneId,
+        new_name: &Symbol,
+        to_be_renamed: BindingId,
+    ) -> Option<BindingId> {
+        let mut hygiene_info = hygiene_info(db, hygiene_id);
+        let mut will_be_resolved_to = None;
+        for scope in self.scopes() {
+            match scope {
+                Scope::ExprScope(scope) => {
+                    for entry in scope.expr_scopes.entries(scope.scope_id) {
+                        if entry.hygiene() == hygiene_id {
+                            if entry.binding() == to_be_renamed {
+                                // This currently resolves to our renamed variable, now `will_be_resolved_to`
+                                // contains `Some` if the meaning will change or `None` if not.
+                                return will_be_resolved_to;
+                            } else if entry.name().symbol() == new_name {
+                                will_be_resolved_to = Some(entry.binding());
+                            }
+                        }
+                    }
+                }
+                Scope::MacroDefScope(macro_id) => {
+                    handle_macro_def_scope(db, &mut hygiene_id, &mut hygiene_info, macro_id)
+                }
+                Scope::GenericParams { params, def } => {
+                    if params.find_const_by_name(current_name, *def).is_some() {
+                        // It does not resolve to our renamed variable.
+                        return None;
+                    }
+                }
+                Scope::AdtScope(_) | Scope::ImplDefScope(_) => continue,
+                Scope::BlockScope(m) => {
+                    if m.resolve_path_in_value_ns(db, current_name_as_path).is_some() {
+                        // It does not resolve to our renamed variable.
+                        return None;
+                    }
+                }
+            }
+        }
+        // It does not resolve to our renamed variable.
+        None
+    }
+
+    /// Checks if we rename `renamed` to `name`, will the meaning of this reference (that contains `name` path) change
+    /// from some other variable (returned as `Some`) to `renamed`.
+    pub fn rename_will_conflict_with_renamed(
+        &self,
+        db: &dyn DefDatabase,
+        name: &Name,
+        name_as_path: &ModPath,
+        mut hygiene_id: HygieneId,
+        to_be_renamed: BindingId,
+    ) -> Option<BindingId> {
+        let mut hygiene_info = hygiene_info(db, hygiene_id);
+        let mut will_resolve_to_renamed = false;
+        for scope in self.scopes() {
+            match scope {
+                Scope::ExprScope(scope) => {
+                    for entry in scope.expr_scopes.entries(scope.scope_id) {
+                        if entry.binding() == to_be_renamed {
+                            will_resolve_to_renamed = true;
+                        } else if entry.hygiene() == hygiene_id && entry.name() == name {
+                            if will_resolve_to_renamed {
+                                // This will resolve to the renamed variable before it resolves to the original variable.
+                                return Some(entry.binding());
+                            } else {
+                                // This will resolve to the original variable.
+                                return None;
+                            }
+                        }
+                    }
+                }
+                Scope::MacroDefScope(macro_id) => {
+                    handle_macro_def_scope(db, &mut hygiene_id, &mut hygiene_info, macro_id)
+                }
+                Scope::GenericParams { params, def } => {
+                    if params.find_const_by_name(name, *def).is_some() {
+                        // Here and below, it might actually resolve to our renamed variable - in which case it'll
+                        // hide the generic parameter or some other thing (not a variable). We don't check for that
+                        // because due to naming conventions, it is rare that variable will shadow a non-variable.
+                        return None;
+                    }
+                }
+                Scope::AdtScope(_) | Scope::ImplDefScope(_) => continue,
+                Scope::BlockScope(m) => {
+                    if m.resolve_path_in_value_ns(db, name_as_path).is_some() {
+                        return None;
+                    }
+                }
+            }
+        }
+        None
+    }
+
     /// `expr_id` is required to be an expression id that comes after the top level expression scope in the given resolver
     #[must_use]
     pub fn update_to_inner_scope(
@@ -795,6 +877,44 @@ impl Resolver {
     }
 }
 
+#[inline]
+fn handle_macro_def_scope(
+    db: &dyn DefDatabase,
+    hygiene_id: &mut HygieneId,
+    hygiene_info: &mut Option<(SyntaxContextId, MacroDefId)>,
+    macro_id: &MacroDefId,
+) {
+    if let Some((parent_ctx, label_macro_id)) = hygiene_info {
+        if label_macro_id == macro_id {
+            // A macro is allowed to refer to variables from before its declaration.
+            // Therefore, if we got to the rib of its declaration, give up its hygiene
+            // and use its parent expansion.
+            let parent_ctx = db.lookup_intern_syntax_context(*parent_ctx);
+            *hygiene_id = HygieneId::new(parent_ctx.opaque_and_semitransparent);
+            *hygiene_info = parent_ctx.outer_expn.map(|expansion| {
+                let expansion = db.lookup_intern_macro_call(expansion);
+                (parent_ctx.parent, expansion.def)
+            });
+        }
+    }
+}
+
+#[inline]
+fn hygiene_info(
+    db: &dyn DefDatabase,
+    hygiene_id: HygieneId,
+) -> Option<(SyntaxContextId, MacroDefId)> {
+    if !hygiene_id.is_root() {
+        let ctx = hygiene_id.lookup(db);
+        ctx.outer_expn.map(|expansion| {
+            let expansion = db.lookup_intern_macro_call(expansion);
+            (ctx.parent, expansion.def)
+        })
+    } else {
+        None
+    }
+}
+
 pub struct UpdateGuard(usize);
 
 impl Resolver {
diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/fixup.rs b/src/tools/rust-analyzer/crates/hir-expand/src/fixup.rs
index eb430177390..28894537d48 100644
--- a/src/tools/rust-analyzer/crates/hir-expand/src/fixup.rs
+++ b/src/tools/rust-analyzer/crates/hir-expand/src/fixup.rs
@@ -148,7 +148,6 @@ pub(crate) fn fixup_syntax(
                     }
                     if it.then_branch().is_none() {
                         append.insert(node.clone().into(), vec![
-                            // FIXME: THis should be a subtree no?
                             Leaf::Punct(Punct {
                                 char: '{',
                                 spacing: Spacing::Alone,
@@ -179,7 +178,6 @@ pub(crate) fn fixup_syntax(
                     }
                     if it.loop_body().is_none() {
                         append.insert(node.clone().into(), vec![
-                            // FIXME: THis should be a subtree no?
                             Leaf::Punct(Punct {
                                 char: '{',
                                 spacing: Spacing::Alone,
@@ -196,7 +194,6 @@ pub(crate) fn fixup_syntax(
                 ast::LoopExpr(it) => {
                     if it.loop_body().is_none() {
                         append.insert(node.clone().into(), vec![
-                            // FIXME: THis should be a subtree no?
                             Leaf::Punct(Punct {
                                 char: '{',
                                 spacing: Spacing::Alone,
@@ -228,7 +225,6 @@ pub(crate) fn fixup_syntax(
                     if it.match_arm_list().is_none() {
                         // No match arms
                         append.insert(node.clone().into(), vec![
-                            // FIXME: THis should be a subtree no?
                             Leaf::Punct(Punct {
                                 char: '{',
                                 spacing: Spacing::Alone,
@@ -269,7 +265,6 @@ pub(crate) fn fixup_syntax(
 
                     if it.loop_body().is_none() {
                         append.insert(node.clone().into(), vec![
-                            // FIXME: THis should be a subtree no?
                             Leaf::Punct(Punct {
                                 char: '{',
                                 spacing: Spacing::Alone,
@@ -309,28 +304,6 @@ pub(crate) fn fixup_syntax(
                         }
                     }
                 },
-                ast::ArgList(it) => {
-                    if it.r_paren_token().is_none() {
-                        append.insert(node.into(), vec![
-                            Leaf::Punct(Punct {
-                                span: fake_span(node_range),
-                                char: ')',
-                                spacing: Spacing::Alone
-                            })
-                        ]);
-                    }
-                },
-                ast::ArgList(it) => {
-                    if it.r_paren_token().is_none() {
-                        append.insert(node.into(), vec![
-                            Leaf::Punct(Punct {
-                                span: fake_span(node_range),
-                                char: ')',
-                                spacing: Spacing::Alone
-                            })
-                        ]);
-                    }
-                },
                 ast::ClosureExpr(it) => {
                     if it.body().is_none() {
                         append.insert(node.into(), vec![
@@ -476,12 +449,12 @@ fn reverse_fixups_(tt: &mut TopSubtree, undo_info: &[TopSubtree]) {
             }
         }
         tt::TokenTree::Subtree(tt) => {
+            // fixup should only create matching delimiters, but proc macros
+            // could just copy the span to one of the delimiters. We don't want
+            // to leak the dummy ID, so we remove both.
             if tt.delimiter.close.anchor.ast_id == FIXUP_DUMMY_AST_ID
                 || tt.delimiter.open.anchor.ast_id == FIXUP_DUMMY_AST_ID
             {
-                // Even though fixup never creates subtrees with fixup spans, the old proc-macro server
-                // might copy them if the proc-macro asks for it, so we need to filter those out
-                // here as well.
                 return TransformTtAction::remove();
             }
             TransformTtAction::Keep
@@ -571,6 +544,17 @@ mod tests {
             parse.syntax_node()
         );
 
+        // the fixed-up tree should not contain braces as punct
+        // FIXME: should probably instead check that it's a valid punctuation character
+        for x in tt.token_trees().flat_tokens() {
+            match x {
+                ::tt::TokenTree::Leaf(::tt::Leaf::Punct(punct)) => {
+                    assert!(!matches!(punct.char, '{' | '}' | '(' | ')' | '[' | ']'))
+                }
+                _ => (),
+            }
+        }
+
         reverse_fixups(&mut tt, &fixups.undo_info);
 
         // the fixed-up + reversed version should be equivalent to the original input
@@ -596,7 +580,7 @@ fn foo() {
 }
 "#,
             expect![[r#"
-fn foo () {for _ in __ra_fixup { }}
+fn foo () {for _ in __ra_fixup {}}
 "#]],
         )
     }
@@ -624,7 +608,7 @@ fn foo() {
 }
 "#,
             expect![[r#"
-fn foo () {for bar in qux { }}
+fn foo () {for bar in qux {}}
 "#]],
         )
     }
@@ -655,7 +639,7 @@ fn foo() {
 }
 "#,
             expect![[r#"
-fn foo () {match __ra_fixup { }}
+fn foo () {match __ra_fixup {}}
 "#]],
         )
     }
@@ -687,7 +671,7 @@ fn foo() {
 }
 "#,
             expect![[r#"
-fn foo () {match __ra_fixup { }}
+fn foo () {match __ra_fixup {}}
 "#]],
         )
     }
@@ -802,7 +786,7 @@ fn foo() {
 }
 "#,
             expect![[r#"
-fn foo () {if a { }}
+fn foo () {if a {}}
 "#]],
         )
     }
@@ -816,7 +800,7 @@ fn foo() {
 }
 "#,
             expect![[r#"
-fn foo () {if __ra_fixup { }}
+fn foo () {if __ra_fixup {}}
 "#]],
         )
     }
@@ -830,7 +814,7 @@ fn foo() {
 }
 "#,
             expect![[r#"
-fn foo () {if __ra_fixup {} { }}
+fn foo () {if __ra_fixup {} {}}
 "#]],
         )
     }
@@ -844,7 +828,7 @@ fn foo() {
 }
 "#,
             expect![[r#"
-fn foo () {while __ra_fixup { }}
+fn foo () {while __ra_fixup {}}
 "#]],
         )
     }
@@ -858,7 +842,7 @@ fn foo() {
 }
 "#,
             expect![[r#"
-fn foo () {while foo { }}
+fn foo () {while foo {}}
 "#]],
         )
     }
@@ -885,7 +869,7 @@ fn foo() {
 }
 "#,
             expect![[r#"
-fn foo () {loop { }}
+fn foo () {loop {}}
 "#]],
         )
     }
@@ -941,7 +925,7 @@ fn foo() {
 }
 "#,
             expect![[r#"
-fn foo () { foo ( a ) }
+fn foo () {foo (a)}
 "#]],
         );
         check(
@@ -951,7 +935,7 @@ fn foo() {
 }
 "#,
             expect![[r#"
-fn foo () { bar . foo ( a ) }
+fn foo () {bar . foo (a)}
 "#]],
         );
     }
diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/lib.rs b/src/tools/rust-analyzer/crates/hir-expand/src/lib.rs
index 41603e3c92e..c1d808cbf2c 100644
--- a/src/tools/rust-analyzer/crates/hir-expand/src/lib.rs
+++ b/src/tools/rust-analyzer/crates/hir-expand/src/lib.rs
@@ -5,6 +5,8 @@
 //! expansion.
 #![cfg_attr(feature = "in-rust-tree", feature(rustc_private))]
 
+pub use intern;
+
 pub mod attrs;
 pub mod builtin;
 pub mod change;
diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/mod_path.rs b/src/tools/rust-analyzer/crates/hir-expand/src/mod_path.rs
index f0cf7ebf479..75b58614540 100644
--- a/src/tools/rust-analyzer/crates/hir-expand/src/mod_path.rs
+++ b/src/tools/rust-analyzer/crates/hir-expand/src/mod_path.rs
@@ -388,7 +388,7 @@ macro_rules! __path {
     ($start:ident $(:: $seg:ident)*) => ({
         $crate::__known_path!($start $(:: $seg)*);
         $crate::mod_path::ModPath::from_segments($crate::mod_path::PathKind::Abs, vec![
-            $crate::name::Name::new_symbol_root(intern::sym::$start.clone()), $($crate::name::Name::new_symbol_root(intern::sym::$seg.clone()),)*
+            $crate::name::Name::new_symbol_root($crate::intern::sym::$start.clone()), $($crate::name::Name::new_symbol_root($crate::intern::sym::$seg.clone()),)*
         ])
     });
 }
@@ -399,7 +399,7 @@ pub use crate::__path as path;
 macro_rules! __tool_path {
     ($start:ident $(:: $seg:ident)*) => ({
         $crate::mod_path::ModPath::from_segments($crate::mod_path::PathKind::Plain, vec![
-            $crate::name::Name::new_symbol_root(intern::sym::rust_analyzer.clone()), $crate::name::Name::new_symbol_root(intern::sym::$start.clone()), $($crate::name::Name::new_symbol_root(intern::sym::$seg.clone()),)*
+            $crate::name::Name::new_symbol_root($crate::intern::sym::rust_analyzer.clone()), $crate::name::Name::new_symbol_root($crate::intern::sym::$start.clone()), $($crate::name::Name::new_symbol_root($crate::intern::sym::$seg.clone()),)*
         ])
     });
 }
diff --git a/src/tools/rust-analyzer/crates/hir-ty/Cargo.toml b/src/tools/rust-analyzer/crates/hir-ty/Cargo.toml
index 27849f3b449..1d12bee646c 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/Cargo.toml
+++ b/src/tools/rust-analyzer/crates/hir-ty/Cargo.toml
@@ -35,7 +35,6 @@ 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
 
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/chalk_db.rs b/src/tools/rust-analyzer/crates/hir-ty/src/chalk_db.rs
index c8ff6cba3dd..67fb73696f7 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/chalk_db.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/chalk_db.rs
@@ -708,6 +708,9 @@ fn well_known_trait_from_lang_item(item: LangItem) -> Option<WellKnownTrait> {
         LangItem::Fn => WellKnownTrait::Fn,
         LangItem::FnMut => WellKnownTrait::FnMut,
         LangItem::FnOnce => WellKnownTrait::FnOnce,
+        LangItem::AsyncFn => WellKnownTrait::AsyncFn,
+        LangItem::AsyncFnMut => WellKnownTrait::AsyncFnMut,
+        LangItem::AsyncFnOnce => WellKnownTrait::AsyncFnOnce,
         LangItem::Coroutine => WellKnownTrait::Coroutine,
         LangItem::Sized => WellKnownTrait::Sized,
         LangItem::Unpin => WellKnownTrait::Unpin,
@@ -715,6 +718,7 @@ fn well_known_trait_from_lang_item(item: LangItem) -> Option<WellKnownTrait> {
         LangItem::Tuple => WellKnownTrait::Tuple,
         LangItem::PointeeTrait => WellKnownTrait::Pointee,
         LangItem::FnPtrTrait => WellKnownTrait::FnPtr,
+        LangItem::Future => WellKnownTrait::Future,
         _ => return None,
     })
 }
@@ -730,6 +734,9 @@ fn lang_item_from_well_known_trait(trait_: WellKnownTrait) -> LangItem {
         WellKnownTrait::Fn => LangItem::Fn,
         WellKnownTrait::FnMut => LangItem::FnMut,
         WellKnownTrait::FnOnce => LangItem::FnOnce,
+        WellKnownTrait::AsyncFn => LangItem::AsyncFn,
+        WellKnownTrait::AsyncFnMut => LangItem::AsyncFnMut,
+        WellKnownTrait::AsyncFnOnce => LangItem::AsyncFnOnce,
         WellKnownTrait::Coroutine => LangItem::Coroutine,
         WellKnownTrait::Sized => LangItem::Sized,
         WellKnownTrait::Tuple => LangItem::Tuple,
@@ -737,6 +744,7 @@ fn lang_item_from_well_known_trait(trait_: WellKnownTrait) -> LangItem {
         WellKnownTrait::Unsize => LangItem::Unsize,
         WellKnownTrait::Pointee => LangItem::PointeeTrait,
         WellKnownTrait::FnPtr => LangItem::FnPtrTrait,
+        WellKnownTrait::Future => LangItem::Future,
     }
 }
 
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 7839589994b..fb604569f43 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/consteval.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/consteval.rs
@@ -15,9 +15,10 @@ use stdx::never;
 use triomphe::Arc;
 
 use crate::{
-    db::HirDatabase, generics::Generics, infer::InferenceContext, lower::ParamLoweringMode,
-    mir::monomorphize_mir_body_bad, to_placeholder_idx, Const, ConstData, ConstScalar, ConstValue,
-    GenericArg, Interner, MemoryMap, Substitution, TraitEnvironment, Ty, TyBuilder,
+    db::HirDatabase, display::DisplayTarget, generics::Generics, infer::InferenceContext,
+    lower::ParamLoweringMode, mir::monomorphize_mir_body_bad, to_placeholder_idx, Const, ConstData,
+    ConstScalar, ConstValue, GenericArg, Interner, MemoryMap, Substitution, TraitEnvironment, Ty,
+    TyBuilder,
 };
 
 use super::mir::{interpret_mir, lower_to_mir, pad16, MirEvalError, MirLowerError};
@@ -62,11 +63,15 @@ impl ConstEvalError {
         f: &mut String,
         db: &dyn HirDatabase,
         span_formatter: impl Fn(span::FileId, span::TextRange) -> String,
-        edition: span::Edition,
+        display_target: DisplayTarget,
     ) -> std::result::Result<(), std::fmt::Error> {
         match self {
-            ConstEvalError::MirLowerError(e) => e.pretty_print(f, db, span_formatter, edition),
-            ConstEvalError::MirEvalError(e) => e.pretty_print(f, db, span_formatter, edition),
+            ConstEvalError::MirLowerError(e) => {
+                e.pretty_print(f, db, span_formatter, display_target)
+            }
+            ConstEvalError::MirEvalError(e) => {
+                e.pretty_print(f, db, span_formatter, display_target)
+            }
         }
     }
 }
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/consteval/tests.rs b/src/tools/rust-analyzer/crates/hir-ty/src/consteval/tests.rs
index 2d7d4cacd2c..26a3b702297 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/consteval/tests.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/consteval/tests.rs
@@ -10,8 +10,8 @@ use test_fixture::WithFixture;
 use test_utils::skip_slow_tests;
 
 use crate::{
-    consteval::try_const_usize, db::HirDatabase, mir::pad16, test_db::TestDB, Const, ConstScalar,
-    Interner, MemoryMap,
+    consteval::try_const_usize, db::HirDatabase, display::DisplayTarget, mir::pad16,
+    test_db::TestDB, Const, ConstScalar, Interner, MemoryMap,
 };
 
 use super::{
@@ -101,11 +101,17 @@ fn check_answer(
 fn pretty_print_err(e: ConstEvalError, db: TestDB) -> String {
     let mut err = String::new();
     let span_formatter = |file, range| format!("{file:?} {range:?}");
-    let edition =
-        db.crate_graph()[*db.crate_graph().crates_in_topological_order().last().unwrap()].edition;
+    let display_target = DisplayTarget::from_crate(
+        &db,
+        *db.crate_graph().crates_in_topological_order().last().unwrap(),
+    );
     match e {
-        ConstEvalError::MirLowerError(e) => e.pretty_print(&mut err, &db, span_formatter, edition),
-        ConstEvalError::MirEvalError(e) => e.pretty_print(&mut err, &db, span_formatter, edition),
+        ConstEvalError::MirLowerError(e) => {
+            e.pretty_print(&mut err, &db, span_formatter, display_target)
+        }
+        ConstEvalError::MirEvalError(e) => {
+            e.pretty_print(&mut err, &db, span_formatter, display_target)
+        }
     }
     .unwrap();
     err
@@ -2713,12 +2719,11 @@ fn const_trait_assoc() {
         r#"
     //- minicore: size_of, fn
     //- /a/lib.rs crate:a
-    use core::mem::size_of;
     pub struct S<T>(T);
     impl<T> S<T> {
         pub const X: usize = {
             let k: T;
-            let f = || core::mem::size_of::<T>();
+            let f = || size_of::<T>();
             f()
         };
     }
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics.rs b/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics.rs
index 30c02a2936d..845d3333353 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics.rs
@@ -9,5 +9,8 @@ pub use crate::diagnostics::{
     expr::{
         record_literal_missing_fields, record_pattern_missing_fields, BodyValidationDiagnostic,
     },
-    unsafe_check::{missing_unsafe, unsafe_expressions, InsideUnsafeBlock, UnsafetyReason},
+    unsafe_check::{
+        missing_unsafe, unsafe_operations, unsafe_operations_for_body, InsideUnsafeBlock,
+        UnsafetyReason,
+    },
 };
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 59aaf85164a..cc6f4d9e52e 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
@@ -16,7 +16,6 @@ use intern::sym;
 use itertools::Itertools;
 use rustc_hash::FxHashSet;
 use rustc_pattern_analysis::constructor::Constructor;
-use span::Edition;
 use syntax::{
     ast::{self, UnaryOp},
     AstNode,
@@ -31,7 +30,7 @@ use crate::{
         self,
         pat_analysis::{self, DeconstructedPat, MatchCheckCtx, WitnessPat},
     },
-    display::HirDisplay,
+    display::{DisplayTarget, HirDisplay},
     Adjust, InferenceResult, Interner, Ty, TyExt, TyKind,
 };
 
@@ -633,24 +632,24 @@ fn missing_match_arms<'p>(
     arms_is_empty: bool,
     krate: CrateId,
 ) -> String {
-    struct DisplayWitness<'a, 'p>(&'a WitnessPat<'p>, &'a MatchCheckCtx<'p>, Edition);
+    struct DisplayWitness<'a, 'p>(&'a WitnessPat<'p>, &'a MatchCheckCtx<'p>, DisplayTarget);
     impl fmt::Display for DisplayWitness<'_, '_> {
         fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-            let DisplayWitness(witness, cx, edition) = *self;
+            let DisplayWitness(witness, cx, display_target) = *self;
             let pat = cx.hoist_witness_pat(witness);
-            write!(f, "{}", pat.display(cx.db, edition))
+            write!(f, "{}", pat.display(cx.db, display_target))
         }
     }
 
-    let edition = cx.db.crate_graph()[krate].edition;
     let non_empty_enum = match scrut_ty.as_adt() {
         Some((AdtId::EnumId(e), _)) => !cx.db.enum_data(e).variants.is_empty(),
         _ => false,
     };
+    let display_target = DisplayTarget::from_crate(cx.db, krate);
     if arms_is_empty && !non_empty_enum {
-        format!("type `{}` is non-empty", scrut_ty.display(cx.db, edition))
+        format!("type `{}` is non-empty", scrut_ty.display(cx.db, display_target))
     } else {
-        let pat_display = |witness| DisplayWitness(witness, cx, edition);
+        let pat_display = |witness| DisplayWitness(witness, cx, display_target);
         const LIMIT: usize = 3;
         match &*witnesses {
             [witness] => format!("`{}` not covered", pat_display(witness)),
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 ac849b0762d..d2b908839c4 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
@@ -95,7 +95,26 @@ enum UnsafeDiagnostic {
     DeprecatedSafe2024 { node: ExprId, inside_unsafe_block: InsideUnsafeBlock },
 }
 
-pub fn unsafe_expressions(
+pub fn unsafe_operations_for_body(
+    db: &dyn HirDatabase,
+    infer: &InferenceResult,
+    def: DefWithBodyId,
+    body: &Body,
+    callback: &mut dyn FnMut(ExprOrPatId),
+) {
+    let mut visitor_callback = |diag| {
+        if let UnsafeDiagnostic::UnsafeOperation { node, .. } = diag {
+            callback(node);
+        }
+    };
+    let mut visitor = UnsafeVisitor::new(db, infer, body, def, &mut visitor_callback);
+    visitor.walk_expr(body.body_expr);
+    for &param in &body.params {
+        visitor.walk_pat(param);
+    }
+}
+
+pub fn unsafe_operations(
     db: &dyn HirDatabase,
     infer: &InferenceResult,
     def: DefWithBodyId,
@@ -281,13 +300,6 @@ impl<'a> UnsafeVisitor<'a> {
                     self.on_unsafe_op(current.into(), UnsafetyReason::RawPtrDeref);
                 }
             }
-            Expr::Unsafe { .. } => {
-                let old_inside_unsafe_block =
-                    mem::replace(&mut self.inside_unsafe_block, InsideUnsafeBlock::Yes);
-                self.body.walk_child_exprs_without_pats(current, |child| self.walk_expr(child));
-                self.inside_unsafe_block = old_inside_unsafe_block;
-                return;
-            }
             &Expr::Assignment { target, value: _ } => {
                 let old_inside_assignment = mem::replace(&mut self.inside_assignment, true);
                 self.walk_pats_top(std::iter::once(target), current);
@@ -306,6 +318,20 @@ impl<'a> UnsafeVisitor<'a> {
                     }
                 }
             }
+            Expr::Unsafe { statements, .. } => {
+                let old_inside_unsafe_block =
+                    mem::replace(&mut self.inside_unsafe_block, InsideUnsafeBlock::Yes);
+                self.walk_pats_top(
+                    statements.iter().filter_map(|statement| match statement {
+                        &Statement::Let { pat, .. } => Some(pat),
+                        _ => None,
+                    }),
+                    current,
+                );
+                self.body.walk_child_exprs_without_pats(current, |child| self.walk_expr(child));
+                self.inside_unsafe_block = old_inside_unsafe_block;
+                return;
+            }
             Expr::Block { statements, .. } | Expr::Async { statements, .. } => {
                 self.walk_pats_top(
                     statements.iter().filter_map(|statement| match statement {
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/display.rs b/src/tools/rust-analyzer/crates/hir-ty/src/display.rs
index 49f061813d1..95ce36390d3 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/display.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/display.rs
@@ -4,7 +4,7 @@
 
 use std::{
     fmt::{self, Debug},
-    mem::{self, size_of},
+    mem,
 };
 
 use base_db::CrateId;
@@ -45,6 +45,7 @@ use crate::{
     db::{HirDatabase, InternedClosure},
     from_assoc_type_id, from_foreign_def_id, from_placeholder_idx,
     generics::generics,
+    infer::normalize,
     layout::Layout,
     lt_from_placeholder_idx,
     mapping::from_chalk,
@@ -87,6 +88,7 @@ pub struct HirFormatter<'a> {
     show_container_bounds: bool,
     omit_verbose_types: bool,
     closure_style: ClosureStyle,
+    display_kind: DisplayKind,
     display_target: DisplayTarget,
     bounds_formatting_ctx: BoundsFormattingCtx,
 }
@@ -164,6 +166,7 @@ pub trait HirDisplay {
         limited_size: Option<usize>,
         omit_verbose_types: bool,
         display_target: DisplayTarget,
+        display_kind: DisplayKind,
         closure_style: ClosureStyle,
         show_container_bounds: bool,
     ) -> HirDisplayWrapper<'a, Self>
@@ -171,7 +174,7 @@ pub trait HirDisplay {
         Self: Sized,
     {
         assert!(
-            !matches!(display_target, DisplayTarget::SourceCode { .. }),
+            !matches!(display_kind, DisplayKind::SourceCode { .. }),
             "HirDisplayWrapper cannot fail with DisplaySourceCodeError, use HirDisplay::hir_fmt directly instead"
         );
         HirDisplayWrapper {
@@ -181,6 +184,7 @@ pub trait HirDisplay {
             limited_size,
             omit_verbose_types,
             display_target,
+            display_kind,
             closure_style,
             show_container_bounds,
         }
@@ -191,7 +195,7 @@ pub trait HirDisplay {
     fn display<'a>(
         &'a self,
         db: &'a dyn HirDatabase,
-        edition: Edition,
+        display_target: DisplayTarget,
     ) -> HirDisplayWrapper<'a, Self>
     where
         Self: Sized,
@@ -203,7 +207,8 @@ pub trait HirDisplay {
             limited_size: None,
             omit_verbose_types: false,
             closure_style: ClosureStyle::ImplFn,
-            display_target: DisplayTarget::Diagnostics { edition },
+            display_target,
+            display_kind: DisplayKind::Diagnostics,
             show_container_bounds: false,
         }
     }
@@ -214,7 +219,7 @@ pub trait HirDisplay {
         &'a self,
         db: &'a dyn HirDatabase,
         max_size: Option<usize>,
-        edition: Edition,
+        display_target: DisplayTarget,
     ) -> HirDisplayWrapper<'a, Self>
     where
         Self: Sized,
@@ -226,7 +231,8 @@ pub trait HirDisplay {
             limited_size: None,
             omit_verbose_types: true,
             closure_style: ClosureStyle::ImplFn,
-            display_target: DisplayTarget::Diagnostics { edition },
+            display_target,
+            display_kind: DisplayKind::Diagnostics,
             show_container_bounds: false,
         }
     }
@@ -237,7 +243,7 @@ pub trait HirDisplay {
         &'a self,
         db: &'a dyn HirDatabase,
         limited_size: Option<usize>,
-        edition: Edition,
+        display_target: DisplayTarget,
     ) -> HirDisplayWrapper<'a, Self>
     where
         Self: Sized,
@@ -249,7 +255,8 @@ pub trait HirDisplay {
             limited_size,
             omit_verbose_types: true,
             closure_style: ClosureStyle::ImplFn,
-            display_target: DisplayTarget::Diagnostics { edition },
+            display_target,
+            display_kind: DisplayKind::Diagnostics,
             show_container_bounds: false,
         }
     }
@@ -272,7 +279,8 @@ pub trait HirDisplay {
             entity_limit: None,
             omit_verbose_types: false,
             closure_style: ClosureStyle::ImplFn,
-            display_target: DisplayTarget::SourceCode { module_id, allow_opaque },
+            display_target: DisplayTarget::from_crate(db, module_id.krate()),
+            display_kind: DisplayKind::SourceCode { target_module_id: module_id, allow_opaque },
             show_container_bounds: false,
             bounds_formatting_ctx: Default::default(),
         }) {
@@ -284,7 +292,11 @@ pub trait HirDisplay {
     }
 
     /// Returns a String representation of `self` for test purposes
-    fn display_test<'a>(&'a self, db: &'a dyn HirDatabase) -> HirDisplayWrapper<'a, Self>
+    fn display_test<'a>(
+        &'a self,
+        db: &'a dyn HirDatabase,
+        display_target: DisplayTarget,
+    ) -> HirDisplayWrapper<'a, Self>
     where
         Self: Sized,
     {
@@ -295,7 +307,8 @@ pub trait HirDisplay {
             limited_size: None,
             omit_verbose_types: false,
             closure_style: ClosureStyle::ImplFn,
-            display_target: DisplayTarget::Test,
+            display_target,
+            display_kind: DisplayKind::Test,
             show_container_bounds: false,
         }
     }
@@ -306,7 +319,7 @@ pub trait HirDisplay {
         &'a self,
         db: &'a dyn HirDatabase,
         show_container_bounds: bool,
-        edition: Edition,
+        display_target: DisplayTarget,
     ) -> HirDisplayWrapper<'a, Self>
     where
         Self: Sized,
@@ -318,21 +331,20 @@ pub trait HirDisplay {
             limited_size: None,
             omit_verbose_types: false,
             closure_style: ClosureStyle::ImplFn,
-            display_target: DisplayTarget::Diagnostics { edition },
+            display_target,
+            display_kind: DisplayKind::Diagnostics,
             show_container_bounds,
         }
     }
 }
 
 impl HirFormatter<'_> {
+    pub fn krate(&self) -> CrateId {
+        self.display_target.krate
+    }
+
     pub fn edition(&self) -> Edition {
-        match self.display_target {
-            DisplayTarget::Diagnostics { edition } => edition,
-            DisplayTarget::SourceCode { module_id, .. } => {
-                self.db.crate_graph()[module_id.krate()].edition
-            }
-            DisplayTarget::Test => Edition::CURRENT,
-        }
+        self.display_target.edition
     }
 
     pub fn write_joined<T: HirDisplay>(
@@ -394,20 +406,33 @@ impl HirFormatter<'_> {
     }
 }
 
+#[derive(Debug, Clone, Copy)]
+pub struct DisplayTarget {
+    krate: CrateId,
+    pub edition: Edition,
+}
+
+impl DisplayTarget {
+    pub fn from_crate(db: &dyn HirDatabase, krate: CrateId) -> Self {
+        let edition = db.crate_graph()[krate].edition;
+        Self { krate, edition }
+    }
+}
+
 #[derive(Clone, Copy)]
-pub enum DisplayTarget {
+pub enum DisplayKind {
     /// Display types for inlays, doc popups, autocompletion, etc...
     /// Showing `{unknown}` or not qualifying paths is fine here.
     /// There's no reason for this to fail.
-    Diagnostics { edition: Edition },
+    Diagnostics,
     /// Display types for inserting them in source files.
     /// The generated code should compile, so paths need to be qualified.
-    SourceCode { module_id: ModuleId, allow_opaque: bool },
+    SourceCode { target_module_id: ModuleId, allow_opaque: bool },
     /// Only for test purpose to keep real types
     Test,
 }
 
-impl DisplayTarget {
+impl DisplayKind {
     fn is_source_code(self) -> bool {
         matches!(self, Self::SourceCode { .. })
     }
@@ -450,6 +475,7 @@ pub struct HirDisplayWrapper<'a, T> {
     limited_size: Option<usize>,
     omit_verbose_types: bool,
     closure_style: ClosureStyle,
+    display_kind: DisplayKind,
     display_target: DisplayTarget,
     show_container_bounds: bool,
 }
@@ -479,6 +505,7 @@ impl<T: HirDisplay> HirDisplayWrapper<'_, T> {
             max_size: self.max_size,
             entity_limit: self.limited_size,
             omit_verbose_types: self.omit_verbose_types,
+            display_kind: self.display_kind,
             display_target: self.display_target,
             closure_style: self.closure_style,
             show_container_bounds: self.show_container_bounds,
@@ -533,7 +560,7 @@ impl HirDisplay for ProjectionTy {
         // if we are projection on a type parameter, check if the projection target has bounds
         // itself, if so, we render them directly as `impl Bound` instead of the less useful
         // `<Param as Trait>::Assoc`
-        if !f.display_target.is_source_code() {
+        if !f.display_kind.is_source_code() {
             if let TyKind::Placeholder(idx) = self_ty.kind(Interner) {
                 if !f.bounds_formatting_ctx.contains(self) {
                     let db = f.db;
@@ -653,10 +680,8 @@ fn render_const_scalar(
     memory_map: &MemoryMap,
     ty: &Ty,
 ) -> Result<(), HirDisplayError> {
-    // FIXME: We need to get krate from the final callers of the hir display
-    // infrastructure and have it here as a field on `f`.
-    let trait_env =
-        TraitEnvironment::empty(*f.db.crate_graph().crates_in_topological_order().last().unwrap());
+    let trait_env = TraitEnvironment::empty(f.krate());
+    let ty = normalize(f.db, trait_env.clone(), ty.clone());
     match ty.kind(Interner) {
         TyKind::Scalar(s) => match s {
             Scalar::Bool => write!(f, "{}", b[0] != 0),
@@ -1109,7 +1134,7 @@ impl HirDisplay for Ty {
                 let def = from_chalk(db, *def);
                 let sig = db.callable_item_signature(def).substitute(Interner, parameters);
 
-                if f.display_target.is_source_code() {
+                if f.display_kind.is_source_code() {
                     // `FnDef` is anonymous and there's no surface syntax for it. Show it as a
                     // function pointer type.
                     return sig.hir_fmt(f);
@@ -1198,8 +1223,8 @@ impl HirDisplay for Ty {
             }
             TyKind::Adt(AdtId(def_id), parameters) => {
                 f.start_location_link((*def_id).into());
-                match f.display_target {
-                    DisplayTarget::Diagnostics { .. } | DisplayTarget::Test => {
+                match f.display_kind {
+                    DisplayKind::Diagnostics { .. } | DisplayKind::Test { .. } => {
                         let name = match *def_id {
                             hir_def::AdtId::StructId(it) => db.struct_data(it).name.clone(),
                             hir_def::AdtId::UnionId(it) => db.union_data(it).name.clone(),
@@ -1207,7 +1232,7 @@ impl HirDisplay for Ty {
                         };
                         write!(f, "{}", name.display(f.db.upcast(), f.edition()))?;
                     }
-                    DisplayTarget::SourceCode { module_id, allow_opaque: _ } => {
+                    DisplayKind::SourceCode { target_module_id: module_id, allow_opaque: _ } => {
                         if let Some(path) = find_path::find_path(
                             db.upcast(),
                             ItemInNs::Types((*def_id).into()),
@@ -1246,7 +1271,7 @@ impl HirDisplay for Ty {
                 let type_alias_data = db.type_alias_data(type_alias);
 
                 // Use placeholder associated types when the target is test (https://rust-lang.github.io/chalk/book/clauses/type_equality.html#placeholder-associated-types)
-                if f.display_target.is_test() {
+                if f.display_kind.is_test() {
                     f.start_location_link(trait_.into());
                     write!(f, "{}", trait_data.name.display(f.db.upcast(), f.edition()))?;
                     f.end_location_link();
@@ -1275,7 +1300,7 @@ impl HirDisplay for Ty {
                 f.end_location_link();
             }
             TyKind::OpaqueType(opaque_ty_id, parameters) => {
-                if !f.display_target.allows_opaque() {
+                if !f.display_kind.allows_opaque() {
                     return Err(HirDisplayError::DisplaySourceCodeError(
                         DisplaySourceCodeError::OpaqueType,
                     ));
@@ -1344,8 +1369,8 @@ impl HirDisplay for Ty {
                 }
             }
             TyKind::Closure(id, substs) => {
-                if f.display_target.is_source_code() {
-                    if !f.display_target.allows_opaque() {
+                if f.display_kind.is_source_code() {
+                    if !f.display_kind.allows_opaque() {
                         return Err(HirDisplayError::DisplaySourceCodeError(
                             DisplaySourceCodeError::OpaqueType,
                         ));
@@ -1465,7 +1490,7 @@ impl HirDisplay for Ty {
             }
             TyKind::Alias(AliasTy::Projection(p_ty)) => p_ty.hir_fmt(f)?,
             TyKind::Alias(AliasTy::Opaque(opaque_ty)) => {
-                if !f.display_target.allows_opaque() {
+                if !f.display_kind.allows_opaque() {
                     return Err(HirDisplayError::DisplaySourceCodeError(
                         DisplaySourceCodeError::OpaqueType,
                     ));
@@ -1508,7 +1533,7 @@ impl HirDisplay for Ty {
                 };
             }
             TyKind::Error => {
-                if f.display_target.is_source_code() {
+                if f.display_kind.is_source_code() {
                     f.write_char('_')?;
                 } else {
                     write!(f, "{{unknown}}")?;
@@ -1516,7 +1541,7 @@ impl HirDisplay for Ty {
             }
             TyKind::InferenceVar(..) => write!(f, "_")?,
             TyKind::Coroutine(_, subst) => {
-                if f.display_target.is_source_code() {
+                if f.display_kind.is_source_code() {
                     return Err(HirDisplayError::DisplaySourceCodeError(
                         DisplaySourceCodeError::Coroutine,
                     ));
@@ -1573,7 +1598,7 @@ fn generic_args_sans_defaults<'ga>(
     generic_def: Option<hir_def::GenericDefId>,
     parameters: &'ga [GenericArg],
 ) -> &'ga [GenericArg] {
-    if f.display_target.is_source_code() || f.omit_verbose_types() {
+    if f.display_kind.is_source_code() || f.omit_verbose_types() {
         match generic_def
             .map(|generic_def_id| f.db.generic_defaults(generic_def_id))
             .filter(|it| !it.is_empty())
@@ -1958,7 +1983,7 @@ impl HirDisplay for LifetimeData {
                 write!(f, "{}", param_data.name.display(f.db.upcast(), f.edition()))?;
                 Ok(())
             }
-            _ if f.display_target.is_source_code() => write!(f, "'_"),
+            _ if f.display_kind.is_source_code() => write!(f, "'_"),
             LifetimeData::BoundVar(idx) => idx.hir_fmt(f),
             LifetimeData::InferenceVar(_) => write!(f, "_"),
             LifetimeData::Static => write!(f, "'static"),
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 2ac1792ba86..bbd419d9659 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/layout.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/layout.rs
@@ -6,9 +6,8 @@ use base_db::ra_salsa::Cycle;
 use chalk_ir::{AdtId, FloatTy, IntTy, TyKind, UintTy};
 use hir_def::{
     layout::{
-        Float, Integer, LayoutCalculator, LayoutCalculatorError,
-        LayoutData, Primitive, ReprOptions, Scalar, StructKind, TargetDataLayout,
-        WrappingRange,
+        Float, Integer, LayoutCalculator, LayoutCalculatorError, LayoutData, Primitive,
+        ReprOptions, Scalar, StructKind, TargetDataLayout, WrappingRange,
     },
     LocalFieldId, StructId,
 };
@@ -192,43 +191,52 @@ pub fn layout_of_ty_query(
                     valid_range: WrappingRange { start: 0, end: 0x10FFFF },
                 },
             ),
-            chalk_ir::Scalar::Int(i) => Layout::scalar(dl, scalar_unit(
+            chalk_ir::Scalar::Int(i) => Layout::scalar(
                 dl,
-                Primitive::Int(
-                    match i {
-                        IntTy::Isize => dl.ptr_sized_integer(),
-                        IntTy::I8 => Integer::I8,
-                        IntTy::I16 => Integer::I16,
-                        IntTy::I32 => Integer::I32,
-                        IntTy::I64 => Integer::I64,
-                        IntTy::I128 => Integer::I128,
-                    },
-                    true,
+                scalar_unit(
+                    dl,
+                    Primitive::Int(
+                        match i {
+                            IntTy::Isize => dl.ptr_sized_integer(),
+                            IntTy::I8 => Integer::I8,
+                            IntTy::I16 => Integer::I16,
+                            IntTy::I32 => Integer::I32,
+                            IntTy::I64 => Integer::I64,
+                            IntTy::I128 => Integer::I128,
+                        },
+                        true,
+                    ),
                 ),
-            )),
-            chalk_ir::Scalar::Uint(i) => Layout::scalar(dl, scalar_unit(
+            ),
+            chalk_ir::Scalar::Uint(i) => Layout::scalar(
                 dl,
-                Primitive::Int(
-                    match i {
-                        UintTy::Usize => dl.ptr_sized_integer(),
-                        UintTy::U8 => Integer::I8,
-                        UintTy::U16 => Integer::I16,
-                        UintTy::U32 => Integer::I32,
-                        UintTy::U64 => Integer::I64,
-                        UintTy::U128 => Integer::I128,
-                    },
-                    false,
+                scalar_unit(
+                    dl,
+                    Primitive::Int(
+                        match i {
+                            UintTy::Usize => dl.ptr_sized_integer(),
+                            UintTy::U8 => Integer::I8,
+                            UintTy::U16 => Integer::I16,
+                            UintTy::U32 => Integer::I32,
+                            UintTy::U64 => Integer::I64,
+                            UintTy::U128 => Integer::I128,
+                        },
+                        false,
+                    ),
                 ),
-            )),
-            chalk_ir::Scalar::Float(f) => Layout::scalar(dl, scalar_unit(
+            ),
+            chalk_ir::Scalar::Float(f) => Layout::scalar(
                 dl,
-                Primitive::Float(match f {
-                    FloatTy::F16 => Float::F16,
-                    FloatTy::F32 => Float::F32,
-                    FloatTy::F64 => Float::F64,
-                    FloatTy::F128 => Float::F128,
-                }),
-            )),
+                scalar_unit(
+                    dl,
+                    Primitive::Float(match f {
+                        FloatTy::F16 => Float::F16,
+                        FloatTy::F32 => Float::F32,
+                        FloatTy::F64 => Float::F64,
+                        FloatTy::F128 => Float::F128,
+                    }),
+                ),
+            ),
         },
         TyKind::Tuple(len, tys) => {
             let kind = if *len == 0 { StructKind::AlwaysSized } else { StructKind::MaybeUnsized };
@@ -341,6 +349,9 @@ pub fn layout_of_ty_query(
         TyKind::Error => return Err(LayoutError::HasErrorType),
         TyKind::AssociatedType(id, subst) => {
             // Try again with `TyKind::Alias` to normalize the associated type.
+            // Usually we should not try to normalize `TyKind::AssociatedType`, but layout calculation is used
+            // in monomorphized MIR where this is okay. If outside monomorphization, this will lead to cycle,
+            // which we will recover from with an error.
             let ty = TyKind::Alias(chalk_ir::AliasTy::Projection(ProjectionTy {
                 associated_ty_id: *id,
                 substitution: subst.clone(),
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/layout/tests.rs b/src/tools/rust-analyzer/crates/hir-ty/src/layout/tests.rs
index 4d3896660b4..8b74b7328bd 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/layout/tests.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/layout/tests.rs
@@ -342,7 +342,7 @@ fn simd_types() {
     check_size_and_align(
         r#"
             #[repr(simd)]
-            struct SimdType(i64, i64);
+            struct SimdType([i64; 2]);
             struct Goal(SimdType);
         "#,
         "",
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 e0dcc01821e..624767cedf8 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs
@@ -21,9 +21,6 @@ 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;
@@ -73,13 +70,15 @@ use intern::{sym, Symbol};
 use la_arena::{Arena, Idx};
 use mir::{MirEvalError, VTableMap};
 use rustc_hash::{FxBuildHasher, FxHashMap, FxHashSet};
-use span::Edition;
 use syntax::ast::{make, ConstArg};
 use traits::FnTrait;
 use triomphe::Arc;
 
 use crate::{
-    consteval::unknown_const, db::HirDatabase, display::HirDisplay, generics::Generics,
+    consteval::unknown_const,
+    db::HirDatabase,
+    display::{DisplayTarget, HirDisplay},
+    generics::Generics,
     infer::unify::InferenceTable,
 };
 
@@ -1041,7 +1040,7 @@ where
 pub fn known_const_to_ast(
     konst: &Const,
     db: &dyn HirDatabase,
-    edition: Edition,
+    display_target: DisplayTarget,
 ) -> Option<ConstArg> {
     if let ConstValue::Concrete(c) = &konst.interned().value {
         match c.interned {
@@ -1052,7 +1051,7 @@ pub fn known_const_to_ast(
             _ => (),
         }
     }
-    Some(make::expr_const_value(konst.display(db, edition).to_string().as_str()))
+    Some(make::expr_const_value(konst.display(db, display_target).to_string().as_str()))
 }
 
 #[derive(Debug, Copy, Clone)]
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 41304bbd8a9..56c431ef8da 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/mir.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir.rs
@@ -5,7 +5,7 @@ use std::{collections::hash_map::Entry, fmt::Display, iter};
 use crate::{
     consteval::usize_const,
     db::HirDatabase,
-    display::HirDisplay,
+    display::{DisplayTarget, HirDisplay},
     infer::{normalize, PointerCast},
     lang_items::is_box,
     mapping::ToChalk,
@@ -168,7 +168,7 @@ impl<V, T> ProjectionElem<V, T> {
                 _ => {
                     never!(
                         "Overloaded deref on type {} is not a projection",
-                        base.display(db, db.crate_graph()[krate].edition)
+                        base.display(db, DisplayTarget::from_crate(db, krate))
                     );
                     TyKind::Error.intern(Interner)
                 }
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir/borrowck.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/borrowck.rs
index 9c86d3b59f6..fbcca388e78 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/mir/borrowck.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir/borrowck.rs
@@ -13,6 +13,7 @@ use triomphe::Arc;
 
 use crate::{
     db::{HirDatabase, InternedClosure},
+    display::DisplayTarget,
     mir::Operand,
     utils::ClosureSubst,
     ClosureId, Interner, Substitution, Ty, TyExt, TypeFlags,
@@ -422,7 +423,10 @@ fn ever_initialized_map(
             let Some(terminator) = &block.terminator else {
                 never!(
                     "Terminator should be none only in construction.\nThe body:\n{}",
-                    body.pretty_print(db)
+                    body.pretty_print(
+                        db,
+                        DisplayTarget::from_crate(db, body.owner.krate(db.upcast()))
+                    )
                 );
                 return;
             };
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 6b20522cf34..74a34e29817 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
@@ -24,7 +24,7 @@ use rustc_apfloat::{
     Float,
 };
 use rustc_hash::{FxHashMap, FxHashSet};
-use span::{Edition, FileId};
+use span::FileId;
 use stdx::never;
 use syntax::{SyntaxNodePtr, TextRange};
 use triomphe::Arc;
@@ -32,7 +32,7 @@ use triomphe::Arc;
 use crate::{
     consteval::{intern_const_scalar, try_const_usize, ConstEvalError},
     db::{HirDatabase, InternedClosure},
-    display::{ClosureStyle, HirDisplay},
+    display::{ClosureStyle, DisplayTarget, HirDisplay},
     infer::PointerCast,
     layout::{Layout, LayoutError, RustcEnumVariantIdx},
     mapping::from_chalk,
@@ -302,7 +302,7 @@ impl Address {
         }
     }
 
-    fn to_bytes(&self) -> [u8; mem::size_of::<usize>()] {
+    fn to_bytes(&self) -> [u8; size_of::<usize>()] {
         usize::to_le_bytes(self.to_usize())
     }
 
@@ -359,7 +359,7 @@ impl MirEvalError {
         f: &mut String,
         db: &dyn HirDatabase,
         span_formatter: impl Fn(FileId, TextRange) -> String,
-        edition: Edition,
+        display_target: DisplayTarget,
     ) -> std::result::Result<(), std::fmt::Error> {
         writeln!(f, "Mir eval error:")?;
         let mut err = self;
@@ -372,7 +372,7 @@ impl MirEvalError {
                         writeln!(
                             f,
                             "In function {} ({:?})",
-                            function_name.name.display(db.upcast(), edition),
+                            function_name.name.display(db.upcast(), display_target.edition),
                             func
                         )?;
                     }
@@ -417,7 +417,7 @@ impl MirEvalError {
                 write!(
                     f,
                     "Layout for type `{}` is not available due {err:?}",
-                    ty.display(db, edition).with_closure_style(ClosureStyle::ClosureWithId)
+                    ty.display(db, display_target).with_closure_style(ClosureStyle::ClosureWithId)
                 )?;
             }
             MirEvalError::MirLowerError(func, err) => {
@@ -428,12 +428,15 @@ impl MirEvalError {
                         let substs = generics.placeholder_subst(db);
                         db.impl_self_ty(impl_id)
                             .substitute(Interner, &substs)
-                            .display(db, edition)
+                            .display(db, display_target)
                             .to_string()
                     }),
-                    ItemContainerId::TraitId(it) => {
-                        Some(db.trait_data(it).name.display(db.upcast(), edition).to_string())
-                    }
+                    ItemContainerId::TraitId(it) => Some(
+                        db.trait_data(it)
+                            .name
+                            .display(db.upcast(), display_target.edition)
+                            .to_string(),
+                    ),
                     _ => None,
                 };
                 writeln!(
@@ -441,17 +444,17 @@ impl MirEvalError {
                     "MIR lowering for function `{}{}{}` ({:?}) failed due:",
                     self_.as_deref().unwrap_or_default(),
                     if self_.is_some() { "::" } else { "" },
-                    function_name.name.display(db.upcast(), edition),
+                    function_name.name.display(db.upcast(), display_target.edition),
                     func
                 )?;
-                err.pretty_print(f, db, span_formatter, edition)?;
+                err.pretty_print(f, db, span_formatter, display_target)?;
             }
             MirEvalError::ConstEvalError(name, err) => {
                 MirLowerError::ConstEvalError((**name).into(), err.clone()).pretty_print(
                     f,
                     db,
                     span_formatter,
-                    edition,
+                    display_target,
                 )?;
             }
             MirEvalError::UndefinedBehavior(_)
@@ -589,7 +592,7 @@ pub fn interpret_mir(
     let ty = body.locals[return_slot()].ty.clone();
     let mut evaluator = Evaluator::new(db, body.owner, assert_placeholder_ty_is_unused, trait_env)?;
     let it: Result<Const> = (|| {
-        if evaluator.ptr_size() != std::mem::size_of::<usize>() {
+        if evaluator.ptr_size() != size_of::<usize>() {
             not_supported!("targets with different pointer size from host");
         }
         let interval = evaluator.interpret_mir(body.clone(), None.into_iter())?;
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 7d3376f56be..f61ecabb7e4 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
@@ -14,6 +14,7 @@ use intern::{sym, Symbol};
 use stdx::never;
 
 use crate::{
+    display::DisplayTarget,
     error_lifetime,
     mir::eval::{
         pad16, Address, AdtId, Arc, BuiltinType, Evaluator, FunctionId, HasModule, HirDisplay,
@@ -835,8 +836,7 @@ impl Evaluator<'_> {
                     // render full paths.
                     Err(_) => {
                         let krate = locals.body.owner.krate(self.db.upcast());
-                        let edition = self.db.crate_graph()[krate].edition;
-                        ty.display(self.db, edition).to_string()
+                        ty.display(self.db, DisplayTarget::from_crate(self.db, krate)).to_string()
                     }
                 };
                 let len = ty_name.len();
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 2b5486fc5fa..084c391d26c 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,6 +3,7 @@ use span::{Edition, EditionedFileId};
 use syntax::{TextRange, TextSize};
 use test_fixture::WithFixture;
 
+use crate::display::DisplayTarget;
 use crate::{db::HirDatabase, mir::MirLowerError, test_db::TestDB, Interner, Substitution};
 
 use super::{interpret_mir, MirEvalError};
@@ -67,7 +68,9 @@ fn check_pass_and_stdio(
             let span_formatter = |file, range: TextRange| {
                 format!("{:?} {:?}..{:?}", file, line_index(range.start()), line_index(range.end()))
             };
-            e.pretty_print(&mut err, &db, span_formatter, Edition::CURRENT).unwrap();
+            let krate = db.module_for_file(file_id).krate();
+            e.pretty_print(&mut err, &db, span_formatter, DisplayTarget::from_crate(&db, krate))
+                .unwrap();
             panic!("Error in interpreting: {err}");
         }
         Ok((stdout, stderr)) => {
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 f88696692e6..520717e7995 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower.rs
@@ -2,7 +2,7 @@
 
 use std::{fmt::Write, iter, mem};
 
-use base_db::ra_salsa::Cycle;
+use base_db::{ra_salsa::Cycle, CrateId};
 use chalk_ir::{BoundVar, ConstData, DebruijnIndex, TyKind};
 use hir_def::{
     data::adt::{StructKind, VariantData},
@@ -29,7 +29,7 @@ use triomphe::Arc;
 use crate::{
     consteval::ConstEvalError,
     db::{HirDatabase, InternedClosure},
-    display::{hir_display_with_types_map, HirDisplay},
+    display::{hir_display_with_types_map, DisplayTarget, HirDisplay},
     error_lifetime,
     generics::generics,
     infer::{cast::CastTy, unify::InferenceTable, CaptureKind, CapturedItem, TypeMismatch},
@@ -160,17 +160,17 @@ impl MirLowerError {
         f: &mut String,
         db: &dyn HirDatabase,
         span_formatter: impl Fn(FileId, TextRange) -> String,
-        edition: Edition,
+        display_target: DisplayTarget,
     ) -> std::result::Result<(), std::fmt::Error> {
         match self {
             MirLowerError::ConstEvalError(name, e) => {
                 writeln!(f, "In evaluating constant {name}")?;
                 match &**e {
                     ConstEvalError::MirLowerError(e) => {
-                        e.pretty_print(f, db, span_formatter, edition)?
+                        e.pretty_print(f, db, span_formatter, display_target)?
                     }
                     ConstEvalError::MirEvalError(e) => {
-                        e.pretty_print(f, db, span_formatter, edition)?
+                        e.pretty_print(f, db, span_formatter, display_target)?
                     }
                 }
             }
@@ -179,15 +179,15 @@ impl MirLowerError {
                 writeln!(
                     f,
                     "Missing function definition for {}",
-                    body.pretty_print_expr(db.upcast(), *owner, *it, edition)
+                    body.pretty_print_expr(db.upcast(), *owner, *it, display_target.edition)
                 )?;
             }
             MirLowerError::HasErrors => writeln!(f, "Type inference result contains errors")?,
             MirLowerError::TypeMismatch(e) => writeln!(
                 f,
                 "Type mismatch: Expected {}, found {}",
-                e.expected.display(db, edition),
-                e.actual.display(db, edition),
+                e.expected.display(db, display_target),
+                e.actual.display(db, display_target),
             )?,
             MirLowerError::GenericArgNotProvided(id, subst) => {
                 let parent = id.parent;
@@ -195,11 +195,14 @@ impl MirLowerError {
                 writeln!(
                     f,
                     "Generic arg not provided for {}",
-                    param.name().unwrap_or(&Name::missing()).display(db.upcast(), edition)
+                    param
+                        .name()
+                        .unwrap_or(&Name::missing())
+                        .display(db.upcast(), display_target.edition)
                 )?;
                 writeln!(f, "Provided args: [")?;
                 for g in subst.iter(Interner) {
-                    write!(f, "    {},", g.display(db, edition))?;
+                    write!(f, "    {},", g.display(db, display_target))?;
                 }
                 writeln!(f, "]")?;
             }
@@ -251,11 +254,11 @@ impl MirLowerError {
     fn unresolved_path(
         db: &dyn HirDatabase,
         p: &Path,
-        edition: Edition,
+        display_target: DisplayTarget,
         types_map: &TypesMap,
     ) -> Self {
         Self::UnresolvedName(
-            hir_display_with_types_map(p, types_map).display(db, edition).to_string(),
+            hir_display_with_types_map(p, types_map).display(db, display_target).to_string(),
         )
     }
 }
@@ -462,7 +465,7 @@ impl<'ctx> MirLowerCtx<'ctx> {
                             MirLowerError::unresolved_path(
                                 self.db,
                                 p,
-                                self.edition(),
+                                DisplayTarget::from_crate(self.db, self.krate()),
                                 &self.body.types,
                             )
                         })?;
@@ -838,7 +841,7 @@ impl<'ctx> MirLowerCtx<'ctx> {
                     self.infer.variant_resolution_for_expr(expr_id).ok_or_else(|| match path {
                         Some(p) => MirLowerError::UnresolvedName(
                             hir_display_with_types_map(&**p, &self.body.types)
-                                .display(self.db, self.edition())
+                                .display(self.db, self.display_target())
                                 .to_string(),
                         ),
                         None => MirLowerError::RecordLiteralWithoutPath,
@@ -1362,9 +1365,16 @@ impl<'ctx> MirLowerCtx<'ctx> {
         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);
+                let owner = self.owner;
+                let db = self.db;
+                let unresolved_name = || {
+                    MirLowerError::unresolved_path(
+                        self.db,
+                        c,
+                        DisplayTarget::from_crate(db, owner.krate(db.upcast())),
+                        &self.body.types,
+                    )
+                };
                 let pr = self
                     .resolver
                     .resolve_path_in_value_ns(self.db.upcast(), c, HygieneId::ROOT)
@@ -1394,7 +1404,7 @@ impl<'ctx> MirLowerCtx<'ctx> {
                 .layout_of_ty(ty.clone(), self.db.trait_environment_for_body(self.owner))
                 .map(|it| it.size.bytes_usize())
         };
-        const USIZE_SIZE: usize = mem::size_of::<usize>();
+        const USIZE_SIZE: usize = size_of::<usize>();
         let bytes: Box<[_]> = match l {
             hir_def::hir::Literal::String(b) => {
                 let b = b.as_str();
@@ -1910,8 +1920,15 @@ impl<'ctx> MirLowerCtx<'ctx> {
     }
 
     fn edition(&self) -> Edition {
-        let krate = self.owner.krate(self.db.upcast());
-        self.db.crate_graph()[krate].edition
+        self.db.crate_graph()[self.krate()].edition
+    }
+
+    fn krate(&self) -> CrateId {
+        self.owner.krate(self.db.upcast())
+    }
+
+    fn display_target(&self) -> DisplayTarget {
+        DisplayTarget::from_crate(self.db, self.krate())
     }
 
     fn drop_until_scope(
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 289175feefb..783f92b2043 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
@@ -350,7 +350,12 @@ impl MirLowerCtx<'_> {
                 )?,
                 None => {
                     let unresolved_name = || {
-                        MirLowerError::unresolved_path(self.db, p, self.edition(), &self.body.types)
+                        MirLowerError::unresolved_path(
+                            self.db,
+                            p,
+                            self.display_target(),
+                            &self.body.types,
+                        )
                     };
                     let hygiene = self.body.pat_path_hygiene(pattern);
                     let pr = self
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 2a26101ac43..7d7d4106cb9 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
@@ -9,11 +9,10 @@ use either::Either;
 use hir_def::{expr_store::Body, hir::BindingId};
 use hir_expand::{name::Name, Lookup};
 use la_arena::ArenaMap;
-use span::Edition;
 
 use crate::{
     db::HirDatabase,
-    display::{ClosureStyle, HirDisplay},
+    display::{ClosureStyle, DisplayTarget, HirDisplay},
     mir::{PlaceElem, ProjectionElem, StatementKind, TerminatorKind},
     ClosureId,
 };
@@ -39,17 +38,21 @@ macro_rules! wln {
 }
 
 impl MirBody {
-    pub fn pretty_print(&self, db: &dyn HirDatabase) -> String {
+    pub fn pretty_print(&self, db: &dyn HirDatabase, display_target: DisplayTarget) -> String {
         let hir_body = db.body(self.owner);
-        let mut ctx = MirPrettyCtx::new(self, &hir_body, db);
+        let mut ctx = MirPrettyCtx::new(self, &hir_body, db, display_target);
         ctx.for_body(|this| match ctx.body.owner {
             hir_def::DefWithBodyId::FunctionId(id) => {
                 let data = db.function_data(id);
-                w!(this, "fn {}() ", data.name.display(db.upcast(), Edition::LATEST));
+                w!(this, "fn {}() ", data.name.display(db.upcast(), this.display_target.edition));
             }
             hir_def::DefWithBodyId::StaticId(id) => {
                 let data = db.static_data(id);
-                w!(this, "static {}: _ = ", data.name.display(db.upcast(), Edition::LATEST));
+                w!(
+                    this,
+                    "static {}: _ = ",
+                    data.name.display(db.upcast(), this.display_target.edition)
+                );
             }
             hir_def::DefWithBodyId::ConstId(id) => {
                 let data = db.const_data(id);
@@ -59,7 +62,7 @@ impl MirBody {
                     data.name
                         .as_ref()
                         .unwrap_or(&Name::missing())
-                        .display(db.upcast(), Edition::LATEST)
+                        .display(db.upcast(), this.display_target.edition)
                 );
             }
             hir_def::DefWithBodyId::VariantId(id) => {
@@ -70,10 +73,10 @@ impl MirBody {
                     "enum {}::{} = ",
                     enum_loc.id.item_tree(db.upcast())[enum_loc.id.value]
                         .name
-                        .display(db.upcast(), Edition::LATEST),
+                        .display(db.upcast(), this.display_target.edition),
                     loc.id.item_tree(db.upcast())[loc.id.value]
                         .name
-                        .display(db.upcast(), Edition::LATEST),
+                        .display(db.upcast(), this.display_target.edition),
                 )
             }
             hir_def::DefWithBodyId::InTypeConstId(id) => {
@@ -85,14 +88,14 @@ impl MirBody {
 
     // String with lines is rendered poorly in `dbg` macros, which I use very much, so this
     // function exists to solve that.
-    pub fn dbg(&self, db: &dyn HirDatabase) -> impl Debug {
+    pub fn dbg(&self, db: &dyn HirDatabase, display_target: DisplayTarget) -> impl Debug {
         struct StringDbg(String);
         impl Debug for StringDbg {
             fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
                 f.write_str(&self.0)
             }
         }
-        StringDbg(self.pretty_print(db))
+        StringDbg(self.pretty_print(db, display_target))
     }
 }
 
@@ -103,6 +106,7 @@ struct MirPrettyCtx<'a> {
     result: String,
     indent: String,
     local_to_binding: ArenaMap<LocalId, BindingId>,
+    display_target: DisplayTarget,
 }
 
 impl Write for MirPrettyCtx<'_> {
@@ -182,7 +186,12 @@ impl<'a> MirPrettyCtx<'a> {
         wln!(self, "}}");
     }
 
-    fn new(body: &'a MirBody, hir_body: &'a Body, db: &'a dyn HirDatabase) -> Self {
+    fn new(
+        body: &'a MirBody,
+        hir_body: &'a Body,
+        db: &'a dyn HirDatabase,
+        display_target: DisplayTarget,
+    ) -> Self {
         let local_to_binding = body.local_to_binding_map();
         MirPrettyCtx {
             body,
@@ -191,6 +200,7 @@ impl<'a> MirPrettyCtx<'a> {
             indent: String::new(),
             local_to_binding,
             hir_body,
+            display_target,
         }
     }
 
@@ -208,7 +218,7 @@ impl<'a> MirPrettyCtx<'a> {
             wln!(
                 self,
                 "let {}: {};",
-                self.local_name(id).display_test(self.db),
+                self.local_name(id).display_test(self.db, self.display_target),
                 self.hir_display(&local.ty)
             );
         }
@@ -242,14 +252,14 @@ impl<'a> MirPrettyCtx<'a> {
                             wln!(
                                 this,
                                 "StorageDead({})",
-                                this.local_name(*p).display_test(self.db)
+                                this.local_name(*p).display_test(this.db, this.display_target)
                             );
                         }
                         StatementKind::StorageLive(p) => {
                             wln!(
                                 this,
                                 "StorageLive({})",
-                                this.local_name(*p).display_test(self.db)
+                                this.local_name(*p).display_test(this.db, this.display_target)
                             );
                         }
                         StatementKind::Deinit(p) => {
@@ -313,7 +323,7 @@ impl<'a> MirPrettyCtx<'a> {
         fn f(this: &mut MirPrettyCtx<'_>, local: LocalId, projections: &[PlaceElem]) {
             let Some((last, head)) = projections.split_last() else {
                 // no projection
-                w!(this, "{}", this.local_name(local).display_test(this.db));
+                w!(this, "{}", this.local_name(local).display_test(this.db, this.display_target));
                 return;
             };
             match last {
@@ -333,13 +343,17 @@ impl<'a> MirPrettyCtx<'a> {
                             w!(
                                 this,
                                 " as {}).{}",
-                                variant_name.display(this.db.upcast(), Edition::LATEST),
-                                name.display(this.db.upcast(), Edition::LATEST)
+                                variant_name.display(this.db.upcast(), this.display_target.edition),
+                                name.display(this.db.upcast(), this.display_target.edition)
                             );
                         }
                         hir_def::VariantId::StructId(_) | hir_def::VariantId::UnionId(_) => {
                             f(this, local, head);
-                            w!(this, ".{}", name.display(this.db.upcast(), Edition::LATEST));
+                            w!(
+                                this,
+                                ".{}",
+                                name.display(this.db.upcast(), this.display_target.edition)
+                            );
                         }
                     }
                 }
@@ -353,7 +367,11 @@ impl<'a> MirPrettyCtx<'a> {
                 }
                 ProjectionElem::Index(l) => {
                     f(this, local, head);
-                    w!(this, "[{}]", this.local_name(*l).display_test(this.db));
+                    w!(
+                        this,
+                        "[{}]",
+                        this.local_name(*l).display_test(this.db, this.display_target)
+                    );
                 }
                 it => {
                     f(this, local, head);
@@ -403,7 +421,7 @@ impl<'a> MirPrettyCtx<'a> {
             Rvalue::Repeat(op, len) => {
                 w!(self, "[");
                 self.operand(op);
-                w!(self, "; {}]", len.display_test(self.db));
+                w!(self, "; {}]", len.display_test(self.db, self.display_target));
             }
             Rvalue::Aggregate(AggregateKind::Adt(_, _), it) => {
                 w!(self, "Adt(");
@@ -478,6 +496,7 @@ impl<'a> MirPrettyCtx<'a> {
     }
 
     fn hir_display<T: HirDisplay>(&self, ty: &'a T) -> impl Display + 'a {
-        ty.display_test(self.db).with_closure_style(ClosureStyle::ClosureWithSubst)
+        ty.display_test(self.db, self.display_target)
+            .with_closure_style(ClosureStyle::ClosureWithSubst)
     }
 }
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 f5a4d4ff35c..81e38be2285 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/tests.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests.rs
@@ -15,7 +15,7 @@ mod type_alias_impl_traits;
 use std::env;
 use std::sync::LazyLock;
 
-use base_db::SourceDatabaseFileInputExt as _;
+use base_db::{CrateId, SourceDatabaseFileInputExt as _};
 use expect_test::Expect;
 use hir_def::{
     db::DefDatabase,
@@ -41,7 +41,7 @@ use triomphe::Arc;
 
 use crate::{
     db::HirDatabase,
-    display::HirDisplay,
+    display::{DisplayTarget, HirDisplay},
     infer::{Adjustment, TypeMismatch},
     test_db::TestDB,
     InferenceResult, Ty,
@@ -124,7 +124,7 @@ fn check_impl(
     }
     assert!(had_annotations || allow_none, "no `//^` annotations found");
 
-    let mut defs: Vec<DefWithBodyId> = Vec::new();
+    let mut defs: Vec<(DefWithBodyId, CrateId)> = Vec::new();
     for file_id in files {
         let module = db.module_for_file_opt(file_id);
         let module = match module {
@@ -133,16 +133,17 @@ fn check_impl(
         };
         let def_map = module.def_map(&db);
         visit_module(&db, &def_map, module.local_id, &mut |it| {
-            defs.push(match it {
+            let def = match it {
                 ModuleDefId::FunctionId(it) => it.into(),
                 ModuleDefId::EnumVariantId(it) => it.into(),
                 ModuleDefId::ConstId(it) => it.into(),
                 ModuleDefId::StaticId(it) => it.into(),
                 _ => return,
-            })
+            };
+            defs.push((def, module.krate()))
         });
     }
-    defs.sort_by_key(|def| match def {
+    defs.sort_by_key(|(def, _)| match def {
         DefWithBodyId::FunctionId(it) => {
             let loc = it.lookup(&db);
             loc.source(&db).value.syntax().text_range().start()
@@ -162,7 +163,8 @@ fn check_impl(
         DefWithBodyId::InTypeConstId(it) => it.source(&db).syntax().text_range().start(),
     });
     let mut unexpected_type_mismatches = String::new();
-    for def in defs {
+    for (def, krate) in defs {
+        let display_target = DisplayTarget::from_crate(&db, krate);
         let (body, body_source_map) = db.body_with_source_map(def);
         let inference_result = db.infer(def);
 
@@ -179,7 +181,7 @@ fn check_impl(
                 let actual = if display_source {
                     ty.display_source_code(&db, def.module(&db), true).unwrap()
                 } else {
-                    ty.display_test(&db).to_string()
+                    ty.display_test(&db, display_target).to_string()
                 };
                 assert_eq!(actual, expected, "type annotation differs at {:#?}", range.range);
             }
@@ -195,7 +197,7 @@ fn check_impl(
                 let actual = if display_source {
                     ty.display_source_code(&db, def.module(&db), true).unwrap()
                 } else {
-                    ty.display_test(&db).to_string()
+                    ty.display_test(&db, display_target).to_string()
                 };
                 assert_eq!(actual, expected, "type annotation differs at {:#?}", range.range);
             }
@@ -224,8 +226,8 @@ fn check_impl(
             let range = node.as_ref().original_file_range_rooted(&db);
             let actual = format!(
                 "expected {}, got {}",
-                mismatch.expected.display_test(&db),
-                mismatch.actual.display_test(&db)
+                mismatch.expected.display_test(&db, display_target),
+                mismatch.actual.display_test(&db, display_target)
             );
             match mismatches.remove(&range) {
                 Some(annotation) => assert_eq!(actual, annotation),
@@ -299,7 +301,9 @@ fn infer_with_mismatches(content: &str, include_mismatches: bool) -> String {
 
     let mut infer_def = |inference_result: Arc<InferenceResult>,
                          body: Arc<Body>,
-                         body_source_map: Arc<BodySourceMap>| {
+                         body_source_map: Arc<BodySourceMap>,
+                         krate: CrateId| {
+        let display_target = DisplayTarget::from_crate(&db, krate);
         let mut types: Vec<(InFile<SyntaxNode>, &Ty)> = Vec::new();
         let mut mismatches: Vec<(InFile<SyntaxNode>, &TypeMismatch)> = Vec::new();
 
@@ -361,7 +365,7 @@ fn infer_with_mismatches(content: &str, include_mismatches: bool) -> String {
                 macro_prefix,
                 range,
                 ellipsize(text, 15),
-                ty.display_test(&db)
+                ty.display_test(&db, display_target)
             );
         }
         if include_mismatches {
@@ -377,8 +381,8 @@ fn infer_with_mismatches(content: &str, include_mismatches: bool) -> String {
                     "{}{:?}: expected {}, got {}\n",
                     macro_prefix,
                     range,
-                    mismatch.expected.display_test(&db),
-                    mismatch.actual.display_test(&db),
+                    mismatch.expected.display_test(&db, display_target),
+                    mismatch.actual.display_test(&db, display_target),
                 );
             }
         }
@@ -387,17 +391,18 @@ fn infer_with_mismatches(content: &str, include_mismatches: bool) -> String {
     let module = db.module_for_file(file_id);
     let def_map = module.def_map(&db);
 
-    let mut defs: Vec<DefWithBodyId> = Vec::new();
+    let mut defs: Vec<(DefWithBodyId, CrateId)> = Vec::new();
     visit_module(&db, &def_map, module.local_id, &mut |it| {
-        defs.push(match it {
+        let def = match it {
             ModuleDefId::FunctionId(it) => it.into(),
             ModuleDefId::EnumVariantId(it) => it.into(),
             ModuleDefId::ConstId(it) => it.into(),
             ModuleDefId::StaticId(it) => it.into(),
             _ => return,
-        })
+        };
+        defs.push((def, module.krate()))
     });
-    defs.sort_by_key(|def| match def {
+    defs.sort_by_key(|(def, _)| match def {
         DefWithBodyId::FunctionId(it) => {
             let loc = it.lookup(&db);
             loc.source(&db).value.syntax().text_range().start()
@@ -416,10 +421,10 @@ fn infer_with_mismatches(content: &str, include_mismatches: bool) -> String {
         }
         DefWithBodyId::InTypeConstId(it) => it.source(&db).syntax().text_range().start(),
     });
-    for def in defs {
+    for (def, krate) in defs {
         let (body, source_map) = db.body_with_source_map(def);
         let infer = db.infer(def);
-        infer_def(infer, body, source_map);
+        infer_def(infer, body, source_map, krate);
     }
 
     buf.truncate(buf.trim_end().len());
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/closure_captures.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/closure_captures.rs
index 34d299edd1b..6f7bfc4ea7a 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/closure_captures.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/closure_captures.rs
@@ -8,7 +8,7 @@ use syntax::{AstNode, AstPtr};
 use test_fixture::WithFixture;
 
 use crate::db::{HirDatabase, InternedClosureId};
-use crate::display::HirDisplay;
+use crate::display::{DisplayTarget, HirDisplay};
 use crate::mir::MirSpan;
 use crate::test_db::TestDB;
 
@@ -66,7 +66,11 @@ fn check_closure_captures(#[rust_analyzer::rust_fixture] ra_fixture: &str, expec
                         .join(", "),
                 };
                 let place = capture.display_place(closure.0, db);
-                let capture_ty = capture.ty.skip_binders().display_test(db).to_string();
+                let capture_ty = capture
+                    .ty
+                    .skip_binders()
+                    .display_test(db, DisplayTarget::from_crate(db, module.krate()))
+                    .to_string();
                 let spans = capture
                     .spans()
                     .iter()
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 50a1ecd006d..4c5cca21655 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
@@ -3861,3 +3861,42 @@ fn main() {
         "#]],
     );
 }
+
+#[test]
+fn regression_19196() {
+    check_infer(
+        r#"
+//- minicore: async_fn
+fn async_closure<F: AsyncFnOnce(i32)>(f: F) {}
+fn closure<F: FnOnce(i32)>(f: F) {}
+
+fn main() {
+    async_closure(async |arg| {
+        arg;
+    });
+    closure(|arg| {
+        arg;
+    });
+}
+"#,
+        expect![[r#"
+            38..39 'f': F
+            44..46 '{}': ()
+            74..75 'f': F
+            80..82 '{}': ()
+            94..191 '{     ... }); }': ()
+            100..113 'async_closure': fn async_closure<impl AsyncFnOnce(i32) -> impl Future<Output = ()>>(impl AsyncFnOnce(i32) -> impl Future<Output = ()>)
+            100..147 'async_...    })': ()
+            114..146 'async ...     }': impl AsyncFnOnce(i32) -> impl Future<Output = ()>
+            121..124 'arg': i32
+            126..146 '{     ...     }': ()
+            136..139 'arg': i32
+            153..160 'closure': fn closure<impl FnOnce(i32)>(impl FnOnce(i32))
+            153..188 'closur...    })': ()
+            161..187 '|arg| ...     }': impl FnOnce(i32)
+            162..165 'arg': i32
+            167..187 '{     ...     }': ()
+            177..180 'arg': i32
+        "#]],
+    );
+}
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 a34b4980832..372c7252934 100644
--- a/src/tools/rust-analyzer/crates/hir/src/has_source.rs
+++ b/src/tools/rust-analyzer/crates/hir/src/has_source.rs
@@ -15,7 +15,7 @@ use tt::TextRange;
 use crate::{
     db::HirDatabase, Adt, Callee, Const, Enum, ExternCrateDecl, Field, FieldSource, Function, Impl,
     InlineAsmOperand, Label, LifetimeParam, LocalSource, Macro, Module, Param, SelfParam, Static,
-    Struct, Trait, TraitAlias, TypeAlias, TypeOrConstParam, Union, Variant,
+    Struct, Trait, TraitAlias, TypeAlias, TypeOrConstParam, Union, Variant, VariantDef,
 };
 
 pub trait HasSource {
@@ -110,6 +110,16 @@ impl HasSource for Adt {
         }
     }
 }
+impl HasSource for VariantDef {
+    type Ast = ast::VariantDef;
+    fn source(self, db: &dyn HirDatabase) -> Option<InFile<Self::Ast>> {
+        match self {
+            VariantDef::Struct(s) => Some(s.source(db)?.map(ast::VariantDef::Struct)),
+            VariantDef::Union(u) => Some(u.source(db)?.map(ast::VariantDef::Union)),
+            VariantDef::Variant(v) => Some(v.source(db)?.map(ast::VariantDef::Variant)),
+        }
+    }
+}
 impl HasSource for Struct {
     type Ast = ast::Struct;
     fn source(self, db: &dyn HirDatabase) -> Option<InFile<Self::Ast>> {
diff --git a/src/tools/rust-analyzer/crates/hir/src/lib.rs b/src/tools/rust-analyzer/crates/hir/src/lib.rs
index 55448d4ae86..a8075509474 100644
--- a/src/tools/rust-analyzer/crates/hir/src/lib.rs
+++ b/src/tools/rust-analyzer/crates/hir/src/lib.rs
@@ -39,7 +39,7 @@ use std::{
 };
 
 use arrayvec::ArrayVec;
-use base_db::{CrateDisplayName, CrateId, CrateOrigin};
+use base_db::{CrateDisplayName, CrateId, CrateOrigin, LangCrateOrigin};
 use either::Either;
 use hir_def::{
     data::{adt::VariantData, TraitFlags},
@@ -139,6 +139,7 @@ pub use {
         },
         hygiene::{marks_rev, SyntaxContextExt},
         inert_attr_macro::AttributeTemplate,
+        mod_path::tool_path,
         name::Name,
         prettify_macro_expansion,
         proc_macro::{ProcMacros, ProcMacrosBuilder},
@@ -147,7 +148,7 @@ pub use {
     hir_ty::{
         consteval::ConstEvalError,
         diagnostics::UnsafetyReason,
-        display::{ClosureStyle, HirDisplay, HirDisplayError, HirWrite},
+        display::{ClosureStyle, DisplayTarget, HirDisplay, HirDisplayError, HirWrite},
         dyn_compatibility::{DynCompatibilityViolation, MethodViolationCode},
         layout::LayoutError,
         method_resolution::TyFingerprint,
@@ -282,6 +283,21 @@ impl Crate {
         let data = &db.crate_graph()[self.id];
         data.potential_cfg_options.clone().unwrap_or_else(|| data.cfg_options.clone())
     }
+
+    pub fn to_display_target(self, db: &dyn HirDatabase) -> DisplayTarget {
+        DisplayTarget::from_crate(db, self.id)
+    }
+
+    fn core(db: &dyn HirDatabase) -> Option<Crate> {
+        let crate_graph = db.crate_graph();
+        let result = crate_graph
+            .iter()
+            .find(|&krate| {
+                matches!(crate_graph[krate].origin, CrateOrigin::Lang(LangCrateOrigin::Core))
+            })
+            .map(Crate::from);
+        result
+    }
 }
 
 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
@@ -296,6 +312,7 @@ pub enum ModuleDef {
     Function(Function),
     Adt(Adt),
     // Can't be directly declared, but can be imported.
+    // FIXME: Rename to `EnumVariant`
     Variant(Variant),
     Const(Const),
     Static(Static),
@@ -469,6 +486,17 @@ impl ModuleDef {
     }
 }
 
+impl HasCrate for ModuleDef {
+    fn krate(&self, db: &dyn HirDatabase) -> Crate {
+        match self.module(db) {
+            Some(module) => module.krate(),
+            None => Crate::core(db).unwrap_or_else(|| {
+                (*db.crate_graph().crates_in_topological_order().last().unwrap()).into()
+            }),
+        }
+    }
+}
+
 impl HasVisibility for ModuleDef {
     fn visibility(&self, db: &dyn HirDatabase) -> Visibility {
         match *self {
@@ -1564,6 +1592,7 @@ impl From<&Variant> for DefWithBodyId {
     }
 }
 
+// FIXME: Rename to `EnumVariant`
 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
 pub struct Variant {
     pub(crate) id: EnumVariantId,
@@ -1863,7 +1892,7 @@ impl DefWithBody {
     pub fn debug_mir(self, db: &dyn HirDatabase) -> String {
         let body = db.mir_body(self.id());
         match body {
-            Ok(body) => body.pretty_print(db),
+            Ok(body) => body.pretty_print(db, self.module(db).krate().to_display_target(db)),
             Err(e) => format!("error:\n{e:?}"),
         }
     }
@@ -2449,8 +2478,6 @@ impl Function {
         db: &dyn HirDatabase,
         span_formatter: impl Fn(FileId, TextRange) -> String,
     ) -> Result<String, ConstEvalError> {
-        let krate = HasModule::krate(&self.id, db.upcast());
-        let edition = db.crate_graph()[krate].edition;
         let body = db.monomorphized_mir_body(
             self.id.into(),
             Substitution::empty(Interner),
@@ -2461,7 +2488,12 @@ impl Function {
             Ok(_) => "pass".to_owned(),
             Err(e) => {
                 let mut r = String::new();
-                _ = e.pretty_print(&mut r, db, &span_formatter, edition);
+                _ = e.pretty_print(
+                    &mut r,
+                    db,
+                    &span_formatter,
+                    self.krate(db).to_display_target(db),
+                );
                 r
             }
         };
@@ -2725,8 +2757,8 @@ pub struct EvaluatedConst {
 }
 
 impl EvaluatedConst {
-    pub fn render(&self, db: &dyn HirDatabase, edition: Edition) -> String {
-        format!("{}", self.const_.display(db, edition))
+    pub fn render(&self, db: &dyn HirDatabase, display_target: DisplayTarget) -> String {
+        format!("{}", self.const_.display(db, display_target))
     }
 
     pub fn render_debug(&self, db: &dyn HirDatabase) -> Result<String, MirEvalError> {
@@ -4195,9 +4227,13 @@ impl ConstParam {
         Type::new(db, self.id.parent(), db.const_param_ty(self.id))
     }
 
-    pub fn default(self, db: &dyn HirDatabase, edition: Edition) -> Option<ast::ConstArg> {
+    pub fn default(
+        self,
+        db: &dyn HirDatabase,
+        display_target: DisplayTarget,
+    ) -> Option<ast::ConstArg> {
         let arg = generic_arg_from_param(db, self.id.into())?;
-        known_const_to_ast(arg.constant(Interner)?, db, edition)
+        known_const_to_ast(arg.constant(Interner)?, db, display_target)
     }
 }
 
@@ -4505,18 +4541,18 @@ impl Closure {
         TyKind::Closure(self.id, self.subst).intern(Interner)
     }
 
-    pub fn display_with_id(&self, db: &dyn HirDatabase, edition: Edition) -> String {
+    pub fn display_with_id(&self, db: &dyn HirDatabase, display_target: DisplayTarget) -> String {
         self.clone()
             .as_ty()
-            .display(db, edition)
+            .display(db, display_target)
             .with_closure_style(ClosureStyle::ClosureWithId)
             .to_string()
     }
 
-    pub fn display_with_impl(&self, db: &dyn HirDatabase, edition: Edition) -> String {
+    pub fn display_with_impl(&self, db: &dyn HirDatabase, display_target: DisplayTarget) -> String {
         self.clone()
             .as_ty()
-            .display(db, edition)
+            .display(db, display_target)
             .with_closure_style(ClosureStyle::ImplFn)
             .to_string()
     }
@@ -5321,7 +5357,7 @@ impl Type {
     pub fn type_and_const_arguments<'a>(
         &'a self,
         db: &'a dyn HirDatabase,
-        edition: Edition,
+        display_target: DisplayTarget,
     ) -> impl Iterator<Item = SmolStr> + 'a {
         self.ty
             .strip_references()
@@ -5331,10 +5367,10 @@ impl Type {
             .filter_map(move |arg| {
                 // arg can be either a `Ty` or `constant`
                 if let Some(ty) = arg.ty(Interner) {
-                    Some(format_smolstr!("{}", ty.display(db, edition)))
+                    Some(format_smolstr!("{}", ty.display(db, display_target)))
                 } else {
                     arg.constant(Interner)
-                        .map(|const_| format_smolstr!("{}", const_.display(db, edition)))
+                        .map(|const_| format_smolstr!("{}", const_.display(db, display_target)))
                 }
             })
     }
@@ -5343,7 +5379,7 @@ impl Type {
     pub fn generic_parameters<'a>(
         &'a self,
         db: &'a dyn HirDatabase,
-        edition: Edition,
+        display_target: DisplayTarget,
     ) -> impl Iterator<Item = SmolStr> + 'a {
         // iterate the lifetime
         self.as_adt()
@@ -5353,7 +5389,7 @@ impl Type {
             })
             .into_iter()
             // add the type and const parameters
-            .chain(self.type_and_const_arguments(db, edition))
+            .chain(self.type_and_const_arguments(db, display_target))
     }
 
     pub fn iterate_method_candidates_with_traits<T>(
diff --git a/src/tools/rust-analyzer/crates/hir/src/semantics.rs b/src/tools/rust-analyzer/crates/hir/src/semantics.rs
index bb67fa63a1d..5e2eebcd13c 100644
--- a/src/tools/rust-analyzer/crates/hir/src/semantics.rs
+++ b/src/tools/rust-analyzer/crates/hir/src/semantics.rs
@@ -12,7 +12,8 @@ use std::{
 
 use either::Either;
 use hir_def::{
-    hir::{Expr, ExprOrPatId},
+    expr_store::{Body, ExprOrPatSource},
+    hir::{BindingId, Expr, ExprId, ExprOrPatId, Pat},
     lower::LowerCtx,
     nameres::{MacroSubNs, ModuleOrigin},
     path::ModPath,
@@ -30,6 +31,7 @@ use hir_expand::{
     name::AsName,
     ExpandResult, FileRange, InMacroFile, MacroCallId, MacroFileId, MacroFileIdExt,
 };
+use hir_ty::diagnostics::unsafe_operations_for_body;
 use intern::{sym, Symbol};
 use itertools::Itertools;
 use rustc_hash::{FxHashMap, FxHashSet};
@@ -48,8 +50,8 @@ use crate::{
     db::HirDatabase,
     semantics::source_to_def::{ChildContainer, SourceToDefCache, SourceToDefCtx},
     source_analyzer::{name_hygiene, resolve_hir_path, SourceAnalyzer},
-    Access, Adjust, Adjustment, Adt, AutoBorrow, BindingMode, BuiltinAttr, Callable, Const,
-    ConstParam, Crate, DeriveHelper, Enum, Field, Function, GenericSubstitution, HasSource,
+    Adjust, Adjustment, Adt, AutoBorrow, BindingMode, BuiltinAttr, Callable, Const, ConstParam,
+    Crate, DefWithBody, DeriveHelper, Enum, Field, Function, GenericSubstitution, HasSource,
     HirFileId, Impl, InFile, InlineAsmOperand, ItemInNs, Label, LifetimeParam, Local, Macro,
     Module, ModuleDef, Name, OverloadedDeref, Path, ScopeDef, Static, Struct, ToolModule, Trait,
     TraitAlias, TupleField, Type, TypeAlias, TypeParam, Union, Variant, VariantDef,
@@ -311,6 +313,14 @@ impl<'db> SemanticsImpl<'db> {
         tree
     }
 
+    /// If not crate is found for the file, returns the last crate in topological order.
+    pub fn first_crate_or_default(&self, file: FileId) -> Crate {
+        match self.file_to_module_defs(file).next() {
+            Some(module) => module.krate(),
+            None => (*self.db.crate_graph().crates_in_topological_order().last().unwrap()).into(),
+        }
+    }
+
     pub fn attach_first_edition(&self, file: FileId) -> Option<EditionedFileId> {
         Some(EditionedFileId::new(
             file,
@@ -627,6 +637,31 @@ impl<'db> SemanticsImpl<'db> {
         )
     }
 
+    /// Checks if renaming `renamed` to `new_name` may introduce conflicts with other locals,
+    /// and returns the conflicting locals.
+    pub fn rename_conflicts(&self, to_be_renamed: &Local, new_name: &str) -> Vec<Local> {
+        let body = self.db.body(to_be_renamed.parent);
+        let resolver = to_be_renamed.parent.resolver(self.db.upcast());
+        let starting_expr =
+            body.binding_owners.get(&to_be_renamed.binding_id).copied().unwrap_or(body.body_expr);
+        let mut visitor = RenameConflictsVisitor {
+            body: &body,
+            conflicts: FxHashSet::default(),
+            db: self.db,
+            new_name: Symbol::intern(new_name),
+            old_name: to_be_renamed.name(self.db).symbol().clone(),
+            owner: to_be_renamed.parent,
+            to_be_renamed: to_be_renamed.binding_id,
+            resolver,
+        };
+        visitor.rename_conflicts(starting_expr);
+        visitor
+            .conflicts
+            .into_iter()
+            .map(|binding_id| Local { parent: to_be_renamed.parent, binding_id })
+            .collect()
+    }
+
     /// Retrieves all the formatting parts of the format_args! (or `asm!`) template string.
     pub fn as_format_args_parts(
         &self,
@@ -1555,6 +1590,19 @@ impl<'db> SemanticsImpl<'db> {
             .matched_arm
     }
 
+    pub fn get_unsafe_ops(&self, def: DefWithBody) -> FxHashSet<ExprOrPatSource> {
+        let def = DefWithBodyId::from(def);
+        let (body, source_map) = self.db.body_with_source_map(def);
+        let infer = self.db.infer(def);
+        let mut res = FxHashSet::default();
+        unsafe_operations_for_body(self.db, &infer, def, &body, &mut |node| {
+            if let Ok(node) = source_map.expr_or_pat_syntax(node) {
+                res.insert(node);
+            }
+        });
+        res
+    }
+
     pub fn is_unsafe_macro_call(&self, macro_call: &ast::MacroCall) -> bool {
         let Some(mac) = self.resolve_macro_call(macro_call) else { return false };
         if mac.is_asm_or_global_asm(self.db) {
@@ -1682,6 +1730,15 @@ impl<'db> SemanticsImpl<'db> {
         Some(res)
     }
 
+    pub fn body_for(&self, node: InFile<&SyntaxNode>) -> Option<DefWithBody> {
+        let container = self.with_ctx(|ctx| ctx.find_container(node))?;
+
+        match container {
+            ChildContainer::DefWithBodyId(def) => Some(def.into()),
+            _ => None,
+        }
+    }
+
     /// Returns none if the file of the node is not part of a crate.
     fn analyze(&self, node: &SyntaxNode) -> Option<SourceAnalyzer> {
         let node = self.find_file(node);
@@ -1783,91 +1840,6 @@ impl<'db> SemanticsImpl<'db> {
         InFile::new(file_id, node)
     }
 
-    pub fn is_unsafe_method_call(&self, method_call_expr: &ast::MethodCallExpr) -> bool {
-        method_call_expr
-            .receiver()
-            .and_then(|expr| {
-                let field_expr = match expr {
-                    ast::Expr::FieldExpr(field_expr) => field_expr,
-                    _ => return None,
-                };
-                let ty = self.type_of_expr(&field_expr.expr()?)?.original;
-                if !ty.is_packed(self.db) {
-                    return None;
-                }
-
-                let func = self.resolve_method_call(method_call_expr)?;
-                let res = match func.self_param(self.db)?.access(self.db) {
-                    Access::Shared | Access::Exclusive => true,
-                    Access::Owned => false,
-                };
-                Some(res)
-            })
-            .unwrap_or(false)
-    }
-
-    pub fn is_unsafe_ref_expr(&self, ref_expr: &ast::RefExpr) -> bool {
-        ref_expr
-            .expr()
-            .and_then(|expr| {
-                let field_expr = match expr {
-                    ast::Expr::FieldExpr(field_expr) => field_expr,
-                    _ => return None,
-                };
-                let expr = field_expr.expr()?;
-                self.type_of_expr(&expr)
-            })
-            // Binding a reference to a packed type is possibly unsafe.
-            .map(|ty| ty.original.is_packed(self.db))
-            .unwrap_or(false)
-
-        // FIXME This needs layout computation to be correct. It will highlight
-        // more than it should with the current implementation.
-    }
-
-    pub fn is_unsafe_ident_pat(&self, ident_pat: &ast::IdentPat) -> bool {
-        if ident_pat.ref_token().is_none() {
-            return false;
-        }
-
-        ident_pat
-            .syntax()
-            .parent()
-            .and_then(|parent| {
-                // `IdentPat` can live under `RecordPat` directly under `RecordPatField` or
-                // `RecordPatFieldList`. `RecordPatField` also lives under `RecordPatFieldList`,
-                // so this tries to lookup the `IdentPat` anywhere along that structure to the
-                // `RecordPat` so we can get the containing type.
-                let record_pat = ast::RecordPatField::cast(parent.clone())
-                    .and_then(|record_pat| record_pat.syntax().parent())
-                    .or_else(|| Some(parent.clone()))
-                    .and_then(|parent| {
-                        ast::RecordPatFieldList::cast(parent)?
-                            .syntax()
-                            .parent()
-                            .and_then(ast::RecordPat::cast)
-                    });
-
-                // If this doesn't match a `RecordPat`, fallback to a `LetStmt` to see if
-                // this is initialized from a `FieldExpr`.
-                if let Some(record_pat) = record_pat {
-                    self.type_of_pat(&ast::Pat::RecordPat(record_pat))
-                } else if let Some(let_stmt) = ast::LetStmt::cast(parent) {
-                    let field_expr = match let_stmt.initializer()? {
-                        ast::Expr::FieldExpr(field_expr) => field_expr,
-                        _ => return None,
-                    };
-
-                    self.type_of_expr(&field_expr.expr()?)
-                } else {
-                    None
-                }
-            })
-            // Binding a reference to a packed type is possibly unsafe.
-            .map(|ty| ty.original.is_packed(self.db))
-            .unwrap_or(false)
-    }
-
     /// Returns `true` if the `node` is inside an `unsafe` context.
     pub fn is_inside_unsafe(&self, expr: &ast::Expr) -> bool {
         let Some(enclosing_item) =
@@ -2155,3 +2127,69 @@ impl ops::Deref for VisibleTraits {
         &self.0
     }
 }
+
+struct RenameConflictsVisitor<'a> {
+    db: &'a dyn HirDatabase,
+    owner: DefWithBodyId,
+    resolver: Resolver,
+    body: &'a Body,
+    to_be_renamed: BindingId,
+    new_name: Symbol,
+    old_name: Symbol,
+    conflicts: FxHashSet<BindingId>,
+}
+
+impl RenameConflictsVisitor<'_> {
+    fn resolve_path(&mut self, node: ExprOrPatId, path: &Path) {
+        if let Path::BarePath(path) = path {
+            if let Some(name) = path.as_ident() {
+                if *name.symbol() == self.new_name {
+                    if let Some(conflicting) = self.resolver.rename_will_conflict_with_renamed(
+                        self.db.upcast(),
+                        name,
+                        path,
+                        self.body.expr_or_pat_path_hygiene(node),
+                        self.to_be_renamed,
+                    ) {
+                        self.conflicts.insert(conflicting);
+                    }
+                } else if *name.symbol() == self.old_name {
+                    if let Some(conflicting) =
+                        self.resolver.rename_will_conflict_with_another_variable(
+                            self.db.upcast(),
+                            name,
+                            path,
+                            self.body.expr_or_pat_path_hygiene(node),
+                            &self.new_name,
+                            self.to_be_renamed,
+                        )
+                    {
+                        self.conflicts.insert(conflicting);
+                    }
+                }
+            }
+        }
+    }
+
+    fn rename_conflicts(&mut self, expr: ExprId) {
+        match &self.body[expr] {
+            Expr::Path(path) => {
+                let guard = self.resolver.update_to_inner_scope(self.db.upcast(), self.owner, expr);
+                self.resolve_path(expr.into(), path);
+                self.resolver.reset_to_guard(guard);
+            }
+            &Expr::Assignment { target, .. } => {
+                let guard = self.resolver.update_to_inner_scope(self.db.upcast(), self.owner, expr);
+                self.body.walk_pats(target, &mut |pat| {
+                    if let Pat::Path(path) = &self.body[pat] {
+                        self.resolve_path(pat.into(), path);
+                    }
+                });
+                self.resolver.reset_to_guard(guard);
+            }
+            _ => {}
+        }
+
+        self.body.walk_child_exprs(expr, |expr| self.rename_conflicts(expr));
+    }
+}
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 9019863f7fd..d1245f5f7d6 100644
--- a/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs
+++ b/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs
@@ -35,7 +35,7 @@ use hir_expand::{
 };
 use hir_ty::{
     diagnostics::{
-        record_literal_missing_fields, record_pattern_missing_fields, unsafe_expressions,
+        record_literal_missing_fields, record_pattern_missing_fields, unsafe_operations,
         InsideUnsafeBlock,
     },
     from_assoc_type_id,
@@ -1160,7 +1160,7 @@ 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| {
+                    unsafe_operations(db, infer, *def, body, expr_id, &mut |inside_unsafe_block| {
                         is_unsafe |= inside_unsafe_block == InsideUnsafeBlock::No
                     })
                 };
diff --git a/src/tools/rust-analyzer/crates/hir/src/symbols.rs b/src/tools/rust-analyzer/crates/hir/src/symbols.rs
index 2ebd88edae2..81eb6a70ad7 100644
--- a/src/tools/rust-analyzer/crates/hir/src/symbols.rs
+++ b/src/tools/rust-analyzer/crates/hir/src/symbols.rs
@@ -13,11 +13,10 @@ use hir_def::{
 use hir_expand::{name::Name, HirFileId};
 use hir_ty::{
     db::HirDatabase,
-    display::{hir_display_with_types_map, HirDisplay},
+    display::{hir_display_with_types_map, DisplayTarget, HirDisplay},
 };
 use intern::Symbol;
 use rustc_hash::FxHashMap;
-use span::Edition;
 use syntax::{ast::HasName, AstNode, AstPtr, SmolStr, SyntaxNode, SyntaxNodePtr, ToSmolStr};
 
 use crate::{Module, ModuleDef, Semantics};
@@ -66,7 +65,7 @@ pub struct SymbolCollector<'a> {
     symbols: FxIndexSet<FileSymbol>,
     work: Vec<SymbolCollectorWork>,
     current_container_name: Option<SmolStr>,
-    edition: Edition,
+    display_target: DisplayTarget,
 }
 
 /// Given a [`ModuleId`] and a [`HirDatabase`], use the DefMap for the module's crate to collect
@@ -78,7 +77,10 @@ impl<'a> SymbolCollector<'a> {
             symbols: Default::default(),
             work: Default::default(),
             current_container_name: None,
-            edition: Edition::Edition2015,
+            display_target: DisplayTarget::from_crate(
+                db,
+                *db.crate_graph().crates_in_topological_order().last().unwrap(),
+            ),
         }
     }
 
@@ -91,7 +93,7 @@ impl<'a> SymbolCollector<'a> {
     pub fn collect(&mut self, module: Module) {
         let _p = tracing::info_span!("SymbolCollector::collect", ?module).entered();
         tracing::info!(?module, "SymbolCollector::collect",);
-        self.edition = module.krate().edition(self.db);
+        self.display_target = module.krate().to_display_target(self.db);
 
         // The initial work is the root module we're collecting, additional work will
         // be populated as we traverse the module's definitions.
@@ -307,7 +309,7 @@ impl<'a> SymbolCollector<'a> {
         let impl_data = self.db.impl_data(impl_id);
         let impl_name = Some(
             hir_display_with_types_map(impl_data.self_ty, &impl_data.types_map)
-                .display(self.db, self.edition)
+                .display(self.db, self.display_target)
                 .to_smolstr(),
         );
         self.with_container_name(impl_name, |s| {
diff --git a/src/tools/rust-analyzer/crates/hir/src/term_search/expr.rs b/src/tools/rust-analyzer/crates/hir/src/term_search/expr.rs
index d2070f0e18b..0d672dc332f 100644
--- a/src/tools/rust-analyzer/crates/hir/src/term_search/expr.rs
+++ b/src/tools/rust-analyzer/crates/hir/src/term_search/expr.rs
@@ -4,7 +4,7 @@ use hir_def::ImportPathConfig;
 use hir_expand::mod_path::ModPath;
 use hir_ty::{
     db::HirDatabase,
-    display::{DisplaySourceCodeError, HirDisplay},
+    display::{DisplaySourceCodeError, DisplayTarget, HirDisplay},
 };
 use itertools::Itertools;
 use span::Edition;
@@ -99,14 +99,16 @@ impl Expr {
         sema_scope: &SemanticsScope<'_>,
         many_formatter: &mut dyn FnMut(&Type) -> String,
         cfg: ImportPathConfig,
-        edition: Edition,
+        display_target: DisplayTarget,
     ) -> Result<String, DisplaySourceCodeError> {
         let db = sema_scope.db;
+        let edition = display_target.edition;
         let mod_item_path_str = |s, def| mod_item_path_str(s, def, cfg, edition);
         match self {
             Expr::Const(it) => match it.as_assoc_item(db).map(|it| it.container(db)) {
                 Some(container) => {
-                    let container_name = container_name(container, sema_scope, cfg, edition)?;
+                    let container_name =
+                        container_name(container, sema_scope, cfg, edition, display_target)?;
                     let const_name = it
                         .name(db)
                         .map(|c| c.display(db.upcast(), edition).to_string())
@@ -122,14 +124,15 @@ impl Expr {
             Expr::Function { func, params, .. } => {
                 let args = params
                     .iter()
-                    .map(|f| f.gen_source_code(sema_scope, many_formatter, cfg, edition))
+                    .map(|f| f.gen_source_code(sema_scope, many_formatter, cfg, display_target))
                     .collect::<Result<Vec<String>, DisplaySourceCodeError>>()?
                     .into_iter()
                     .join(", ");
 
                 match func.as_assoc_item(db).map(|it| it.container(db)) {
                     Some(container) => {
-                        let container_name = container_name(container, sema_scope, cfg, edition)?;
+                        let container_name =
+                            container_name(container, sema_scope, cfg, edition, display_target)?;
                         let fn_name = func.name(db).display(db.upcast(), edition).to_string();
                         Ok(format!("{container_name}::{fn_name}({args})"))
                     }
@@ -147,10 +150,10 @@ impl Expr {
                 let func_name = func.name(db).display(db.upcast(), edition).to_string();
                 let self_param = func.self_param(db).unwrap();
                 let target_str =
-                    target.gen_source_code(sema_scope, many_formatter, cfg, edition)?;
+                    target.gen_source_code(sema_scope, many_formatter, cfg, display_target)?;
                 let args = params
                     .iter()
-                    .map(|f| f.gen_source_code(sema_scope, many_formatter, cfg, edition))
+                    .map(|f| f.gen_source_code(sema_scope, many_formatter, cfg, display_target))
                     .collect::<Result<Vec<String>, DisplaySourceCodeError>>()?
                     .into_iter()
                     .join(", ");
@@ -180,7 +183,9 @@ impl Expr {
                     StructKind::Tuple => {
                         let args = params
                             .iter()
-                            .map(|f| f.gen_source_code(sema_scope, many_formatter, cfg, edition))
+                            .map(|f| {
+                                f.gen_source_code(sema_scope, many_formatter, cfg, display_target)
+                            })
                             .collect::<Result<Vec<String>, DisplaySourceCodeError>>()?
                             .into_iter()
                             .join(", ");
@@ -195,7 +200,12 @@ impl Expr {
                                 let tmp = format!(
                                     "{}: {}",
                                     f.name(db).display(db.upcast(), edition),
-                                    a.gen_source_code(sema_scope, many_formatter, cfg, edition)?
+                                    a.gen_source_code(
+                                        sema_scope,
+                                        many_formatter,
+                                        cfg,
+                                        display_target
+                                    )?
                                 );
                                 Ok(tmp)
                             })
@@ -215,7 +225,9 @@ impl Expr {
                     StructKind::Tuple => {
                         let args = params
                             .iter()
-                            .map(|a| a.gen_source_code(sema_scope, many_formatter, cfg, edition))
+                            .map(|a| {
+                                a.gen_source_code(sema_scope, many_formatter, cfg, display_target)
+                            })
                             .collect::<Result<Vec<String>, DisplaySourceCodeError>>()?
                             .into_iter()
                             .join(", ");
@@ -230,7 +242,12 @@ impl Expr {
                                 let tmp = format!(
                                     "{}: {}",
                                     f.name(db).display(db.upcast(), edition),
-                                    a.gen_source_code(sema_scope, many_formatter, cfg, edition)?
+                                    a.gen_source_code(
+                                        sema_scope,
+                                        many_formatter,
+                                        cfg,
+                                        display_target
+                                    )?
                                 );
                                 Ok(tmp)
                             })
@@ -248,7 +265,7 @@ impl Expr {
             Expr::Tuple { params, .. } => {
                 let args = params
                     .iter()
-                    .map(|a| a.gen_source_code(sema_scope, many_formatter, cfg, edition))
+                    .map(|a| a.gen_source_code(sema_scope, many_formatter, cfg, display_target))
                     .collect::<Result<Vec<String>, DisplaySourceCodeError>>()?
                     .into_iter()
                     .join(", ");
@@ -260,7 +277,8 @@ impl Expr {
                     return Ok(many_formatter(&expr.ty(db)));
                 }
 
-                let strukt = expr.gen_source_code(sema_scope, many_formatter, cfg, edition)?;
+                let strukt =
+                    expr.gen_source_code(sema_scope, many_formatter, cfg, display_target)?;
                 let field = field.name(db).display(db.upcast(), edition).to_string();
                 Ok(format!("{strukt}.{field}"))
             }
@@ -269,7 +287,8 @@ impl Expr {
                     return Ok(many_formatter(&expr.ty(db)));
                 }
 
-                let inner = expr.gen_source_code(sema_scope, many_formatter, cfg, edition)?;
+                let inner =
+                    expr.gen_source_code(sema_scope, many_formatter, cfg, display_target)?;
                 Ok(format!("&{inner}"))
             }
             Expr::Many(ty) => Ok(many_formatter(ty)),
@@ -358,6 +377,7 @@ fn container_name(
     sema_scope: &SemanticsScope<'_>,
     cfg: ImportPathConfig,
     edition: Edition,
+    display_target: DisplayTarget,
 ) -> Result<String, DisplaySourceCodeError> {
     let container_name = match container {
         crate::AssocItemContainer::Trait(trait_) => {
@@ -368,7 +388,7 @@ fn container_name(
             // Should it be guaranteed that `mod_item_path` always exists?
             match self_ty.as_adt().and_then(|adt| mod_item_path(sema_scope, &adt.into(), cfg)) {
                 Some(path) => path.display(sema_scope.db.upcast(), edition).to_string(),
-                None => self_ty.display(sema_scope.db, edition).to_string(),
+                None => self_ty.display(sema_scope.db, display_target).to_string(),
             }
         }
     };
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/explicit_enum_discriminant.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_explicit_enum_discriminant.rs
index fafc3448a87..1a5de9cb071 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/explicit_enum_discriminant.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_explicit_enum_discriminant.rs
@@ -8,7 +8,7 @@ use syntax::{ast, AstNode};
 
 use crate::{AssistContext, Assists};
 
-// Assist: explicit_enum_discriminant
+// Assist: add_explicit_enum_discriminant
 //
 // Adds explicit discriminant to all enum variants.
 //
@@ -29,7 +29,10 @@ use crate::{AssistContext, Assists};
 //     Quux = 43,
 // }
 // ```
-pub(crate) fn explicit_enum_discriminant(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
+pub(crate) fn add_explicit_enum_discriminant(
+    acc: &mut Assists,
+    ctx: &AssistContext<'_>,
+) -> Option<()> {
     let enum_node = ctx.find_node_at_offset::<ast::Enum>()?;
     let enum_def = ctx.sema.to_def(&enum_node)?;
 
@@ -50,7 +53,7 @@ pub(crate) fn explicit_enum_discriminant(acc: &mut Assists, ctx: &AssistContext<
     }
 
     acc.add(
-        AssistId("explicit_enum_discriminant", AssistKind::RefactorRewrite),
+        AssistId("add_explicit_enum_discriminant", AssistKind::RefactorRewrite),
         "Add explicit enum discriminants",
         enum_node.syntax().text_range(),
         |builder| {
@@ -88,12 +91,12 @@ fn add_variant_discriminant(
 mod tests {
     use crate::tests::{check_assist, check_assist_not_applicable};
 
-    use super::explicit_enum_discriminant;
+    use super::add_explicit_enum_discriminant;
 
     #[test]
     fn non_primitive_repr_non_data_bearing_add_discriminant() {
         check_assist(
-            explicit_enum_discriminant,
+            add_explicit_enum_discriminant,
             r#"
 enum TheEnum$0 {
     Foo,
@@ -120,7 +123,7 @@ enum TheEnum {
     #[test]
     fn primitive_repr_data_bearing_add_discriminant() {
         check_assist(
-            explicit_enum_discriminant,
+            add_explicit_enum_discriminant,
             r#"
 #[repr(u8)]
 $0enum TheEnum {
@@ -145,7 +148,7 @@ enum TheEnum {
     #[test]
     fn non_primitive_repr_data_bearing_not_applicable() {
         check_assist_not_applicable(
-            explicit_enum_discriminant,
+            add_explicit_enum_discriminant,
             r#"
 enum TheEnum$0 {
     Foo,
@@ -159,7 +162,7 @@ enum TheEnum$0 {
     #[test]
     fn primitive_repr_non_data_bearing_add_discriminant() {
         check_assist(
-            explicit_enum_discriminant,
+            add_explicit_enum_discriminant,
             r#"
 #[repr(i64)]
 enum TheEnum {
@@ -184,7 +187,7 @@ enum TheEnum {
     #[test]
     fn discriminants_already_explicit_not_applicable() {
         check_assist_not_applicable(
-            explicit_enum_discriminant,
+            add_explicit_enum_discriminant,
             r#"
 enum TheEnum$0 {
     Foo = 0,
@@ -197,7 +200,7 @@ enum TheEnum$0 {
     #[test]
     fn empty_enum_not_applicable() {
         check_assist_not_applicable(
-            explicit_enum_discriminant,
+            add_explicit_enum_discriminant,
             r#"
 enum TheEnum$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 77562c588e2..67bf8eed23d 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
@@ -128,7 +128,9 @@ pub(crate) fn apply_demorgan(acc: &mut Assists, ctx: &AssistContext<'_>) -> Opti
                     let parent = neg_expr.syntax().parent();
                     editor = builder.make_editor(neg_expr.syntax());
 
-                    if parent.is_some_and(|parent| demorganed.needs_parens_in(&parent)) {
+                    if parent.is_some_and(|parent| {
+                        demorganed.needs_parens_in_place_of(&parent, neg_expr.syntax())
+                    }) {
                         cov_mark::hit!(demorgan_keep_parens_for_op_precedence2);
                         editor.replace(neg_expr.syntax(), make.expr_paren(demorganed).syntax());
                     } else {
@@ -392,15 +394,19 @@ fn f() { !(S <= S || S < S) }
 
     #[test]
     fn demorgan_keep_pars_for_op_precedence3() {
-        check_assist(apply_demorgan, "fn f() { (a || !(b &&$0 c); }", "fn f() { (a || !b || !c; }");
+        check_assist(
+            apply_demorgan,
+            "fn f() { (a || !(b &&$0 c); }",
+            "fn f() { (a || (!b || !c); }",
+        );
     }
 
     #[test]
-    fn demorgan_removes_pars_in_eq_precedence() {
+    fn demorgan_keeps_pars_in_eq_precedence() {
         check_assist(
             apply_demorgan,
             "fn() { let x = a && !(!b |$0| !c); }",
-            "fn() { let x = a && b && c; }",
+            "fn() { let x = a && (b && c); }",
         )
     }
 
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/bool_to_enum.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_bool_to_enum.rs
index cbd39796241..7716e99e604 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/bool_to_enum.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_bool_to_enum.rs
@@ -26,7 +26,7 @@ use crate::{
     utils,
 };
 
-// Assist: bool_to_enum
+// Assist: convert_bool_to_enum
 //
 // 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
@@ -55,14 +55,14 @@ use crate::{
 //     }
 // }
 // ```
-pub(crate) fn bool_to_enum(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
+pub(crate) fn convert_bool_to_enum(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
     let BoolNodeData { target_node, name, ty_annotation, initializer, definition } =
         find_bool_node(ctx)?;
     let target_module = ctx.sema.scope(&target_node)?.module().nearest_non_block_module(ctx.db());
 
     let target = name.syntax().text_range();
     acc.add(
-        AssistId("bool_to_enum", AssistKind::RefactorRewrite),
+        AssistId("convert_bool_to_enum", AssistKind::RefactorRewrite),
         "Convert boolean to enum",
         target,
         |edit| {
@@ -549,7 +549,7 @@ mod tests {
     #[test]
     fn parameter_with_first_param_usage() {
         check_assist(
-            bool_to_enum,
+            convert_bool_to_enum,
             r#"
 fn function($0foo: bool, bar: bool) {
     if foo {
@@ -573,7 +573,7 @@ fn function(foo: Bool, bar: bool) {
     #[test]
     fn no_duplicate_enums() {
         check_assist(
-            bool_to_enum,
+            convert_bool_to_enum,
             r#"
 #[derive(PartialEq, Eq)]
 enum Bool { True, False }
@@ -600,7 +600,7 @@ fn function(foo: bool, bar: Bool) {
     #[test]
     fn parameter_with_last_param_usage() {
         check_assist(
-            bool_to_enum,
+            convert_bool_to_enum,
             r#"
 fn function(foo: bool, $0bar: bool) {
     if bar {
@@ -624,7 +624,7 @@ fn function(foo: bool, bar: Bool) {
     #[test]
     fn parameter_with_middle_param_usage() {
         check_assist(
-            bool_to_enum,
+            convert_bool_to_enum,
             r#"
 fn function(foo: bool, $0bar: bool, baz: bool) {
     if bar {
@@ -648,7 +648,7 @@ fn function(foo: bool, bar: Bool, baz: bool) {
     #[test]
     fn parameter_with_closure_usage() {
         check_assist(
-            bool_to_enum,
+            convert_bool_to_enum,
             r#"
 fn main() {
     let foo = |$0bar: bool| bar;
@@ -668,7 +668,7 @@ fn main() {
     #[test]
     fn local_variable_with_usage() {
         check_assist(
-            bool_to_enum,
+            convert_bool_to_enum,
             r#"
 fn main() {
     let $0foo = true;
@@ -697,7 +697,7 @@ fn main() {
     fn local_variable_with_usage_negated() {
         cov_mark::check!(replaces_negation);
         check_assist(
-            bool_to_enum,
+            convert_bool_to_enum,
             r#"
 fn main() {
     let $0foo = true;
@@ -726,7 +726,7 @@ fn main() {
     fn local_variable_with_type_annotation() {
         cov_mark::check!(replaces_ty_annotation);
         check_assist(
-            bool_to_enum,
+            convert_bool_to_enum,
             r#"
 fn main() {
     let $0foo: bool = false;
@@ -746,7 +746,7 @@ fn main() {
     #[test]
     fn local_variable_with_non_literal_initializer() {
         check_assist(
-            bool_to_enum,
+            convert_bool_to_enum,
             r#"
 fn main() {
     let $0foo = 1 == 2;
@@ -766,7 +766,7 @@ fn main() {
     #[test]
     fn local_variable_binexpr_usage() {
         check_assist(
-            bool_to_enum,
+            convert_bool_to_enum,
             r#"
 fn main() {
     let $0foo = false;
@@ -796,7 +796,7 @@ fn main() {
     #[test]
     fn local_variable_unop_usage() {
         check_assist(
-            bool_to_enum,
+            convert_bool_to_enum,
             r#"
 fn main() {
     let $0foo = true;
@@ -825,7 +825,7 @@ fn main() {
     fn local_variable_assigned_later() {
         cov_mark::check!(replaces_assignment);
         check_assist(
-            bool_to_enum,
+            convert_bool_to_enum,
             r#"
 fn main() {
     let $0foo: bool;
@@ -847,7 +847,7 @@ fn main() {
     #[test]
     fn local_variable_does_not_apply_recursively() {
         check_assist(
-            bool_to_enum,
+            convert_bool_to_enum,
             r#"
 fn main() {
     let $0foo = true;
@@ -878,7 +878,7 @@ fn main() {
     fn local_variable_nested_in_negation() {
         cov_mark::check!(dont_overwrite_expression_inside_negation);
         check_assist(
-            bool_to_enum,
+            convert_bool_to_enum,
             r#"
 fn main() {
     if !"foo".chars().any(|c| {
@@ -909,7 +909,7 @@ fn main() {
     fn local_variable_non_bool() {
         cov_mark::check!(not_applicable_non_bool_local);
         check_assist_not_applicable(
-            bool_to_enum,
+            convert_bool_to_enum,
             r#"
 fn main() {
     let $0foo = 1;
@@ -921,7 +921,7 @@ fn main() {
     #[test]
     fn local_variable_cursor_not_on_ident() {
         check_assist_not_applicable(
-            bool_to_enum,
+            convert_bool_to_enum,
             r#"
 fn main() {
     let foo = $0true;
@@ -933,7 +933,7 @@ fn main() {
     #[test]
     fn local_variable_non_ident_pat() {
         check_assist_not_applicable(
-            bool_to_enum,
+            convert_bool_to_enum,
             r#"
 fn main() {
     let ($0foo, bar) = (true, false);
@@ -945,7 +945,7 @@ fn main() {
     #[test]
     fn local_var_init_struct_usage() {
         check_assist(
-            bool_to_enum,
+            convert_bool_to_enum,
             r#"
 struct Foo {
     foo: bool,
@@ -975,7 +975,7 @@ fn main() {
     #[test]
     fn local_var_init_struct_usage_in_macro() {
         check_assist(
-            bool_to_enum,
+            convert_bool_to_enum,
             r#"
 struct Struct {
     boolean: bool,
@@ -1018,7 +1018,7 @@ fn new() -> Struct {
     fn field_struct_basic() {
         cov_mark::check!(replaces_record_expr);
         check_assist(
-            bool_to_enum,
+            convert_bool_to_enum,
             r#"
 struct Foo {
     $0bar: bool,
@@ -1057,7 +1057,7 @@ fn main() {
     fn field_enum_basic() {
         cov_mark::check!(replaces_record_pat);
         check_assist(
-            bool_to_enum,
+            convert_bool_to_enum,
             r#"
 enum Foo {
     Foo,
@@ -1100,7 +1100,7 @@ fn main() {
     fn field_enum_cross_file() {
         // FIXME: The import is missing
         check_assist(
-            bool_to_enum,
+            convert_bool_to_enum,
             r#"
 //- /foo.rs
 pub enum Foo {
@@ -1151,7 +1151,7 @@ fn main() {
     fn field_enum_shorthand() {
         cov_mark::check!(replaces_record_pat_shorthand);
         check_assist(
-            bool_to_enum,
+            convert_bool_to_enum,
             r#"
 enum Foo {
     Foo,
@@ -1200,7 +1200,7 @@ fn main() {
     fn field_enum_replaces_literal_patterns() {
         cov_mark::check!(replaces_literal_pat);
         check_assist(
-            bool_to_enum,
+            convert_bool_to_enum,
             r#"
 enum Foo {
     Foo,
@@ -1238,7 +1238,7 @@ fn main() {
     #[test]
     fn field_enum_keeps_wildcard_patterns() {
         check_assist(
-            bool_to_enum,
+            convert_bool_to_enum,
             r#"
 enum Foo {
     Foo,
@@ -1276,7 +1276,7 @@ fn main() {
     #[test]
     fn field_union_basic() {
         check_assist(
-            bool_to_enum,
+            convert_bool_to_enum,
             r#"
 union Foo {
     $0foo: bool,
@@ -1314,7 +1314,7 @@ fn main() {
     #[test]
     fn field_negated() {
         check_assist(
-            bool_to_enum,
+            convert_bool_to_enum,
             r#"
 struct Foo {
     $0bar: bool,
@@ -1350,7 +1350,7 @@ fn main() {
     #[test]
     fn field_in_mod_properly_indented() {
         check_assist(
-            bool_to_enum,
+            convert_bool_to_enum,
             r#"
 mod foo {
     struct Bar {
@@ -1386,7 +1386,7 @@ mod foo {
     #[test]
     fn field_multiple_initializations() {
         check_assist(
-            bool_to_enum,
+            convert_bool_to_enum,
             r#"
 struct Foo {
     $0bar: bool,
@@ -1427,7 +1427,7 @@ fn main() {
     fn field_assigned_to_another() {
         cov_mark::check!(dont_assign_incorrect_ref);
         check_assist(
-            bool_to_enum,
+            convert_bool_to_enum,
             r#"
 struct Foo {
     $0foo: bool,
@@ -1469,7 +1469,7 @@ fn main() {
     #[test]
     fn field_initialized_with_other() {
         check_assist(
-            bool_to_enum,
+            convert_bool_to_enum,
             r#"
 struct Foo {
     $0foo: bool,
@@ -1507,7 +1507,7 @@ fn main() {
     #[test]
     fn field_method_chain_usage() {
         check_assist(
-            bool_to_enum,
+            convert_bool_to_enum,
             r#"
 struct Foo {
     $0bool: bool,
@@ -1539,7 +1539,7 @@ fn main() {
     #[test]
     fn field_in_macro() {
         check_assist(
-            bool_to_enum,
+            convert_bool_to_enum,
             r#"
 struct Struct {
     $0boolean: bool,
@@ -1580,7 +1580,7 @@ fn new() -> Struct {
     fn field_non_bool() {
         cov_mark::check!(not_applicable_non_bool_field);
         check_assist_not_applicable(
-            bool_to_enum,
+            convert_bool_to_enum,
             r#"
 struct Foo {
     $0bar: usize,
@@ -1596,7 +1596,7 @@ fn main() {
     #[test]
     fn const_basic() {
         check_assist(
-            bool_to_enum,
+            convert_bool_to_enum,
             r#"
 const $0FOO: bool = false;
 
@@ -1624,7 +1624,7 @@ fn main() {
     #[test]
     fn const_in_module() {
         check_assist(
-            bool_to_enum,
+            convert_bool_to_enum,
             r#"
 fn main() {
     if foo::FOO {
@@ -1658,7 +1658,7 @@ mod foo {
     #[test]
     fn const_in_module_with_import() {
         check_assist(
-            bool_to_enum,
+            convert_bool_to_enum,
             r#"
 fn main() {
     use foo::FOO;
@@ -1696,7 +1696,7 @@ mod foo {
     #[test]
     fn const_cross_file() {
         check_assist(
-            bool_to_enum,
+            convert_bool_to_enum,
             r#"
 //- /main.rs
 mod foo;
@@ -1734,7 +1734,7 @@ pub const FOO: Bool = Bool::True;
     #[test]
     fn const_cross_file_and_module() {
         check_assist(
-            bool_to_enum,
+            convert_bool_to_enum,
             r#"
 //- /main.rs
 mod foo;
@@ -1780,7 +1780,7 @@ pub mod bar {
     #[test]
     fn const_in_impl_cross_file() {
         check_assist(
-            bool_to_enum,
+            convert_bool_to_enum,
             r#"
 //- /main.rs
 mod foo;
@@ -1824,7 +1824,7 @@ fn foo() -> bool {
     #[test]
     fn const_in_trait() {
         check_assist(
-            bool_to_enum,
+            convert_bool_to_enum,
             r#"
 trait Foo {
     const $0BOOL: bool;
@@ -1865,7 +1865,7 @@ fn main() {
     fn const_non_bool() {
         cov_mark::check!(not_applicable_non_bool_const);
         check_assist_not_applicable(
-            bool_to_enum,
+            convert_bool_to_enum,
             r#"
 const $0FOO: &str = "foo";
 
@@ -1879,7 +1879,7 @@ fn main() {
     #[test]
     fn static_basic() {
         check_assist(
-            bool_to_enum,
+            convert_bool_to_enum,
             r#"
 static mut $0BOOL: bool = true;
 
@@ -1910,7 +1910,7 @@ fn main() {
     fn static_non_bool() {
         cov_mark::check!(not_applicable_non_bool_static);
         check_assist_not_applicable(
-            bool_to_enum,
+            convert_bool_to_enum,
             r#"
 static mut $0FOO: usize = 0;
 
@@ -1925,6 +1925,6 @@ fn main() {
 
     #[test]
     fn not_applicable_to_other_names() {
-        check_assist_not_applicable(bool_to_enum, "fn $0main() {}")
+        check_assist_not_applicable(convert_bool_to_enum, "fn $0main() {}")
     }
 }
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/fill_record_pattern_fields.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/expand_rest_pattern.rs
index ee321864805..c79a982c38d 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/fill_record_pattern_fields.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/expand_rest_pattern.rs
@@ -1,11 +1,29 @@
+use hir::{PathResolution, StructKind};
+use ide_db::syntax_helpers::suggest_name::NameGenerator;
 use syntax::{
     ast::{self, make},
-    AstNode, ToSmolStr,
+    match_ast, AstNode, ToSmolStr,
 };
 
 use crate::{AssistContext, AssistId, Assists};
 
-// Assist: fill_record_pattern_fields
+pub(crate) fn expand_rest_pattern(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
+    let rest_pat = ctx.find_node_at_offset::<ast::RestPat>()?;
+    let parent = rest_pat.syntax().parent()?;
+    match_ast! {
+        match parent {
+            ast::RecordPatFieldList(it) => expand_record_rest_pattern(acc, ctx, it.syntax().parent().and_then(ast::RecordPat::cast)?, rest_pat),
+            ast::TupleStructPat(it) => expand_tuple_struct_rest_pattern(acc, ctx, it, rest_pat),
+            // FIXME
+            // ast::TuplePat(it) => (),
+            // FIXME
+            // ast::SlicePat(it) => (),
+            _ => return None,
+        }
+    }
+}
+
+// Assist: expand_record_rest_pattern
 //
 // Fills fields by replacing rest pattern in record patterns.
 //
@@ -24,16 +42,12 @@ use crate::{AssistContext, AssistId, Assists};
 //     let Bar { y, z  } = bar;
 // }
 // ```
-pub(crate) fn fill_record_pattern_fields(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
-    let record_pat = ctx.find_node_at_offset::<ast::RecordPat>()?;
-
-    let ellipsis = record_pat.record_pat_field_list().and_then(|r| r.rest_pat())?;
-    if !ellipsis.syntax().text_range().contains_inclusive(ctx.offset()) {
-        return None;
-    }
-
-    let target_range = ellipsis.syntax().text_range();
-
+fn expand_record_rest_pattern(
+    acc: &mut Assists,
+    ctx: &AssistContext<'_>,
+    record_pat: ast::RecordPat,
+    rest_pat: ast::RestPat,
+) -> Option<()> {
     let missing_fields = ctx.sema.record_pattern_missing_fields(&record_pat);
 
     if missing_fields.is_empty() {
@@ -42,6 +56,11 @@ pub(crate) fn fill_record_pattern_fields(acc: &mut Assists, ctx: &AssistContext<
     }
 
     let old_field_list = record_pat.record_pat_field_list()?;
+    let old_range = ctx.sema.original_range_opt(old_field_list.syntax())?;
+    if old_range.file_id != ctx.file_id() {
+        return None;
+    }
+
     let new_field_list =
         make::record_pat_field_list(old_field_list.fields(), None).clone_for_update();
     for (f, _) in missing_fields.iter() {
@@ -52,16 +71,93 @@ pub(crate) fn fill_record_pattern_fields(acc: &mut Assists, ctx: &AssistContext<
         new_field_list.add_field(field.clone_for_update());
     }
 
-    let old_range = ctx.sema.original_range_opt(old_field_list.syntax())?;
+    let target_range = rest_pat.syntax().text_range();
+    acc.add(
+        AssistId("expand_record_rest_pattern", crate::AssistKind::RefactorRewrite),
+        "Fill struct fields",
+        target_range,
+        move |builder| builder.replace_ast(old_field_list, new_field_list),
+    )
+}
+// Assist: expand_tuple_struct_rest_pattern
+//
+// Fills fields by replacing rest pattern in tuple struct patterns.
+//
+// ```
+// struct Bar(Y, Z);
+//
+// fn foo(bar: Bar) {
+//     let Bar(..$0) = bar;
+// }
+// ```
+// ->
+// ```
+// struct Bar(Y, Z);
+//
+// fn foo(bar: Bar) {
+//     let Bar(_0, _1) = bar;
+// }
+// ```
+fn expand_tuple_struct_rest_pattern(
+    acc: &mut Assists,
+    ctx: &AssistContext<'_>,
+    pat: ast::TupleStructPat,
+    rest_pat: ast::RestPat,
+) -> Option<()> {
+    let path = pat.path()?;
+    let fields = match ctx.sema.type_of_pat(&pat.clone().into())?.original.as_adt()? {
+        hir::Adt::Struct(s) if s.kind(ctx.sema.db) == StructKind::Tuple => s.fields(ctx.sema.db),
+        hir::Adt::Enum(_) => match ctx.sema.resolve_path(&path)? {
+            PathResolution::Def(hir::ModuleDef::Variant(v))
+                if v.kind(ctx.sema.db) == StructKind::Tuple =>
+            {
+                v.fields(ctx.sema.db)
+            }
+            _ => return None,
+        },
+        _ => return None,
+    };
+
+    let rest_pat = rest_pat.into();
+    let mut pats = pat.fields();
+    let prefix_count = pats.by_ref().position(|p| p == rest_pat)?;
+    let suffix_count = pats.count();
+
+    if fields.len().saturating_sub(prefix_count).saturating_sub(suffix_count) == 0 {
+        cov_mark::hit!(no_missing_fields_tuple_struct);
+        return None;
+    }
+
+    let old_range = ctx.sema.original_range_opt(pat.syntax())?;
     if old_range.file_id != ctx.file_id() {
         return None;
     }
 
+    let mut name_gen = NameGenerator::new_from_scope_locals(ctx.sema.scope(pat.syntax()));
+    let new_pat = make::tuple_struct_pat(
+        path,
+        pat.fields()
+            .take(prefix_count)
+            .chain(fields[prefix_count..fields.len() - suffix_count].iter().map(|f| {
+                make::ident_pat(
+                    false,
+                    false,
+                    match name_gen.for_type(&f.ty(ctx.sema.db), ctx.sema.db, ctx.edition()) {
+                        Some(name) => make::name(&name),
+                        None => make::name(&format!("_{}", f.index())),
+                    },
+                )
+                .into()
+            }))
+            .chain(pat.fields().skip(prefix_count + 1)),
+    );
+
+    let target_range = rest_pat.syntax().text_range();
     acc.add(
-        AssistId("fill_record_pattern_fields", crate::AssistKind::RefactorRewrite),
-        "Fill structure fields",
+        AssistId("expand_tuple_struct_rest_pattern", crate::AssistKind::RefactorRewrite),
+        "Fill tuple struct fields",
         target_range,
-        move |builder| builder.replace_ast(old_field_list, new_field_list),
+        move |builder| builder.replace_ast(pat, new_pat),
     )
 }
 
@@ -73,7 +169,7 @@ mod tests {
     #[test]
     fn fill_fields_enum_with_only_ellipsis() {
         check_assist(
-            fill_record_pattern_fields,
+            expand_rest_pattern,
             r#"
 enum Foo {
     A(X),
@@ -106,7 +202,7 @@ fn bar(foo: Foo) {
     #[test]
     fn fill_fields_enum_with_fields() {
         check_assist(
-            fill_record_pattern_fields,
+            expand_rest_pattern,
             r#"
 enum Foo {
     A(X),
@@ -139,7 +235,7 @@ fn bar(foo: Foo) {
     #[test]
     fn fill_fields_struct_with_only_ellipsis() {
         check_assist(
-            fill_record_pattern_fields,
+            expand_rest_pattern,
             r#"
 struct Bar {
     y: Y,
@@ -160,13 +256,34 @@ fn foo(bar: Bar) {
     let Bar { y, z  } = bar;
 }
 "#,
+        );
+        check_assist(
+            expand_rest_pattern,
+            r#"
+struct Y;
+struct Z;
+struct Bar(Y, Z)
+
+fn foo(bar: Bar) {
+    let Bar(..$0) = bar;
+}
+"#,
+            r#"
+struct Y;
+struct Z;
+struct Bar(Y, Z)
+
+fn foo(bar: Bar) {
+    let Bar(y, z) = bar;
+}
+"#,
         )
     }
 
     #[test]
     fn fill_fields_struct_with_fields() {
         check_assist(
-            fill_record_pattern_fields,
+            expand_rest_pattern,
             r#"
 struct Bar {
     y: Y,
@@ -187,13 +304,36 @@ fn foo(bar: Bar) {
     let Bar { y, z } = bar;
 }
 "#,
+        );
+        check_assist(
+            expand_rest_pattern,
+            r#"
+struct X;
+struct Y;
+struct Z;
+struct Bar(X, Y, Z)
+
+fn foo(bar: Bar) {
+    let Bar(x, ..$0, z) = bar;
+}
+"#,
+            r#"
+struct X;
+struct Y;
+struct Z;
+struct Bar(X, Y, Z)
+
+fn foo(bar: Bar) {
+    let Bar(x, y, z) = bar;
+}
+"#,
         )
     }
 
     #[test]
     fn fill_fields_struct_generated_by_macro() {
         check_assist(
-            fill_record_pattern_fields,
+            expand_rest_pattern,
             r#"
 macro_rules! position {
     ($t: ty) => {
@@ -226,7 +366,7 @@ fn macro_call(pos: Pos) {
     #[test]
     fn fill_fields_enum_generated_by_macro() {
         check_assist(
-            fill_record_pattern_fields,
+            expand_rest_pattern,
             r#"
 macro_rules! enum_gen {
     ($t: ty) => {
@@ -271,7 +411,7 @@ fn macro_call(foo: Foo) {
     #[test]
     fn not_applicable_when_not_in_ellipsis() {
         check_assist_not_applicable(
-            fill_record_pattern_fields,
+            expand_rest_pattern,
             r#"
 enum Foo {
     A(X),
@@ -287,7 +427,7 @@ fn bar(foo: Foo) {
 "#,
         );
         check_assist_not_applicable(
-            fill_record_pattern_fields,
+            expand_rest_pattern,
             r#"
 enum Foo {
     A(X),
@@ -303,7 +443,7 @@ fn bar(foo: Foo) {
 "#,
         );
         check_assist_not_applicable(
-            fill_record_pattern_fields,
+            expand_rest_pattern,
             r#"
 enum Foo {
     A(X),
@@ -324,8 +464,9 @@ fn bar(foo: Foo) {
     fn not_applicable_when_no_missing_fields() {
         // This is still possible even though it's meaningless
         cov_mark::check!(no_missing_fields);
+        cov_mark::check!(no_missing_fields_tuple_struct);
         check_assist_not_applicable(
-            fill_record_pattern_fields,
+            expand_rest_pattern,
             r#"
 enum Foo {
     A(X),
@@ -341,7 +482,7 @@ fn bar(foo: Foo) {
 "#,
         );
         check_assist_not_applicable(
-            fill_record_pattern_fields,
+            expand_rest_pattern,
             r#"
 struct Bar {
     y: Y,
@@ -353,5 +494,15 @@ fn foo(bar: Bar) {
 }
 "#,
         );
+        check_assist_not_applicable(
+            expand_rest_pattern,
+            r#"
+struct Bar(Y, Z)
+
+fn foo(bar: Bar) {
+    let Bar(y, ..$0, z) = bar;
+}
+"#,
+        );
     }
 }
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_function.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_function.rs
index 751e4a5a571..330587e0dbf 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_function.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_function.rs
@@ -750,7 +750,10 @@ impl FunctionBody {
                         ast::Stmt::Item(_) => (),
                         ast::Stmt::LetStmt(stmt) => {
                             if let Some(pat) = stmt.pat() {
-                                walk_pat(&pat, cb);
+                                walk_pat(&pat, &mut |pat| {
+                                    cb(pat);
+                                    std::ops::ControlFlow::<(), ()>::Continue(())
+                                });
                             }
                             if let Some(expr) = stmt.initializer() {
                                 walk_patterns_in_expr(&expr, cb);
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_function.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_function.rs
index 91e248a1de5..7af2a2e1e6a 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_function.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_function.rs
@@ -724,9 +724,9 @@ fn fn_generic_params(
     filter_unnecessary_bounds(&mut generic_params, &mut where_preds, necessary_params);
     filter_bounds_in_scope(&mut generic_params, &mut where_preds, ctx, target);
 
-    let generic_params: Vec<_> =
+    let generic_params: Vec<ast::GenericParam> =
         generic_params.into_iter().map(|it| it.node.clone_for_update()).collect();
-    let where_preds: Vec<_> =
+    let where_preds: Vec<ast::WherePred> =
         where_preds.into_iter().map(|it| it.node.clone_for_update()).collect();
 
     // 4. Rewrite paths
@@ -1116,9 +1116,12 @@ fn fn_arg_type(
 
         if ty.is_reference() || ty.is_mutable_reference() {
             let famous_defs = &FamousDefs(&ctx.sema, ctx.sema.scope(fn_arg.syntax())?.krate());
-            let target_edition = target_module.krate().edition(ctx.db());
             convert_reference_type(ty.strip_references(), ctx.db(), famous_defs)
-                .map(|conversion| conversion.convert_type(ctx.db(), target_edition).to_string())
+                .map(|conversion| {
+                    conversion
+                        .convert_type(ctx.db(), target_module.krate().to_display_target(ctx.db()))
+                        .to_string()
+                })
                 .or_else(|| ty.display_source_code(ctx.db(), target_module.into(), true).ok())
         } else {
             ty.display_source_code(ctx.db(), target_module.into(), true).ok()
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_getter_or_setter.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_getter_or_setter.rs
index ac58af62525..1b16ba5fc8f 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_getter_or_setter.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_getter_or_setter.rs
@@ -233,7 +233,7 @@ fn generate_getter_from_info(
                 .map(|conversion| {
                     cov_mark::hit!(convert_reference_type);
                     (
-                        conversion.convert_type(ctx.db(), krate.edition(ctx.db())),
+                        conversion.convert_type(ctx.db(), krate.to_display_target(ctx.db())),
                         conversion.getter(record_field_info.field_name.to_string()),
                     )
                 })
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/inline_const_as_literal.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/inline_const_as_literal.rs
index 139078eee7c..ca5882d0313 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/inline_const_as_literal.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/inline_const_as_literal.rs
@@ -42,7 +42,7 @@ pub(crate) fn inline_const_as_literal(acc: &mut Assists, ctx: &AssistContext<'_>
         let value = konst
             .eval(ctx.sema.db)
             .ok()?
-            .render(ctx.sema.db, konst.krate(ctx.sema.db).edition(ctx.sema.db));
+            .render(ctx.sema.db, konst.krate(ctx.sema.db).to_display_target(ctx.sema.db));
 
         let id = AssistId("inline_const_as_literal", AssistKind::RefactorInline);
 
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/inline_local_variable.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/inline_local_variable.rs
index cc7bea5152b..36eed290dc8 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/inline_local_variable.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/inline_local_variable.rs
@@ -5,7 +5,7 @@ use ide_db::{
     EditionedFileId, RootDatabase,
 };
 use syntax::{
-    ast::{self, AstNode, AstToken, HasName},
+    ast::{self, syntax_factory::SyntaxFactory, AstNode, AstToken, HasName},
     SyntaxElement, TextRange,
 };
 
@@ -43,22 +43,6 @@ pub(crate) fn inline_local_variable(acc: &mut Assists, ctx: &AssistContext<'_>)
         }?;
     let initializer_expr = let_stmt.initializer()?;
 
-    let delete_range = delete_let.then(|| {
-        if let Some(whitespace) = let_stmt
-            .syntax()
-            .next_sibling_or_token()
-            .and_then(SyntaxElement::into_token)
-            .and_then(ast::Whitespace::cast)
-        {
-            TextRange::new(
-                let_stmt.syntax().text_range().start(),
-                whitespace.syntax().text_range().end(),
-            )
-        } else {
-            let_stmt.syntax().text_range()
-        }
-    });
-
     let wrap_in_parens = references
         .into_iter()
         .filter_map(|FileReference { range, name, .. }| match name {
@@ -73,40 +57,60 @@ pub(crate) fn inline_local_variable(acc: &mut Assists, ctx: &AssistContext<'_>)
             }
             let usage_node =
                 name_ref.syntax().ancestors().find(|it| ast::PathExpr::can_cast(it.kind()));
-            let usage_parent_option = usage_node.and_then(|it| it.parent());
+            let usage_parent_option = usage_node.as_ref().and_then(|it| it.parent());
             let usage_parent = match usage_parent_option {
                 Some(u) => u,
-                None => return Some((range, name_ref, false)),
+                None => return Some((name_ref, false)),
             };
-            Some((range, name_ref, initializer_expr.needs_parens_in(&usage_parent)))
+            let should_wrap = initializer_expr
+                .needs_parens_in_place_of(&usage_parent, usage_node.as_ref().unwrap());
+            Some((name_ref, should_wrap))
         })
         .collect::<Option<Vec<_>>>()?;
 
-    let init_str = initializer_expr.syntax().text().to_string();
-    let init_in_paren = format!("({init_str})");
-
     let target = match target {
-        ast::NameOrNameRef::Name(it) => it.syntax().text_range(),
-        ast::NameOrNameRef::NameRef(it) => it.syntax().text_range(),
+        ast::NameOrNameRef::Name(it) => it.syntax().clone(),
+        ast::NameOrNameRef::NameRef(it) => it.syntax().clone(),
     };
 
     acc.add(
         AssistId("inline_local_variable", AssistKind::RefactorInline),
         "Inline variable",
-        target,
+        target.text_range(),
         move |builder| {
-            if let Some(range) = delete_range {
-                builder.delete(range);
+            let mut editor = builder.make_editor(&target);
+            if delete_let {
+                editor.delete(let_stmt.syntax());
+                if let Some(whitespace) = let_stmt
+                    .syntax()
+                    .next_sibling_or_token()
+                    .and_then(SyntaxElement::into_token)
+                    .and_then(ast::Whitespace::cast)
+                {
+                    editor.delete(whitespace.syntax());
+                }
             }
-            for (range, name, should_wrap) in wrap_in_parens {
-                let replacement = if should_wrap { &init_in_paren } else { &init_str };
-                if ast::RecordExprField::for_field_name(&name).is_some() {
+
+            let make = SyntaxFactory::new();
+
+            for (name, should_wrap) in wrap_in_parens {
+                let replacement = if should_wrap {
+                    make.expr_paren(initializer_expr.clone()).into()
+                } else {
+                    initializer_expr.clone()
+                };
+
+                if let Some(record_field) = ast::RecordExprField::for_field_name(&name) {
                     cov_mark::hit!(inline_field_shorthand);
-                    builder.insert(range.end(), format!(": {replacement}"));
+                    let replacement = make.record_expr_field(name, Some(replacement));
+                    editor.replace(record_field.syntax(), replacement.syntax());
                 } else {
-                    builder.replace(range, replacement.clone())
+                    editor.replace(name.syntax(), replacement.syntax());
                 }
             }
+
+            editor.add_mappings(make.finish_with_mappings());
+            builder.add_file_edits(ctx.file_id(), editor);
         },
     )
 }
@@ -942,4 +946,52 @@ fn main() {
 "#,
         );
     }
+
+    #[test]
+    fn test_wrap_in_parens() {
+        check_assist(
+            inline_local_variable,
+            r#"
+fn main() {
+    let $0a = 123 < 456;
+    let b = !a;
+}
+"#,
+            r#"
+fn main() {
+    let b = !(123 < 456);
+}
+"#,
+        );
+        check_assist(
+            inline_local_variable,
+            r#"
+trait Foo {
+    fn foo(&self);
+}
+
+impl Foo for bool {
+    fn foo(&self) {}
+}
+
+fn main() {
+    let $0a = 123 < 456;
+    let b = a.foo();
+}
+"#,
+            r#"
+trait Foo {
+    fn foo(&self);
+}
+
+impl Foo for bool {
+    fn foo(&self) {}
+}
+
+fn main() {
+    let b = (123 < 456).foo();
+}
+"#,
+        );
+    }
 }
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/introduce_named_generic.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/introduce_named_type_parameter.rs
index 8c276415bb1..994e4a0edda 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/introduce_named_generic.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/introduce_named_type_parameter.rs
@@ -4,7 +4,7 @@ use syntax::ast::{self, syntax_factory::SyntaxFactory, AstNode, HasGenericParams
 
 use crate::{AssistContext, AssistId, AssistKind, Assists};
 
-// Assist: introduce_named_generic
+// Assist: introduce_named_type_parameter
 //
 // Replaces `impl Trait` function argument with the named generic.
 //
@@ -15,18 +15,20 @@ use crate::{AssistContext, AssistId, AssistKind, Assists};
 // ```
 // fn foo<$0B: Bar>(bar: B) {}
 // ```
-pub(crate) fn introduce_named_generic(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
+pub(crate) fn introduce_named_type_parameter(
+    acc: &mut Assists,
+    ctx: &AssistContext<'_>,
+) -> Option<()> {
     let impl_trait_type = ctx.find_node_at_offset::<ast::ImplTraitType>()?;
     let param = impl_trait_type.syntax().ancestors().find_map(ast::Param::cast)?;
-    let fn_ = param.syntax().ancestors().find_map(ast::Fn::cast)?;
-
+    let fn_ = param.syntax().ancestors().nth(2).and_then(ast::Fn::cast)?;
     let type_bound_list = impl_trait_type.type_bound_list()?;
 
     let make = SyntaxFactory::new();
     let target = fn_.syntax().text_range();
     acc.add(
-        AssistId("introduce_named_generic", AssistKind::RefactorRewrite),
-        "Replace impl trait with generic",
+        AssistId("introduce_named_type_parameter", AssistKind::RefactorRewrite),
+        "Replace impl trait with type parameter",
         target,
         |builder| {
             let mut editor = builder.make_editor(fn_.syntax());
@@ -71,7 +73,7 @@ mod tests {
     #[test]
     fn introduce_named_generic_params() {
         check_assist(
-            introduce_named_generic,
+            introduce_named_type_parameter,
             r#"fn foo<G>(bar: $0impl Bar) {}"#,
             r#"fn foo<G, $0B: Bar>(bar: B) {}"#,
         );
@@ -80,7 +82,7 @@ mod tests {
     #[test]
     fn replace_impl_trait_without_generic_params() {
         check_assist(
-            introduce_named_generic,
+            introduce_named_type_parameter,
             r#"fn foo(bar: $0impl Bar) {}"#,
             r#"fn foo<$0B: Bar>(bar: B) {}"#,
         );
@@ -89,7 +91,7 @@ mod tests {
     #[test]
     fn replace_two_impl_trait_with_generic_params() {
         check_assist(
-            introduce_named_generic,
+            introduce_named_type_parameter,
             r#"fn foo<G>(foo: impl Foo, bar: $0impl Bar) {}"#,
             r#"fn foo<G, $0B: Bar>(foo: impl Foo, bar: B) {}"#,
         );
@@ -98,7 +100,7 @@ mod tests {
     #[test]
     fn replace_impl_trait_with_empty_generic_params() {
         check_assist(
-            introduce_named_generic,
+            introduce_named_type_parameter,
             r#"fn foo<>(bar: $0impl Bar) {}"#,
             r#"fn foo<$0B: Bar>(bar: B) {}"#,
         );
@@ -107,7 +109,7 @@ mod tests {
     #[test]
     fn replace_impl_trait_with_empty_multiline_generic_params() {
         check_assist(
-            introduce_named_generic,
+            introduce_named_type_parameter,
             r#"
 fn foo<
 >(bar: $0impl Bar) {}
@@ -122,7 +124,7 @@ fn foo<$0B: Bar
     #[test]
     fn replace_impl_trait_with_exist_generic_letter() {
         check_assist(
-            introduce_named_generic,
+            introduce_named_type_parameter,
             r#"fn foo<B>(bar: $0impl Bar) {}"#,
             r#"fn foo<B, $0B1: Bar>(bar: B1) {}"#,
         );
@@ -131,7 +133,7 @@ fn foo<$0B: Bar
     #[test]
     fn replace_impl_trait_with_more_exist_generic_letter() {
         check_assist(
-            introduce_named_generic,
+            introduce_named_type_parameter,
             r#"fn foo<B, B0, B1, B3>(bar: $0impl Bar) {}"#,
             r#"fn foo<B, B0, B1, B3, $0B4: Bar>(bar: B4) {}"#,
         );
@@ -140,7 +142,7 @@ fn foo<$0B: Bar
     #[test]
     fn replace_impl_trait_with_multiline_generic_params() {
         check_assist(
-            introduce_named_generic,
+            introduce_named_type_parameter,
             r#"
 fn foo<
     G: Foo,
@@ -161,7 +163,7 @@ fn foo<
     #[test]
     fn replace_impl_trait_multiple() {
         check_assist(
-            introduce_named_generic,
+            introduce_named_type_parameter,
             r#"fn foo(bar: $0impl Foo + Bar) {}"#,
             r#"fn foo<$0F: Foo + Bar>(bar: F) {}"#,
         );
@@ -170,7 +172,7 @@ fn foo<
     #[test]
     fn replace_impl_with_mut() {
         check_assist(
-            introduce_named_generic,
+            introduce_named_type_parameter,
             r#"fn f(iter: &mut $0impl Iterator<Item = i32>) {}"#,
             r#"fn f<$0I: Iterator<Item = i32>>(iter: &mut I) {}"#,
         );
@@ -179,7 +181,7 @@ fn foo<
     #[test]
     fn replace_impl_inside() {
         check_assist(
-            introduce_named_generic,
+            introduce_named_type_parameter,
             r#"fn f(x: &mut Vec<$0impl Iterator<Item = i32>>) {}"#,
             r#"fn f<$0I: Iterator<Item = i32>>(x: &mut Vec<I>) {}"#,
         );
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/term_search.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/term_search.rs
index 66671c934c4..e10897b3bef 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/term_search.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/term_search.rs
@@ -52,8 +52,13 @@ pub(crate) fn term_search(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<
     let paths = paths
         .into_iter()
         .filter_map(|path| {
-            path.gen_source_code(&scope, &mut formatter, ctx.config.import_path_config(), edition)
-                .ok()
+            path.gen_source_code(
+                &scope,
+                &mut formatter,
+                ctx.config.import_path_config(),
+                scope.krate().to_display_target(ctx.db()),
+            )
+            .ok()
         })
         .unique();
 
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 448bcadb8ef..e8480b0de19 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/lib.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/lib.rs
@@ -105,6 +105,7 @@ mod handlers {
     pub(crate) type Handler = fn(&mut Assists, &AssistContext<'_>) -> Option<()>;
 
     mod add_braces;
+    mod add_explicit_enum_discriminant;
     mod add_explicit_type;
     mod add_label_to_loop;
     mod add_lifetime_to_type;
@@ -115,9 +116,9 @@ mod handlers {
     mod apply_demorgan;
     mod auto_import;
     mod bind_unused_param;
-    mod bool_to_enum;
     mod change_visibility;
     mod convert_bool_then;
+    mod convert_bool_to_enum;
     mod convert_closure_to_fn;
     mod convert_comment_block;
     mod convert_comment_from_or_to_doc;
@@ -138,14 +139,13 @@ mod handlers {
     mod destructure_tuple_binding;
     mod desugar_doc_comment;
     mod expand_glob_import;
-    mod explicit_enum_discriminant;
+    mod expand_rest_pattern;
     mod extract_expressions_from_format_string;
     mod extract_function;
     mod extract_module;
     mod extract_struct_from_enum_variant;
     mod extract_type_alias;
     mod extract_variable;
-    mod fill_record_pattern_fields;
     mod fix_visibility;
     mod flip_binexpr;
     mod flip_comma;
@@ -177,8 +177,8 @@ mod handlers {
     mod inline_macro;
     mod inline_type_alias;
     mod into_to_qualified_from;
-    mod introduce_named_generic;
     mod introduce_named_lifetime;
+    mod introduce_named_type_parameter;
     mod invert_if;
     mod merge_imports;
     mod merge_match_arms;
@@ -234,49 +234,47 @@ mod handlers {
         &[
             // These are alphabetic for the foolish consistency
             add_braces::add_braces,
+            add_explicit_enum_discriminant::add_explicit_enum_discriminant,
             add_explicit_type::add_explicit_type,
             add_label_to_loop::add_label_to_loop,
-            add_missing_match_arms::add_missing_match_arms,
             add_lifetime_to_type::add_lifetime_to_type,
+            add_missing_match_arms::add_missing_match_arms,
             add_return_type::add_return_type,
             add_turbo_fish::add_turbo_fish,
-            apply_demorgan::apply_demorgan,
             apply_demorgan::apply_demorgan_iterator,
+            apply_demorgan::apply_demorgan,
             auto_import::auto_import,
             bind_unused_param::bind_unused_param,
-            bool_to_enum::bool_to_enum,
             change_visibility::change_visibility,
             convert_bool_then::convert_bool_then_to_if,
             convert_bool_then::convert_if_to_bool_then,
-            toggle_async_sugar::desugar_async_into_impl_future,
-            toggle_async_sugar::sugar_impl_future_into_async,
+            convert_bool_to_enum::convert_bool_to_enum,
+            convert_closure_to_fn::convert_closure_to_fn,
             convert_comment_block::convert_comment_block,
             convert_comment_from_or_to_doc::convert_comment_from_or_to_doc,
-            convert_closure_to_fn::convert_closure_to_fn,
             convert_from_to_tryfrom::convert_from_to_tryfrom,
             convert_integer_literal::convert_integer_literal,
             convert_into_to_from::convert_into_to_from,
-            convert_iter_for_each_to_for::convert_iter_for_each_to_for,
             convert_iter_for_each_to_for::convert_for_loop_with_for_each,
+            convert_iter_for_each_to_for::convert_iter_for_each_to_for,
             convert_let_else_to_match::convert_let_else_to_match,
             convert_match_to_let_else::convert_match_to_let_else,
-            convert_tuple_return_type_to_struct::convert_tuple_return_type_to_struct,
             convert_named_struct_to_tuple_struct::convert_named_struct_to_tuple_struct,
             convert_nested_function_to_closure::convert_nested_function_to_closure,
             convert_to_guarded_return::convert_to_guarded_return,
+            convert_tuple_return_type_to_struct::convert_tuple_return_type_to_struct,
             convert_tuple_struct_to_named_struct::convert_tuple_struct_to_named_struct,
             convert_two_arm_bool_match_to_matches_macro::convert_two_arm_bool_match_to_matches_macro,
             convert_while_to_loop::convert_while_to_loop,
-            desugar_doc_comment::desugar_doc_comment,
-            destructure_tuple_binding::destructure_tuple_binding,
             destructure_struct_binding::destructure_struct_binding,
+            destructure_tuple_binding::destructure_tuple_binding,
+            desugar_doc_comment::desugar_doc_comment,
             expand_glob_import::expand_glob_import,
             expand_glob_import::expand_glob_reexport,
-            explicit_enum_discriminant::explicit_enum_discriminant,
+            expand_rest_pattern::expand_rest_pattern,
             extract_expressions_from_format_string::extract_expressions_from_format_string,
             extract_struct_from_enum_variant::extract_struct_from_enum_variant,
             extract_type_alias::extract_type_alias,
-            fill_record_pattern_fields::fill_record_pattern_fields,
             fix_visibility::fix_visibility,
             flip_binexpr::flip_binexpr,
             flip_comma::flip_comma,
@@ -287,8 +285,8 @@ mod handlers {
             generate_default_from_new::generate_default_from_new,
             generate_delegate_trait::generate_delegate_trait,
             generate_derive::generate_derive,
-            generate_documentation_template::generate_documentation_template,
             generate_documentation_template::generate_doc_example,
+            generate_documentation_template::generate_documentation_template,
             generate_enum_is_method::generate_enum_is_method,
             generate_enum_projection_method::generate_enum_as_method,
             generate_enum_projection_method::generate_enum_try_into_method,
@@ -298,8 +296,8 @@ mod handlers {
             generate_function::generate_function,
             generate_impl::generate_impl,
             generate_impl::generate_trait_impl,
-            generate_mut_trait_impl::generate_mut_trait_impl,
             generate_is_empty_from_len::generate_is_empty_from_len,
+            generate_mut_trait_impl::generate_mut_trait_impl,
             generate_new::generate_new,
             generate_trait_from_impl::generate_trait_from_impl,
             inline_call::inline_call,
@@ -307,39 +305,41 @@ mod handlers {
             inline_const_as_literal::inline_const_as_literal,
             inline_local_variable::inline_local_variable,
             inline_macro::inline_macro,
-            inline_type_alias::inline_type_alias,
             inline_type_alias::inline_type_alias_uses,
+            inline_type_alias::inline_type_alias,
             into_to_qualified_from::into_to_qualified_from,
-            introduce_named_generic::introduce_named_generic,
             introduce_named_lifetime::introduce_named_lifetime,
+            introduce_named_type_parameter::introduce_named_type_parameter,
             invert_if::invert_if,
             merge_imports::merge_imports,
             merge_match_arms::merge_match_arms,
             merge_nested_if::merge_nested_if,
             move_bounds::move_bounds_to_where_clause,
             move_const_to_impl::move_const_to_impl,
+            move_from_mod_rs::move_from_mod_rs,
             move_guard::move_arm_cond_to_match_guard,
             move_guard::move_guard_to_arm_body,
             move_module_to_file::move_module_to_file,
             move_to_mod_rs::move_to_mod_rs,
-            move_from_mod_rs::move_from_mod_rs,
             normalize_import::normalize_import,
             number_representation::reformat_number_literal,
-            pull_assignment_up::pull_assignment_up,
             promote_local_to_const::promote_local_to_const,
-            qualify_path::qualify_path,
+            pull_assignment_up::pull_assignment_up,
             qualify_method_call::qualify_method_call,
+            qualify_path::qualify_path,
             raw_string::add_hash,
             raw_string::make_usual_string,
             raw_string::remove_hash,
             remove_dbg::remove_dbg,
             remove_mut::remove_mut,
+            remove_parentheses::remove_parentheses,
             remove_unused_imports::remove_unused_imports,
             remove_unused_param::remove_unused_param,
-            remove_parentheses::remove_parentheses,
             reorder_fields::reorder_fields,
             reorder_impl_items::reorder_impl_items,
-            replace_try_expr_with_match::replace_try_expr_with_match,
+            replace_arith_op::replace_arith_with_checked,
+            replace_arith_op::replace_arith_with_saturating,
+            replace_arith_op::replace_arith_with_wrapping,
             replace_derive_with_manual_impl::replace_derive_with_manual_impl,
             replace_if_let_with_match::replace_if_let_with_match,
             replace_if_let_with_match::replace_match_with_if_let,
@@ -348,23 +348,23 @@ mod handlers {
             replace_method_eager_lazy::replace_with_eager_method,
             replace_method_eager_lazy::replace_with_lazy_method,
             replace_named_generic_with_impl::replace_named_generic_with_impl,
-            replace_turbofish_with_explicit_type::replace_turbofish_with_explicit_type,
             replace_qualified_name_with_use::replace_qualified_name_with_use,
-            replace_arith_op::replace_arith_with_wrapping,
-            replace_arith_op::replace_arith_with_checked,
-            replace_arith_op::replace_arith_with_saturating,
+            replace_try_expr_with_match::replace_try_expr_with_match,
+            replace_turbofish_with_explicit_type::replace_turbofish_with_explicit_type,
             sort_items::sort_items,
             split_import::split_import,
             term_search::term_search,
+            toggle_async_sugar::desugar_async_into_impl_future,
+            toggle_async_sugar::sugar_impl_future_into_async,
             toggle_ignore::toggle_ignore,
             toggle_macro_delimiter::toggle_macro_delimiter,
             unmerge_match_arm::unmerge_match_arm,
             unmerge_use::unmerge_use,
             unnecessary_async::unnecessary_async,
+            unqualify_method_call::unqualify_method_call,
             unwrap_block::unwrap_block,
             unwrap_return_type::unwrap_return_type,
             unwrap_tuple::unwrap_tuple,
-            unqualify_method_call::unqualify_method_call,
             wrap_return_type::wrap_return_type,
             wrap_unwrap_cfg_attr::wrap_unwrap_cfg_attr,
 
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 11aeb21c77e..7d7012c4622 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/tests.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/tests.rs
@@ -710,18 +710,22 @@ pub fn test_some_range(a: int) -> bool {
                                         Indel {
                                             insert: "let",
                                             delete: 45..47,
+                                            annotation: None,
                                         },
                                         Indel {
                                             insert: "var_name",
                                             delete: 48..60,
+                                            annotation: None,
                                         },
                                         Indel {
                                             insert: "=",
                                             delete: 61..81,
+                                            annotation: None,
                                         },
                                         Indel {
                                             insert: "5;\n    if let 2..6 = var_name {\n        true\n    } else {\n        false\n    }",
                                             delete: 82..108,
+                                            annotation: None,
                                         },
                                     ],
                                 },
@@ -739,6 +743,8 @@ pub fn test_some_range(a: int) -> bool {
                         },
                         file_system_edits: [],
                         is_snippet: true,
+                        annotations: {},
+                        next_annotation_id: 0,
                     },
                 ),
                 command: Some(
@@ -839,18 +845,22 @@ pub fn test_some_range(a: int) -> bool {
                                         Indel {
                                             insert: "let",
                                             delete: 45..47,
+                                            annotation: None,
                                         },
                                         Indel {
                                             insert: "var_name",
                                             delete: 48..60,
+                                            annotation: None,
                                         },
                                         Indel {
                                             insert: "=",
                                             delete: 61..81,
+                                            annotation: None,
                                         },
                                         Indel {
                                             insert: "5;\n    if let 2..6 = var_name {\n        true\n    } else {\n        false\n    }",
                                             delete: 82..108,
+                                            annotation: None,
                                         },
                                     ],
                                 },
@@ -868,6 +878,8 @@ pub fn test_some_range(a: int) -> bool {
                         },
                         file_system_edits: [],
                         is_snippet: true,
+                        annotations: {},
+                        next_annotation_id: 0,
                     },
                 ),
                 command: Some(
@@ -902,22 +914,27 @@ pub fn test_some_range(a: int) -> bool {
                                         Indel {
                                             insert: "const",
                                             delete: 45..47,
+                                            annotation: None,
                                         },
                                         Indel {
                                             insert: "VAR_NAME:",
                                             delete: 48..60,
+                                            annotation: None,
                                         },
                                         Indel {
                                             insert: "i32",
                                             delete: 61..81,
+                                            annotation: None,
                                         },
                                         Indel {
                                             insert: "=",
                                             delete: 82..86,
+                                            annotation: None,
                                         },
                                         Indel {
                                             insert: "5;\n    if let 2..6 = VAR_NAME {\n        true\n    } else {\n        false\n    }",
                                             delete: 87..108,
+                                            annotation: None,
                                         },
                                     ],
                                 },
@@ -935,6 +952,8 @@ pub fn test_some_range(a: int) -> bool {
                         },
                         file_system_edits: [],
                         is_snippet: true,
+                        annotations: {},
+                        next_annotation_id: 0,
                     },
                 ),
                 command: Some(
@@ -969,22 +988,27 @@ pub fn test_some_range(a: int) -> bool {
                                         Indel {
                                             insert: "static",
                                             delete: 45..47,
+                                            annotation: None,
                                         },
                                         Indel {
                                             insert: "VAR_NAME:",
                                             delete: 48..60,
+                                            annotation: None,
                                         },
                                         Indel {
                                             insert: "i32",
                                             delete: 61..81,
+                                            annotation: None,
                                         },
                                         Indel {
                                             insert: "=",
                                             delete: 82..86,
+                                            annotation: None,
                                         },
                                         Indel {
                                             insert: "5;\n    if let 2..6 = VAR_NAME {\n        true\n    } else {\n        false\n    }",
                                             delete: 87..108,
+                                            annotation: None,
                                         },
                                     ],
                                 },
@@ -1002,6 +1026,8 @@ pub fn test_some_range(a: int) -> bool {
                         },
                         file_system_edits: [],
                         is_snippet: true,
+                        annotations: {},
+                        next_annotation_id: 0,
                     },
                 ),
                 command: Some(
@@ -1036,10 +1062,12 @@ pub fn test_some_range(a: int) -> bool {
                                         Indel {
                                             insert: "fun_name()",
                                             delete: 59..60,
+                                            annotation: None,
                                         },
                                         Indel {
                                             insert: "\n\nfn fun_name() -> i32 {\n    5\n}",
                                             delete: 110..110,
+                                            annotation: None,
                                         },
                                     ],
                                 },
@@ -1057,6 +1085,8 @@ pub fn test_some_range(a: int) -> bool {
                         },
                         file_system_edits: [],
                         is_snippet: true,
+                        annotations: {},
+                        next_annotation_id: 0,
                     },
                 ),
                 command: None,
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 91c1a3e1bd7..4234124d670 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
@@ -28,6 +28,29 @@ fn foo(n: i32) -> i32 {
 }
 
 #[test]
+fn doctest_add_explicit_enum_discriminant() {
+    check_doc_test(
+        "add_explicit_enum_discriminant",
+        r#####"
+enum TheEnum$0 {
+    Foo,
+    Bar,
+    Baz = 42,
+    Quux,
+}
+"#####,
+        r#####"
+enum TheEnum {
+    Foo = 0,
+    Bar = 1,
+    Baz = 42,
+    Quux = 43,
+}
+"#####,
+    )
+}
+
+#[test]
 fn doctest_add_explicit_type() {
     check_doc_test(
         "add_explicit_type",
@@ -305,34 +328,6 @@ fn some_function(x: i32) {
 }
 
 #[test]
-fn doctest_bool_to_enum() {
-    check_doc_test(
-        "bool_to_enum",
-        r#####"
-fn main() {
-    let $0bool = true;
-
-    if bool {
-        println!("foo");
-    }
-}
-"#####,
-        r#####"
-#[derive(PartialEq, Eq)]
-enum Bool { True, False }
-
-fn main() {
-    let bool = Bool::True;
-
-    if bool == Bool::True {
-        println!("foo");
-    }
-}
-"#####,
-    )
-}
-
-#[test]
 fn doctest_change_visibility() {
     check_doc_test(
         "change_visibility",
@@ -383,6 +378,34 @@ fn main() {
 }
 
 #[test]
+fn doctest_convert_bool_to_enum() {
+    check_doc_test(
+        "convert_bool_to_enum",
+        r#####"
+fn main() {
+    let $0bool = true;
+
+    if bool {
+        println!("foo");
+    }
+}
+"#####,
+        r#####"
+#[derive(PartialEq, Eq)]
+enum Bool { True, False }
+
+fn main() {
+    let bool = Bool::True;
+
+    if bool == Bool::True {
+        println!("foo");
+    }
+}
+"#####,
+    )
+}
+
+#[test]
 fn doctest_convert_closure_to_fn() {
     check_doc_test(
         "convert_closure_to_fn",
@@ -933,23 +956,42 @@ pub use foo::{Bar, Baz};
 }
 
 #[test]
-fn doctest_explicit_enum_discriminant() {
+fn doctest_expand_record_rest_pattern() {
     check_doc_test(
-        "explicit_enum_discriminant",
+        "expand_record_rest_pattern",
         r#####"
-enum TheEnum$0 {
-    Foo,
-    Bar,
-    Baz = 42,
-    Quux,
+struct Bar { y: Y, z: Z }
+
+fn foo(bar: Bar) {
+    let Bar { ..$0 } = bar;
 }
 "#####,
         r#####"
-enum TheEnum {
-    Foo = 0,
-    Bar = 1,
-    Baz = 42,
-    Quux = 43,
+struct Bar { y: Y, z: Z }
+
+fn foo(bar: Bar) {
+    let Bar { y, z  } = bar;
+}
+"#####,
+    )
+}
+
+#[test]
+fn doctest_expand_tuple_struct_rest_pattern() {
+    check_doc_test(
+        "expand_tuple_struct_rest_pattern",
+        r#####"
+struct Bar(Y, Z);
+
+fn foo(bar: Bar) {
+    let Bar(..$0) = bar;
+}
+"#####,
+        r#####"
+struct Bar(Y, Z);
+
+fn foo(bar: Bar) {
+    let Bar(_0, _1) = bar;
 }
 "#####,
     )
@@ -1118,27 +1160,6 @@ fn main() {
 }
 
 #[test]
-fn doctest_fill_record_pattern_fields() {
-    check_doc_test(
-        "fill_record_pattern_fields",
-        r#####"
-struct Bar { y: Y, z: Z }
-
-fn foo(bar: Bar) {
-    let Bar { ..$0 } = bar;
-}
-"#####,
-        r#####"
-struct Bar { y: Y, z: Z }
-
-fn foo(bar: Bar) {
-    let Bar { y, z  } = bar;
-}
-"#####,
-    )
-}
-
-#[test]
 fn doctest_fix_visibility() {
     check_doc_test(
         "fix_visibility",
@@ -2194,19 +2215,6 @@ fn main() -> () {
 }
 
 #[test]
-fn doctest_introduce_named_generic() {
-    check_doc_test(
-        "introduce_named_generic",
-        r#####"
-fn foo(bar: $0impl Bar) {}
-"#####,
-        r#####"
-fn foo<$0B: Bar>(bar: B) {}
-"#####,
-    )
-}
-
-#[test]
 fn doctest_introduce_named_lifetime() {
     check_doc_test(
         "introduce_named_lifetime",
@@ -2232,6 +2240,19 @@ impl<'a> Cursor<'a> {
 }
 
 #[test]
+fn doctest_introduce_named_type_parameter() {
+    check_doc_test(
+        "introduce_named_type_parameter",
+        r#####"
+fn foo(bar: $0impl Bar) {}
+"#####,
+        r#####"
+fn foo<$0B: Bar>(bar: B) {}
+"#####,
+    )
+}
+
+#[test]
 fn doctest_invert_if() {
     check_doc_test(
         "invert_if",
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 39686f065a9..a6fa1706710 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/utils.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/utils.rs
@@ -3,7 +3,8 @@
 pub(crate) use gen_trait_fn_body::gen_trait_fn_body;
 use hir::{
     db::{ExpandDatabase, HirDatabase},
-    HasAttrs as HirHasAttrs, HirDisplay, InFile, ModuleDef, PathResolution, Semantics,
+    DisplayTarget, HasAttrs as HirHasAttrs, HirDisplay, InFile, ModuleDef, PathResolution,
+    Semantics,
 };
 use ide_db::{
     famous_defs::FamousDefs,
@@ -21,7 +22,7 @@ use syntax::{
         syntax_factory::SyntaxFactory,
         HasArgList, HasAttrs, HasGenericParams, HasName, HasTypeBounds, Whitespace,
     },
-    ted, AstNode, AstToken, Direction, Edition, NodeOrToken, SourceFile,
+    ted, AstNode, AstToken, Direction, NodeOrToken, SourceFile,
     SyntaxKind::*,
     SyntaxNode, SyntaxToken, TextRange, TextSize, WalkEvent, T,
 };
@@ -793,31 +794,50 @@ enum ReferenceConversionType {
 }
 
 impl ReferenceConversion {
-    pub(crate) fn convert_type(&self, db: &dyn HirDatabase, edition: Edition) -> ast::Type {
+    pub(crate) fn convert_type(
+        &self,
+        db: &dyn HirDatabase,
+        display_target: DisplayTarget,
+    ) -> ast::Type {
         let ty = match self.conversion {
-            ReferenceConversionType::Copy => self.ty.display(db, edition).to_string(),
+            ReferenceConversionType::Copy => self.ty.display(db, display_target).to_string(),
             ReferenceConversionType::AsRefStr => "&str".to_owned(),
             ReferenceConversionType::AsRefSlice => {
-                let type_argument_name =
-                    self.ty.type_arguments().next().unwrap().display(db, edition).to_string();
+                let type_argument_name = self
+                    .ty
+                    .type_arguments()
+                    .next()
+                    .unwrap()
+                    .display(db, display_target)
+                    .to_string();
                 format!("&[{type_argument_name}]")
             }
             ReferenceConversionType::Dereferenced => {
-                let type_argument_name =
-                    self.ty.type_arguments().next().unwrap().display(db, edition).to_string();
+                let type_argument_name = self
+                    .ty
+                    .type_arguments()
+                    .next()
+                    .unwrap()
+                    .display(db, display_target)
+                    .to_string();
                 format!("&{type_argument_name}")
             }
             ReferenceConversionType::Option => {
-                let type_argument_name =
-                    self.ty.type_arguments().next().unwrap().display(db, edition).to_string();
+                let type_argument_name = self
+                    .ty
+                    .type_arguments()
+                    .next()
+                    .unwrap()
+                    .display(db, display_target)
+                    .to_string();
                 format!("Option<&{type_argument_name}>")
             }
             ReferenceConversionType::Result => {
                 let mut type_arguments = self.ty.type_arguments();
                 let first_type_argument_name =
-                    type_arguments.next().unwrap().display(db, edition).to_string();
+                    type_arguments.next().unwrap().display(db, display_target).to_string();
                 let second_type_argument_name =
-                    type_arguments.next().unwrap().display(db, edition).to_string();
+                    type_arguments.next().unwrap().display(db, display_target).to_string();
                 format!("Result<&{first_type_argument_name}, &{second_type_argument_name}>")
             }
         };
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/expr.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/expr.rs
index 365d2dde7e9..b28b6e50e22 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/expr.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/expr.rs
@@ -365,7 +365,8 @@ pub(crate) fn complete_expr_path(
                     add_keyword("false", "false");
 
                     if in_condition || in_block_expr {
-                        add_keyword("let", "let");
+                        add_keyword("letm", "let mut $0");
+                        add_keyword("let", "let $0");
                     }
 
                     if after_if_expr {
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/keyword.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/keyword.rs
index 6541ee502d8..26c29e0202c 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/keyword.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/keyword.rs
@@ -330,4 +330,34 @@ fn main() {
 ",
         )
     }
+
+    #[test]
+    fn completes_let_with_space() {
+        check_edit(
+            "let",
+            r#"
+fn main() {
+    $0
+}
+"#,
+            r#"
+fn main() {
+    let $0
+}
+"#,
+        );
+        check_edit(
+            "letm",
+            r#"
+fn main() {
+    $0
+}
+"#,
+            r#"
+fn main() {
+    let mut $0
+}
+"#,
+        );
+    }
 }
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 919b30f7f97..e686a293094 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/context.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/context.rs
@@ -7,8 +7,8 @@ mod tests;
 use std::{iter, ops::ControlFlow};
 
 use hir::{
-    HasAttrs, Local, ModPath, ModuleDef, ModuleSource, Name, PathResolution, ScopeDef, Semantics,
-    SemanticsScope, Symbol, Type, TypeInfo,
+    DisplayTarget, HasAttrs, Local, ModPath, ModuleDef, ModuleSource, Name, PathResolution,
+    ScopeDef, Semantics, SemanticsScope, Symbol, Type, TypeInfo,
 };
 use ide_db::{
     base_db::SourceDatabase, famous_defs::FamousDefs, helpers::is_editable_crate, FilePosition,
@@ -440,6 +440,7 @@ pub(crate) struct CompletionContext<'a> {
     pub(crate) token: SyntaxToken,
     /// The crate of the current file.
     pub(crate) krate: hir::Crate,
+    pub(crate) display_target: DisplayTarget,
     /// The module of the `scope`.
     pub(crate) module: hir::Module,
     /// The function where we're completing, if inside a function.
@@ -867,6 +868,7 @@ impl<'a> CompletionContext<'a> {
             CompleteSemicolon::DoNotComplete
         };
 
+        let display_target = krate.to_display_target(db);
         let ctx = CompletionContext {
             sema,
             scope,
@@ -888,6 +890,7 @@ impl<'a> CompletionContext<'a> {
             exclude_flyimport,
             exclude_traits,
             complete_semicolon,
+            display_target,
         };
         Some((ctx, analysis))
     }
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/context/tests.rs b/src/tools/rust-analyzer/crates/ide-completion/src/context/tests.rs
index fc2bfc01e62..a03f632cdfd 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/context/tests.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/context/tests.rs
@@ -13,7 +13,7 @@ fn check_expected_type_and_name(#[rust_analyzer::rust_fixture] ra_fixture: &str,
 
     let ty = completion_context
         .expected_type
-        .map(|t| t.display_test(&db).to_string())
+        .map(|t| t.display_test(&db, completion_context.krate.to_display_target(&db)).to_string())
         .unwrap_or("?".to_owned());
 
     let name =
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/item.rs b/src/tools/rust-analyzer/crates/ide-completion/src/item.rs
index b3dd8a8d06e..8d6dc4c8013 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/item.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/item.rs
@@ -252,14 +252,16 @@ impl CompletionRelevance {
     /// Provides a relevance score. Higher values are more relevant.
     ///
     /// The absolute value of the relevance score is not meaningful, for
-    /// example a value of 0 doesn't mean "not relevant", rather
+    /// example a value of BASE_SCORE doesn't mean "not relevant", rather
     /// it means "least relevant". The score value should only be used
     /// for relative ordering.
     ///
     /// See is_relevant if you need to make some judgement about score
     /// in an absolute sense.
+    const BASE_SCORE: u32 = u32::MAX / 2;
+
     pub fn score(self) -> u32 {
-        let mut score = !0 / 2;
+        let mut score = Self::BASE_SCORE;
         let CompletionRelevance {
             exact_name_match,
             type_match,
@@ -350,7 +352,7 @@ impl CompletionRelevance {
     /// some threshold such that we think it is especially likely
     /// to be relevant.
     pub fn is_relevant(&self) -> bool {
-        self.score() > 0
+        self.score() > Self::BASE_SCORE
     }
 }
 
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/render.rs b/src/tools/rust-analyzer/crates/ide-completion/src/render.rs
index 4f6c4cb6639..c82905eddef 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/render.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/render.rs
@@ -144,7 +144,7 @@ pub(crate) fn render_field(
         is_skipping_completion: receiver.is_some(),
         ..CompletionRelevance::default()
     });
-    item.detail(ty.display(db, ctx.completion.edition).to_string())
+    item.detail(ty.display(db, ctx.completion.display_target).to_string())
         .set_documentation(field.docs(db))
         .set_deprecated(is_deprecated)
         .lookup_by(name);
@@ -212,7 +212,7 @@ pub(crate) fn render_tuple_field(
         field_with_receiver(receiver.as_deref(), &field.to_string()),
         ctx.completion.edition,
     );
-    item.detail(ty.display(ctx.db(), ctx.completion.edition).to_string())
+    item.detail(ty.display(ctx.db(), ctx.completion.display_target).to_string())
         .lookup_by(field.to_string());
     item.set_relevance(CompletionRelevance {
         is_skipping_completion: receiver.is_some(),
@@ -303,7 +303,8 @@ pub(crate) fn render_expr(
 
     let cfg = ctx.config.import_path_config(ctx.is_nightly);
 
-    let label = expr.gen_source_code(&ctx.scope, &mut label_formatter, cfg, ctx.edition).ok()?;
+    let label =
+        expr.gen_source_code(&ctx.scope, &mut label_formatter, cfg, ctx.display_target).ok()?;
 
     let source_range = match ctx.original_token.parent() {
         Some(node) => match node.ancestors().find_map(ast::Path::cast) {
@@ -318,7 +319,7 @@ pub(crate) fn render_expr(
 
     let snippet = format!(
         "{}$0",
-        expr.gen_source_code(&ctx.scope, &mut snippet_formatter, cfg, ctx.edition).ok()?
+        expr.gen_source_code(&ctx.scope, &mut snippet_formatter, cfg, ctx.display_target).ok()?
     );
     let edit = TextEdit::replace(source_range, snippet);
     item.snippet_edit(ctx.config.snippet_cap?, edit);
@@ -398,6 +399,8 @@ fn render_resolution_path(
     let _p = tracing::info_span!("render_resolution_path").entered();
     use hir::ModuleDef::*;
 
+    let krate = ctx.completion.display_target;
+
     match resolution {
         ScopeDef::ModuleDef(Macro(mac)) => {
             let ctx = ctx.import_to_add(import_to_add);
@@ -459,7 +462,7 @@ fn render_resolution_path(
 
     let mut set_item_relevance = |ty: Type| {
         if !ty.is_unknown() {
-            item.detail(ty.display(db, completion.edition).to_string());
+            item.detail(ty.display(db, krate).to_string());
         }
 
         item.set_relevance(CompletionRelevance {
@@ -1151,6 +1154,24 @@ fn main() { Foo::Fo$0 }
                         ),
                         lookup: "Foo{}",
                         detail: "Foo { x: i32, y: i32 }",
+                        relevance: CompletionRelevance {
+                            exact_name_match: false,
+                            type_match: None,
+                            is_local: false,
+                            trait_: None,
+                            is_name_already_imported: false,
+                            requires_import: false,
+                            is_private_editable: false,
+                            postfix_match: None,
+                            function: Some(
+                                CompletionRelevanceFn {
+                                    has_params: true,
+                                    has_self_param: false,
+                                    return_type: DirectConstructor,
+                                },
+                            ),
+                            is_skipping_completion: false,
+                        },
                         trigger_call_info: true,
                     },
                 ]
@@ -1183,6 +1204,24 @@ fn main() { Foo::Fo$0 }
                         ),
                         lookup: "Foo()",
                         detail: "Foo(i32, i32)",
+                        relevance: CompletionRelevance {
+                            exact_name_match: false,
+                            type_match: None,
+                            is_local: false,
+                            trait_: None,
+                            is_name_already_imported: false,
+                            requires_import: false,
+                            is_private_editable: false,
+                            postfix_match: None,
+                            function: Some(
+                                CompletionRelevanceFn {
+                                    has_params: true,
+                                    has_self_param: false,
+                                    return_type: DirectConstructor,
+                                },
+                            ),
+                            is_skipping_completion: false,
+                        },
                         trigger_call_info: true,
                     },
                 ]
@@ -1261,6 +1300,24 @@ fn main() { Foo::Fo$0 }
                             Variant,
                         ),
                         detail: "Foo",
+                        relevance: CompletionRelevance {
+                            exact_name_match: false,
+                            type_match: None,
+                            is_local: false,
+                            trait_: None,
+                            is_name_already_imported: false,
+                            requires_import: false,
+                            is_private_editable: false,
+                            postfix_match: None,
+                            function: Some(
+                                CompletionRelevanceFn {
+                                    has_params: false,
+                                    has_self_param: false,
+                                    return_type: DirectConstructor,
+                                },
+                            ),
+                            is_skipping_completion: false,
+                        },
                         trigger_call_info: true,
                     },
                 ]
@@ -1335,7 +1392,13 @@ fn main() { let _: m::Spam = S$0 }
                             requires_import: false,
                             is_private_editable: false,
                             postfix_match: None,
-                            function: None,
+                            function: Some(
+                                CompletionRelevanceFn {
+                                    has_params: true,
+                                    has_self_param: false,
+                                    return_type: DirectConstructor,
+                                },
+                            ),
                             is_skipping_completion: false,
                         },
                         trigger_call_info: true,
@@ -1365,7 +1428,13 @@ fn main() { let _: m::Spam = S$0 }
                             requires_import: false,
                             is_private_editable: false,
                             postfix_match: None,
-                            function: None,
+                            function: Some(
+                                CompletionRelevanceFn {
+                                    has_params: false,
+                                    has_self_param: false,
+                                    return_type: DirectConstructor,
+                                },
+                            ),
                             is_skipping_completion: false,
                         },
                         trigger_call_info: true,
@@ -1590,6 +1659,24 @@ use self::E::*;
                         documentation: Documentation(
                             "variant docs",
                         ),
+                        relevance: CompletionRelevance {
+                            exact_name_match: false,
+                            type_match: None,
+                            is_local: false,
+                            trait_: None,
+                            is_name_already_imported: false,
+                            requires_import: false,
+                            is_private_editable: false,
+                            postfix_match: None,
+                            function: Some(
+                                CompletionRelevanceFn {
+                                    has_params: false,
+                                    has_self_param: false,
+                                    return_type: DirectConstructor,
+                                },
+                            ),
+                            is_skipping_completion: false,
+                        },
                         trigger_call_info: true,
                     },
                     CompletionItem {
@@ -2081,8 +2168,8 @@ fn main() {
 }
             "#,
             expect![[r#"
-                lc ssss S [type+local]
                 st S S [type]
+                lc ssss S [type+local]
                 st S S [type]
                 ex ssss  [type]
                 ex S  [type]
@@ -2153,14 +2240,14 @@ fn main() {
 }
             "#,
             expect![[r#"
+                st S S []
+                st &S [type]
                 ex core::ops::Deref::deref(&t)  [type_could_unify]
                 lc m i32 [local]
                 lc t T [local]
                 lc &t [type+local]
                 st S S []
                 st &S [type]
-                st S S []
-                st &S [type]
                 st T T []
                 st &T [type]
                 fn foo(…) fn(&S) []
@@ -2202,14 +2289,14 @@ fn main() {
 }
             "#,
             expect![[r#"
+                st S S []
+                st &mut S [type]
                 ex core::ops::DerefMut::deref_mut(&mut t)  [type_could_unify]
                 lc m i32 [local]
                 lc t T [local]
                 lc &mut t [type+local]
                 st S S []
                 st &mut S [type]
-                st S S []
-                st &mut S [type]
                 st T T []
                 st &mut T [type]
                 fn foo(…) fn(&mut S) []
@@ -2306,9 +2393,9 @@ fn main() {
 }
 "#,
             expect![[r#"
-                ex core::ops::Deref::deref(&bar())  [type_could_unify]
                 st S S []
                 st &S [type]
+                ex core::ops::Deref::deref(&bar())  [type_could_unify]
                 st S S []
                 st &S [type]
                 st T T []
@@ -2686,10 +2773,12 @@ fn foo(f: Foo) { let _: &u32 = f.b$0 }
                                 Indel {
                                     insert: "(",
                                     delete: 107..107,
+                                    annotation: None,
                                 },
                                 Indel {
                                     insert: "qux)()",
                                     delete: 109..110,
+                                    annotation: None,
                                 },
                             ],
                         },
@@ -2827,11 +2916,11 @@ fn foo() {
 }
 "#,
             expect![[r#"
+                ev Foo::B Foo::B [type_could_unify]
+                ev Foo::A(…) Foo::A(T) [type_could_unify]
                 lc foo Foo<u32> [type+local]
                 ex foo  [type]
                 ex Foo::B  [type]
-                ev Foo::A(…) Foo::A(T) [type_could_unify]
-                ev Foo::B Foo::B [type_could_unify]
                 en Foo Foo<{unknown}> [type_could_unify]
                 fn foo() fn() []
                 fn bar() fn() -> Foo<u8> []
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/render/const_.rs b/src/tools/rust-analyzer/crates/ide-completion/src/render/const_.rs
index e357ab24d22..f11b3023679 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/render/const_.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/render/const_.rs
@@ -16,7 +16,7 @@ fn render(ctx: RenderContext<'_>, const_: hir::Const) -> Option<CompletionItem>
     let name = const_.name(db)?;
     let (name, escaped_name) =
         (name.as_str().to_smolstr(), name.display(db, ctx.completion.edition).to_smolstr());
-    let detail = const_.display(db, ctx.completion.edition).to_string();
+    let detail = const_.display(db, ctx.completion.display_target).to_string();
 
     let mut item =
         CompletionItem::new(SymbolKind::Const, ctx.source_range(), name, ctx.completion.edition);
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 fd90613964a..4693bdc047f 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
@@ -4,7 +4,7 @@ use hir::{db::HirDatabase, AsAssocItem, HirDisplay};
 use ide_db::{SnippetCap, SymbolKind};
 use itertools::Itertools;
 use stdx::{format_to, to_lower_snake_case};
-use syntax::{format_smolstr, AstNode, Edition, SmolStr, ToSmolStr};
+use syntax::{format_smolstr, AstNode, SmolStr, ToSmolStr};
 
 use crate::{
     context::{
@@ -142,9 +142,9 @@ fn render(
     }
 
     let detail = if ctx.completion.config.full_function_signatures {
-        detail_full(db, func, ctx.completion.edition)
+        detail_full(ctx.completion, func)
     } else {
-        detail(ctx.completion, func, ctx.completion.edition)
+        detail(ctx.completion, func)
     };
     item.set_documentation(ctx.docs(func))
         .set_deprecated(ctx.is_deprecated(func) || ctx.is_deprecated_assoc_item(func))
@@ -251,7 +251,7 @@ pub(super) fn add_call_parens<'b>(
                     format!(
                         "{}(${{1:{}}}{}{})$0",
                         escaped_name,
-                        self_param.display(ctx.db, ctx.edition),
+                        self_param.display(ctx.db, ctx.display_target),
                         if params.is_empty() { "" } else { ", " },
                         function_params_snippet
                     )
@@ -307,7 +307,7 @@ fn ref_of_param(ctx: &CompletionContext<'_>, arg: &str, ty: &hir::Type) -> &'sta
     ""
 }
 
-fn detail(ctx: &CompletionContext<'_>, func: hir::Function, edition: Edition) -> String {
+fn detail(ctx: &CompletionContext<'_>, func: hir::Function) -> String {
     let mut ret_ty = func.ret_type(ctx.db);
     let mut detail = String::new();
 
@@ -324,15 +324,15 @@ fn detail(ctx: &CompletionContext<'_>, func: hir::Function, edition: Edition) ->
         format_to!(detail, "unsafe ");
     }
 
-    format_to!(detail, "fn({})", params_display(ctx.db, func, edition));
+    format_to!(detail, "fn({})", params_display(ctx, func));
     if !ret_ty.is_unit() {
-        format_to!(detail, " -> {}", ret_ty.display(ctx.db, edition));
+        format_to!(detail, " -> {}", ret_ty.display(ctx.db, ctx.display_target));
     }
     detail
 }
 
-fn detail_full(db: &dyn HirDatabase, func: hir::Function, edition: Edition) -> String {
-    let signature = format!("{}", func.display(db, edition));
+fn detail_full(ctx: &CompletionContext<'_>, func: hir::Function) -> String {
+    let signature = format!("{}", func.display(ctx.db, ctx.display_target));
     let mut detail = String::with_capacity(signature.len());
 
     for segment in signature.split_whitespace() {
@@ -346,24 +346,24 @@ fn detail_full(db: &dyn HirDatabase, func: hir::Function, edition: Edition) -> S
     detail
 }
 
-fn params_display(db: &dyn HirDatabase, func: hir::Function, edition: Edition) -> String {
-    if let Some(self_param) = func.self_param(db) {
-        let assoc_fn_params = func.assoc_fn_params(db);
+fn params_display(ctx: &CompletionContext<'_>, func: hir::Function) -> String {
+    if let Some(self_param) = func.self_param(ctx.db) {
+        let assoc_fn_params = func.assoc_fn_params(ctx.db);
         let params = assoc_fn_params
             .iter()
             .skip(1) // skip the self param because we are manually handling that
-            .map(|p| p.ty().display(db, edition));
+            .map(|p| p.ty().display(ctx.db, ctx.display_target));
         format!(
             "{}{}",
-            self_param.display(db, edition),
+            self_param.display(ctx.db, ctx.display_target),
             params.format_with("", |display, f| {
                 f(&", ")?;
                 f(&display)
             })
         )
     } else {
-        let assoc_fn_params = func.assoc_fn_params(db);
-        assoc_fn_params.iter().map(|p| p.ty().display(db, edition)).join(", ")
+        let assoc_fn_params = func.assoc_fn_params(ctx.db);
+        assoc_fn_params.iter().map(|p| p.ty().display(ctx.db, ctx.display_target)).join(", ")
     }
 }
 
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/render/literal.rs b/src/tools/rust-analyzer/crates/ide-completion/src/render/literal.rs
index aab54ca5e01..ffda52fb478 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/render/literal.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/render/literal.rs
@@ -8,7 +8,7 @@ use ide_db::{
 
 use crate::{
     context::{CompletionContext, PathCompletionCtx, PathKind},
-    item::{Builder, CompletionItem},
+    item::{Builder, CompletionItem, CompletionRelevanceFn},
     render::{
         compute_type_match,
         variant::{
@@ -17,7 +17,7 @@ use crate::{
         },
         RenderContext,
     },
-    CompletionItemKind, CompletionRelevance,
+    CompletionItemKind, CompletionRelevance, CompletionRelevanceReturnType,
 };
 
 pub(crate) fn render_variant_lit(
@@ -82,10 +82,10 @@ fn render(
 
     let mut rendered = match kind {
         StructKind::Tuple if should_add_parens => {
-            render_tuple_lit(db, snippet_cap, &fields, &escaped_qualified_name, completion.edition)
+            render_tuple_lit(completion, snippet_cap, &fields, &escaped_qualified_name)
         }
         StructKind::Record if should_add_parens => {
-            render_record_lit(db, snippet_cap, &fields, &escaped_qualified_name, completion.edition)
+            render_record_lit(completion, snippet_cap, &fields, &escaped_qualified_name)
         }
         _ => RenderedLiteral {
             literal: escaped_qualified_name.clone(),
@@ -131,6 +131,12 @@ fn render(
     let ty = thing.ty(db);
     item.set_relevance(CompletionRelevance {
         type_match: compute_type_match(ctx.completion, &ty),
+        // function is a misnomer here, this is more about constructor information
+        function: Some(CompletionRelevanceFn {
+            has_params: !fields.is_empty(),
+            has_self_param: false,
+            return_type: CompletionRelevanceReturnType::DirectConstructor,
+        }),
         ..ctx.completion_relevance()
     });
 
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/render/macro_.rs b/src/tools/rust-analyzer/crates/ide-completion/src/render/macro_.rs
index e265e92f979..8b2476d153f 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/render/macro_.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/render/macro_.rs
@@ -62,7 +62,7 @@ fn render(
         completion.edition,
     );
     item.set_deprecated(ctx.is_deprecated(macro_))
-        .detail(macro_.display(completion.db, completion.edition).to_string())
+        .detail(macro_.display(completion.db, completion.display_target).to_string())
         .set_documentation(docs)
         .set_relevance(ctx.completion_relevance());
 
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/render/type_alias.rs b/src/tools/rust-analyzer/crates/ide-completion/src/render/type_alias.rs
index 1b952f31360..d57feee4fa6 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/render/type_alias.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/render/type_alias.rs
@@ -38,7 +38,7 @@ fn render(
     } else {
         (name.as_str().to_smolstr(), name.display_no_db(ctx.completion.edition).to_smolstr())
     };
-    let detail = type_alias.display(db, ctx.completion.edition).to_string();
+    let detail = type_alias.display(db, ctx.completion.display_target).to_string();
 
     let mut item = CompletionItem::new(
         SymbolKind::TypeAlias,
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/render/union_literal.rs b/src/tools/rust-analyzer/crates/ide-completion/src/render/union_literal.rs
index 74203626521..09154e81c03 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/render/union_literal.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/render/union_literal.rs
@@ -88,7 +88,7 @@ pub(crate) fn render_union_literal(
             f(&format_args!(
                 "{}: {}",
                 field.name(ctx.db()).display(ctx.db(), ctx.completion.edition),
-                field.ty(ctx.db()).display(ctx.db(), ctx.completion.edition)
+                field.ty(ctx.db()).display(ctx.db(), ctx.completion.display_target)
             ))
         }),
         if fields_omitted { ", .." } else { "" }
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/render/variant.rs b/src/tools/rust-analyzer/crates/ide-completion/src/render/variant.rs
index d8516ea1078..83718e57229 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/render/variant.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/render/variant.rs
@@ -1,10 +1,10 @@
 //! Code common to structs, unions, and enum variants.
 
 use crate::context::CompletionContext;
-use hir::{db::HirDatabase, sym, HasAttrs, HasCrate, HasVisibility, HirDisplay, StructKind};
+use hir::{sym, HasAttrs, HasCrate, HasVisibility, HirDisplay, StructKind};
 use ide_db::SnippetCap;
 use itertools::Itertools;
-use syntax::{Edition, SmolStr};
+use syntax::SmolStr;
 
 /// A rendered struct, union, or enum variant, split into fields for actual
 /// auto-completion (`literal`, using `field: ()`) and display in the
@@ -17,11 +17,10 @@ pub(crate) struct RenderedLiteral {
 /// Render a record type (or sub-type) to a `RenderedCompound`. Use `None` for
 /// the `name` argument for an anonymous type.
 pub(crate) fn render_record_lit(
-    db: &dyn HirDatabase,
+    ctx: &CompletionContext<'_>,
     snippet_cap: Option<SnippetCap>,
     fields: &[hir::Field],
     path: &str,
-    edition: Edition,
 ) -> RenderedLiteral {
     if snippet_cap.is_none() {
         return RenderedLiteral { literal: path.to_owned(), detail: path.to_owned() };
@@ -30,19 +29,19 @@ pub(crate) fn render_record_lit(
         if snippet_cap.is_some() {
             f(&format_args!(
                 "{}: ${{{}:()}}",
-                field.name(db).display(db.upcast(), edition),
+                field.name(ctx.db).display(ctx.db, ctx.edition),
                 idx + 1
             ))
         } else {
-            f(&format_args!("{}: ()", field.name(db).display(db.upcast(), edition)))
+            f(&format_args!("{}: ()", field.name(ctx.db).display(ctx.db, ctx.edition)))
         }
     });
 
     let types = fields.iter().format_with(", ", |field, f| {
         f(&format_args!(
             "{}: {}",
-            field.name(db).display(db.upcast(), edition),
-            field.ty(db).display(db, edition)
+            field.name(ctx.db).display(ctx.db, ctx.edition),
+            field.ty(ctx.db).display(ctx.db, ctx.display_target)
         ))
     });
 
@@ -55,11 +54,10 @@ pub(crate) fn render_record_lit(
 /// Render a tuple type (or sub-type) to a `RenderedCompound`. Use `None` for
 /// the `name` argument for an anonymous type.
 pub(crate) fn render_tuple_lit(
-    db: &dyn HirDatabase,
+    ctx: &CompletionContext<'_>,
     snippet_cap: Option<SnippetCap>,
     fields: &[hir::Field],
     path: &str,
-    edition: Edition,
 ) -> RenderedLiteral {
     if snippet_cap.is_none() {
         return RenderedLiteral { literal: path.to_owned(), detail: path.to_owned() };
@@ -72,7 +70,9 @@ pub(crate) fn render_tuple_lit(
         }
     });
 
-    let types = fields.iter().format_with(", ", |field, f| f(&field.ty(db).display(db, edition)));
+    let types = fields
+        .iter()
+        .format_with(", ", |field, f| f(&field.ty(ctx.db).display(ctx.db, ctx.display_target)));
 
     RenderedLiteral {
         literal: format!("{path}({completions})"),
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 37557512837..9b3c676c48a 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
@@ -170,6 +170,7 @@ impl Unit {
             kw if let
             kw impl
             kw let
+            kw letm
             kw loop
             kw match
             kw mod
@@ -247,6 +248,7 @@ fn complete_in_block() {
             kw if let
             kw impl
             kw let
+            kw letm
             kw loop
             kw match
             kw mod
@@ -297,6 +299,7 @@ fn complete_after_if_expr() {
             kw if let
             kw impl
             kw let
+            kw letm
             kw loop
             kw match
             kw mod
@@ -370,6 +373,7 @@ fn completes_in_loop_ctx() {
             kw if let
             kw impl
             kw let
+            kw letm
             kw loop
             kw match
             kw mod
@@ -942,6 +946,7 @@ fn foo() { if foo {} $0 }
             kw if let
             kw impl
             kw let
+            kw letm
             kw loop
             kw match
             kw mod
@@ -983,6 +988,7 @@ fn foo() { if foo {} el$0 }
             kw if let
             kw impl
             kw let
+            kw letm
             kw loop
             kw match
             kw mod
@@ -1072,6 +1078,7 @@ fn foo() { if foo {} $0 let x = 92; }
             kw if let
             kw impl
             kw let
+            kw letm
             kw loop
             kw match
             kw mod
@@ -1113,6 +1120,7 @@ fn foo() { if foo {} el$0 let x = 92; }
             kw if let
             kw impl
             kw let
+            kw letm
             kw loop
             kw match
             kw mod
@@ -1154,6 +1162,7 @@ fn foo() { if foo {} el$0 { let x = 92; } }
             kw if let
             kw impl
             kw let
+            kw letm
             kw loop
             kw match
             kw mod
@@ -1205,6 +1214,7 @@ pub struct UnstableThisShouldNotBeListed;
             kw if let
             kw impl
             kw let
+            kw letm
             kw loop
             kw match
             kw mod
@@ -1258,6 +1268,7 @@ pub struct UnstableButWeAreOnNightlyAnyway;
             kw if let
             kw impl
             kw let
+            kw letm
             kw loop
             kw match
             kw mod
@@ -1495,6 +1506,7 @@ fn main() {
             kw if let
             kw impl
             kw let
+            kw letm
             kw loop
             kw match
             kw mod
@@ -1920,6 +1932,7 @@ fn bar() {
             md rust_2015 (use core::prelude::rust_2015)
             md rust_2018 (use core::prelude::rust_2018)
             md rust_2021 (use core::prelude::rust_2021)
+            md rust_2024 (use core::prelude::rust_2024)
             tt Clone
             tt Copy
             tt IntoIterator
@@ -1944,6 +1957,7 @@ fn bar() {
             kw if let
             kw impl
             kw let
+            kw letm
             kw loop
             kw match
             kw mod
@@ -2015,6 +2029,7 @@ fn foo() {
             kw if let
             kw impl
             kw let
+            kw letm
             kw loop
             kw match
             kw mod
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/tests/item.rs b/src/tools/rust-analyzer/crates/ide-completion/src/tests/item.rs
index bea6d60769c..be2c37d1016 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/tests/item.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/tests/item.rs
@@ -285,6 +285,7 @@ fn bar() {
             kw if let
             kw impl
             kw let
+            kw letm
             kw loop
             kw match
             kw mod
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/tests/special.rs b/src/tools/rust-analyzer/crates/ide-completion/src/tests/special.rs
index 2b05184bdbe..005263d100a 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/tests/special.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/tests/special.rs
@@ -1009,6 +1009,7 @@ fn here_we_go() {
             kw if let
             kw impl
             kw let
+            kw letm
             kw loop
             kw match
             kw mod
@@ -1059,6 +1060,7 @@ fn here_we_go() {
             kw if let
             kw impl
             kw let
+            kw letm
             kw loop
             kw match
             kw mod
@@ -1182,6 +1184,7 @@ fn bar() { qu$0 }
             kw if let
             kw impl
             kw let
+            kw letm
             kw loop
             kw match
             kw mod
@@ -1437,6 +1440,7 @@ fn foo() {
             kw if let
             kw impl
             kw let
+            kw letm
             kw loop
             kw match
             kw mod
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 6f71c3d9bd7..502314ed1e0 100644
--- a/src/tools/rust-analyzer/crates/ide-db/src/defs.rs
+++ b/src/tools/rust-analyzer/crates/ide-db/src/defs.rs
@@ -12,11 +12,11 @@ use arrayvec::ArrayVec;
 use either::Either;
 use hir::{
     Adt, AsAssocItem, AsExternAssocItem, AssocItem, AttributeTemplate, BuiltinAttr, BuiltinType,
-    Const, Crate, DefWithBody, DeriveHelper, DocLinkDef, ExternAssocItem, ExternCrateDecl, Field,
-    Function, GenericDef, GenericParam, GenericSubstitution, HasContainer, HasVisibility,
-    HirDisplay, Impl, InlineAsmOperand, ItemContainer, Label, Local, Macro, Module, ModuleDef,
-    Name, PathResolution, Semantics, Static, StaticLifetime, Struct, ToolModule, Trait, TraitAlias,
-    TupleField, TypeAlias, Variant, VariantDef, Visibility,
+    Const, Crate, DefWithBody, DeriveHelper, DisplayTarget, DocLinkDef, ExternAssocItem,
+    ExternCrateDecl, Field, Function, GenericDef, GenericParam, GenericSubstitution, HasContainer,
+    HasVisibility, HirDisplay, Impl, InlineAsmOperand, ItemContainer, Label, Local, Macro, Module,
+    ModuleDef, Name, PathResolution, Semantics, Static, StaticLifetime, Struct, ToolModule, Trait,
+    TraitAlias, TupleField, TypeAlias, Variant, VariantDef, Visibility,
 };
 use span::Edition;
 use stdx::{format_to, impl_from};
@@ -207,7 +207,7 @@ impl Definition {
         &self,
         db: &RootDatabase,
         famous_defs: Option<&FamousDefs<'_, '_>>,
-        edition: Edition,
+        display_target: DisplayTarget,
     ) -> Option<Documentation> {
         let docs = match self {
             Definition::Macro(it) => it.docs(db),
@@ -228,7 +228,7 @@ impl Definition {
                     let docs = adt.docs(db)?;
                     let docs = format!(
                         "*This is the documentation for* `{}`\n\n{}",
-                        adt.display(db, edition),
+                        adt.display(db, display_target),
                         docs.as_str()
                     );
                     Some(Documentation::new(docs))
@@ -237,8 +237,9 @@ impl Definition {
             Definition::BuiltinType(it) => {
                 famous_defs.and_then(|fd| {
                     // std exposes prim_{} modules with docstrings on the root to document the builtins
-                    let primitive_mod = format!("prim_{}", it.name().display(fd.0.db, edition));
-                    let doc_owner = find_std_module(fd, &primitive_mod, edition)?;
+                    let primitive_mod =
+                        format!("prim_{}", it.name().display(fd.0.db, display_target.edition));
+                    let doc_owner = find_std_module(fd, &primitive_mod, display_target.edition)?;
                     doc_owner.docs(fd.0.db)
                 })
             }
@@ -256,16 +257,21 @@ impl Definition {
                 let AttributeTemplate { word, list, name_value_str } = it.template(db)?;
                 let mut docs = "Valid forms are:".to_owned();
                 if word {
-                    format_to!(docs, "\n - #\\[{}]", name.display(db, edition));
+                    format_to!(docs, "\n - #\\[{}]", name.display(db, display_target.edition));
                 }
                 if let Some(list) = list {
-                    format_to!(docs, "\n - #\\[{}({})]", name.display(db, edition), list);
+                    format_to!(
+                        docs,
+                        "\n - #\\[{}({})]",
+                        name.display(db, display_target.edition),
+                        list
+                    );
                 }
                 if let Some(name_value_str) = name_value_str {
                     format_to!(
                         docs,
                         "\n - #\\[{} = {}]",
-                        name.display(db, edition),
+                        name.display(db, display_target.edition),
                         name_value_str
                     );
                 }
@@ -288,49 +294,60 @@ impl Definition {
         })
     }
 
-    pub fn label(&self, db: &RootDatabase, edition: Edition) -> String {
+    pub fn label(&self, db: &RootDatabase, display_target: DisplayTarget) -> String {
         match *self {
-            Definition::Macro(it) => it.display(db, edition).to_string(),
-            Definition::Field(it) => it.display(db, edition).to_string(),
-            Definition::TupleField(it) => it.display(db, edition).to_string(),
-            Definition::Module(it) => it.display(db, edition).to_string(),
-            Definition::Crate(it) => it.display(db, edition).to_string(),
-            Definition::Function(it) => it.display(db, edition).to_string(),
-            Definition::Adt(it) => it.display(db, edition).to_string(),
-            Definition::Variant(it) => it.display(db, edition).to_string(),
-            Definition::Const(it) => it.display(db, edition).to_string(),
-            Definition::Static(it) => it.display(db, edition).to_string(),
-            Definition::Trait(it) => it.display(db, edition).to_string(),
-            Definition::TraitAlias(it) => it.display(db, edition).to_string(),
-            Definition::TypeAlias(it) => it.display(db, edition).to_string(),
-            Definition::BuiltinType(it) => it.name().display(db, edition).to_string(),
-            Definition::BuiltinLifetime(it) => it.name().display(db, edition).to_string(),
+            Definition::Macro(it) => it.display(db, display_target).to_string(),
+            Definition::Field(it) => it.display(db, display_target).to_string(),
+            Definition::TupleField(it) => it.display(db, display_target).to_string(),
+            Definition::Module(it) => it.display(db, display_target).to_string(),
+            Definition::Crate(it) => it.display(db, display_target).to_string(),
+            Definition::Function(it) => it.display(db, display_target).to_string(),
+            Definition::Adt(it) => it.display(db, display_target).to_string(),
+            Definition::Variant(it) => it.display(db, display_target).to_string(),
+            Definition::Const(it) => it.display(db, display_target).to_string(),
+            Definition::Static(it) => it.display(db, display_target).to_string(),
+            Definition::Trait(it) => it.display(db, display_target).to_string(),
+            Definition::TraitAlias(it) => it.display(db, display_target).to_string(),
+            Definition::TypeAlias(it) => it.display(db, display_target).to_string(),
+            Definition::BuiltinType(it) => {
+                it.name().display(db, display_target.edition).to_string()
+            }
+            Definition::BuiltinLifetime(it) => {
+                it.name().display(db, display_target.edition).to_string()
+            }
             Definition::Local(it) => {
                 let ty = it.ty(db);
-                let ty_display = ty.display_truncated(db, None, edition);
+                let ty_display = ty.display_truncated(db, None, display_target);
                 let is_mut = if it.is_mut(db) { "mut " } else { "" };
                 if it.is_self(db) {
                     format!("{is_mut}self: {ty_display}")
                 } else {
                     let name = it.name(db);
                     let let_kw = if it.is_param(db) { "" } else { "let " };
-                    format!("{let_kw}{is_mut}{}: {ty_display}", name.display(db, edition))
+                    format!(
+                        "{let_kw}{is_mut}{}: {ty_display}",
+                        name.display(db, display_target.edition)
+                    )
                 }
             }
             Definition::SelfType(impl_def) => {
                 let self_ty = &impl_def.self_ty(db);
                 match self_ty.as_adt() {
-                    Some(it) => it.display(db, edition).to_string(),
-                    None => self_ty.display(db, edition).to_string(),
+                    Some(it) => it.display(db, display_target).to_string(),
+                    None => self_ty.display(db, display_target).to_string(),
                 }
             }
-            Definition::GenericParam(it) => it.display(db, edition).to_string(),
-            Definition::Label(it) => it.name(db).display(db, edition).to_string(),
-            Definition::ExternCrateDecl(it) => it.display(db, edition).to_string(),
-            Definition::BuiltinAttr(it) => format!("#[{}]", it.name(db).display(db, edition)),
-            Definition::ToolModule(it) => it.name(db).display(db, edition).to_string(),
+            Definition::GenericParam(it) => it.display(db, display_target).to_string(),
+            Definition::Label(it) => it.name(db).display(db, display_target.edition).to_string(),
+            Definition::ExternCrateDecl(it) => it.display(db, display_target).to_string(),
+            Definition::BuiltinAttr(it) => {
+                format!("#[{}]", it.name(db).display(db, display_target.edition))
+            }
+            Definition::ToolModule(it) => {
+                it.name(db).display(db, display_target.edition).to_string()
+            }
             Definition::DeriveHelper(it) => {
-                format!("derive_helper {}", it.name(db).display(db, edition))
+                format!("derive_helper {}", it.name(db).display(db, display_target.edition))
             }
             // FIXME
             Definition::InlineAsmRegOrRegClass(_) => "inline_asm_reg_or_reg_class".to_owned(),
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 9c983e7c4a6..0a7a7d1fb24 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
@@ -15052,7 +15052,7 @@ cannot be represented as the underlying type without loss."##,
     },
     Lint {
         label: "clippy::manual_bits",
-        description: r##"Checks for usage of `std::mem::size_of::<T>() * 8` when
+        description: r##"Checks for usage of `size_of::<T>() * 8` when
 `T::BITS` is available."##,
         default_severity: Severity::Allow,
         warn_since: None,
@@ -17394,7 +17394,7 @@ count of elements of type `T`"##,
     },
     Lint {
         label: "clippy::size_of_ref",
-        description: r##"Checks for calls to `std::mem::size_of_val()` where the argument is
+        description: r##"Checks for calls to `size_of_val()` where the argument is
 a reference to a reference."##,
         default_severity: Severity::Allow,
         warn_since: None,
diff --git a/src/tools/rust-analyzer/crates/ide-db/src/path_transform.rs b/src/tools/rust-analyzer/crates/ide-db/src/path_transform.rs
index 126b30470b7..a348a4ef7d3 100644
--- a/src/tools/rust-analyzer/crates/ide-db/src/path_transform.rs
+++ b/src/tools/rust-analyzer/crates/ide-db/src/path_transform.rs
@@ -192,7 +192,9 @@ impl<'a> PathTransform<'a> {
                     }
                 }
                 (Either::Left(k), None) => {
-                    if let Some(default) = k.default(db, target_edition) {
+                    if let Some(default) =
+                        k.default(db, target_module.krate().to_display_target(db))
+                    {
                         if let Some(default) = default.expr() {
                             const_substs.insert(k, default.syntax().clone_for_update());
                             defaulted_params.push(Either::Right(k));
diff --git a/src/tools/rust-analyzer/crates/ide-db/src/rename.rs b/src/tools/rust-analyzer/crates/ide-db/src/rename.rs
index 59914bedde4..1633065f652 100644
--- a/src/tools/rust-analyzer/crates/ide-db/src/rename.rs
+++ b/src/tools/rust-analyzer/crates/ide-db/src/rename.rs
@@ -22,7 +22,10 @@
 //! Our current behavior is ¯\_(ツ)_/¯.
 use std::fmt;
 
-use crate::text_edit::{TextEdit, TextEditBuilder};
+use crate::{
+    source_change::ChangeAnnotation,
+    text_edit::{TextEdit, TextEditBuilder},
+};
 use base_db::AnchoredPathBuf;
 use either::Either;
 use hir::{FieldSource, FileRange, HirFileIdExt, InFile, ModuleSource, Semantics};
@@ -365,10 +368,12 @@ fn rename_reference(
     }));
 
     let mut insert_def_edit = |def| {
-        let (file_id, edit) = source_edit_from_def(sema, def, new_name)?;
+        let (file_id, edit) = source_edit_from_def(sema, def, new_name, &mut source_change)?;
         source_change.insert_source_edit(file_id, edit);
         Ok(())
     };
+    // This needs to come after the references edits, because we change the annotation of existing edits
+    // if a conflict is detected.
     insert_def_edit(def)?;
     Ok(source_change)
 }
@@ -537,6 +542,7 @@ fn source_edit_from_def(
     sema: &Semantics<'_, RootDatabase>,
     def: Definition,
     new_name: &str,
+    source_change: &mut SourceChange,
 ) -> Result<(FileId, TextEdit)> {
     let new_name_edition_aware = |new_name: &str, file_id: EditionedFileId| {
         if is_raw_identifier(new_name, file_id.edition()) {
@@ -548,6 +554,23 @@ fn source_edit_from_def(
     let mut edit = TextEdit::builder();
     if let Definition::Local(local) = def {
         let mut file_id = None;
+
+        let conflict_annotation = if !sema.rename_conflicts(&local, new_name).is_empty() {
+            Some(
+                source_change.insert_annotation(ChangeAnnotation {
+                    label: "This rename will change the program's meaning".to_owned(),
+                    needs_confirmation: true,
+                    description: Some(
+                        "Some variable(s) will shadow the renamed variable \
+                        or be shadowed by it if the rename is performed"
+                            .to_owned(),
+                    ),
+                }),
+            )
+        } else {
+            None
+        };
+
         for source in local.sources(sema.db) {
             let source = match source.source.clone().original_ast_node_rooted(sema.db) {
                 Some(source) => source,
@@ -611,8 +634,15 @@ fn source_edit_from_def(
                 }
             }
         }
+        let mut edit = edit.finish();
+
+        for (edit, _) in source_change.source_file_edits.values_mut() {
+            edit.set_annotation(conflict_annotation);
+        }
+        edit.set_annotation(conflict_annotation);
+
         let Some(file_id) = file_id else { bail!("No file available to rename") };
-        return Ok((EditionedFileId::file_id(file_id), edit.finish()));
+        return Ok((EditionedFileId::file_id(file_id), edit));
     }
     let FileRange { file_id, range } = def
         .range_for_rename(sema)
diff --git a/src/tools/rust-analyzer/crates/ide-db/src/source_change.rs b/src/tools/rust-analyzer/crates/ide-db/src/source_change.rs
index 34642d7eaf9..b4d0b0dc9f0 100644
--- a/src/tools/rust-analyzer/crates/ide-db/src/source_change.rs
+++ b/src/tools/rust-analyzer/crates/ide-db/src/source_change.rs
@@ -3,7 +3,7 @@
 //!
 //! It can be viewed as a dual for `Change`.
 
-use std::{collections::hash_map::Entry, iter, mem};
+use std::{collections::hash_map::Entry, fmt, iter, mem};
 
 use crate::text_edit::{TextEdit, TextEditBuilder};
 use crate::{assists::Command, syntax_helpers::tree_diff::diff, SnippetCap};
@@ -18,23 +18,33 @@ use syntax::{
     AstNode, SyntaxElement, SyntaxNode, SyntaxNodePtr, SyntaxToken, TextRange, TextSize,
 };
 
+/// An annotation ID associated with an indel, to describe changes.
+#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
+pub struct ChangeAnnotationId(u32);
+
+impl fmt::Display for ChangeAnnotationId {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        fmt::Display::fmt(&self.0, f)
+    }
+}
+
+#[derive(Debug, Clone)]
+pub struct ChangeAnnotation {
+    pub label: String,
+    pub needs_confirmation: bool,
+    pub description: Option<String>,
+}
+
 #[derive(Default, Debug, Clone)]
 pub struct SourceChange {
     pub source_file_edits: IntMap<FileId, (TextEdit, Option<SnippetEdit>)>,
     pub file_system_edits: Vec<FileSystemEdit>,
     pub is_snippet: bool,
+    pub annotations: FxHashMap<ChangeAnnotationId, ChangeAnnotation>,
+    next_annotation_id: u32,
 }
 
 impl SourceChange {
-    /// Creates a new SourceChange with the given label
-    /// from the edits.
-    pub fn from_edits(
-        source_file_edits: IntMap<FileId, (TextEdit, Option<SnippetEdit>)>,
-        file_system_edits: Vec<FileSystemEdit>,
-    ) -> Self {
-        SourceChange { source_file_edits, file_system_edits, is_snippet: false }
-    }
-
     pub fn from_text_edit(file_id: impl Into<FileId>, edit: TextEdit) -> Self {
         SourceChange {
             source_file_edits: iter::once((file_id.into(), (edit, None))).collect(),
@@ -42,6 +52,13 @@ impl SourceChange {
         }
     }
 
+    pub fn insert_annotation(&mut self, annotation: ChangeAnnotation) -> ChangeAnnotationId {
+        let id = ChangeAnnotationId(self.next_annotation_id);
+        self.next_annotation_id += 1;
+        self.annotations.insert(id, annotation);
+        id
+    }
+
     /// Inserts a [`TextEdit`] for the given [`FileId`]. This properly handles merging existing
     /// edits for a file if some already exist.
     pub fn insert_source_edit(&mut self, file_id: impl Into<FileId>, edit: TextEdit) {
@@ -120,7 +137,12 @@ impl From<IntMap<FileId, TextEdit>> for SourceChange {
     fn from(source_file_edits: IntMap<FileId, TextEdit>) -> SourceChange {
         let source_file_edits =
             source_file_edits.into_iter().map(|(file_id, edit)| (file_id, (edit, None))).collect();
-        SourceChange { source_file_edits, file_system_edits: Vec::new(), is_snippet: false }
+        SourceChange {
+            source_file_edits,
+            file_system_edits: Vec::new(),
+            is_snippet: false,
+            ..SourceChange::default()
+        }
     }
 }
 
@@ -482,6 +504,7 @@ impl From<FileSystemEdit> for SourceChange {
             source_file_edits: Default::default(),
             file_system_edits: vec![edit],
             is_snippet: false,
+            ..SourceChange::default()
         }
     }
 }
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 bb4c289c908..2737436993d 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
@@ -24,7 +24,6 @@ use std::{
     cmp::Ordering,
     fmt,
     hash::{Hash, Hasher},
-    mem,
     ops::ControlFlow,
 };
 
@@ -299,7 +298,7 @@ impl SymbolIndex {
     }
 
     pub fn memory_size(&self) -> usize {
-        self.map.as_fst().size() + self.symbols.len() * mem::size_of::<FileSymbol>()
+        self.map.as_fst().size() + self.symbols.len() * size_of::<FileSymbol>()
     }
 
     fn range_to_map_value(start: usize, end: usize) -> u64 {
diff --git a/src/tools/rust-analyzer/crates/ide-db/src/syntax_helpers/node_ext.rs b/src/tools/rust-analyzer/crates/ide-db/src/syntax_helpers/node_ext.rs
index 74c0b8e2baa..0b2e8aa6836 100644
--- a/src/tools/rust-analyzer/crates/ide-db/src/syntax_helpers/node_ext.rs
+++ b/src/tools/rust-analyzer/crates/ide-db/src/syntax_helpers/node_ext.rs
@@ -1,4 +1,6 @@
 //! Various helper functions to work with SyntaxNodes.
+use std::ops::ControlFlow;
+
 use itertools::Itertools;
 use parser::T;
 use span::Edition;
@@ -119,7 +121,10 @@ pub fn walk_patterns_in_expr(start: &ast::Expr, cb: &mut dyn FnMut(ast::Pat)) {
         match ast::Stmt::cast(node.clone()) {
             Some(ast::Stmt::LetStmt(l)) => {
                 if let Some(pat) = l.pat() {
-                    walk_pat(&pat, cb);
+                    walk_pat(&pat, &mut |pat| {
+                        cb(pat);
+                        ControlFlow::<(), ()>::Continue(())
+                    });
                 }
                 if let Some(expr) = l.initializer() {
                     walk_patterns_in_expr(&expr, cb);
@@ -154,7 +159,10 @@ pub fn walk_patterns_in_expr(start: &ast::Expr, cb: &mut dyn FnMut(ast::Pat)) {
                     }
                 } else if let Some(pat) = ast::Pat::cast(node) {
                     preorder.skip_subtree();
-                    walk_pat(&pat, cb);
+                    walk_pat(&pat, &mut |pat| {
+                        cb(pat);
+                        ControlFlow::<(), ()>::Continue(())
+                    });
                 }
             }
         }
@@ -162,7 +170,10 @@ pub fn walk_patterns_in_expr(start: &ast::Expr, cb: &mut dyn FnMut(ast::Pat)) {
 }
 
 /// Preorder walk all the pattern's sub patterns.
-pub fn walk_pat(pat: &ast::Pat, cb: &mut dyn FnMut(ast::Pat)) {
+pub fn walk_pat<T>(
+    pat: &ast::Pat,
+    cb: &mut dyn FnMut(ast::Pat) -> ControlFlow<T>,
+) -> ControlFlow<T> {
     let mut preorder = pat.syntax().preorder();
     while let Some(event) = preorder.next() {
         let node = match event {
@@ -173,10 +184,10 @@ pub fn walk_pat(pat: &ast::Pat, cb: &mut dyn FnMut(ast::Pat)) {
         match ast::Pat::cast(node) {
             Some(pat @ ast::Pat::ConstBlockPat(_)) => {
                 preorder.skip_subtree();
-                cb(pat);
+                cb(pat)?;
             }
             Some(pat) => {
-                cb(pat);
+                cb(pat)?;
             }
             // skip const args
             None if ast::GenericArg::can_cast(kind) => {
@@ -185,6 +196,7 @@ pub fn walk_pat(pat: &ast::Pat, cb: &mut dyn FnMut(ast::Pat)) {
             None => (),
         }
     }
+    ControlFlow::Continue(())
 }
 
 /// Preorder walk all the type's sub types.
diff --git a/src/tools/rust-analyzer/crates/ide-db/src/text_edit.rs b/src/tools/rust-analyzer/crates/ide-db/src/text_edit.rs
index 0c675f0619f..b59010f2f8c 100644
--- a/src/tools/rust-analyzer/crates/ide-db/src/text_edit.rs
+++ b/src/tools/rust-analyzer/crates/ide-db/src/text_edit.rs
@@ -8,6 +8,8 @@ use itertools::Itertools;
 pub use span::{TextRange, TextSize};
 use std::cmp::max;
 
+use crate::source_change::ChangeAnnotationId;
+
 /// `InsertDelete` -- a single "atomic" change to text
 ///
 /// Must not overlap with other `InDel`s
@@ -16,6 +18,7 @@ pub struct Indel {
     pub insert: String,
     /// Refers to offsets in the original text
     pub delete: TextRange,
+    pub annotation: Option<ChangeAnnotationId>,
 }
 
 #[derive(Default, Debug, Clone)]
@@ -37,7 +40,7 @@ impl Indel {
         Indel::replace(range, String::new())
     }
     pub fn replace(range: TextRange, replace_with: String) -> Indel {
-        Indel { delete: range, insert: replace_with }
+        Indel { delete: range, insert: replace_with, annotation: None }
     }
 
     pub fn apply(&self, text: &mut String) {
@@ -138,6 +141,14 @@ impl TextEdit {
         }
         Some(res)
     }
+
+    pub fn set_annotation(&mut self, annotation: Option<ChangeAnnotationId>) {
+        if annotation.is_some() {
+            for indel in &mut self.indels {
+                indel.annotation = annotation;
+            }
+        }
+    }
 }
 
 impl IntoIterator for TextEdit {
@@ -180,7 +191,7 @@ impl TextEditBuilder {
     pub fn invalidates_offset(&self, offset: TextSize) -> bool {
         self.indels.iter().any(|indel| indel.delete.contains_inclusive(offset))
     }
-    fn indel(&mut self, indel: Indel) {
+    pub fn indel(&mut self, indel: Indel) {
         self.indels.push(indel);
         if self.indels.len() <= 16 {
             assert_disjoint_or_equal(&mut self.indels);
diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/expected_function.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/expected_function.rs
index e3a1e12e029..af25c2b2e33 100644
--- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/expected_function.rs
+++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/expected_function.rs
@@ -12,7 +12,7 @@ pub(crate) fn expected_function(
     Diagnostic::new_with_syntax_node_ptr(
         ctx,
         DiagnosticCode::RustcHardError("E0618"),
-        format!("expected function, found {}", d.found.display(ctx.sema.db, ctx.edition)),
+        format!("expected function, found {}", d.found.display(ctx.sema.db, ctx.display_target)),
         d.call.map(|it| it.into()),
     )
     .experimental()
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 5730508436d..82cd1f2fde6 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
@@ -8,7 +8,7 @@ macro_rules! format_ty {
             $fmt,
             $(
                 $arg
-                    .display($ctx.sema.db, $ctx.edition)
+                    .display($ctx.sema.db, $ctx.display_target)
                     .with_closure_style(ClosureStyle::ClosureWithId)
             ),*
         )
diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/moved_out_of_ref.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/moved_out_of_ref.rs
index a9ff06fb0ab..7d0f10983d7 100644
--- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/moved_out_of_ref.rs
+++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/moved_out_of_ref.rs
@@ -8,7 +8,7 @@ pub(crate) fn moved_out_of_ref(ctx: &DiagnosticsContext<'_>, d: &hir::MovedOutOf
     Diagnostic::new_with_syntax_node_ptr(
         ctx,
         DiagnosticCode::RustcHardError("E0507"),
-        format!("cannot move `{}` out of reference", d.ty.display(ctx.sema.db, ctx.edition)),
+        format!("cannot move `{}` out of reference", d.ty.display(ctx.sema.db, ctx.display_target)),
         d.span,
     )
     .experimental() // spans are broken, and I'm not sure how precise we can detect copy types
diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/trait_impl_redundant_assoc_item.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/trait_impl_redundant_assoc_item.rs
index 1363a8ff0dd..3db2e013a39 100644
--- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/trait_impl_redundant_assoc_item.rs
+++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/trait_impl_redundant_assoc_item.rs
@@ -30,7 +30,7 @@ pub(crate) fn trait_impl_redundant_assoc_item(
             (
                 format!("`fn {redundant_assoc_item_name}`"),
                 function.source(db).map(|it| it.syntax().text_range()).unwrap_or(default_range),
-                format!("\n    {};", function.display(db, ctx.edition)),
+                format!("\n    {};", function.display(db, ctx.display_target)),
             )
         }
         hir::AssocItem::Const(id) => {
@@ -38,7 +38,7 @@ pub(crate) fn trait_impl_redundant_assoc_item(
             (
                 format!("`const {redundant_assoc_item_name}`"),
                 constant.source(db).map(|it| it.syntax().text_range()).unwrap_or(default_range),
-                format!("\n    {};", constant.display(db, ctx.edition)),
+                format!("\n    {};", constant.display(db, ctx.display_target)),
             )
         }
         hir::AssocItem::TypeAlias(id) => {
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 7cf8282d052..c726a3bcd3c 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
@@ -45,10 +45,10 @@ pub(crate) fn type_mismatch(ctx: &DiagnosticsContext<'_>, d: &hir::TypeMismatch)
         format!(
             "expected {}, found {}",
             d.expected
-                .display(ctx.sema.db, ctx.edition)
+                .display(ctx.sema.db, ctx.display_target)
                 .with_closure_style(ClosureStyle::ClosureWithId),
             d.actual
-                .display(ctx.sema.db, ctx.edition)
+                .display(ctx.sema.db, ctx.display_target)
                 .with_closure_style(ClosureStyle::ClosureWithId),
         ),
         display_range,
@@ -306,8 +306,8 @@ fn str_ref_to_owned(
     expr_ptr: &InFile<AstPtr<ast::Expr>>,
     acc: &mut Vec<Assist>,
 ) -> Option<()> {
-    let expected = d.expected.display(ctx.sema.db, ctx.edition);
-    let actual = d.actual.display(ctx.sema.db, ctx.edition);
+    let expected = d.expected.display(ctx.sema.db, ctx.display_target);
+    let actual = d.actual.display(ctx.sema.db, ctx.display_target);
 
     // FIXME do this properly
     if expected.to_string() != "String" || actual.to_string() != "&str" {
diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/typed_hole.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/typed_hole.rs
index b023a95fb35..c25318eda48 100644
--- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/typed_hole.rs
+++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/typed_hole.rs
@@ -27,7 +27,7 @@ pub(crate) fn typed_hole(ctx: &DiagnosticsContext<'_>, d: &hir::TypedHole) -> Di
             format!(
                 "invalid `_` expression, expected type `{}`",
                 d.expected
-                    .display(ctx.sema.db, ctx.edition)
+                    .display(ctx.sema.db, ctx.display_target)
                     .with_closure_style(ClosureStyle::ClosureWithId),
             ),
             fixes(ctx, d),
@@ -72,7 +72,7 @@ fn fixes(ctx: &DiagnosticsContext<'_>, d: &hir::TypedHole) -> Option<Vec<Assist>
                     prefer_absolute: ctx.config.prefer_absolute,
                     allow_unstable: ctx.is_nightly,
                 },
-                ctx.edition,
+                ctx.display_target,
             )
             .ok()
         })
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 dfb03eee732..6ab713a5896 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
@@ -38,7 +38,7 @@ pub(crate) fn unresolved_field(
         format!(
             "no field `{}` on type `{}`{method_suffix}",
             d.name.display(ctx.sema.db, ctx.edition),
-            d.receiver.display(ctx.sema.db, ctx.edition)
+            d.receiver.display(ctx.sema.db, ctx.display_target)
         ),
         adjusted_display_range(ctx, d.expr, &|expr| {
             Some(
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 e4de107249b..35e7521af70 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
@@ -31,7 +31,7 @@ pub(crate) fn unresolved_method(
         format!(
             "no method `{}` on type `{}`{suffix}",
             d.name.display(ctx.sema.db, ctx.edition),
-            d.receiver.display(ctx.sema.db, ctx.edition)
+            d.receiver.display(ctx.sema.db, ctx.display_target)
         ),
         adjusted_display_range(ctx, d.expr, &|expr| {
             Some(
@@ -152,7 +152,7 @@ fn assoc_func_fix(ctx: &DiagnosticsContext<'_>, d: &hir::UnresolvedMethodCall) -
             receiver_type.as_adt()?.name(db).display_no_db(ctx.edition).to_smolstr();
 
         let generic_parameters: Vec<SmolStr> =
-            receiver_type.generic_parameters(db, ctx.edition).collect();
+            receiver_type.generic_parameters(db, ctx.display_target).collect();
         // if receiver should be pass as first arg in the assoc func,
         // we could omit generic parameters cause compiler can deduce it automatically
         if !need_to_take_receiver_as_first_arg && !generic_parameters.is_empty() {
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 0a55b6e9bee..e15d3495789 100644
--- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/lib.rs
+++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/lib.rs
@@ -81,7 +81,10 @@ mod tests;
 use std::{collections::hash_map, iter, sync::LazyLock};
 
 use either::Either;
-use hir::{db::ExpandDatabase, diagnostics::AnyDiagnostic, Crate, HirFileId, InFile, Semantics};
+use hir::{
+    db::ExpandDatabase, diagnostics::AnyDiagnostic, Crate, DisplayTarget, HirFileId, InFile,
+    Semantics,
+};
 use ide_db::{
     assists::{Assist, AssistId, AssistKind, AssistResolveStrategy},
     base_db::{ReleaseChannel, SourceDatabase},
@@ -277,6 +280,7 @@ struct DiagnosticsContext<'a> {
     sema: Semantics<'a, RootDatabase>,
     resolve: &'a AssistResolveStrategy,
     edition: Edition,
+    display_target: DisplayTarget,
     is_nightly: bool,
 }
 
@@ -374,7 +378,18 @@ pub fn semantic_diagnostics(
         module.and_then(|m| db.toolchain_channel(m.krate().into())),
         Some(ReleaseChannel::Nightly) | None
     );
-    let ctx = DiagnosticsContext { config, sema, resolve, edition: file_id.edition(), is_nightly };
+    let krate = module.map(|module| module.krate()).unwrap_or_else(|| {
+        (*db.crate_graph().crates_in_topological_order().last().unwrap()).into()
+    });
+    let display_target = krate.to_display_target(db);
+    let ctx = DiagnosticsContext {
+        config,
+        sema,
+        resolve,
+        edition: file_id.edition(),
+        is_nightly,
+        display_target,
+    };
 
     let mut diags = Vec::new();
     match module {
diff --git a/src/tools/rust-analyzer/crates/ide-ssr/src/matching.rs b/src/tools/rust-analyzer/crates/ide-ssr/src/matching.rs
index 4bead14e31d..e219ba4bf63 100644
--- a/src/tools/rust-analyzer/crates/ide-ssr/src/matching.rs
+++ b/src/tools/rust-analyzer/crates/ide-ssr/src/matching.rs
@@ -7,8 +7,7 @@ use crate::{
     SsrMatches,
 };
 use hir::{FileRange, ImportPathConfig, Semantics};
-use ide_db::FxHashMap;
-use parser::Edition;
+use ide_db::{base_db::SourceDatabase, FxHashMap};
 use std::{cell::Cell, iter::Peekable};
 use syntax::{
     ast::{self, AstNode, AstToken, HasGenericArgs},
@@ -627,22 +626,23 @@ impl<'db, 'sema> Matcher<'db, 'sema> {
                 match_error!("Failed to get receiver type for `{}`", expr.syntax().text())
             })?
             .original;
-        let edition = self
-            .sema
-            .scope(expr.syntax())
-            .map(|it| it.krate().edition(self.sema.db))
-            .unwrap_or(Edition::CURRENT);
-        // Temporary needed to make the borrow checker happy.
+        let krate = self.sema.scope(expr.syntax()).map(|it| it.krate()).unwrap_or_else(|| {
+            hir::Crate::from(
+                *self.sema.db.crate_graph().crates_in_topological_order().last().unwrap(),
+            )
+        });
         let res = code_type
             .autoderef(self.sema.db)
             .enumerate()
             .find(|(_, deref_code_type)| pattern_type == deref_code_type)
             .map(|(count, _)| count)
             .ok_or_else(|| {
+                let display_target = krate.to_display_target(self.sema.db);
+                // Temporary needed to make the borrow checker happy.
                 match_error!(
                     "Pattern type `{}` didn't match code type `{}`",
-                    pattern_type.display(self.sema.db, edition),
-                    code_type.display(self.sema.db, edition)
+                    pattern_type.display(self.sema.db, display_target),
+                    code_type.display(self.sema.db, display_target)
                 )
             });
         res
diff --git a/src/tools/rust-analyzer/crates/ide/src/hover.rs b/src/tools/rust-analyzer/crates/ide/src/hover.rs
index 9a3e77f3a93..b00de6ba408 100644
--- a/src/tools/rust-analyzer/crates/ide/src/hover.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/hover.rs
@@ -7,7 +7,8 @@ use std::{iter, ops::Not};
 
 use either::Either;
 use hir::{
-    db::DefDatabase, GenericDef, GenericSubstitution, HasCrate, HasSource, LangItem, Semantics,
+    db::DefDatabase, DisplayTarget, GenericDef, GenericSubstitution, HasCrate, HasSource, LangItem,
+    Semantics,
 };
 use ide_db::{
     defs::{Definition, IdentClass, NameRefClass, OperatorClass},
@@ -129,10 +130,18 @@ pub(crate) fn hover(
     let file = 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 display_target = sema.first_crate_or_default(file_id).to_display_target(db);
     let mut res = if range.is_empty() {
-        hover_offset(sema, FilePosition { file_id, offset: range.start() }, file, config, edition)
+        hover_offset(
+            sema,
+            FilePosition { file_id, offset: range.start() },
+            file,
+            config,
+            edition,
+            display_target,
+        )
     } else {
-        hover_ranged(sema, frange, file, config, edition)
+        hover_ranged(sema, frange, file, config, edition, display_target)
     }?;
 
     if let HoverDocFormat::PlainText = config.format {
@@ -148,6 +157,7 @@ fn hover_offset(
     file: SyntaxNode,
     config: &HoverConfig,
     edition: Edition,
+    display_target: DisplayTarget,
 ) -> Option<RangeInfo<HoverResult>> {
     let original_token = pick_best_token(file.token_at_offset(offset), |kind| match kind {
         IDENT
@@ -169,8 +179,18 @@ fn hover_offset(
     if let Some(doc_comment) = token_as_doc_comment(&original_token) {
         cov_mark::hit!(no_highlight_on_comment_hover);
         return doc_comment.get_definition_with_descend_at(sema, offset, |def, node, range| {
-            let res =
-                hover_for_definition(sema, file_id, def, None, &node, None, false, config, edition);
+            let res = hover_for_definition(
+                sema,
+                file_id,
+                def,
+                None,
+                &node,
+                None,
+                false,
+                config,
+                edition,
+                display_target,
+            );
             Some(RangeInfo::new(range, res))
         });
     }
@@ -188,6 +208,7 @@ fn hover_offset(
             false,
             config,
             edition,
+            display_target,
         );
         return Some(RangeInfo::new(range, res));
     }
@@ -277,6 +298,7 @@ fn hover_offset(
                         hovered_definition,
                         config,
                         edition,
+                        display_target,
                     )
                 })
                 .collect::<Vec<_>>(),
@@ -286,12 +308,12 @@ fn hover_offset(
             res.extend(definitions);
             continue;
         }
-        let keywords = || render::keyword(sema, config, &token, edition);
+        let keywords = || render::keyword(sema, config, &token, edition, display_target);
         let underscore = || {
             if !is_same_kind {
                 return None;
             }
-            render::underscore(sema, config, &token, edition)
+            render::underscore(sema, config, &token, edition, display_target)
         };
         let rest_pat = || {
             if !is_same_kind || token.kind() != DOT2 {
@@ -305,7 +327,7 @@ fn hover_offset(
             let record_pat =
                 record_pat_field_list.syntax().parent().and_then(ast::RecordPat::cast)?;
 
-            Some(render::struct_rest_pat(sema, config, &record_pat, edition))
+            Some(render::struct_rest_pat(sema, config, &record_pat, edition, display_target))
         };
         let call = || {
             if !is_same_kind || token.kind() != T!['('] && token.kind() != T![')'] {
@@ -319,17 +341,17 @@ fn hover_offset(
                     _ => return None,
                 }
             };
-            render::type_info_of(sema, config, &Either::Left(call_expr), edition)
+            render::type_info_of(sema, config, &Either::Left(call_expr), edition, display_target)
         };
         let closure = || {
             if !is_same_kind || token.kind() != T![|] {
                 return None;
             }
             let c = token.parent().and_then(|x| x.parent()).and_then(ast::ClosureExpr::cast)?;
-            render::closure_expr(sema, config, c, edition)
+            render::closure_expr(sema, config, c, edition, display_target)
         };
         let literal = || {
-            render::literal(sema, original_token.clone(), edition)
+            render::literal(sema, original_token.clone(), display_target)
                 .map(|markup| HoverResult { markup, actions: vec![] })
         };
         if let Some(result) = keywords()
@@ -362,6 +384,7 @@ fn hover_ranged(
     file: SyntaxNode,
     config: &HoverConfig,
     edition: Edition,
+    display_target: DisplayTarget,
 ) -> Option<RangeInfo<HoverResult>> {
     // FIXME: make this work in attributes
     let expr_or_pat = file
@@ -371,16 +394,17 @@ fn hover_ranged(
         .find_map(Either::<ast::Expr, ast::Pat>::cast)?;
     let res = match &expr_or_pat {
         Either::Left(ast::Expr::TryExpr(try_expr)) => {
-            render::try_expr(sema, config, try_expr, edition)
+            render::try_expr(sema, config, try_expr, edition, display_target)
         }
         Either::Left(ast::Expr::PrefixExpr(prefix_expr))
             if prefix_expr.op_kind() == Some(ast::UnaryOp::Deref) =>
         {
-            render::deref_expr(sema, config, prefix_expr, edition)
+            render::deref_expr(sema, config, prefix_expr, edition, display_target)
         }
         _ => None,
     };
-    let res = res.or_else(|| render::type_info_of(sema, config, &expr_or_pat, edition));
+    let res =
+        res.or_else(|| render::type_info_of(sema, config, &expr_or_pat, edition, display_target));
     res.map(|it| {
         let range = match expr_or_pat {
             Either::Left(it) => it.syntax().text_range(),
@@ -401,6 +425,7 @@ pub(crate) fn hover_for_definition(
     hovered_definition: bool,
     config: &HoverConfig,
     edition: Edition,
+    display_target: DisplayTarget,
 ) -> HoverResult {
     let famous_defs = match &def {
         Definition::BuiltinType(_) => sema.scope(scope_node).map(|it| FamousDefs(sema, it.krate())),
@@ -435,6 +460,7 @@ pub(crate) fn hover_for_definition(
         subst_types.as_ref(),
         config,
         edition,
+        display_target,
     );
     HoverResult {
         markup: render::process_markup(sema.db, def, &markup, config),
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 c5a83e58cea..31ef89a07cd 100644
--- a/src/tools/rust-analyzer/crates/ide/src/hover/render.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/hover/render.rs
@@ -3,7 +3,7 @@ use std::{env, mem, ops::Not};
 
 use either::Either;
 use hir::{
-    db::ExpandDatabase, Adt, AsAssocItem, AsExternAssocItem, CaptureKind, DropGlue,
+    db::ExpandDatabase, Adt, AsAssocItem, AsExternAssocItem, CaptureKind, DisplayTarget, DropGlue,
     DynCompatibilityViolation, HasCrate, HasSource, HirDisplay, Layout, LayoutError,
     MethodViolationCode, Name, Semantics, Symbol, Trait, Type, TypeInfo, VariantDef,
 };
@@ -38,12 +38,13 @@ pub(super) fn type_info_of(
     _config: &HoverConfig,
     expr_or_pat: &Either<ast::Expr, ast::Pat>,
     edition: Edition,
+    display_target: DisplayTarget,
 ) -> Option<HoverResult> {
     let ty_info = match expr_or_pat {
         Either::Left(expr) => sema.type_of_expr(expr)?,
         Either::Right(pat) => sema.type_of_pat(pat)?,
     };
-    type_info(sema, _config, ty_info, edition)
+    type_info(sema, _config, ty_info, edition, display_target)
 }
 
 pub(super) fn closure_expr(
@@ -51,9 +52,10 @@ pub(super) fn closure_expr(
     config: &HoverConfig,
     c: ast::ClosureExpr,
     edition: Edition,
+    display_target: DisplayTarget,
 ) -> Option<HoverResult> {
     let TypeInfo { original, .. } = sema.type_of_expr(&c.into())?;
-    closure_ty(sema, config, &TypeInfo { original, adjusted: None }, edition)
+    closure_ty(sema, config, &TypeInfo { original, adjusted: None }, edition, display_target)
 }
 
 pub(super) fn try_expr(
@@ -61,6 +63,7 @@ pub(super) fn try_expr(
     _config: &HoverConfig,
     try_expr: &ast::TryExpr,
     edition: Edition,
+    display_target: DisplayTarget,
 ) -> Option<HoverResult> {
     let inner_ty = sema.type_of_expr(&try_expr.expr()?)?.original;
     let mut ancestors = try_expr.syntax().ancestors();
@@ -127,8 +130,8 @@ pub(super) fn try_expr(
         res.actions.push(actions);
     }
 
-    let inner_ty = inner_ty.display(sema.db, edition).to_string();
-    let body_ty = body_ty.display(sema.db, edition).to_string();
+    let inner_ty = inner_ty.display(sema.db, display_target).to_string();
+    let body_ty = body_ty.display(sema.db, display_target).to_string();
     let ty_len_max = inner_ty.len().max(body_ty.len());
 
     let l = "Propagated as: ".len() - " Type: ".len();
@@ -153,6 +156,7 @@ pub(super) fn deref_expr(
     _config: &HoverConfig,
     deref_expr: &ast::PrefixExpr,
     edition: Edition,
+    display_target: DisplayTarget,
 ) -> Option<HoverResult> {
     let inner_ty = sema.type_of_expr(&deref_expr.expr()?)?.original;
     let TypeInfo { original, adjusted } =
@@ -170,9 +174,9 @@ pub(super) fn deref_expr(
 
     res.markup = if let Some(adjusted_ty) = adjusted {
         walk_and_push_ty(sema.db, &adjusted_ty, &mut push_new_def);
-        let original = original.display(sema.db, edition).to_string();
-        let adjusted = adjusted_ty.display(sema.db, edition).to_string();
-        let inner = inner_ty.display(sema.db, edition).to_string();
+        let original = original.display(sema.db, display_target).to_string();
+        let adjusted = adjusted_ty.display(sema.db, display_target).to_string();
+        let inner = inner_ty.display(sema.db, display_target).to_string();
         let type_len = "To type: ".len();
         let coerced_len = "Coerced to: ".len();
         let deref_len = "Dereferenced from: ".len();
@@ -190,8 +194,8 @@ pub(super) fn deref_expr(
         )
         .into()
     } else {
-        let original = original.display(sema.db, edition).to_string();
-        let inner = inner_ty.display(sema.db, edition).to_string();
+        let original = original.display(sema.db, display_target).to_string();
+        let inner = inner_ty.display(sema.db, display_target).to_string();
         let type_len = "To type: ".len();
         let deref_len = "Dereferenced from: ".len();
         let max_len = (original.len() + type_len).max(inner.len() + deref_len);
@@ -216,6 +220,7 @@ pub(super) fn underscore(
     config: &HoverConfig,
     token: &SyntaxToken,
     edition: Edition,
+    display_target: DisplayTarget,
 ) -> Option<HoverResult> {
     if token.kind() != T![_] {
         return None;
@@ -224,8 +229,8 @@ pub(super) fn underscore(
     let _it = match_ast! {
         match parent {
             ast::InferType(it) => it,
-            ast::UnderscoreExpr(it) => return type_info_of(sema, config, &Either::Left(ast::Expr::UnderscoreExpr(it)),edition),
-            ast::WildcardPat(it) => return type_info_of(sema, config, &Either::Right(ast::Pat::WildcardPat(it)),edition),
+            ast::UnderscoreExpr(it) => return type_info_of(sema, config, &Either::Left(ast::Expr::UnderscoreExpr(it)),edition, display_target),
+            ast::WildcardPat(it) => return type_info_of(sema, config, &Either::Right(ast::Pat::WildcardPat(it)),edition, display_target),
             _ => return None,
         }
     };
@@ -259,6 +264,7 @@ pub(super) fn keyword(
     config: &HoverConfig,
     token: &SyntaxToken,
     edition: Edition,
+    display_target: DisplayTarget,
 ) -> Option<HoverResult> {
     if !token.kind().is_keyword(edition) || !config.documentation || !config.keywords {
         return None;
@@ -267,7 +273,7 @@ pub(super) fn keyword(
     let famous_defs = FamousDefs(sema, sema.scope(&parent)?.krate());
 
     let KeywordHint { description, keyword_mod, actions } =
-        keyword_hints(sema, token, parent, edition);
+        keyword_hints(sema, token, parent, edition, display_target);
 
     let doc_owner = find_std_module(&famous_defs, &keyword_mod, edition)?;
     let docs = doc_owner.docs(sema.db)?;
@@ -288,6 +294,7 @@ pub(super) fn struct_rest_pat(
     _config: &HoverConfig,
     pattern: &ast::RecordPat,
     edition: Edition,
+    display_target: DisplayTarget,
 ) -> HoverResult {
     let missing_fields = sema.record_pattern_missing_fields(pattern);
 
@@ -309,7 +316,7 @@ pub(super) fn struct_rest_pat(
     res.markup = {
         let mut s = String::from(".., ");
         for (f, _) in &missing_fields {
-            s += f.display(sema.db, edition).to_string().as_ref();
+            s += f.display(sema.db, display_target).to_string().as_ref();
             s += ", ";
         }
         // get rid of trailing comma
@@ -479,41 +486,44 @@ pub(super) fn definition(
     subst_types: Option<&Vec<(Symbol, Type)>>,
     config: &HoverConfig,
     edition: Edition,
+    display_target: DisplayTarget,
 ) -> Markup {
     let mod_path = definition_path(db, &def, edition);
     let label = match def {
-        Definition::Trait(trait_) => {
-            trait_.display_limited(db, config.max_trait_assoc_items_count, edition).to_string()
-        }
+        Definition::Trait(trait_) => trait_
+            .display_limited(db, config.max_trait_assoc_items_count, display_target)
+            .to_string(),
         Definition::Adt(adt @ (Adt::Struct(_) | Adt::Union(_))) => {
-            adt.display_limited(db, config.max_fields_count, edition).to_string()
+            adt.display_limited(db, config.max_fields_count, display_target).to_string()
         }
         Definition::Variant(variant) => {
-            variant.display_limited(db, config.max_fields_count, edition).to_string()
+            variant.display_limited(db, config.max_fields_count, display_target).to_string()
         }
         Definition::Adt(adt @ Adt::Enum(_)) => {
-            adt.display_limited(db, config.max_enum_variants_count, edition).to_string()
+            adt.display_limited(db, config.max_enum_variants_count, display_target).to_string()
         }
         Definition::SelfType(impl_def) => {
             let self_ty = &impl_def.self_ty(db);
             match self_ty.as_adt() {
-                Some(adt) => adt.display_limited(db, config.max_fields_count, edition).to_string(),
-                None => self_ty.display(db, edition).to_string(),
+                Some(adt) => {
+                    adt.display_limited(db, config.max_fields_count, display_target).to_string()
+                }
+                None => self_ty.display(db, display_target).to_string(),
             }
         }
         Definition::Macro(it) => {
-            let mut label = it.display(db, edition).to_string();
+            let mut label = it.display(db, display_target).to_string();
             if let Some(macro_arm) = macro_arm {
                 format_to!(label, " // matched arm #{}", macro_arm);
             }
             label
         }
         Definition::Function(fn_) => {
-            fn_.display_with_container_bounds(db, true, edition).to_string()
+            fn_.display_with_container_bounds(db, true, display_target).to_string()
         }
-        _ => def.label(db, edition),
+        _ => def.label(db, display_target),
     };
-    let docs = def.docs(db, famous_defs, edition);
+    let docs = def.docs(db, famous_defs, display_target);
     let value = || match def {
         Definition::Variant(it) => {
             if !it.parent_enum(db).is_data_carrying(db) {
@@ -525,7 +535,10 @@ pub(super) fn definition(
                         let res = it.value(db).map(|it| format!("{it:?}"));
                         if env::var_os("RA_DEV").is_some() {
                             let res = res.as_deref().unwrap_or("");
-                            Some(format!("{res} ({})", render_const_eval_error(db, err, edition)))
+                            Some(format!(
+                                "{res} ({})",
+                                render_const_eval_error(db, err, display_target)
+                            ))
                         } else {
                             res
                         }
@@ -541,9 +554,12 @@ pub(super) fn definition(
                 Ok(it) => match it.render_debug(db) {
                     Ok(it) => it,
                     Err(err) => {
-                        let it = it.render(db, edition);
+                        let it = it.render(db, display_target);
                         if env::var_os("RA_DEV").is_some() {
-                            format!("{it}\n{}", render_const_eval_error(db, err.into(), edition))
+                            format!(
+                                "{it}\n{}",
+                                render_const_eval_error(db, err.into(), display_target)
+                            )
                         } else {
                             it
                         }
@@ -557,7 +573,7 @@ pub(super) fn definition(
                         body = prettify_macro_expansion(db, body, &span_map, it.krate(db).into());
                     }
                     if env::var_os("RA_DEV").is_some() {
-                        format!("{body}\n{}", render_const_eval_error(db, err, edition))
+                        format!("{body}\n{}", render_const_eval_error(db, err, display_target))
                     } else {
                         body.to_string()
                     }
@@ -570,9 +586,12 @@ pub(super) fn definition(
                 Ok(it) => match it.render_debug(db) {
                     Ok(it) => it,
                     Err(err) => {
-                        let it = it.render(db, edition);
+                        let it = it.render(db, display_target);
                         if env::var_os("RA_DEV").is_some() {
-                            format!("{it}\n{}", render_const_eval_error(db, err.into(), edition))
+                            format!(
+                                "{it}\n{}",
+                                render_const_eval_error(db, err.into(), display_target)
+                            )
                         } else {
                             it
                         }
@@ -586,7 +605,7 @@ pub(super) fn definition(
                         body = prettify_macro_expansion(db, body, &span_map, it.krate(db).into());
                     }
                     if env::var_os("RA_DEV").is_some() {
-                        format!("{body}\n{}", render_const_eval_error(db, err, edition))
+                        format!("{body}\n{}", render_const_eval_error(db, err, display_target))
                     } else {
                         body.to_string()
                     }
@@ -728,7 +747,9 @@ pub(super) fn definition(
 
     let mut extra = String::new();
     if hovered_definition {
-        if let Some(notable_traits) = render_notable_trait(db, notable_traits, edition) {
+        if let Some(notable_traits) =
+            render_notable_trait(db, notable_traits, edition, display_target)
+        {
             extra.push_str("\n___\n");
             extra.push_str(&notable_traits);
         }
@@ -772,7 +793,7 @@ pub(super) fn definition(
                         .format_with(", ", |(name, ty), fmt| {
                             fmt(&format_args!(
                                 "`{name}` = `{}`",
-                                ty.display_truncated(db, limit, edition)
+                                ty.display_truncated(db, limit, display_target)
                             ))
                         })
                         .to_string()
@@ -799,7 +820,7 @@ struct DropInfo {
 pub(super) fn literal(
     sema: &Semantics<'_, RootDatabase>,
     token: SyntaxToken,
-    edition: Edition,
+    display_target: DisplayTarget,
 ) -> Option<Markup> {
     let lit = token.parent().and_then(ast::Literal::cast)?;
     let ty = if let Some(p) = lit.syntax().parent().and_then(ast::Pat::cast) {
@@ -847,7 +868,7 @@ pub(super) fn literal(
             _ => return None
         }
     };
-    let ty = ty.display(sema.db, edition);
+    let ty = ty.display(sema.db, display_target);
 
     let mut s = format!("```rust\n{ty}\n```\n___\n\n");
     match value {
@@ -879,6 +900,7 @@ fn render_notable_trait(
     db: &RootDatabase,
     notable_traits: &[(Trait, Vec<(Option<Type>, Name)>)],
     edition: Edition,
+    display_target: DisplayTarget,
 ) -> Option<String> {
     let mut desc = String::new();
     let mut needs_impl_header = true;
@@ -898,7 +920,7 @@ fn render_notable_trait(
                     f(&name.display(db, edition))?;
                     f(&" = ")?;
                     match ty {
-                        Some(ty) => f(&ty.display(db, edition)),
+                        Some(ty) => f(&ty.display(db, display_target)),
                         None => f(&"?"),
                     }
                 })
@@ -914,8 +936,9 @@ fn type_info(
     config: &HoverConfig,
     ty: TypeInfo,
     edition: Edition,
+    display_target: DisplayTarget,
 ) -> Option<HoverResult> {
-    if let Some(res) = closure_ty(sema, config, &ty, edition) {
+    if let Some(res) = closure_ty(sema, config, &ty, edition, display_target) {
         return Some(res);
     };
     let db = sema.db;
@@ -951,7 +974,7 @@ fn type_info(
                             f(&name.display(db, edition))?;
                             f(&" = ")?;
                             match ty {
-                                Some(ty) => f(&ty.display(db, edition)),
+                                Some(ty) => f(&ty.display(db, display_target)),
                                 None => f(&"?"),
                             }
                         })
@@ -965,8 +988,8 @@ fn type_info(
             desc
         };
 
-        let original = original.display(db, edition).to_string();
-        let adjusted = adjusted_ty.display(db, edition).to_string();
+        let original = original.display(db, display_target).to_string();
+        let adjusted = adjusted_ty.display(db, display_target).to_string();
         let static_text_diff_len = "Coerced to: ".len() - "Type: ".len();
         format!(
             "```text\nType: {:>apad$}\nCoerced to: {:>opad$}\n{notable}```\n",
@@ -977,8 +1000,10 @@ fn type_info(
         )
         .into()
     } else {
-        let mut desc = format!("```rust\n{}\n```", original.display(db, edition));
-        if let Some(extra) = render_notable_trait(db, &notable_traits(db, &original), edition) {
+        let mut desc = format!("```rust\n{}\n```", original.display(db, display_target));
+        if let Some(extra) =
+            render_notable_trait(db, &notable_traits(db, &original), edition, display_target)
+        {
             desc.push_str("\n___\n");
             desc.push_str(&extra);
         };
@@ -995,6 +1020,7 @@ fn closure_ty(
     config: &HoverConfig,
     TypeInfo { original, adjusted }: &TypeInfo,
     edition: Edition,
+    display_target: DisplayTarget,
 ) -> Option<HoverResult> {
     let c = original.as_closure()?;
     let mut captures_rendered = c.captured_items(sema.db)
@@ -1027,12 +1053,14 @@ fn closure_ty(
         walk_and_push_ty(sema.db, adjusted_ty, &mut push_new_def);
         format!(
             "\nCoerced to: {}",
-            adjusted_ty.display(sema.db, edition).with_closure_style(hir::ClosureStyle::ImplFn)
+            adjusted_ty
+                .display(sema.db, display_target)
+                .with_closure_style(hir::ClosureStyle::ImplFn)
         )
     } else {
         String::new()
     };
-    let mut markup = format!("```rust\n{}\n```", c.display_with_impl(sema.db, edition));
+    let mut markup = format!("```rust\n{}\n```", c.display_with_impl(sema.db, display_target));
 
     if let Some(trait_) = c.fn_trait(sema.db).get_id(sema.db, original.krate(sema.db).into()) {
         push_new_def(hir::Trait::from(trait_).into())
@@ -1213,6 +1241,7 @@ fn keyword_hints(
     token: &SyntaxToken,
     parent: syntax::SyntaxNode,
     edition: Edition,
+    display_target: DisplayTarget,
 ) -> KeywordHint {
     match token.kind() {
         T![await] | T![loop] | T![match] | T![unsafe] | T![as] | T![try] | T![if] | T![else] => {
@@ -1230,7 +1259,8 @@ fn keyword_hints(
                     walk_and_push_ty(sema.db, &ty.original, &mut push_new_def);
 
                     let ty = ty.adjusted();
-                    let description = format!("{}: {}", token.text(), ty.display(sema.db, edition));
+                    let description =
+                        format!("{}: {}", token.text(), ty.display(sema.db, display_target));
 
                     KeywordHint {
                         description,
diff --git a/src/tools/rust-analyzer/crates/ide/src/hover/tests.rs b/src/tools/rust-analyzer/crates/ide/src/hover/tests.rs
index 7c720d97cb6..6b470d921f7 100644
--- a/src/tools/rust-analyzer/crates/ide/src/hover/tests.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/hover/tests.rs
@@ -10947,3 +10947,42 @@ pub struct ManuallyDrop$0<T: ?Sized> {
         "#]],
     );
 }
+
+#[test]
+fn projection_const() {
+    // This uses two crates, which have *no* relation between them, to test another thing:
+    // `render_const_scalar()` used to just use the last crate for the trait env, which will
+    // fail in this scenario.
+    check(
+        r#"
+//- /foo.rs crate:foo
+pub trait PublicFlags {
+    type Internal;
+}
+
+pub struct NoteDialects(<NoteDialects as PublicFlags>::Internal);
+
+impl NoteDialects {
+    pub const CLAP$0: Self = Self(InternalBitFlags);
+}
+
+pub struct InternalBitFlags;
+
+impl PublicFlags for NoteDialects {
+    type Internal = InternalBitFlags;
+}
+//- /bar.rs crate:bar
+    "#,
+        expect![[r#"
+            *CLAP*
+
+            ```rust
+            foo::NoteDialects
+            ```
+
+            ```rust
+            pub const CLAP: Self = NoteDialects(InternalBitFlags)
+            ```
+        "#]],
+    );
+}
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 63039b1cd34..6babdff52a2 100644
--- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints.rs
@@ -5,14 +5,14 @@ use std::{
 
 use either::Either;
 use hir::{
-    sym, ClosureStyle, HasVisibility, HirDisplay, HirDisplayError, HirWrite, ModuleDef,
-    ModuleDefId, Semantics,
+    sym, ClosureStyle, DisplayTarget, HasVisibility, HirDisplay, HirDisplayError, HirWrite,
+    ModuleDef, ModuleDefId, Semantics,
 };
 use ide_db::{famous_defs::FamousDefs, FileRange, RootDatabase};
 use ide_db::{text_edit::TextEdit, FxHashSet};
 use itertools::Itertools;
 use smallvec::{smallvec, SmallVec};
-use span::{Edition, EditionedFileId};
+use span::EditionedFileId;
 use stdx::never;
 use syntax::{
     ast::{self, AstNode, HasGenericParams},
@@ -207,7 +207,8 @@ fn hints(
     file_id: EditionedFileId,
     node: SyntaxNode,
 ) {
-    closing_brace::hints(hints, sema, config, file_id, node.clone());
+    let display_target = sema.first_crate_or_default(file_id.file_id()).to_display_target(sema.db);
+    closing_brace::hints(hints, sema, config, file_id, display_target, node.clone());
     if let Some(any_has_generic_args) = ast::AnyHasGenericArgs::cast(node.clone()) {
         generic_param::hints(hints, famous_defs, config, any_has_generic_args);
     }
@@ -215,8 +216,8 @@ fn hints(
     match_ast! {
         match node {
             ast::Expr(expr) => {
-                chaining::hints(hints, famous_defs, config, file_id, &expr);
-                adjustment::hints(hints, famous_defs, config, file_id, &expr);
+                chaining::hints(hints, famous_defs, config, display_target, &expr);
+                adjustment::hints(hints, famous_defs, config, display_target, &expr);
                 match expr {
                     ast::Expr::CallExpr(it) => param_name::hints(hints, famous_defs, config, file_id, ast::Expr::from(it)),
                     ast::Expr::MethodCallExpr(it) => {
@@ -224,7 +225,7 @@ fn hints(
                     }
                     ast::Expr::ClosureExpr(it) => {
                         closure_captures::hints(hints, famous_defs, config, file_id, it.clone());
-                        closure_ret::hints(hints, famous_defs, config, file_id, it)
+                        closure_ret::hints(hints, famous_defs, config, display_target, it)
                     },
                     ast::Expr::RangeExpr(it) => range_exclusive::hints(hints, famous_defs, config, file_id,  it),
                     _ => Some(()),
@@ -234,7 +235,7 @@ fn hints(
                 binding_mode::hints(hints, famous_defs, config, file_id,  &it);
                 match it {
                     ast::Pat::IdentPat(it) => {
-                        bind_pat::hints(hints, famous_defs, config, file_id, &it);
+                        bind_pat::hints(hints, famous_defs, config, display_target, &it);
                     }
                     ast::Pat::RangePat(it) => {
                         range_exclusive::hints(hints, famous_defs, config, file_id, it);
@@ -704,7 +705,7 @@ fn label_of_ty(
     famous_defs @ FamousDefs(sema, _): &FamousDefs<'_, '_>,
     config: &InlayHintsConfig,
     ty: &hir::Type,
-    edition: Edition,
+    display_target: DisplayTarget,
 ) -> Option<InlayHintLabel> {
     fn rec(
         sema: &Semantics<'_, RootDatabase>,
@@ -713,7 +714,7 @@ fn label_of_ty(
         ty: &hir::Type,
         label_builder: &mut InlayHintLabelBuilder<'_>,
         config: &InlayHintsConfig,
-        edition: Edition,
+        display_target: DisplayTarget,
     ) -> Result<(), HirDisplayError> {
         let iter_item_type = hint_iterator(sema, famous_defs, ty);
         match iter_item_type {
@@ -744,12 +745,12 @@ fn label_of_ty(
                 label_builder.write_str(LABEL_ITEM)?;
                 label_builder.end_location_link();
                 label_builder.write_str(LABEL_MIDDLE2)?;
-                rec(sema, famous_defs, max_length, &ty, label_builder, config, edition)?;
+                rec(sema, famous_defs, max_length, &ty, label_builder, config, display_target)?;
                 label_builder.write_str(LABEL_END)?;
                 Ok(())
             }
             None => ty
-                .display_truncated(sema.db, max_length, edition)
+                .display_truncated(sema.db, max_length, display_target)
                 .with_closure_style(config.closure_style)
                 .write_to(label_builder),
         }
@@ -762,7 +763,8 @@ fn label_of_ty(
         result: InlayHintLabel::default(),
         resolve: config.fields_to_resolve.resolve_label_location,
     };
-    let _ = rec(sema, famous_defs, config.max_length, ty, &mut label_builder, config, edition);
+    let _ =
+        rec(sema, famous_defs, config.max_length, ty, &mut label_builder, config, display_target);
     let r = label_builder.finish();
     Some(r)
 }
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 8522ef0a6d5..91b81872952 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
@@ -7,12 +7,12 @@ use std::ops::Not;
 
 use either::Either;
 use hir::{
-    Adjust, Adjustment, AutoBorrow, HirDisplay, Mutability, OverloadedDeref, PointerCast, Safety,
+    Adjust, Adjustment, AutoBorrow, DisplayTarget, HirDisplay, Mutability, OverloadedDeref,
+    PointerCast, Safety,
 };
 use ide_db::famous_defs::FamousDefs;
 
 use ide_db::text_edit::TextEditBuilder;
-use span::EditionedFileId;
 use syntax::ast::{self, prec::ExprPrecedence, AstNode};
 
 use crate::{
@@ -24,7 +24,7 @@ pub(super) fn hints(
     acc: &mut Vec<InlayHint>,
     FamousDefs(sema, _): &FamousDefs<'_, '_>,
     config: &InlayHintsConfig,
-    file_id: EditionedFileId,
+    display_target: DisplayTarget,
     expr: &ast::Expr,
 ) -> Option<()> {
     if config.adjustment_hints_hide_outside_unsafe && !sema.is_inside_unsafe(expr) {
@@ -163,8 +163,8 @@ pub(super) fn hints(
             tooltip: Some(config.lazy_tooltip(|| {
                 InlayTooltip::Markdown(format!(
                     "`{}` → `{}` ({coercion} coercion)",
-                    source.display(sema.db, file_id.edition()),
-                    target.display(sema.db, file_id.edition()),
+                    source.display(sema.db, display_target),
+                    target.display(sema.db, display_target),
                 ))
             })),
         };
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 c2986a9aa66..4379153acaa 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
@@ -3,11 +3,10 @@
 //! fn f(a: i32, b: i32) -> i32 { a + b }
 //! let _x /* i32 */= f(4, 4);
 //! ```
-use hir::Semantics;
+use hir::{DisplayTarget, Semantics};
 use ide_db::{famous_defs::FamousDefs, RootDatabase};
 
 use itertools::Itertools;
-use span::EditionedFileId;
 use syntax::{
     ast::{self, AstNode, HasGenericArgs, HasName},
     match_ast,
@@ -22,7 +21,7 @@ pub(super) fn hints(
     acc: &mut Vec<InlayHint>,
     famous_defs @ FamousDefs(sema, _): &FamousDefs<'_, '_>,
     config: &InlayHintsConfig,
-    file_id: EditionedFileId,
+    display_target: DisplayTarget,
     pat: &ast::IdentPat,
 ) -> Option<()> {
     if !config.type_hints {
@@ -70,7 +69,7 @@ pub(super) fn hints(
         return None;
     }
 
-    let mut label = label_of_ty(famous_defs, config, &ty, file_id.edition())?;
+    let mut label = label_of_ty(famous_defs, config, &ty, display_target)?;
 
     if config.hide_named_constructor_hints
         && is_named_constructor(sema, pat, &label.to_string()).is_some()
diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/chaining.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/chaining.rs
index 8471547727f..604719bc366 100644
--- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/chaining.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/chaining.rs
@@ -1,6 +1,6 @@
 //! Implementation of "chaining" inlay hints.
+use hir::DisplayTarget;
 use ide_db::famous_defs::FamousDefs;
-use span::EditionedFileId;
 use syntax::{
     ast::{self, AstNode},
     Direction, NodeOrToken, SyntaxKind, T,
@@ -14,7 +14,7 @@ pub(super) fn hints(
     acc: &mut Vec<InlayHint>,
     famous_defs @ FamousDefs(sema, _): &FamousDefs<'_, '_>,
     config: &InlayHintsConfig,
-    file_id: EditionedFileId,
+    display_target: DisplayTarget,
     expr: &ast::Expr,
 ) -> Option<()> {
     if !config.chaining_hints {
@@ -58,7 +58,7 @@ pub(super) fn hints(
                     }
                 }
             }
-            let label = label_of_ty(famous_defs, config, &ty, file_id.edition())?;
+            let label = label_of_ty(famous_defs, config, &ty, display_target)?;
             acc.push(InlayHint {
                 range: expr.syntax().text_range(),
                 kind: InlayKind::Chaining,
diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/closing_brace.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/closing_brace.rs
index 3767d34e2c7..bec6d38ee9c 100644
--- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/closing_brace.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/closing_brace.rs
@@ -3,7 +3,7 @@
 //! fn g() {
 //! } /* fn g */
 //! ```
-use hir::{HirDisplay, Semantics};
+use hir::{DisplayTarget, HirDisplay, Semantics};
 use ide_db::{FileRange, RootDatabase};
 use span::EditionedFileId;
 use syntax::{
@@ -21,6 +21,7 @@ pub(super) fn hints(
     sema: &Semantics<'_, RootDatabase>,
     config: &InlayHintsConfig,
     file_id: EditionedFileId,
+    display_target: DisplayTarget,
     original_node: SyntaxNode,
 ) -> Option<()> {
     let min_lines = config.closing_brace_hints_min_lines?;
@@ -43,9 +44,9 @@ pub(super) fn hints(
                         Some(tr) => format!(
                             "impl {} for {}",
                             tr.name(sema.db).display(sema.db, file_id.edition()),
-                            ty.display_truncated(sema.db, config.max_length, file_id.edition(),
+                            ty.display_truncated(sema.db, config.max_length, display_target,
                         )),
-                        None => format!("impl {}", ty.display_truncated(sema.db, config.max_length, file_id.edition())),
+                        None => format!("impl {}", ty.display_truncated(sema.db, config.max_length, display_target)),
                     };
                     (hint_text, None)
                 },
diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/closure_ret.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/closure_ret.rs
index 7858b1d90a3..61c9c25fe73 100644
--- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/closure_ret.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/closure_ret.rs
@@ -1,8 +1,8 @@
 //! Implementation of "closure return type" inlay hints.
 //!
 //! Tests live in [`bind_pat`][super::bind_pat] module.
+use hir::DisplayTarget;
 use ide_db::famous_defs::FamousDefs;
-use span::EditionedFileId;
 use syntax::ast::{self, AstNode};
 
 use crate::{
@@ -14,7 +14,7 @@ pub(super) fn hints(
     acc: &mut Vec<InlayHint>,
     famous_defs @ FamousDefs(sema, _): &FamousDefs<'_, '_>,
     config: &InlayHintsConfig,
-    file_id: EditionedFileId,
+    display_target: DisplayTarget,
     closure: ast::ClosureExpr,
 ) -> Option<()> {
     if config.closure_return_type_hints == ClosureReturnTypeHints::Never {
@@ -43,7 +43,7 @@ pub(super) fn hints(
         return None;
     }
 
-    let mut label = label_of_ty(famous_defs, config, &ty, file_id.edition())?;
+    let mut label = label_of_ty(famous_defs, config, &ty, display_target)?;
 
     if arrow.is_none() {
         label.prepend_str(" -> ");
diff --git a/src/tools/rust-analyzer/crates/ide/src/interpret.rs b/src/tools/rust-analyzer/crates/ide/src/interpret.rs
index ae11072e34b..74dad488b4d 100644
--- a/src/tools/rust-analyzer/crates/ide/src/interpret.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/interpret.rs
@@ -1,6 +1,5 @@
-use hir::{ConstEvalError, DefWithBody, Semantics};
+use hir::{ConstEvalError, DefWithBody, DisplayTarget, Semantics};
 use ide_db::{base_db::SourceRootDatabase, FilePosition, LineIndexDatabase, RootDatabase};
-use span::Edition;
 use std::time::{Duration, Instant};
 use stdx::format_to;
 use syntax::{algo::ancestors_at_offset, ast, AstNode, TextRange};
@@ -46,15 +45,15 @@ fn find_and_interpret(db: &RootDatabase, position: FilePosition) -> Option<(Dura
             None => format!("file://{path} range {text_range:?}"),
         }
     };
-    let edition = def.module(db).krate().edition(db);
+    let display_target = def.module(db).krate().to_display_target(db);
     let start_time = Instant::now();
     let res = match def {
         DefWithBody::Function(it) => it.eval(db, span_formatter),
-        DefWithBody::Static(it) => it.eval(db).map(|it| it.render(db, edition)),
-        DefWithBody::Const(it) => it.eval(db).map(|it| it.render(db, edition)),
+        DefWithBody::Static(it) => it.eval(db).map(|it| it.render(db, display_target)),
+        DefWithBody::Const(it) => it.eval(db).map(|it| it.render(db, display_target)),
         _ => unreachable!(),
     };
-    let res = res.unwrap_or_else(|e| render_const_eval_error(db, e, edition));
+    let res = res.unwrap_or_else(|e| render_const_eval_error(db, e, display_target));
     let duration = Instant::now() - start_time;
     Some((duration, res))
 }
@@ -62,7 +61,7 @@ fn find_and_interpret(db: &RootDatabase, position: FilePosition) -> Option<(Dura
 pub(crate) fn render_const_eval_error(
     db: &RootDatabase,
     e: ConstEvalError,
-    edition: Edition,
+    display_target: DisplayTarget,
 ) -> String {
     let span_formatter = |file_id, text_range: TextRange| {
         let path = &db
@@ -76,6 +75,6 @@ pub(crate) fn render_const_eval_error(
         }
     };
     let mut r = String::new();
-    _ = e.pretty_print(&mut r, db, span_formatter, edition);
+    _ = e.pretty_print(&mut r, db, span_formatter, display_target);
     r
 }
diff --git a/src/tools/rust-analyzer/crates/ide/src/moniker.rs b/src/tools/rust-analyzer/crates/ide/src/moniker.rs
index 25d12a4c0b4..5754b4fa82f 100644
--- a/src/tools/rust-analyzer/crates/ide/src/moniker.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/moniker.rs
@@ -11,7 +11,6 @@ use ide_db::{
     FilePosition, RootDatabase,
 };
 use itertools::Itertools;
-use span::Edition;
 use syntax::{AstNode, SyntaxKind::*, T};
 
 use crate::{doc_links::token_as_doc_comment, parent_module::crates_for, RangeInfo};
@@ -305,13 +304,13 @@ fn def_to_non_local_moniker(
                 if let Some(trait_ref) = impl_.trait_ref(db) {
                     // Trait impls use the trait type for the 2nd parameter.
                     reverse_description.push(MonikerDescriptor {
-                        name: display(db, edition, module, trait_ref),
+                        name: display(db, module, trait_ref),
                         desc: MonikerDescriptorKind::TypeParameter,
                     });
                 }
                 // Both inherent and trait impls use the self type for the first parameter.
                 reverse_description.push(MonikerDescriptor {
-                    name: display(db, edition, module, impl_.self_ty(db)),
+                    name: display(db, module, impl_.self_ty(db)),
                     desc: MonikerDescriptorKind::TypeParameter,
                 });
                 reverse_description.push(MonikerDescriptor {
@@ -390,17 +389,12 @@ fn def_to_non_local_moniker(
     })
 }
 
-fn display<T: HirDisplay>(
-    db: &RootDatabase,
-    edition: Edition,
-    module: hir::Module,
-    it: T,
-) -> String {
+fn display<T: HirDisplay>(db: &RootDatabase, module: hir::Module, it: T) -> String {
     match it.display_source_code(db, module.into(), true) {
         Ok(result) => result,
         // Fallback on display variant that always succeeds
         Err(_) => {
-            let fallback_result = it.display(db, edition).to_string();
+            let fallback_result = it.display(db, module.krate().to_display_target(db)).to_string();
             tracing::error!(
                 display = %fallback_result, "`display_source_code` failed; falling back to using display"
             );
diff --git a/src/tools/rust-analyzer/crates/ide/src/navigation_target.rs b/src/tools/rust-analyzer/crates/ide/src/navigation_target.rs
index d9f80cb53dd..d67aaac06fb 100644
--- a/src/tools/rust-analyzer/crates/ide/src/navigation_target.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/navigation_target.rs
@@ -6,7 +6,7 @@ use arrayvec::ArrayVec;
 use either::Either;
 use hir::{
     db::ExpandDatabase, symbols::FileSymbol, AssocItem, FieldSource, HasContainer, HasCrate,
-    HasSource, HirDisplay, HirFileId, HirFileIdExt, InFile, LocalSource, ModuleSource,
+    HasSource, HirDisplay, HirFileId, InFile, LocalSource, ModuleSource,
 };
 use ide_db::{
     defs::Definition,
@@ -116,7 +116,9 @@ impl NavigationTarget {
                             SymbolKind::Module,
                         );
                         res.docs = module.docs(db);
-                        res.description = Some(module.display(db, edition).to_string());
+                        res.description = Some(
+                            module.display(db, module.krate().to_display_target(db)).to_string(),
+                        );
                         res
                     },
                 )
@@ -183,6 +185,7 @@ impl TryToNav for FileSymbol {
     fn try_to_nav(&self, db: &RootDatabase) -> Option<UpmappingResult<NavigationTarget>> {
         let edition =
             self.def.module(db).map(|it| it.krate().edition(db)).unwrap_or(Edition::CURRENT);
+        let display_target = self.def.krate(db).to_display_target(db);
         Some(
             orig_range_with_focus_r(
                 db,
@@ -203,16 +206,34 @@ impl TryToNav for FileSymbol {
                     focus_range,
                     container_name: self.container_name.clone(),
                     description: match self.def {
-                        hir::ModuleDef::Module(it) => Some(it.display(db, edition).to_string()),
-                        hir::ModuleDef::Function(it) => Some(it.display(db, edition).to_string()),
-                        hir::ModuleDef::Adt(it) => Some(it.display(db, edition).to_string()),
-                        hir::ModuleDef::Variant(it) => Some(it.display(db, edition).to_string()),
-                        hir::ModuleDef::Const(it) => Some(it.display(db, edition).to_string()),
-                        hir::ModuleDef::Static(it) => Some(it.display(db, edition).to_string()),
-                        hir::ModuleDef::Trait(it) => Some(it.display(db, edition).to_string()),
-                        hir::ModuleDef::TraitAlias(it) => Some(it.display(db, edition).to_string()),
-                        hir::ModuleDef::TypeAlias(it) => Some(it.display(db, edition).to_string()),
-                        hir::ModuleDef::Macro(it) => Some(it.display(db, edition).to_string()),
+                        hir::ModuleDef::Module(it) => {
+                            Some(it.display(db, display_target).to_string())
+                        }
+                        hir::ModuleDef::Function(it) => {
+                            Some(it.display(db, display_target).to_string())
+                        }
+                        hir::ModuleDef::Adt(it) => Some(it.display(db, display_target).to_string()),
+                        hir::ModuleDef::Variant(it) => {
+                            Some(it.display(db, display_target).to_string())
+                        }
+                        hir::ModuleDef::Const(it) => {
+                            Some(it.display(db, display_target).to_string())
+                        }
+                        hir::ModuleDef::Static(it) => {
+                            Some(it.display(db, display_target).to_string())
+                        }
+                        hir::ModuleDef::Trait(it) => {
+                            Some(it.display(db, display_target).to_string())
+                        }
+                        hir::ModuleDef::TraitAlias(it) => {
+                            Some(it.display(db, display_target).to_string())
+                        }
+                        hir::ModuleDef::TypeAlias(it) => {
+                            Some(it.display(db, display_target).to_string())
+                        }
+                        hir::ModuleDef::Macro(it) => {
+                            Some(it.display(db, display_target).to_string())
+                        }
                         hir::ModuleDef::BuiltinType(_) => None,
                     },
                     docs: None,
@@ -353,12 +374,11 @@ impl ToNavFromAst for hir::TraitAlias {
 
 impl<D> TryToNav for D
 where
-    D: HasSource + ToNavFromAst + Copy + HasDocs + HirDisplay,
+    D: HasSource + ToNavFromAst + Copy + HasDocs + HirDisplay + HasCrate,
     D::Ast: ast::HasName,
 {
     fn try_to_nav(&self, db: &RootDatabase) -> Option<UpmappingResult<NavigationTarget>> {
         let src = self.source(db)?;
-        let edition = src.file_id.original_file(db).edition();
         Some(
             NavigationTarget::from_named(
                 db,
@@ -367,7 +387,8 @@ where
             )
             .map(|mut res| {
                 res.docs = self.docs(db);
-                res.description = Some(self.display(db, edition).to_string());
+                res.description =
+                    Some(self.display(db, self.krate(db).to_display_target(db)).to_string());
                 res.container_name = self.container_name(db);
                 res
             }),
@@ -439,7 +460,8 @@ impl TryToNav for hir::ExternCrateDecl {
         let focus = value
             .rename()
             .map_or_else(|| value.name_ref().map(Either::Left), |it| it.name().map(Either::Right));
-        let edition = self.module(db).krate().edition(db);
+        let krate = self.module(db).krate();
+        let edition = krate.edition(db);
 
         Some(orig_range_with_focus(db, file_id, value.syntax(), focus).map(
             |(FileRange { file_id, range: full_range }, focus_range)| {
@@ -455,7 +477,7 @@ impl TryToNav for hir::ExternCrateDecl {
                 );
 
                 res.docs = self.docs(db);
-                res.description = Some(self.display(db, edition).to_string());
+                res.description = Some(self.display(db, krate.to_display_target(db)).to_string());
                 res.container_name = container_name(db, *self, edition);
                 res
             },
@@ -466,14 +488,15 @@ impl TryToNav for hir::ExternCrateDecl {
 impl TryToNav for hir::Field {
     fn try_to_nav(&self, db: &RootDatabase) -> Option<UpmappingResult<NavigationTarget>> {
         let src = self.source(db)?;
-        let edition = self.parent_def(db).module(db).krate().edition(db);
+        let krate = self.parent_def(db).module(db).krate();
 
         let field_source = match &src.value {
             FieldSource::Named(it) => {
                 NavigationTarget::from_named(db, src.with_value(it), SymbolKind::Field).map(
                     |mut res| {
                         res.docs = self.docs(db);
-                        res.description = Some(self.display(db, edition).to_string());
+                        res.description =
+                            Some(self.display(db, krate.to_display_target(db)).to_string());
                         res
                     },
                 )
diff --git a/src/tools/rust-analyzer/crates/ide/src/rename.rs b/src/tools/rust-analyzer/crates/ide/src/rename.rs
index 3e8295e3f08..d0e1c2097a7 100644
--- a/src/tools/rust-analyzer/crates/ide/src/rename.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/rename.rs
@@ -446,6 +446,7 @@ mod tests {
     use expect_test::{expect, Expect};
     use ide_db::source_change::SourceChange;
     use ide_db::text_edit::TextEdit;
+    use itertools::Itertools;
     use stdx::trim_indent;
     use test_utils::assert_eq_text;
 
@@ -496,6 +497,31 @@ mod tests {
         };
     }
 
+    #[track_caller]
+    fn check_conflicts(new_name: &str, #[rust_analyzer::rust_fixture] ra_fixture: &str) {
+        let (analysis, position, conflicts) = fixture::annotations(ra_fixture);
+        let source_change = analysis.rename(position, new_name).unwrap().unwrap();
+        let expected_conflicts = conflicts
+            .into_iter()
+            .map(|(file_range, _)| (file_range.file_id, file_range.range))
+            .sorted_unstable_by_key(|(file_id, range)| (*file_id, range.start()))
+            .collect_vec();
+        let found_conflicts = source_change
+            .source_file_edits
+            .iter()
+            .flat_map(|(file_id, (edit, _))| {
+                edit.into_iter()
+                    .filter(|edit| edit.annotation.is_some())
+                    .map(move |edit| (*file_id, edit.delete))
+            })
+            .sorted_unstable_by_key(|(file_id, range)| (*file_id, range.start()))
+            .collect_vec();
+        assert_eq!(
+            expected_conflicts, found_conflicts,
+            "rename conflicts mismatch: {source_change:#?}"
+        );
+    }
+
     fn check_expect(
         new_name: &str,
         #[rust_analyzer::rust_fixture] ra_fixture: &str,
@@ -548,6 +574,37 @@ mod tests {
     }
 
     #[test]
+    fn rename_will_shadow() {
+        check_conflicts(
+            "new_name",
+            r#"
+fn foo() {
+    let mut new_name = 123;
+    let old_name$0 = 456;
+     // ^^^^^^^^
+    new_name = 789 + new_name;
+}
+        "#,
+        );
+    }
+
+    #[test]
+    fn rename_will_be_shadowed() {
+        check_conflicts(
+            "new_name",
+            r#"
+fn foo() {
+    let mut old_name$0 = 456;
+         // ^^^^^^^^
+    let new_name = 123;
+    old_name = 789 + old_name;
+ // ^^^^^^^^         ^^^^^^^^
+}
+        "#,
+        );
+    }
+
+    #[test]
     fn test_prepare_rename_namelikes() {
         check_prepare(r"fn name$0<'lifetime>() {}", expect![[r#"3..7: name"#]]);
         check_prepare(r"fn name<'lifetime$0>() {}", expect![[r#"9..17: lifetime"#]]);
@@ -1024,6 +1081,7 @@ mod foo$0;
                             Indel {
                                 insert: "foo2",
                                 delete: 4..7,
+                                annotation: None,
                             },
                         ],
                     ),
@@ -1071,6 +1129,7 @@ use crate::foo$0::FooContent;
                             Indel {
                                 insert: "quux",
                                 delete: 8..11,
+                                annotation: None,
                             },
                         ],
                     ),
@@ -1082,6 +1141,7 @@ use crate::foo$0::FooContent;
                             Indel {
                                 insert: "quux",
                                 delete: 11..14,
+                                annotation: None,
                             },
                         ],
                     ),
@@ -1123,6 +1183,7 @@ mod fo$0o;
                             Indel {
                                 insert: "foo2",
                                 delete: 4..7,
+                                annotation: None,
                             },
                         ],
                     ),
@@ -1171,6 +1232,7 @@ mod outer { mod fo$0o; }
                             Indel {
                                 insert: "bar",
                                 delete: 16..19,
+                                annotation: None,
                             },
                         ],
                     ),
@@ -1242,6 +1304,7 @@ pub mod foo$0;
                             Indel {
                                 insert: "foo2",
                                 delete: 27..30,
+                                annotation: None,
                             },
                         ],
                     ),
@@ -1253,6 +1316,7 @@ pub mod foo$0;
                             Indel {
                                 insert: "foo2",
                                 delete: 8..11,
+                                annotation: None,
                             },
                         ],
                     ),
@@ -1308,6 +1372,7 @@ mod quux;
                             Indel {
                                 insert: "foo2",
                                 delete: 4..7,
+                                annotation: None,
                             },
                         ],
                     ),
@@ -1441,10 +1506,12 @@ pub fn baz() {}
                             Indel {
                                 insert: "r#fn",
                                 delete: 4..7,
+                                annotation: None,
                             },
                             Indel {
                                 insert: "r#fn",
                                 delete: 22..25,
+                                annotation: None,
                             },
                         ],
                     ),
@@ -1509,10 +1576,12 @@ pub fn baz() {}
                             Indel {
                                 insert: "foo",
                                 delete: 4..8,
+                                annotation: None,
                             },
                             Indel {
                                 insert: "foo",
                                 delete: 23..27,
+                                annotation: None,
                             },
                         ],
                     ),
@@ -1574,6 +1643,7 @@ fn bar() {
                             Indel {
                                 insert: "dyn",
                                 delete: 7..10,
+                                annotation: None,
                             },
                         ],
                     ),
@@ -1585,6 +1655,7 @@ fn bar() {
                             Indel {
                                 insert: "r#dyn",
                                 delete: 18..21,
+                                annotation: None,
                             },
                         ],
                     ),
@@ -1614,6 +1685,7 @@ fn bar() {
                             Indel {
                                 insert: "r#dyn",
                                 delete: 7..10,
+                                annotation: None,
                             },
                         ],
                     ),
@@ -1625,6 +1697,7 @@ fn bar() {
                             Indel {
                                 insert: "dyn",
                                 delete: 18..21,
+                                annotation: None,
                             },
                         ],
                     ),
@@ -1654,6 +1727,7 @@ fn bar() {
                             Indel {
                                 insert: "r#dyn",
                                 delete: 7..10,
+                                annotation: None,
                             },
                         ],
                     ),
@@ -1665,6 +1739,7 @@ fn bar() {
                             Indel {
                                 insert: "dyn",
                                 delete: 18..21,
+                                annotation: None,
                             },
                         ],
                     ),
@@ -1701,10 +1776,12 @@ fn bar() {
                             Indel {
                                 insert: "abc",
                                 delete: 7..10,
+                                annotation: None,
                             },
                             Indel {
                                 insert: "abc",
                                 delete: 32..35,
+                                annotation: None,
                             },
                         ],
                     ),
@@ -1716,6 +1793,7 @@ fn bar() {
                             Indel {
                                 insert: "abc",
                                 delete: 18..23,
+                                annotation: None,
                             },
                         ],
                     ),
@@ -1749,10 +1827,12 @@ fn bar() {
                             Indel {
                                 insert: "abc",
                                 delete: 7..12,
+                                annotation: None,
                             },
                             Indel {
                                 insert: "abc",
                                 delete: 34..39,
+                                annotation: None,
                             },
                         ],
                     ),
@@ -1764,6 +1844,7 @@ fn bar() {
                             Indel {
                                 insert: "abc",
                                 delete: 18..21,
+                                annotation: None,
                             },
                         ],
                     ),
diff --git a/src/tools/rust-analyzer/crates/ide/src/runnables.rs b/src/tools/rust-analyzer/crates/ide/src/runnables.rs
index 509ae3204c3..b8deed01fb7 100644
--- a/src/tools/rust-analyzer/crates/ide/src/runnables.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/runnables.rs
@@ -9,6 +9,7 @@ use hir::{
 };
 use ide_assists::utils::{has_test_related_attribute, test_related_attribute_syn};
 use ide_db::{
+    base_db::SourceDatabase,
     defs::Definition,
     documentation::docs_from_attrs,
     helpers::visit_file_defs,
@@ -399,7 +400,8 @@ pub(crate) fn runnable_impl(
     sema: &Semantics<'_, RootDatabase>,
     def: &hir::Impl,
 ) -> Option<Runnable> {
-    let edition = def.module(sema.db).krate().edition(sema.db);
+    let display_target = def.module(sema.db).krate().to_display_target(sema.db);
+    let edition = display_target.edition;
     let attrs = def.attrs(sema.db);
     if !has_runnable_doc_test(&attrs) {
         return None;
@@ -408,7 +410,7 @@ pub(crate) fn runnable_impl(
     let nav = def.try_to_nav(sema.db)?.call_site();
     let ty = def.self_ty(sema.db);
     let adt_name = ty.as_adt()?.name(sema.db);
-    let mut ty_args = ty.generic_parameters(sema.db, edition).peekable();
+    let mut ty_args = ty.generic_parameters(sema.db, display_target).peekable();
     let params = if ty_args.peek().is_some() {
         format!("<{}>", ty_args.format_with(",", |ty, cb| cb(&ty)))
     } else {
@@ -494,7 +496,11 @@ fn module_def_doctest(db: &RootDatabase, def: Definition) -> Option<Runnable> {
         Definition::SelfType(it) => it.attrs(db),
         _ => return None,
     };
-    let edition = def.krate(db).map(|it| it.edition(db)).unwrap_or(Edition::CURRENT);
+    let krate = def.krate(db);
+    let edition = krate.map(|it| it.edition(db)).unwrap_or(Edition::CURRENT);
+    let display_target = krate
+        .unwrap_or_else(|| (*db.crate_graph().crates_in_topological_order().last().unwrap()).into())
+        .to_display_target(db);
     if !has_runnable_doc_test(&attrs) {
         return None;
     }
@@ -509,7 +515,7 @@ fn module_def_doctest(db: &RootDatabase, def: Definition) -> Option<Runnable> {
             if let Some(ty) = assoc_item.implementing_ty(db) {
                 if let Some(adt) = ty.as_adt() {
                     let name = adt.name(db);
-                    let mut ty_args = ty.generic_parameters(db, edition).peekable();
+                    let mut ty_args = ty.generic_parameters(db, display_target).peekable();
                     format_to!(path, "{}", name.display(db, edition));
                     if ty_args.peek().is_some() {
                         format_to!(path, "<{}>", ty_args.format_with(",", |ty, cb| cb(&ty)));
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 f9972116004..b5468a5aee9 100644
--- a/src/tools/rust-analyzer/crates/ide/src/signature_help.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/signature_help.rs
@@ -4,7 +4,9 @@
 use std::collections::BTreeSet;
 
 use either::Either;
-use hir::{AssocItem, GenericParam, HirDisplay, ModuleDef, PathResolution, Semantics, Trait};
+use hir::{
+    AssocItem, DisplayTarget, GenericParam, HirDisplay, ModuleDef, PathResolution, Semantics, Trait,
+};
 use ide_db::{
     active_parameter::{callable_for_node, generic_def_for_node},
     documentation::{Documentation, HasDocs},
@@ -82,6 +84,7 @@ pub(crate) fn signature_help(
     let token = sema.descend_into_macros_single_exact(token);
     let edition =
         sema.attach_first_edition(file_id).map(|it| it.edition()).unwrap_or(Edition::CURRENT);
+    let display_target = sema.first_crate_or_default(file_id).to_display_target(db);
 
     for node in token.parent_ancestors() {
         match_ast! {
@@ -91,49 +94,49 @@ pub(crate) fn signature_help(
                     if cursor_outside {
                         continue;
                     }
-                    return signature_help_for_call(&sema, arg_list, token, edition);
+                    return signature_help_for_call(&sema, arg_list, token, edition, display_target);
                 },
                 ast::GenericArgList(garg_list) => {
                     let cursor_outside = garg_list.r_angle_token().as_ref() == Some(&token);
                     if cursor_outside {
                         continue;
                     }
-                    return signature_help_for_generics(&sema, garg_list, token, edition);
+                    return signature_help_for_generics(&sema, garg_list, token, edition, display_target);
                 },
                 ast::RecordExpr(record) => {
                     let cursor_outside = record.record_expr_field_list().and_then(|list| list.r_curly_token()).as_ref() == Some(&token);
                     if cursor_outside {
                         continue;
                     }
-                    return signature_help_for_record_lit(&sema, record, token, edition);
+                    return signature_help_for_record_lit(&sema, record, token, edition, display_target);
                 },
                 ast::RecordPat(record) => {
                     let cursor_outside = record.record_pat_field_list().and_then(|list| list.r_curly_token()).as_ref() == Some(&token);
                     if cursor_outside {
                         continue;
                     }
-                    return signature_help_for_record_pat(&sema, record, token, edition);
+                    return signature_help_for_record_pat(&sema, record, token, edition, display_target);
                 },
                 ast::TupleStructPat(tuple_pat) => {
                     let cursor_outside = tuple_pat.r_paren_token().as_ref() == Some(&token);
                     if cursor_outside {
                         continue;
                     }
-                    return signature_help_for_tuple_struct_pat(&sema, tuple_pat, token, edition);
+                    return signature_help_for_tuple_struct_pat(&sema, tuple_pat, token, edition, display_target);
                 },
                 ast::TuplePat(tuple_pat) => {
                     let cursor_outside = tuple_pat.r_paren_token().as_ref() == Some(&token);
                     if cursor_outside {
                         continue;
                     }
-                    return signature_help_for_tuple_pat(&sema, tuple_pat, token, edition);
+                    return signature_help_for_tuple_pat(&sema, tuple_pat, token, display_target);
                 },
                 ast::TupleExpr(tuple_expr) => {
                     let cursor_outside = tuple_expr.r_paren_token().as_ref() == Some(&token);
                     if cursor_outside {
                         continue;
                     }
-                    return signature_help_for_tuple_expr(&sema, tuple_expr, token, edition);
+                    return signature_help_for_tuple_expr(&sema, tuple_expr, token, display_target);
                 },
                 _ => (),
             }
@@ -158,6 +161,7 @@ fn signature_help_for_call(
     arg_list: ast::ArgList,
     token: SyntaxToken,
     edition: Edition,
+    display_target: DisplayTarget,
 ) -> Option<SignatureHelp> {
     // Find the calling expression and its NameRef
     let mut nodes = arg_list.syntax().ancestors().skip(1);
@@ -221,7 +225,7 @@ fn signature_help_for_call(
     res.signature.push('(');
     {
         if let Some((self_param, _)) = callable.receiver_param(db) {
-            format_to!(res.signature, "{}", self_param.display(db, edition))
+            format_to!(res.signature, "{}", self_param.display(db, display_target))
         }
         let mut buf = String::new();
         for (idx, p) in callable.params().into_iter().enumerate() {
@@ -242,9 +246,9 @@ fn signature_help_for_call(
             // (see FIXME in tests::impl_trait) and falling back on any unknowns.
             match (p.ty().contains_unknown(), fn_params.as_deref()) {
                 (true, Some(fn_params)) => {
-                    format_to!(buf, "{}", fn_params[idx].ty().display(db, edition))
+                    format_to!(buf, "{}", fn_params[idx].ty().display(db, display_target))
                 }
-                _ => format_to!(buf, "{}", p.ty().display(db, edition)),
+                _ => format_to!(buf, "{}", p.ty().display(db, display_target)),
             }
             res.push_call_param(&buf);
         }
@@ -253,7 +257,7 @@ fn signature_help_for_call(
 
     let mut render = |ret_type: hir::Type| {
         if !ret_type.is_unit() {
-            format_to!(res.signature, " -> {}", ret_type.display(db, edition));
+            format_to!(res.signature, " -> {}", ret_type.display(db, display_target));
         }
     };
     match callable.kind() {
@@ -274,6 +278,7 @@ fn signature_help_for_generics(
     arg_list: ast::GenericArgList,
     token: SyntaxToken,
     edition: Edition,
+    display_target: DisplayTarget,
 ) -> Option<SignatureHelp> {
     let (generics_def, mut active_parameter, first_arg_is_non_lifetime, variant) =
         generic_def_for_node(sema, &arg_list, &token)?;
@@ -345,7 +350,7 @@ fn signature_help_for_generics(
         }
 
         buf.clear();
-        format_to!(buf, "{}", param.display(db, edition));
+        format_to!(buf, "{}", param.display(db, display_target));
         res.push_generic_param(&buf);
     }
     if let hir::GenericDef::Trait(tr) = generics_def {
@@ -400,6 +405,7 @@ fn signature_help_for_record_lit(
     record: ast::RecordExpr,
     token: SyntaxToken,
     edition: Edition,
+    display_target: DisplayTarget,
 ) -> Option<SignatureHelp> {
     signature_help_for_record_(
         sema,
@@ -412,6 +418,7 @@ fn signature_help_for_record_lit(
             .map(|(field, _, ty)| (field, ty)),
         token,
         edition,
+        display_target,
     )
 }
 
@@ -420,6 +427,7 @@ fn signature_help_for_record_pat(
     record: ast::RecordPat,
     token: SyntaxToken,
     edition: Edition,
+    display_target: DisplayTarget,
 ) -> Option<SignatureHelp> {
     signature_help_for_record_(
         sema,
@@ -431,6 +439,7 @@ fn signature_help_for_record_pat(
             .filter_map(|field| sema.resolve_record_pat_field(&field)),
         token,
         edition,
+        display_target,
     )
 }
 
@@ -439,6 +448,7 @@ fn signature_help_for_tuple_struct_pat(
     pat: ast::TupleStructPat,
     token: SyntaxToken,
     edition: Edition,
+    display_target: DisplayTarget,
 ) -> Option<SignatureHelp> {
     let path = pat.path()?;
     let path_res = sema.resolve_path(&path)?;
@@ -484,7 +494,7 @@ fn signature_help_for_tuple_struct_pat(
         token,
         pat.fields(),
         fields.into_iter().map(|it| it.ty(db)),
-        edition,
+        display_target,
     ))
 }
 
@@ -492,7 +502,7 @@ fn signature_help_for_tuple_pat(
     sema: &Semantics<'_, RootDatabase>,
     pat: ast::TuplePat,
     token: SyntaxToken,
-    edition: Edition,
+    display_target: DisplayTarget,
 ) -> Option<SignatureHelp> {
     let db = sema.db;
     let field_pats = pat.fields();
@@ -512,7 +522,7 @@ fn signature_help_for_tuple_pat(
         token,
         field_pats,
         fields.into_iter(),
-        edition,
+        display_target,
     ))
 }
 
@@ -520,7 +530,7 @@ fn signature_help_for_tuple_expr(
     sema: &Semantics<'_, RootDatabase>,
     expr: ast::TupleExpr,
     token: SyntaxToken,
-    edition: Edition,
+    display_target: DisplayTarget,
 ) -> Option<SignatureHelp> {
     let active_parameter = Some(
         expr.syntax()
@@ -542,7 +552,7 @@ fn signature_help_for_tuple_expr(
     let fields = expr.original.tuple_fields(db);
     let mut buf = String::new();
     for ty in fields {
-        format_to!(buf, "{}", ty.display_truncated(db, Some(20), edition));
+        format_to!(buf, "{}", ty.display_truncated(db, Some(20), display_target));
         res.push_call_param(&buf);
         buf.clear();
     }
@@ -557,6 +567,7 @@ fn signature_help_for_record_(
     fields2: impl Iterator<Item = (hir::Field, hir::Type)>,
     token: SyntaxToken,
     edition: Edition,
+    display_target: DisplayTarget,
 ) -> Option<SignatureHelp> {
     let active_parameter = field_list_children
         .filter_map(NodeOrToken::into_token)
@@ -617,7 +628,7 @@ fn signature_help_for_record_(
             buf,
             "{}: {}",
             name.display(db, edition),
-            ty.display_truncated(db, Some(20), edition)
+            ty.display_truncated(db, Some(20), display_target)
         );
         res.push_record_field(&buf);
         buf.clear();
@@ -632,7 +643,7 @@ fn signature_help_for_record_(
             buf,
             "{}: {}",
             name.display(db, edition),
-            field.ty(db).display_truncated(db, Some(20), edition)
+            field.ty(db).display_truncated(db, Some(20), display_target)
         );
         res.push_record_field(&buf);
         buf.clear();
@@ -648,7 +659,7 @@ fn signature_help_for_tuple_pat_ish(
     token: SyntaxToken,
     mut field_pats: AstChildren<ast::Pat>,
     fields: impl ExactSizeIterator<Item = hir::Type>,
-    edition: Edition,
+    display_target: DisplayTarget,
 ) -> SignatureHelp {
     let rest_pat = field_pats.find(|it| matches!(it, ast::Pat::RestPat(_)));
     let is_left_of_rest_pat =
@@ -675,7 +686,7 @@ fn signature_help_for_tuple_pat_ish(
 
     let mut buf = String::new();
     for ty in fields {
-        format_to!(buf, "{}", ty.display_truncated(db, Some(20), edition));
+        format_to!(buf, "{}", ty.display_truncated(db, Some(20), display_target));
         res.push_call_param(&buf);
         buf.clear();
     }
diff --git a/src/tools/rust-analyzer/crates/ide/src/ssr.rs b/src/tools/rust-analyzer/crates/ide/src/ssr.rs
index 77a011cac19..90e350949b8 100644
--- a/src/tools/rust-analyzer/crates/ide/src/ssr.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/ssr.rs
@@ -139,6 +139,7 @@ mod tests {
                                         Indel {
                                             insert: "3",
                                             delete: 33..34,
+                                            annotation: None,
                                         },
                                     ],
                                 },
@@ -147,6 +148,8 @@ mod tests {
                         },
                         file_system_edits: [],
                         is_snippet: false,
+                        annotations: {},
+                        next_annotation_id: 0,
                     },
                 ),
                 command: None,
@@ -179,6 +182,7 @@ mod tests {
                                         Indel {
                                             insert: "3",
                                             delete: 33..34,
+                                            annotation: None,
                                         },
                                     ],
                                 },
@@ -192,6 +196,7 @@ mod tests {
                                         Indel {
                                             insert: "3",
                                             delete: 11..12,
+                                            annotation: None,
                                         },
                                     ],
                                 },
@@ -200,6 +205,8 @@ mod tests {
                         },
                         file_system_edits: [],
                         is_snippet: false,
+                        annotations: {},
+                        next_annotation_id: 0,
                     },
                 ),
                 command: None,
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 41957bad7e0..332aecf1e3c 100644
--- a/src/tools/rust-analyzer/crates/ide/src/static_index.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/static_index.rs
@@ -3,7 +3,7 @@
 
 use hir::{db::HirDatabase, Crate, HirFileIdExt, Module, Semantics};
 use ide_db::{
-    base_db::{SourceRootDatabase, VfsPath},
+    base_db::{SourceDatabase, SourceRootDatabase, VfsPath},
     defs::Definition,
     documentation::Documentation,
     famous_defs::FamousDefs,
@@ -118,7 +118,11 @@ fn documentation_for_definition(
     def.docs(
         sema.db,
         famous_defs.as_ref(),
-        def.krate(sema.db).map(|it| it.edition(sema.db)).unwrap_or(Edition::CURRENT),
+        def.krate(sema.db)
+            .unwrap_or_else(|| {
+                (*sema.db.crate_graph().crates_in_topological_order().last().unwrap()).into()
+            })
+            .to_display_target(sema.db),
     )
 }
 
@@ -173,6 +177,7 @@ impl StaticIndex<'_> {
         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 display_target = sema.first_crate_or_default(file_id).to_display_target(self.db);
         let tokens = root.descendants_with_tokens().filter_map(|it| match it {
             syntax::NodeOrToken::Node(_) => None,
             syntax::NodeOrToken::Token(it) => Some(it),
@@ -213,6 +218,7 @@ impl StaticIndex<'_> {
                         false,
                         &hover_config,
                         edition,
+                        display_target,
                     )),
                     definition: def.try_to_nav(self.db).map(UpmappingResult::call_site).map(|it| {
                         FileRange { file_id: it.file_id, range: it.focus_or_full_range() }
@@ -222,7 +228,7 @@ impl StaticIndex<'_> {
                     display_name: def
                         .name(self.db)
                         .map(|name| name.display(self.db, edition).to_string()),
-                    signature: Some(def.label(self.db, edition)),
+                    signature: Some(def.label(self.db, display_target)),
                     kind: def_to_kind(self.db, def),
                 });
                 self.def_map.insert(def, it);
diff --git a/src/tools/rust-analyzer/crates/ide/src/status.rs b/src/tools/rust-analyzer/crates/ide/src/status.rs
index f8ecaa8fdf2..a44be67668c 100644
--- a/src/tools/rust-analyzer/crates/ide/src/status.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/status.rs
@@ -268,8 +268,7 @@ struct AttrsStats {
 
 impl fmt::Display for AttrsStats {
     fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
-        let size =
-            self.entries * std::mem::size_of::<Attrs>() + self.total * std::mem::size_of::<Attr>();
+        let size = self.entries * size_of::<Attrs>() + self.total * size_of::<Attr>();
         let size = Bytes::new(size as _);
         write!(
             fmt,
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 519133e3ad1..83082496d5b 100644
--- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting.rs
@@ -14,8 +14,11 @@ mod tests;
 
 use std::ops::ControlFlow;
 
-use hir::{HirFileIdExt, InFile, InRealFile, MacroFileIdExt, MacroKind, Name, Semantics};
-use ide_db::{FxHashMap, Ranker, RootDatabase, SymbolKind};
+use either::Either;
+use hir::{
+    DefWithBody, HirFileIdExt, InFile, InRealFile, MacroFileIdExt, MacroKind, Name, Semantics,
+};
+use ide_db::{FxHashMap, FxHashSet, Ranker, RootDatabase, SymbolKind};
 use span::EditionedFileId;
 use syntax::{
     ast::{self, IsString},
@@ -232,7 +235,6 @@ fn traverse(
     range_to_highlight: TextRange,
 ) {
     let is_unlinked = sema.file_to_module_def(file_id).is_none();
-    let mut bindings_shadow_count: FxHashMap<Name, u32> = FxHashMap::default();
 
     enum AttrOrDerive {
         Attr(ast::Item),
@@ -247,13 +249,22 @@ fn traverse(
         }
     }
 
+    let empty = FxHashSet::default();
+
+    // FIXME: accommodate range highlighting
     let mut tt_level = 0;
+    // FIXME: accommodate range highlighting
     let mut attr_or_derive_item = None;
 
     // FIXME: these are not perfectly accurate, we determine them by the real file's syntax tree
     // an attribute nested in a macro call will not emit `inside_attribute`
     let mut inside_attribute = false;
 
+    // FIXME: accommodate range highlighting
+    let mut body_stack: Vec<Option<DefWithBody>> = vec![];
+    let mut per_body_cache: FxHashMap<DefWithBody, (FxHashSet<_>, FxHashMap<Name, u32>)> =
+        FxHashMap::default();
+
     // Walk all nodes, keeping track of whether we are inside a macro or not.
     // If in macro, expand it first and highlight the expanded code.
     let mut preorder = root.preorder_with_tokens();
@@ -282,48 +293,68 @@ fn traverse(
             Leave(NodeOrToken::Node(node)) if ast::Attr::can_cast(node.kind()) => {
                 inside_attribute = false
             }
-
             Enter(NodeOrToken::Node(node)) => {
-                if let Some(item) = ast::Item::cast(node.clone()) {
+                if let Some(item) = <Either<ast::Item, ast::Variant>>::cast(node.clone()) {
                     match item {
-                        ast::Item::Fn(_) | ast::Item::Const(_) | ast::Item::Static(_) => {
-                            bindings_shadow_count.clear()
-                        }
-                        _ => (),
-                    }
-
-                    if attr_or_derive_item.is_none() {
-                        if sema.is_attr_macro_call(InFile::new(file_id.into(), &item)) {
-                            attr_or_derive_item = Some(AttrOrDerive::Attr(item));
-                        } else {
-                            let adt = match item {
-                                ast::Item::Enum(it) => Some(ast::Adt::Enum(it)),
-                                ast::Item::Struct(it) => Some(ast::Adt::Struct(it)),
-                                ast::Item::Union(it) => Some(ast::Adt::Union(it)),
-                                _ => None,
-                            };
-                            match adt {
-                                Some(adt)
-                                    if sema
-                                        .is_derive_annotated(InFile::new(file_id.into(), &adt)) =>
-                                {
-                                    attr_or_derive_item =
-                                        Some(AttrOrDerive::Derive(ast::Item::from(adt)));
+                        Either::Left(item) => {
+                            match &item {
+                                ast::Item::Fn(it) => {
+                                    body_stack.push(sema.to_def(it).map(Into::into))
+                                }
+                                ast::Item::Const(it) => {
+                                    body_stack.push(sema.to_def(it).map(Into::into))
+                                }
+                                ast::Item::Static(it) => {
+                                    body_stack.push(sema.to_def(it).map(Into::into))
                                 }
                                 _ => (),
                             }
+
+                            if attr_or_derive_item.is_none() {
+                                if sema.is_attr_macro_call(InFile::new(file_id.into(), &item)) {
+                                    attr_or_derive_item = Some(AttrOrDerive::Attr(item));
+                                } else {
+                                    let adt = match item {
+                                        ast::Item::Enum(it) => Some(ast::Adt::Enum(it)),
+                                        ast::Item::Struct(it) => Some(ast::Adt::Struct(it)),
+                                        ast::Item::Union(it) => Some(ast::Adt::Union(it)),
+                                        _ => None,
+                                    };
+                                    match adt {
+                                        Some(adt)
+                                            if sema.is_derive_annotated(InFile::new(
+                                                file_id.into(),
+                                                &adt,
+                                            )) =>
+                                        {
+                                            attr_or_derive_item =
+                                                Some(AttrOrDerive::Derive(ast::Item::from(adt)));
+                                        }
+                                        _ => (),
+                                    }
+                                }
+                            }
                         }
+                        Either::Right(it) => body_stack.push(sema.to_def(&it).map(Into::into)),
                     }
                 }
             }
-            Leave(NodeOrToken::Node(node)) if ast::Item::can_cast(node.kind()) => {
+            Leave(NodeOrToken::Node(node))
+                if <Either<ast::Item, ast::Variant>>::can_cast(node.kind()) =>
+            {
                 match ast::Item::cast(node.clone()) {
-                    Some(item)
-                        if attr_or_derive_item.as_ref().is_some_and(|it| *it.item() == item) =>
-                    {
-                        attr_or_derive_item = None;
+                    Some(item) => {
+                        if attr_or_derive_item.as_ref().is_some_and(|it| *it.item() == item) {
+                            attr_or_derive_item = None;
+                        }
+                        if matches!(
+                            item,
+                            ast::Item::Fn(_) | ast::Item::Const(_) | ast::Item::Static(_)
+                        ) {
+                            body_stack.pop();
+                        }
                     }
-                    _ => (),
+                    None => _ = body_stack.pop(),
                 }
             }
             _ => (),
@@ -361,16 +392,22 @@ fn traverse(
                 None => false,
             };
 
-        let descended_element = if in_macro {
+        let (descended_element, current_body) = match element {
             // Attempt to descend tokens into macro-calls.
-            match element {
-                NodeOrToken::Token(token) => descend_token(sema, InRealFile::new(file_id, token)),
-                n => InFile::new(file_id.into(), n),
+            NodeOrToken::Token(token) if in_macro => {
+                let descended = descend_token(sema, InRealFile::new(file_id, token));
+                let body = match &descended.value {
+                    NodeOrToken::Node(n) => {
+                        sema.body_for(InFile::new(descended.file_id, n.syntax()))
+                    }
+                    NodeOrToken::Token(t) => {
+                        t.parent().and_then(|it| sema.body_for(InFile::new(descended.file_id, &it)))
+                    }
+                };
+                (descended, body)
             }
-        } else {
-            InFile::new(file_id.into(), element)
+            n => (InFile::new(file_id.into(), n), body_stack.last().copied().flatten()),
         };
-
         // string highlight injections
         if let (Some(original_token), Some(descended_token)) =
             (original_token, descended_element.value.as_token())
@@ -390,12 +427,24 @@ fn traverse(
         }
 
         let edition = descended_element.file_id.edition(sema.db);
+        let (unsafe_ops, bindings_shadow_count) = match current_body {
+            Some(current_body) => {
+                let (ops, bindings) = per_body_cache
+                    .entry(current_body)
+                    .or_insert_with(|| (sema.get_unsafe_ops(current_body), Default::default()));
+                (&*ops, Some(bindings))
+            }
+            None => (&empty, None),
+        };
+        let is_unsafe_node =
+            |node| unsafe_ops.contains(&InFile::new(descended_element.file_id, node));
         let element = match descended_element.value {
             NodeOrToken::Node(name_like) => {
                 let hl = highlight::name_like(
                     sema,
                     krate,
-                    &mut bindings_shadow_count,
+                    bindings_shadow_count,
+                    &is_unsafe_node,
                     config.syntactic_name_ref_highlighting,
                     name_like,
                     edition,
@@ -408,7 +457,8 @@ fn traverse(
                 hl
             }
             NodeOrToken::Token(token) => {
-                highlight::token(sema, token, edition, tt_level > 0).zip(Some(None))
+                highlight::token(sema, token, edition, &is_unsafe_node, tt_level > 0)
+                    .zip(Some(None))
             }
         };
         if let Some((mut highlight, binding_hash)) = element {
@@ -543,7 +593,7 @@ fn filter_by_config(highlight: &mut Highlight, config: HighlightConfig) -> bool
                 *tag = HlTag::Punctuation(HlPunct::Other);
             }
         }
-        HlTag::Punctuation(_) if !config.punctuation => return false,
+        HlTag::Punctuation(_) if !config.punctuation && highlight.mods.is_empty() => return false,
         tag @ HlTag::Punctuation(_) if !config.specialize_punctuation => {
             *tag = HlTag::Punctuation(HlPunct::Other);
         }
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 c63043621c2..cc02aff2acf 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
@@ -41,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), edition),
+                    highlight: highlight_def(sema, krate, Definition::from(res), edition, true),
                     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 127861a04bd..282fbb4433b 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
@@ -1,17 +1,20 @@
 //! Computes color for a single element.
 
+use std::ops::ControlFlow;
+
 use either::Either;
 use hir::{AsAssocItem, HasVisibility, MacroFileIdExt, Semantics};
 use ide_db::{
     defs::{Definition, IdentClass, NameClass, NameRefClass},
+    syntax_helpers::node_ext::walk_pat,
     FxHashMap, RootDatabase, SymbolKind,
 };
 use span::Edition;
 use stdx::hash_once;
 use syntax::{
-    ast, match_ast, AstNode, AstToken, NodeOrToken,
+    ast, match_ast, AstNode, AstPtr, AstToken, NodeOrToken,
     SyntaxKind::{self, *},
-    SyntaxNode, SyntaxToken, T,
+    SyntaxNode, SyntaxNodePtr, SyntaxToken, T,
 };
 
 use crate::{
@@ -23,6 +26,7 @@ pub(super) fn token(
     sema: &Semantics<'_, RootDatabase>,
     token: SyntaxToken,
     edition: Edition,
+    is_unsafe_node: &impl Fn(AstPtr<Either<ast::Expr, ast::Pat>>) -> bool,
     in_tt: bool,
 ) -> Option<Highlight> {
     if let Some(comment) = ast::Comment::cast(token.clone()) {
@@ -33,11 +37,8 @@ pub(super) fn token(
         });
     }
 
-    let highlight: Highlight = match token.kind() {
+    let h = match token.kind() {
         STRING | BYTE_STRING | C_STRING => HlTag::StringLiteral.into(),
-        INT_NUMBER if token.parent_ancestors().nth(1).map(|it| it.kind()) == Some(FIELD_EXPR) => {
-            SymbolKind::Field.into()
-        }
         INT_NUMBER | FLOAT_NUMBER => HlTag::NumericLiteral.into(),
         BYTE => HlTag::ByteLiteral.into(),
         CHAR => HlTag::CharLiteral.into(),
@@ -46,24 +47,25 @@ pub(super) fn token(
             // that were not mapped down into macro invocations
             HlTag::None.into()
         }
-        p if p.is_punct() => punctuation(sema, token, p),
+        p if p.is_punct() => punctuation(sema, token, p, is_unsafe_node),
         k if k.is_keyword(edition) => {
             if in_tt && token.prev_token().is_some_and(|t| t.kind() == T![$]) {
                 // we are likely within a macro definition where our keyword is a fragment name
                 HlTag::None.into()
             } else {
-                keyword(sema, token, k)?
+                keyword(token, k)
             }
         }
         _ => return None,
     };
-    Some(highlight)
+    Some(h)
 }
 
 pub(super) fn name_like(
     sema: &Semantics<'_, RootDatabase>,
     krate: hir::Crate,
-    bindings_shadow_count: &mut FxHashMap<hir::Name, u32>,
+    bindings_shadow_count: Option<&mut FxHashMap<hir::Name, u32>>,
+    is_unsafe_node: &impl Fn(AstPtr<Either<ast::Expr, ast::Pat>>) -> bool,
     syntactic_name_ref_highlighting: bool,
     name_like: ast::NameLike,
     edition: Edition,
@@ -75,19 +77,26 @@ pub(super) fn name_like(
             krate,
             bindings_shadow_count,
             &mut binding_hash,
+            is_unsafe_node,
             syntactic_name_ref_highlighting,
             name_ref,
             edition,
         ),
-        ast::NameLike::Name(name) => {
-            highlight_name(sema, bindings_shadow_count, &mut binding_hash, krate, name, edition)
-        }
+        ast::NameLike::Name(name) => highlight_name(
+            sema,
+            bindings_shadow_count,
+            &mut binding_hash,
+            is_unsafe_node,
+            krate,
+            name,
+            edition,
+        ),
         ast::NameLike::Lifetime(lifetime) => match IdentClass::classify_lifetime(sema, &lifetime) {
             Some(IdentClass::NameClass(NameClass::Definition(def))) => {
-                highlight_def(sema, krate, def, edition) | HlMod::Definition
+                highlight_def(sema, krate, def, edition, false) | HlMod::Definition
             }
             Some(IdentClass::NameRefClass(NameRefClass::Definition(def, _))) => {
-                highlight_def(sema, krate, def, edition)
+                highlight_def(sema, krate, def, edition, true)
             }
             // FIXME: Fallback for '_, as we do not resolve these yet
             _ => SymbolKind::LifetimeParam.into(),
@@ -100,44 +109,49 @@ fn punctuation(
     sema: &Semantics<'_, RootDatabase>,
     token: SyntaxToken,
     kind: SyntaxKind,
+    is_unsafe_node: &impl Fn(AstPtr<Either<ast::Expr, ast::Pat>>) -> bool,
 ) -> Highlight {
-    let parent = token.parent();
-    let parent_kind = parent.as_ref().map_or(EOF, SyntaxNode::kind);
-    match (kind, parent_kind) {
+    let operator_parent = token.parent();
+    let parent_kind = operator_parent.as_ref().map_or(EOF, SyntaxNode::kind);
+    let h = match (kind, parent_kind) {
         (T![?], TRY_EXPR) => HlTag::Operator(HlOperator::Other) | HlMod::ControlFlow,
         (T![&], BIN_EXPR) => HlOperator::Bitwise.into(),
-        (T![&], REF_EXPR) => {
-            let h = HlTag::Operator(HlOperator::Other).into();
-            let is_unsafe = parent
-                .and_then(ast::RefExpr::cast)
-                .map(|ref_expr| sema.is_unsafe_ref_expr(&ref_expr));
-            if let Some(true) = is_unsafe {
-                h | HlMod::Unsafe
+        (T![&], REF_EXPR | REF_PAT) => HlTag::Operator(HlOperator::Other).into(),
+        (T![..] | T![..=], _) => match token.parent().and_then(ast::Pat::cast) {
+            Some(pat) if is_unsafe_node(AstPtr::new(&pat).wrap_right()) => {
+                Highlight::from(HlOperator::Other) | HlMod::Unsafe
+            }
+            _ => HlOperator::Other.into(),
+        },
+        (T![::] | T![->] | T![=>] | T![=] | T![@] | T![.], _) => HlOperator::Other.into(),
+        (T![!], MACRO_CALL) => {
+            if operator_parent
+                .and_then(ast::MacroCall::cast)
+                .is_some_and(|macro_call| sema.is_unsafe_macro_call(&macro_call))
+            {
+                Highlight::from(HlPunct::MacroBang) | HlMod::Unsafe
             } else {
-                h
+                HlPunct::MacroBang.into()
             }
         }
-        (T![::] | T![->] | T![=>] | T![..] | T![..=] | T![=] | T![@] | T![.], _) => {
-            HlOperator::Other.into()
-        }
-        (T![!], MACRO_CALL | MACRO_RULES) => HlPunct::MacroBang.into(),
+        (T![!], MACRO_RULES) => HlPunct::MacroBang.into(),
         (T![!], NEVER_TYPE) => HlTag::BuiltinType.into(),
         (T![!], PREFIX_EXPR) => HlOperator::Logical.into(),
         (T![*], PTR_TYPE) => HlTag::Keyword.into(),
         (T![*], PREFIX_EXPR) => {
-            let is_raw_ptr = (|| {
-                let prefix_expr = parent.and_then(ast::PrefixExpr::cast)?;
-                let expr = prefix_expr.expr()?;
-                sema.type_of_expr(&expr)?.original.is_raw_ptr().then_some(())
-            })();
-            if let Some(()) = is_raw_ptr {
-                HlTag::Operator(HlOperator::Other) | HlMod::Unsafe
+            let h = HlTag::Operator(HlOperator::Other).into();
+            let ptr = operator_parent
+                .as_ref()
+                .and_then(|it| AstPtr::try_from_raw(SyntaxNodePtr::new(it)));
+            if ptr.is_some_and(is_unsafe_node) {
+                h | HlMod::Unsafe
             } else {
-                HlOperator::Other.into()
+                h
             }
         }
         (T![-], PREFIX_EXPR) => {
-            let prefix_expr = parent.and_then(ast::PrefixExpr::cast).and_then(|e| e.expr());
+            let prefix_expr =
+                operator_parent.and_then(ast::PrefixExpr::cast).and_then(|e| e.expr());
             match prefix_expr {
                 Some(ast::Expr::Literal(_)) => HlTag::NumericLiteral,
                 _ => HlTag::Operator(HlOperator::Other),
@@ -157,36 +171,90 @@ fn punctuation(
             HlOperator::Comparison.into()
         }
         (_, ATTR) => HlTag::AttributeBracket.into(),
+        (T![>], _)
+            if operator_parent
+                .as_ref()
+                .and_then(SyntaxNode::parent)
+                .is_some_and(|it| it.kind() == MACRO_RULES) =>
+        {
+            HlOperator::Other.into()
+        }
         (kind, _) => match kind {
-            T!['['] | T![']'] => HlPunct::Bracket,
-            T!['{'] | T!['}'] => HlPunct::Brace,
-            T!['('] | T![')'] => HlPunct::Parenthesis,
-            T![>]
-                if parent
+            T!['['] | T![']'] => {
+                let is_unsafe_macro = operator_parent
                     .as_ref()
-                    .and_then(SyntaxNode::parent)
-                    .is_some_and(|it| it.kind() == MACRO_RULES) =>
-            {
-                return HlOperator::Other.into()
+                    .and_then(|it| ast::TokenTree::cast(it.clone())?.syntax().parent())
+                    .and_then(ast::MacroCall::cast)
+                    .is_some_and(|macro_call| sema.is_unsafe_macro_call(&macro_call));
+                let is_unsafe = is_unsafe_macro
+                    || operator_parent
+                        .as_ref()
+                        .and_then(|it| AstPtr::try_from_raw(SyntaxNodePtr::new(it)))
+                        .is_some_and(is_unsafe_node);
+                if is_unsafe {
+                    return Highlight::from(HlPunct::Bracket) | HlMod::Unsafe;
+                } else {
+                    HlPunct::Bracket
+                }
+            }
+            T!['{'] | T!['}'] => {
+                let is_unsafe_macro = operator_parent
+                    .as_ref()
+                    .and_then(|it| ast::TokenTree::cast(it.clone())?.syntax().parent())
+                    .and_then(ast::MacroCall::cast)
+                    .is_some_and(|macro_call| sema.is_unsafe_macro_call(&macro_call));
+                let is_unsafe = is_unsafe_macro
+                    || operator_parent
+                        .as_ref()
+                        .and_then(|it| AstPtr::try_from_raw(SyntaxNodePtr::new(it)))
+                        .is_some_and(is_unsafe_node);
+                if is_unsafe {
+                    return Highlight::from(HlPunct::Brace) | HlMod::Unsafe;
+                } else {
+                    HlPunct::Brace
+                }
+            }
+            T!['('] | T![')'] => {
+                let is_unsafe_macro = operator_parent
+                    .as_ref()
+                    .and_then(|it| ast::TokenTree::cast(it.clone())?.syntax().parent())
+                    .and_then(ast::MacroCall::cast)
+                    .is_some_and(|macro_call| sema.is_unsafe_macro_call(&macro_call));
+                let is_unsafe = is_unsafe_macro
+                    || operator_parent
+                        .and_then(|it| {
+                            if ast::ArgList::can_cast(it.kind()) {
+                                it.parent()
+                            } else {
+                                Some(it)
+                            }
+                        })
+                        .and_then(|it| AstPtr::try_from_raw(SyntaxNodePtr::new(&it)))
+                        .is_some_and(is_unsafe_node);
+
+                if is_unsafe {
+                    return Highlight::from(HlPunct::Parenthesis) | HlMod::Unsafe;
+                } else {
+                    HlPunct::Parenthesis
+                }
             }
             T![<] | T![>] => HlPunct::Angle,
-            T![,] => HlPunct::Comma,
+            // Early return as otherwise we'd highlight these in
+            // asm expressions
+            T![,] => return HlPunct::Comma.into(),
             T![:] => HlPunct::Colon,
             T![;] => HlPunct::Semi,
             T![.] => HlPunct::Dot,
             _ => HlPunct::Other,
         }
         .into(),
-    }
+    };
+    h
 }
 
-fn keyword(
-    sema: &Semantics<'_, RootDatabase>,
-    token: SyntaxToken,
-    kind: SyntaxKind,
-) -> Option<Highlight> {
+fn keyword(token: SyntaxToken, kind: SyntaxKind) -> Highlight {
     let h = Highlight::new(HlTag::Keyword);
-    let h = match kind {
+    match kind {
         T![await] => h | HlMod::Async | HlMod::ControlFlow,
         T![async] => h | HlMod::Async,
         T![break]
@@ -202,53 +270,33 @@ fn keyword(
         T![do] | T![yeet] if parent_matches::<ast::YeetExpr>(&token) => h | HlMod::ControlFlow,
         T![for] if parent_matches::<ast::ForExpr>(&token) => h | HlMod::ControlFlow,
         T![unsafe] => h | HlMod::Unsafe,
-        T![const]
-            if token.parent().is_some_and(|it| {
-                matches!(
-                    it.kind(),
-                    SyntaxKind::CONST
-                        | SyntaxKind::FN
-                        | SyntaxKind::IMPL
-                        | SyntaxKind::BLOCK_EXPR
-                        | SyntaxKind::CLOSURE_EXPR
-                        | SyntaxKind::FN_PTR_TYPE
-                        | SyntaxKind::TYPE_BOUND
-                        | SyntaxKind::CONST_BLOCK_PAT
-                )
-            }) =>
-        {
-            h | HlMod::Const
-        }
+        T![const] => h | HlMod::Const,
         T![true] | T![false] => HlTag::BoolLiteral.into(),
         // crate is handled just as a token if it's in an `extern crate`
         T![crate] if parent_matches::<ast::ExternCrate>(&token) => h,
-        T![ref] => match token.parent().and_then(ast::IdentPat::cast) {
-            Some(ident) if sema.is_unsafe_ident_pat(&ident) => h | HlMod::Unsafe,
-            _ => h,
-        },
         _ => h,
-    };
-    Some(h)
+    }
 }
 
 fn highlight_name_ref(
     sema: &Semantics<'_, RootDatabase>,
     krate: hir::Crate,
-    bindings_shadow_count: &mut FxHashMap<hir::Name, u32>,
+    bindings_shadow_count: Option<&mut FxHashMap<hir::Name, u32>>,
     binding_hash: &mut Option<u64>,
+    is_unsafe_node: &impl Fn(AstPtr<Either<ast::Expr, ast::Pat>>) -> bool,
     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, edition) {
+    if let Some(res) = highlight_method_call_by_name_ref(sema, krate, &name_ref, is_unsafe_node) {
         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, edition)
+            return highlight_name_ref_by_syntax(name_ref, sema, krate, is_unsafe_node)
         }
         // FIXME: This is required for helper attributes used by proc-macros, as those do not map down
         // to anything when used.
@@ -267,17 +315,20 @@ fn highlight_name_ref(
     let mut h = match name_class {
         NameRefClass::Definition(def, _) => {
             if let Definition::Local(local) = &def {
-                let name = local.name(db);
-                let shadow_count = bindings_shadow_count.entry(name.clone()).or_default();
-                *binding_hash = Some(calc_binding_hash(&name, *shadow_count))
+                if let Some(bindings_shadow_count) = bindings_shadow_count {
+                    let name = local.name(sema.db);
+                    let shadow_count = bindings_shadow_count.entry(name.clone()).or_default();
+                    *binding_hash = Some(calc_binding_hash(&name, *shadow_count))
+                }
             };
 
-            let mut h = highlight_def(sema, krate, def, edition);
+            let mut h = highlight_def(sema, krate, def, edition, true);
 
             match def {
                 Definition::Local(local) if is_consumed_lvalue(name_ref.syntax(), &local, db) => {
                     h |= HlMod::Consuming;
                 }
+                // highlight unsafe traits as unsafe only in their implementations
                 Definition::Trait(trait_) if trait_.is_unsafe(db) => {
                     if ast::Impl::for_trait_name_ref(&name_ref)
                         .is_some_and(|impl_| impl_.unsafe_token().is_some())
@@ -285,23 +336,66 @@ fn highlight_name_ref(
                         h |= HlMod::Unsafe;
                     }
                 }
-                Definition::Field(field) => {
-                    if let Some(parent) = name_ref.syntax().parent() {
-                        if matches!(parent.kind(), FIELD_EXPR | RECORD_PAT_FIELD) {
-                            if let hir::VariantDef::Union(_) = field.parent_def(db) {
-                                h |= HlMod::Unsafe;
-                            }
-                        }
+                Definition::Function(_) => {
+                    let is_unsafe = name_ref
+                        .syntax()
+                        .parent()
+                        .and_then(|it| ast::PathSegment::cast(it)?.parent_path().syntax().parent())
+                        .and_then(ast::PathExpr::cast)
+                        .and_then(|it| it.syntax().parent())
+                        .and_then(ast::CallExpr::cast)
+                        .is_some_and(|it| {
+                            is_unsafe_node(AstPtr::new(&ast::Expr::CallExpr(it)).wrap_left())
+                        });
+                    if is_unsafe {
+                        h |= HlMod::Unsafe;
                     }
                 }
                 Definition::Macro(_) => {
-                    if let Some(macro_call) =
-                        ide_db::syntax_helpers::node_ext::full_path_of_name_ref(&name_ref)
-                            .and_then(|it| it.syntax().parent().and_then(ast::MacroCall::cast))
-                    {
-                        if sema.is_unsafe_macro_call(&macro_call) {
-                            h |= HlMod::Unsafe;
-                        }
+                    let is_unsafe = name_ref
+                        .syntax()
+                        .parent()
+                        .and_then(|it| ast::PathSegment::cast(it)?.parent_path().syntax().parent())
+                        .and_then(ast::MacroCall::cast)
+                        .is_some_and(|macro_call| sema.is_unsafe_macro_call(&macro_call));
+                    if is_unsafe {
+                        h |= HlMod::Unsafe;
+                    }
+                }
+                Definition::Field(_) => {
+                    let is_unsafe = name_ref
+                        .syntax()
+                        .parent()
+                        .and_then(|it| {
+                            match_ast! { match it {
+                                ast::FieldExpr(expr) => Some(is_unsafe_node(AstPtr::new(&Either::Left(expr.into())))),
+                                ast::RecordPatField(pat) => {
+                                    walk_pat(&pat.pat()?, &mut |pat| {
+                                        if is_unsafe_node(AstPtr::new(&Either::Right(pat))) {
+                                            ControlFlow::Break(true)
+                                        }
+                                         else {ControlFlow::Continue(())}
+                                    }).break_value()
+                                },
+                                _ => None,
+                            }}
+                        })
+                        .unwrap_or(false);
+                    if is_unsafe {
+                        h |= HlMod::Unsafe;
+                    }
+                }
+                Definition::Static(_) => {
+                    let is_unsafe = name_ref
+                        .syntax()
+                        .parent()
+                        .and_then(|it| ast::PathSegment::cast(it)?.parent_path().syntax().parent())
+                        .and_then(ast::PathExpr::cast)
+                        .is_some_and(|it| {
+                            is_unsafe_node(AstPtr::new(&ast::Expr::PathExpr(it)).wrap_left())
+                        });
+                    if is_unsafe {
+                        h |= HlMod::Unsafe;
                     }
                 }
                 _ => (),
@@ -310,7 +404,7 @@ fn highlight_name_ref(
             h
         }
         NameRefClass::FieldShorthand { field_ref, .. } => {
-            highlight_def(sema, krate, field_ref.into(), edition)
+            highlight_def(sema, krate, field_ref.into(), edition, true)
         }
         NameRefClass::ExternCrateShorthand { decl, krate: resolved_krate } => {
             let mut h = HlTag::Symbol(SymbolKind::Module).into();
@@ -342,22 +436,25 @@ fn highlight_name_ref(
 
 fn highlight_name(
     sema: &Semantics<'_, RootDatabase>,
-    bindings_shadow_count: &mut FxHashMap<hir::Name, u32>,
+    bindings_shadow_count: Option<&mut FxHashMap<hir::Name, u32>>,
     binding_hash: &mut Option<u64>,
+    is_unsafe_node: &impl Fn(AstPtr<Either<ast::Expr, ast::Pat>>) -> bool,
     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 {
-        let name = local.name(sema.db);
-        let shadow_count = bindings_shadow_count.entry(name.clone()).or_default();
-        *shadow_count += 1;
-        *binding_hash = Some(calc_binding_hash(&name, *shadow_count))
+        if let Some(bindings_shadow_count) = bindings_shadow_count {
+            let name = local.name(sema.db);
+            let shadow_count = bindings_shadow_count.entry(name.clone()).or_default();
+            *shadow_count += 1;
+            *binding_hash = Some(calc_binding_hash(&name, *shadow_count))
+        }
     };
     match name_kind {
         Some(NameClass::Definition(def)) => {
-            let mut h = highlight_def(sema, krate, def, edition) | HlMod::Definition;
+            let mut h = highlight_def(sema, krate, def, edition, false) | HlMod::Definition;
             if let Definition::Trait(trait_) = &def {
                 if trait_.is_unsafe(sema.db) {
                     h |= HlMod::Unsafe;
@@ -365,10 +462,14 @@ fn highlight_name(
             }
             h
         }
-        Some(NameClass::ConstReference(def)) => highlight_def(sema, krate, def, edition),
-        Some(NameClass::PatFieldShorthand { field_ref, .. }) => {
+        Some(NameClass::ConstReference(def)) => highlight_def(sema, krate, def, edition, true),
+        Some(NameClass::PatFieldShorthand { .. }) => {
             let mut h = HlTag::Symbol(SymbolKind::Field).into();
-            if let hir::VariantDef::Union(_) = field_ref.parent_def(sema.db) {
+            let is_unsafe =
+                name.syntax().parent().and_then(ast::IdentPat::cast).is_some_and(|it| {
+                    is_unsafe_node(AstPtr::new(&ast::Pat::IdentPat(it)).wrap_right())
+                });
+            if is_unsafe {
                 h |= HlMod::Unsafe;
             }
             h
@@ -386,6 +487,7 @@ pub(super) fn highlight_def(
     krate: hir::Crate,
     def: Definition,
     edition: Edition,
+    is_ref: bool,
 ) -> Highlight {
     let db = sema.db;
     let mut h = match def {
@@ -439,7 +541,7 @@ pub(super) fn highlight_def(
             // 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) {
+            if !is_ref && func.is_unsafe_to_call(db, None, edition) {
                 h |= HlMod::Unsafe;
             }
             if func.is_async(db) {
@@ -509,7 +611,9 @@ pub(super) fn highlight_def(
 
             if s.is_mut(db) {
                 h |= HlMod::Mutable;
-                h |= HlMod::Unsafe;
+                if !is_ref {
+                    h |= HlMod::Unsafe;
+                }
             }
 
             h
@@ -587,23 +691,24 @@ fn highlight_method_call_by_name_ref(
     sema: &Semantics<'_, RootDatabase>,
     krate: hir::Crate,
     name_ref: &ast::NameRef,
-    edition: Edition,
+    is_unsafe_node: &impl Fn(AstPtr<Either<ast::Expr, ast::Pat>>) -> bool,
 ) -> Option<Highlight> {
     let mc = name_ref.syntax().parent().and_then(ast::MethodCallExpr::cast)?;
-    highlight_method_call(sema, krate, &mc, edition)
+    highlight_method_call(sema, krate, &mc, is_unsafe_node)
 }
 
 fn highlight_method_call(
     sema: &Semantics<'_, RootDatabase>,
     krate: hir::Crate,
     method_call: &ast::MethodCallExpr,
-    edition: Edition,
+    is_unsafe_node: &impl Fn(AstPtr<Either<ast::Expr, ast::Pat>>) -> bool,
 ) -> Option<Highlight> {
     let func = sema.resolve_method_call(method_call)?;
 
     let mut h = SymbolKind::Method.into();
 
-    if func.is_unsafe_to_call(sema.db, None, edition) || sema.is_unsafe_method_call(method_call) {
+    let is_unsafe = is_unsafe_node(AstPtr::new(method_call).upcast::<ast::Expr>().wrap_left());
+    if is_unsafe {
         h |= HlMod::Unsafe;
     }
     if func.is_async(sema.db) {
@@ -695,7 +800,7 @@ fn highlight_name_ref_by_syntax(
     name: ast::NameRef,
     sema: &Semantics<'_, RootDatabase>,
     krate: hir::Crate,
-    edition: Edition,
+    is_unsafe_node: &impl Fn(AstPtr<Either<ast::Expr, ast::Pat>>) -> bool,
 ) -> Highlight {
     let default = HlTag::UnresolvedReference;
 
@@ -707,19 +812,13 @@ 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, edition))
+            .and_then(|it| highlight_method_call(sema, krate, &it, is_unsafe_node))
             .unwrap_or_else(|| SymbolKind::Method.into()),
         FIELD_EXPR => {
             let h = HlTag::Symbol(SymbolKind::Field);
-            let is_union = ast::FieldExpr::cast(parent)
-                .and_then(|field_expr| sema.resolve_field(&field_expr))
-                .is_some_and(|field| match field {
-                    Either::Left(field) => {
-                        matches!(field.parent_def(sema.db), hir::VariantDef::Union(_))
-                    }
-                    Either::Right(_) => false,
-                });
-            if is_union {
+            let is_unsafe = ast::Expr::cast(parent)
+                .is_some_and(|it| is_unsafe_node(AstPtr::new(&it).wrap_left()));
+            if is_unsafe {
                 h | HlMod::Unsafe
             } else {
                 h.into()
diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/html.rs b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/html.rs
index 47ad54759a8..07d40bafeba 100644
--- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/html.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/html.rs
@@ -88,12 +88,6 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
 .string_literal     { color: #CC9393; }
 .field              { color: #94BFF3; }
 .function           { color: #93E0E3; }
-.function.unsafe    { color: #BC8383; }
-.trait.unsafe       { color: #BC8383; }
-.operator.unsafe    { color: #BC8383; }
-.mutable.unsafe     { color: #BC8383; text-decoration: underline; }
-.keyword.unsafe     { color: #BC8383; font-weight: bold; }
-.macro.unsafe       { color: #BC8383; }
 .parameter          { color: #94BFF3; }
 .text               { color: #DCDCCC; }
 .type               { color: #7CB8BB; }
@@ -115,6 +109,7 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
 .control            { font-style: italic; }
 .reference          { font-style: italic; font-weight: bold; }
 .const              { font-weight: bolder; }
+.unsafe             { color: #BC8383; }
 
 .invalid_escape_sequence { color: #FC5555; text-decoration: wavy underline; }
 .unresolved_reference    { color: #FC5555; text-decoration: wavy underline; }
diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_asm.html b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_asm.html
index 2bc22f960bd..c8ffa9e8558 100644
--- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_asm.html
+++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_asm.html
@@ -14,12 +14,6 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
 .string_literal     { color: #CC9393; }
 .field              { color: #94BFF3; }
 .function           { color: #93E0E3; }
-.function.unsafe    { color: #BC8383; }
-.trait.unsafe       { color: #BC8383; }
-.operator.unsafe    { color: #BC8383; }
-.mutable.unsafe     { color: #BC8383; text-decoration: underline; }
-.keyword.unsafe     { color: #BC8383; font-weight: bold; }
-.macro.unsafe       { color: #BC8383; }
 .parameter          { color: #94BFF3; }
 .text               { color: #DCDCCC; }
 .type               { color: #7CB8BB; }
@@ -41,6 +35,7 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
 .control            { font-style: italic; }
 .reference          { font-style: italic; font-weight: bold; }
 .const              { font-weight: bolder; }
+.unsafe             { color: #BC8383; }
 
 .invalid_escape_sequence { color: #FC5555; text-decoration: wavy underline; }
 .unresolved_reference    { color: #FC5555; text-decoration: wavy underline; }
@@ -49,26 +44,26 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
     <span class="keyword unsafe">unsafe</span> <span class="brace">{</span>
         <span class="keyword">let</span> <span class="variable declaration">foo</span> <span class="operator">=</span> <span class="numeric_literal">1</span><span class="semicolon">;</span>
         <span class="keyword">let</span> <span class="keyword">mut</span> <span class="variable declaration mutable">o</span> <span class="operator">=</span> <span class="numeric_literal">0</span><span class="semicolon">;</span>
-        <span class="module crate_root default_library library">core</span><span class="operator">::</span><span class="module default_library library">arch</span><span class="operator">::</span><span class="macro default_library library unsafe">asm</span><span class="macro_bang">!</span><span class="parenthesis">(</span>
+        <span class="module crate_root default_library library">core</span><span class="operator">::</span><span class="module default_library library">arch</span><span class="operator">::</span><span class="macro default_library library unsafe">asm</span><span class="macro_bang unsafe">!</span><span class="parenthesis unsafe">(</span>
             <span class="string_literal macro">"%input = OpLoad _ {</span><span class="variable">0</span><span class="string_literal macro">}"</span><span class="comma macro">,</span>
             <span class="macro default_library library macro">concat</span><span class="macro_bang macro">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"%result = "</span><span class="comma macro">,</span> <span class="string_literal macro">"bar"</span><span class="comma macro">,</span> <span class="string_literal macro">" _ %input"</span><span class="parenthesis macro">)</span><span class="comma macro">,</span>
             <span class="string_literal macro">"OpStore {</span><span class="variable">1</span><span class="string_literal macro">} %result"</span><span class="comma macro">,</span>
             <span class="keyword control macro">in</span><span class="parenthesis macro">(</span><span class="reg library macro">reg</span><span class="parenthesis macro">)</span> <span class="operator macro">&</span><span class="variable macro">foo</span><span class="comma macro">,</span>
             <span class="keyword control macro">in</span><span class="parenthesis macro">(</span><span class="reg library macro">reg</span><span class="parenthesis macro">)</span> <span class="operator macro">&</span><span class="keyword macro">mut</span> <span class="variable macro mutable">o</span><span class="comma macro">,</span>
-        <span class="parenthesis">)</span><span class="semicolon">;</span>
+        <span class="parenthesis unsafe">)</span><span class="semicolon">;</span>
 
         <span class="keyword">let</span> <span class="variable declaration">thread_id</span><span class="colon">:</span> <span class="builtin_type">usize</span><span class="semicolon">;</span>
-        <span class="module crate_root default_library library">core</span><span class="operator">::</span><span class="module default_library library">arch</span><span class="operator">::</span><span class="macro default_library library unsafe">asm</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal macro">"
+        <span class="module crate_root default_library library">core</span><span class="operator">::</span><span class="module default_library library">arch</span><span class="operator">::</span><span class="macro default_library library unsafe">asm</span><span class="macro_bang unsafe">!</span><span class="parenthesis unsafe">(</span><span class="string_literal macro">"
             mov {</span><span class="variable">0</span><span class="string_literal macro">}, gs:[0x30]
             mov {</span><span class="variable">0</span><span class="string_literal macro">}, [{</span><span class="variable">0</span><span class="string_literal macro">}+0x48]
-        "</span><span class="comma macro">,</span> <span class="keyword macro">out</span><span class="parenthesis macro">(</span><span class="reg library macro">reg</span><span class="parenthesis macro">)</span> <span class="variable macro">thread_id</span><span class="comma macro">,</span> <span class="keyword macro">options</span><span class="parenthesis macro">(</span><span class="keyword macro">pure</span><span class="comma macro">,</span> <span class="keyword macro">readonly</span><span class="comma macro">,</span> <span class="keyword macro">nostack</span><span class="parenthesis macro">)</span><span class="parenthesis">)</span><span class="semicolon">;</span>
+        "</span><span class="comma macro">,</span> <span class="keyword macro">out</span><span class="parenthesis macro">(</span><span class="reg library macro">reg</span><span class="parenthesis macro">)</span> <span class="variable macro">thread_id</span><span class="comma macro">,</span> <span class="keyword macro">options</span><span class="parenthesis macro">(</span><span class="keyword macro">pure</span><span class="comma macro">,</span> <span class="keyword macro">readonly</span><span class="comma macro">,</span> <span class="keyword macro">nostack</span><span class="parenthesis macro">)</span><span class="parenthesis unsafe">)</span><span class="semicolon">;</span>
 
         <span class="keyword">static</span> <span class="static declaration">UNMAP_BASE</span><span class="colon">:</span> <span class="builtin_type">usize</span><span class="semicolon">;</span>
         <span class="keyword const">const</span> <span class="constant const declaration">MEM_RELEASE</span><span class="colon">:</span> <span class="builtin_type">usize</span><span class="semicolon">;</span>
         <span class="keyword">static</span> <span class="static declaration">VirtualFree</span><span class="colon">:</span> <span class="builtin_type">usize</span><span class="semicolon">;</span>
         <span class="keyword const">const</span> <span class="constant const declaration">OffPtr</span><span class="colon">:</span> <span class="builtin_type">usize</span><span class="semicolon">;</span>
         <span class="keyword const">const</span> <span class="constant const declaration">OffFn</span><span class="colon">:</span> <span class="builtin_type">usize</span><span class="semicolon">;</span>
-        <span class="module crate_root default_library library">core</span><span class="operator">::</span><span class="module default_library library">arch</span><span class="operator">::</span><span class="macro default_library library unsafe">asm</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal macro">"
+        <span class="module crate_root default_library library">core</span><span class="operator">::</span><span class="module default_library library">arch</span><span class="operator">::</span><span class="macro default_library library unsafe">asm</span><span class="macro_bang unsafe">!</span><span class="parenthesis unsafe">(</span><span class="string_literal macro">"
             push {</span><span class="variable">free_type</span><span class="string_literal macro">}
             push {</span><span class="variable">free_size</span><span class="string_literal macro">}
             push {</span><span class="variable">base</span><span class="string_literal macro">}
@@ -82,26 +77,26 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
 
             jmp {</span><span class="variable">virtual_free</span><span class="string_literal macro">}
             "</span><span class="comma macro">,</span>
-            <span class="variable declaration macro">off_ptr</span> <span class="operator macro">=</span> <span class="keyword macro">const</span> <span class="constant const macro">OffPtr</span><span class="comma macro">,</span>
-            <span class="variable declaration macro">off_fn</span>  <span class="operator macro">=</span> <span class="keyword macro">const</span> <span class="constant const macro">OffFn</span><span class="comma macro">,</span>
+            <span class="variable declaration macro">off_ptr</span> <span class="operator macro">=</span> <span class="keyword const macro">const</span> <span class="constant const macro">OffPtr</span><span class="comma macro">,</span>
+            <span class="variable declaration macro">off_fn</span>  <span class="operator macro">=</span> <span class="keyword const macro">const</span> <span class="constant const macro">OffFn</span><span class="comma macro">,</span>
 
-            <span class="variable declaration macro">free_size</span> <span class="operator macro">=</span> <span class="keyword macro">const</span> <span class="numeric_literal macro">0</span><span class="comma macro">,</span>
-            <span class="variable declaration macro">free_type</span> <span class="operator macro">=</span> <span class="keyword macro">const</span> <span class="constant const macro">MEM_RELEASE</span><span class="comma macro">,</span>
+            <span class="variable declaration macro">free_size</span> <span class="operator macro">=</span> <span class="keyword const macro">const</span> <span class="numeric_literal macro">0</span><span class="comma macro">,</span>
+            <span class="variable declaration macro">free_type</span> <span class="operator macro">=</span> <span class="keyword const macro">const</span> <span class="constant const macro">MEM_RELEASE</span><span class="comma macro">,</span>
 
             <span class="variable declaration macro">virtual_free</span> <span class="operator macro">=</span> <span class="keyword macro">sym</span> <span class="static macro">VirtualFree</span><span class="comma macro">,</span>
 
             <span class="variable declaration macro">base</span> <span class="operator macro">=</span> <span class="keyword macro">sym</span> <span class="static macro">UNMAP_BASE</span><span class="comma macro">,</span>
             <span class="keyword macro">options</span><span class="parenthesis macro">(</span><span class="keyword macro">noreturn</span><span class="parenthesis macro">)</span><span class="comma macro">,</span>
-        <span class="parenthesis">)</span><span class="semicolon">;</span>
+        <span class="parenthesis unsafe">)</span><span class="semicolon">;</span>
     <span class="brace">}</span>
 <span class="brace">}</span>
 <span class="comment">// taken from https://github.com/rust-embedded/cortex-m/blob/47921b51f8b960344fcfa1255a50a0d19efcde6d/cortex-m/src/asm.rs#L254-L274</span>
 <span class="attribute_bracket attribute">#</span><span class="attribute_bracket attribute">[</span><span class="builtin_attr attribute">inline</span><span class="attribute_bracket attribute">]</span>
-<span class="keyword">pub</span> <span class="keyword unsafe">unsafe</span> <span class="keyword">fn</span> <span class="function declaration public unsafe">bootstrap</span><span class="parenthesis">(</span><span class="value_param declaration">msp</span><span class="colon">:</span> <span class="keyword">*</span><span class="keyword">const</span> <span class="builtin_type">u32</span><span class="comma">,</span> <span class="value_param declaration">rv</span><span class="colon">:</span> <span class="keyword">*</span><span class="keyword">const</span> <span class="builtin_type">u32</span><span class="parenthesis">)</span> <span class="operator">-&gt;</span> <span class="builtin_type">!</span> <span class="brace">{</span>
+<span class="keyword">pub</span> <span class="keyword unsafe">unsafe</span> <span class="keyword">fn</span> <span class="function declaration public unsafe">bootstrap</span><span class="parenthesis">(</span><span class="value_param declaration">msp</span><span class="colon">:</span> <span class="keyword">*</span><span class="keyword const">const</span> <span class="builtin_type">u32</span><span class="comma">,</span> <span class="value_param declaration">rv</span><span class="colon">:</span> <span class="keyword">*</span><span class="keyword const">const</span> <span class="builtin_type">u32</span><span class="parenthesis">)</span> <span class="operator">-&gt;</span> <span class="builtin_type">!</span> <span class="brace">{</span>
     <span class="comment">// Ensure thumb mode is set.</span>
     <span class="keyword">let</span> <span class="variable declaration">rv</span> <span class="operator">=</span> <span class="parenthesis">(</span><span class="value_param">rv</span> <span class="keyword">as</span> <span class="builtin_type">u32</span><span class="parenthesis">)</span> <span class="bitwise">|</span> <span class="numeric_literal">1</span><span class="semicolon">;</span>
     <span class="keyword">let</span> <span class="variable declaration">msp</span> <span class="operator">=</span> <span class="value_param">msp</span> <span class="keyword">as</span> <span class="builtin_type">u32</span><span class="semicolon">;</span>
-    <span class="module crate_root default_library library">core</span><span class="operator">::</span><span class="module default_library library">arch</span><span class="operator">::</span><span class="macro default_library library unsafe">asm</span><span class="macro_bang">!</span><span class="parenthesis">(</span>
+    <span class="module crate_root default_library library">core</span><span class="operator">::</span><span class="module default_library library">arch</span><span class="operator">::</span><span class="macro default_library library unsafe">asm</span><span class="macro_bang unsafe">!</span><span class="parenthesis unsafe">(</span>
         <span class="string_literal macro">"mrs {</span><span class="variable">tmp</span><span class="string_literal macro">}, CONTROL"</span><span class="comma macro">,</span>
         <span class="string_literal macro">"bics {</span><span class="variable">tmp</span><span class="string_literal macro">}, {</span><span class="variable">spsel</span><span class="string_literal macro">}"</span><span class="comma macro">,</span>
         <span class="string_literal macro">"msr CONTROL, {</span><span class="variable">tmp</span><span class="string_literal macro">}"</span><span class="comma macro">,</span>
@@ -115,5 +110,5 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
         <span class="variable declaration macro">msp</span> <span class="operator macro">=</span> <span class="keyword control macro">in</span><span class="parenthesis macro">(</span><span class="reg library macro">reg</span><span class="parenthesis macro">)</span> <span class="variable macro">msp</span><span class="comma macro">,</span>
         <span class="variable declaration macro">rv</span> <span class="operator macro">=</span> <span class="keyword control macro">in</span><span class="parenthesis macro">(</span><span class="reg library macro">reg</span><span class="parenthesis macro">)</span> <span class="variable macro">rv</span><span class="comma macro">,</span>
         <span class="keyword macro">options</span><span class="parenthesis macro">(</span><span class="keyword macro">noreturn</span><span class="comma macro">,</span> <span class="keyword macro">nomem</span><span class="comma macro">,</span> <span class="keyword macro">nostack</span><span class="parenthesis macro">)</span><span class="comma macro">,</span>
-    <span class="parenthesis">)</span><span class="semicolon">;</span>
+    <span class="parenthesis unsafe">)</span><span class="semicolon">;</span>
 <span class="brace">}</span></code></pre>
\ No newline at end of file
diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_assoc_functions.html b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_assoc_functions.html
index 9c7f03fc158..faace6eaff8 100644
--- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_assoc_functions.html
+++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_assoc_functions.html
@@ -14,12 +14,6 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
 .string_literal     { color: #CC9393; }
 .field              { color: #94BFF3; }
 .function           { color: #93E0E3; }
-.function.unsafe    { color: #BC8383; }
-.trait.unsafe       { color: #BC8383; }
-.operator.unsafe    { color: #BC8383; }
-.mutable.unsafe     { color: #BC8383; text-decoration: underline; }
-.keyword.unsafe     { color: #BC8383; font-weight: bold; }
-.macro.unsafe       { color: #BC8383; }
 .parameter          { color: #94BFF3; }
 .text               { color: #DCDCCC; }
 .type               { color: #7CB8BB; }
@@ -41,6 +35,7 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
 .control            { font-style: italic; }
 .reference          { font-style: italic; font-weight: bold; }
 .const              { font-weight: bolder; }
+.unsafe             { color: #BC8383; }
 
 .invalid_escape_sequence { color: #FC5555; text-decoration: wavy underline; }
 .unresolved_reference    { color: #FC5555; text-decoration: wavy underline; }
diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_attributes.html b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_attributes.html
index e1d51dc0b71..d59f4caa97f 100644
--- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_attributes.html
+++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_attributes.html
@@ -14,12 +14,6 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
 .string_literal     { color: #CC9393; }
 .field              { color: #94BFF3; }
 .function           { color: #93E0E3; }
-.function.unsafe    { color: #BC8383; }
-.trait.unsafe       { color: #BC8383; }
-.operator.unsafe    { color: #BC8383; }
-.mutable.unsafe     { color: #BC8383; text-decoration: underline; }
-.keyword.unsafe     { color: #BC8383; font-weight: bold; }
-.macro.unsafe       { color: #BC8383; }
 .parameter          { color: #94BFF3; }
 .text               { color: #DCDCCC; }
 .type               { color: #7CB8BB; }
@@ -41,6 +35,7 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
 .control            { font-style: italic; }
 .reference          { font-style: italic; font-weight: bold; }
 .const              { font-weight: bolder; }
+.unsafe             { color: #BC8383; }
 
 .invalid_escape_sequence { color: #FC5555; text-decoration: wavy underline; }
 .unresolved_reference    { color: #FC5555; text-decoration: wavy underline; }
diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_block_mod_items.html b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_block_mod_items.html
index af29af3f03c..3beda396da8 100644
--- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_block_mod_items.html
+++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_block_mod_items.html
@@ -14,12 +14,6 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
 .string_literal     { color: #CC9393; }
 .field              { color: #94BFF3; }
 .function           { color: #93E0E3; }
-.function.unsafe    { color: #BC8383; }
-.trait.unsafe       { color: #BC8383; }
-.operator.unsafe    { color: #BC8383; }
-.mutable.unsafe     { color: #BC8383; text-decoration: underline; }
-.keyword.unsafe     { color: #BC8383; font-weight: bold; }
-.macro.unsafe       { color: #BC8383; }
 .parameter          { color: #94BFF3; }
 .text               { color: #DCDCCC; }
 .type               { color: #7CB8BB; }
@@ -41,6 +35,7 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
 .control            { font-style: italic; }
 .reference          { font-style: italic; font-weight: bold; }
 .const              { font-weight: bolder; }
+.unsafe             { color: #BC8383; }
 
 .invalid_escape_sequence { color: #FC5555; text-decoration: wavy underline; }
 .unresolved_reference    { color: #FC5555; text-decoration: wavy underline; }
@@ -58,7 +53,7 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
         <span class="macro public">foo</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="struct declaration macro public">Bar</span><span class="parenthesis">)</span><span class="semicolon">;</span>
         <span class="keyword">fn</span> <span class="function declaration">func</span><span class="parenthesis">(</span><span class="punctuation">_</span><span class="colon">:</span> <span class="module">y</span><span class="operator">::</span><span class="struct public">Bar</span><span class="parenthesis">)</span> <span class="brace">{</span>
             <span class="keyword">mod</span> <span class="module declaration">inner</span> <span class="brace">{</span>
-                <span class="keyword">struct</span> <span class="struct declaration">Innerest</span><span class="angle">&lt;</span><span class="keyword">const</span> <span class="const_param const declaration">C</span><span class="colon">:</span> <span class="unresolved_reference">usize</span><span class="angle">&gt;</span> <span class="brace">{</span> <span class="field declaration">field</span><span class="colon">:</span> <span class="bracket">[</span><span class="parenthesis">(</span><span class="parenthesis">)</span><span class="semicolon">;</span> <span class="brace">{</span><span class="const_param const">C</span><span class="brace">}</span><span class="bracket">]</span> <span class="brace">}</span>
+                <span class="keyword">struct</span> <span class="struct declaration">Innerest</span><span class="angle">&lt;</span><span class="keyword const">const</span> <span class="const_param const declaration">C</span><span class="colon">:</span> <span class="unresolved_reference">usize</span><span class="angle">&gt;</span> <span class="brace">{</span> <span class="field declaration">field</span><span class="colon">:</span> <span class="bracket">[</span><span class="parenthesis">(</span><span class="parenthesis">)</span><span class="semicolon">;</span> <span class="brace">{</span><span class="const_param const">C</span><span class="brace">}</span><span class="bracket">]</span> <span class="brace">}</span>
             <span class="brace">}</span>
         <span class="brace">}</span>
     <span class="brace">}</span>
diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_const.html b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_const.html
index 6d8f6b3c6e3..9c7324eafa3 100644
--- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_const.html
+++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_const.html
@@ -14,12 +14,6 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
 .string_literal     { color: #CC9393; }
 .field              { color: #94BFF3; }
 .function           { color: #93E0E3; }
-.function.unsafe    { color: #BC8383; }
-.trait.unsafe       { color: #BC8383; }
-.operator.unsafe    { color: #BC8383; }
-.mutable.unsafe     { color: #BC8383; text-decoration: underline; }
-.keyword.unsafe     { color: #BC8383; font-weight: bold; }
-.macro.unsafe       { color: #BC8383; }
 .parameter          { color: #94BFF3; }
 .text               { color: #DCDCCC; }
 .type               { color: #7CB8BB; }
@@ -41,6 +35,7 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
 .control            { font-style: italic; }
 .reference          { font-style: italic; font-weight: bold; }
 .const              { font-weight: bolder; }
+.unsafe             { color: #BC8383; }
 
 .invalid_escape_sequence { color: #FC5555; text-decoration: wavy underline; }
 .unresolved_reference    { color: #FC5555; text-decoration: wavy underline; }
@@ -50,21 +45,21 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
         <span class="punctuation">$</span><span class="parenthesis">(</span><span class="punctuation">$</span>tt<span class="parenthesis">)</span><span class="punctuation">*</span>
     <span class="brace">}</span><span class="semicolon">;</span>
 <span class="brace">}</span>
-<span class="keyword const">const</span> <span class="constant const declaration">CONST_ITEM</span><span class="colon">:</span> <span class="keyword">*</span><span class="keyword">const</span> <span class="parenthesis">(</span><span class="parenthesis">)</span> <span class="operator">=</span> <span class="operator">&</span><span class="keyword">raw</span> <span class="keyword">const</span> <span class="parenthesis">(</span><span class="parenthesis">)</span><span class="semicolon">;</span>
-<span class="keyword const">const</span> <span class="keyword">fn</span> <span class="function const declaration">const_fn</span><span class="angle">&lt;</span><span class="keyword">const</span> <span class="const_param const declaration">CONST_PARAM</span><span class="colon">:</span> <span class="parenthesis">(</span><span class="parenthesis">)</span><span class="angle">&gt;</span><span class="parenthesis">(</span><span class="keyword const">const</span> <span class="brace">{</span><span class="brace">}</span><span class="colon">:</span> <span class="keyword">const</span> <span class="keyword">fn</span><span class="parenthesis">(</span><span class="parenthesis">)</span><span class="parenthesis">)</span> <span class="keyword">where</span> <span class="parenthesis">(</span><span class="parenthesis">)</span><span class="colon">:</span> <span class="keyword const">const</span> <span class="trait">ConstTrait</span> <span class="brace">{</span>
+<span class="keyword const">const</span> <span class="constant const declaration">CONST_ITEM</span><span class="colon">:</span> <span class="keyword">*</span><span class="keyword const">const</span> <span class="parenthesis">(</span><span class="parenthesis">)</span> <span class="operator">=</span> <span class="operator">&</span><span class="keyword">raw</span> <span class="keyword const">const</span> <span class="parenthesis">(</span><span class="parenthesis">)</span><span class="semicolon">;</span>
+<span class="keyword const">const</span> <span class="keyword">fn</span> <span class="function const declaration">const_fn</span><span class="angle">&lt;</span><span class="keyword const">const</span> <span class="const_param const declaration">CONST_PARAM</span><span class="colon">:</span> <span class="parenthesis">(</span><span class="parenthesis">)</span><span class="angle">&gt;</span><span class="parenthesis">(</span><span class="keyword const">const</span> <span class="brace">{</span><span class="brace">}</span><span class="colon">:</span> <span class="keyword const">const</span> <span class="keyword">fn</span><span class="parenthesis">(</span><span class="parenthesis">)</span><span class="parenthesis">)</span> <span class="keyword">where</span> <span class="parenthesis">(</span><span class="parenthesis">)</span><span class="colon">:</span> <span class="keyword const">const</span> <span class="trait">ConstTrait</span> <span class="brace">{</span>
     <span class="constant const">CONST_ITEM</span><span class="semicolon">;</span>
     <span class="const_param const">CONST_PARAM</span><span class="semicolon">;</span>
     <span class="keyword const">const</span> <span class="brace">{</span>
-        <span class="keyword">const</span> <span class="punctuation">|</span><span class="punctuation">|</span> <span class="brace">{</span><span class="brace">}</span>
+        <span class="keyword const">const</span> <span class="punctuation">|</span><span class="punctuation">|</span> <span class="brace">{</span><span class="brace">}</span>
     <span class="brace">}</span>
     <span class="macro public">id</span><span class="macro_bang">!</span><span class="parenthesis">(</span>
         <span class="constant const macro">CONST_ITEM</span><span class="semicolon macro">;</span>
         <span class="const_param const macro">CONST_PARAM</span><span class="semicolon macro">;</span>
         <span class="keyword const macro">const</span> <span class="brace macro">{</span>
-            <span class="keyword macro">const</span> <span class="punctuation macro">|</span><span class="punctuation macro">|</span> <span class="brace macro">{</span><span class="brace macro">}</span>
+            <span class="keyword const macro">const</span> <span class="punctuation macro">|</span><span class="punctuation macro">|</span> <span class="brace macro">{</span><span class="brace macro">}</span>
         <span class="brace macro">}</span><span class="semicolon macro">;</span>
-        <span class="operator macro">&</span><span class="keyword macro">raw</span> <span class="keyword macro">const</span> <span class="parenthesis macro">(</span><span class="parenthesis macro">)</span><span class="semicolon macro">;</span>
-        <span class="keyword macro">const</span>
+        <span class="operator macro">&</span><span class="keyword macro">raw</span> <span class="keyword const macro">const</span> <span class="parenthesis macro">(</span><span class="parenthesis macro">)</span><span class="semicolon macro">;</span>
+        <span class="keyword const macro">const</span>
     <span class="parenthesis">)</span><span class="semicolon">;</span>
     <span class="parenthesis">(</span><span class="parenthesis">)</span><span class="operator">.</span><span class="method const consuming trait">assoc_const_method</span><span class="parenthesis">(</span><span class="parenthesis">)</span><span class="semicolon">;</span>
 <span class="brace">}</span>
@@ -80,6 +75,6 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
 
 <span class="keyword">macro_rules</span><span class="macro_bang">!</span> <span class="macro declaration public">unsafe_deref</span> <span class="brace">{</span>
     <span class="parenthesis">(</span><span class="parenthesis">)</span> <span class="operator">=</span><span class="operator">&gt;</span> <span class="brace">{</span>
-        <span class="punctuation">*</span><span class="parenthesis">(</span><span class="punctuation">&</span><span class="parenthesis">(</span><span class="parenthesis">)</span> <span class="keyword">as</span> <span class="punctuation">*</span><span class="keyword">const</span> <span class="parenthesis">(</span><span class="parenthesis">)</span><span class="parenthesis">)</span>
+        <span class="punctuation">*</span><span class="parenthesis">(</span><span class="punctuation">&</span><span class="parenthesis">(</span><span class="parenthesis">)</span> <span class="keyword">as</span> <span class="punctuation">*</span><span class="keyword const">const</span> <span class="parenthesis">(</span><span class="parenthesis">)</span><span class="parenthesis">)</span>
     <span class="brace">}</span><span class="semicolon">;</span>
 <span class="brace">}</span></code></pre>
\ No newline at end of file
diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_crate_root.html b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_crate_root.html
index 63e1560b921..4613c65ee61 100644
--- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_crate_root.html
+++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_crate_root.html
@@ -14,12 +14,6 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
 .string_literal     { color: #CC9393; }
 .field              { color: #94BFF3; }
 .function           { color: #93E0E3; }
-.function.unsafe    { color: #BC8383; }
-.trait.unsafe       { color: #BC8383; }
-.operator.unsafe    { color: #BC8383; }
-.mutable.unsafe     { color: #BC8383; text-decoration: underline; }
-.keyword.unsafe     { color: #BC8383; font-weight: bold; }
-.macro.unsafe       { color: #BC8383; }
 .parameter          { color: #94BFF3; }
 .text               { color: #DCDCCC; }
 .type               { color: #7CB8BB; }
@@ -41,6 +35,7 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
 .control            { font-style: italic; }
 .reference          { font-style: italic; font-weight: bold; }
 .const              { font-weight: bolder; }
+.unsafe             { color: #BC8383; }
 
 .invalid_escape_sequence { color: #FC5555; text-decoration: wavy underline; }
 .unresolved_reference    { color: #FC5555; text-decoration: wavy underline; }
diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_default_library.html b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_default_library.html
index dfad3a6605c..b1b2c659a22 100644
--- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_default_library.html
+++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_default_library.html
@@ -14,12 +14,6 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
 .string_literal     { color: #CC9393; }
 .field              { color: #94BFF3; }
 .function           { color: #93E0E3; }
-.function.unsafe    { color: #BC8383; }
-.trait.unsafe       { color: #BC8383; }
-.operator.unsafe    { color: #BC8383; }
-.mutable.unsafe     { color: #BC8383; text-decoration: underline; }
-.keyword.unsafe     { color: #BC8383; font-weight: bold; }
-.macro.unsafe       { color: #BC8383; }
 .parameter          { color: #94BFF3; }
 .text               { color: #DCDCCC; }
 .type               { color: #7CB8BB; }
@@ -41,6 +35,7 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
 .control            { font-style: italic; }
 .reference          { font-style: italic; font-weight: bold; }
 .const              { font-weight: bolder; }
+.unsafe             { color: #BC8383; }
 
 .invalid_escape_sequence { color: #FC5555; text-decoration: wavy underline; }
 .unresolved_reference    { color: #FC5555; text-decoration: wavy underline; }
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 263e4545fb5..c8c8c5dba4c 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
@@ -14,12 +14,6 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
 .string_literal     { color: #CC9393; }
 .field              { color: #94BFF3; }
 .function           { color: #93E0E3; }
-.function.unsafe    { color: #BC8383; }
-.trait.unsafe       { color: #BC8383; }
-.operator.unsafe    { color: #BC8383; }
-.mutable.unsafe     { color: #BC8383; text-decoration: underline; }
-.keyword.unsafe     { color: #BC8383; font-weight: bold; }
-.macro.unsafe       { color: #BC8383; }
 .parameter          { color: #94BFF3; }
 .text               { color: #DCDCCC; }
 .type               { color: #7CB8BB; }
@@ -41,6 +35,7 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
 .control            { font-style: italic; }
 .reference          { font-style: italic; font-weight: bold; }
 .const              { font-weight: bolder; }
+.unsafe             { color: #BC8383; }
 
 .invalid_escape_sequence { color: #FC5555; text-decoration: wavy underline; }
 .unresolved_reference    { color: #FC5555; text-decoration: wavy underline; }
@@ -129,7 +124,7 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
     <span class="comment documentation">///</span>
     <span class="comment documentation">/// ```</span>
     <span class="comment documentation">///</span><span class="comment documentation"> </span><span class="comment injected">// functions</span>
-    <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">foo</span><span class="angle injected">&lt;</span><span class="type_param declaration injected">T</span><span class="comma injected">,</span><span class="none injected"> </span><span class="keyword injected">const</span><span class="none injected"> </span><span class="const_param const declaration injected">X</span><span class="colon injected">:</span><span class="none injected"> </span><span class="builtin_type injected">usize</span><span class="angle injected">&gt;</span><span class="parenthesis injected">(</span><span class="value_param declaration injected">arg</span><span class="colon injected">:</span><span class="none injected"> </span><span class="builtin_type injected">i32</span><span class="parenthesis injected">)</span><span class="none injected"> </span><span class="brace injected">{</span>
+    <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">foo</span><span class="angle injected">&lt;</span><span class="type_param declaration injected">T</span><span class="comma injected">,</span><span class="none injected"> </span><span class="keyword const injected">const</span><span class="none injected"> </span><span class="const_param const declaration injected">X</span><span class="colon injected">:</span><span class="none injected"> </span><span class="builtin_type injected">usize</span><span class="angle injected">&gt;</span><span class="parenthesis injected">(</span><span class="value_param declaration injected">arg</span><span class="colon injected">:</span><span class="none injected"> </span><span class="builtin_type injected">i32</span><span class="parenthesis injected">)</span><span class="none injected"> </span><span class="brace injected">{</span>
     <span class="comment documentation">///</span><span class="comment documentation"> </span><span class="none injected">    </span><span class="keyword injected">let</span><span class="none injected"> </span><span class="variable declaration injected">x</span><span class="colon injected">:</span><span class="none injected"> </span><span class="type_param injected">T</span><span class="none injected"> </span><span class="operator injected">=</span><span class="none injected"> </span><span class="const_param const injected">X</span><span class="semicolon injected">;</span>
     <span class="comment documentation">///</span><span class="comment documentation"> </span><span class="brace injected">}</span>
     <span class="comment documentation">/// ```</span>
diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_extern_crate.html b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_extern_crate.html
index fe5f5ab6a9a..5399f83085e 100644
--- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_extern_crate.html
+++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_extern_crate.html
@@ -14,12 +14,6 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
 .string_literal     { color: #CC9393; }
 .field              { color: #94BFF3; }
 .function           { color: #93E0E3; }
-.function.unsafe    { color: #BC8383; }
-.trait.unsafe       { color: #BC8383; }
-.operator.unsafe    { color: #BC8383; }
-.mutable.unsafe     { color: #BC8383; text-decoration: underline; }
-.keyword.unsafe     { color: #BC8383; font-weight: bold; }
-.macro.unsafe       { color: #BC8383; }
 .parameter          { color: #94BFF3; }
 .text               { color: #DCDCCC; }
 .type               { color: #7CB8BB; }
@@ -41,6 +35,7 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
 .control            { font-style: italic; }
 .reference          { font-style: italic; font-weight: bold; }
 .const              { font-weight: bolder; }
+.unsafe             { color: #BC8383; }
 
 .invalid_escape_sequence { color: #FC5555; text-decoration: wavy underline; }
 .unresolved_reference    { color: #FC5555; text-decoration: wavy underline; }
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 eb532a5639d..00925bd81ed 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
@@ -14,12 +14,6 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
 .string_literal     { color: #CC9393; }
 .field              { color: #94BFF3; }
 .function           { color: #93E0E3; }
-.function.unsafe    { color: #BC8383; }
-.trait.unsafe       { color: #BC8383; }
-.operator.unsafe    { color: #BC8383; }
-.mutable.unsafe     { color: #BC8383; text-decoration: underline; }
-.keyword.unsafe     { color: #BC8383; font-weight: bold; }
-.macro.unsafe       { color: #BC8383; }
 .parameter          { color: #94BFF3; }
 .text               { color: #DCDCCC; }
 .type               { color: #7CB8BB; }
@@ -41,6 +35,7 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
 .control            { font-style: italic; }
 .reference          { font-style: italic; font-weight: bold; }
 .const              { font-weight: bolder; }
+.unsafe             { color: #BC8383; }
 
 .invalid_escape_sequence { color: #FC5555; text-decoration: wavy underline; }
 .unresolved_reference    { color: #FC5555; text-decoration: wavy underline; }
@@ -109,7 +104,7 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
     <span class="keyword control">loop</span> <span class="brace">{</span><span class="brace">}</span>
 <span class="brace">}</span>
 
-<span class="keyword">fn</span> <span class="function declaration">const_param</span><span class="angle">&lt;</span><span class="keyword">const</span> <span class="const_param const declaration">FOO</span><span class="colon">:</span> <span class="builtin_type">usize</span><span class="angle">&gt;</span><span class="parenthesis">(</span><span class="parenthesis">)</span> <span class="operator">-&gt;</span> <span class="builtin_type">usize</span> <span class="brace">{</span>
+<span class="keyword">fn</span> <span class="function declaration">const_param</span><span class="angle">&lt;</span><span class="keyword const">const</span> <span class="const_param const declaration">FOO</span><span class="colon">:</span> <span class="builtin_type">usize</span><span class="angle">&gt;</span><span class="parenthesis">(</span><span class="parenthesis">)</span> <span class="operator">-&gt;</span> <span class="builtin_type">usize</span> <span class="brace">{</span>
     <span class="function">const_param</span><span class="operator">::</span><span class="angle">&lt;</span><span class="brace">{</span> <span class="const_param const">FOO</span> <span class="brace">}</span><span class="angle">&gt;</span><span class="parenthesis">(</span><span class="parenthesis">)</span><span class="semicolon">;</span>
     <span class="const_param const">FOO</span>
 <span class="brace">}</span>
diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_injection.html b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_injection.html
index 1f9422161de..3b468ab6dba 100644
--- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_injection.html
+++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_injection.html
@@ -14,12 +14,6 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
 .string_literal     { color: #CC9393; }
 .field              { color: #94BFF3; }
 .function           { color: #93E0E3; }
-.function.unsafe    { color: #BC8383; }
-.trait.unsafe       { color: #BC8383; }
-.operator.unsafe    { color: #BC8383; }
-.mutable.unsafe     { color: #BC8383; text-decoration: underline; }
-.keyword.unsafe     { color: #BC8383; font-weight: bold; }
-.macro.unsafe       { color: #BC8383; }
 .parameter          { color: #94BFF3; }
 .text               { color: #DCDCCC; }
 .type               { color: #7CB8BB; }
@@ -41,6 +35,7 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
 .control            { font-style: italic; }
 .reference          { font-style: italic; font-weight: bold; }
 .const              { font-weight: bolder; }
+.unsafe             { color: #BC8383; }
 
 .invalid_escape_sequence { color: #FC5555; text-decoration: wavy underline; }
 .unresolved_reference    { color: #FC5555; text-decoration: wavy underline; }
diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_issue_18089.html b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_issue_18089.html
index a846addba3f..5ef64465c98 100644
--- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_issue_18089.html
+++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_issue_18089.html
@@ -14,12 +14,6 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
 .string_literal     { color: #CC9393; }
 .field              { color: #94BFF3; }
 .function           { color: #93E0E3; }
-.function.unsafe    { color: #BC8383; }
-.trait.unsafe       { color: #BC8383; }
-.operator.unsafe    { color: #BC8383; }
-.mutable.unsafe     { color: #BC8383; text-decoration: underline; }
-.keyword.unsafe     { color: #BC8383; font-weight: bold; }
-.macro.unsafe       { color: #BC8383; }
 .parameter          { color: #94BFF3; }
 .text               { color: #DCDCCC; }
 .type               { color: #7CB8BB; }
@@ -41,6 +35,7 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
 .control            { font-style: italic; }
 .reference          { font-style: italic; font-weight: bold; }
 .const              { font-weight: bolder; }
+.unsafe             { color: #BC8383; }
 
 .invalid_escape_sequence { color: #FC5555; text-decoration: wavy underline; }
 .unresolved_reference    { color: #FC5555; text-decoration: wavy underline; }
diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_keywords_2015.html b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_keywords_2015.html
index c3377614d72..0407e6896e9 100644
--- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_keywords_2015.html
+++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_keywords_2015.html
@@ -14,12 +14,6 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
 .string_literal     { color: #CC9393; }
 .field              { color: #94BFF3; }
 .function           { color: #93E0E3; }
-.function.unsafe    { color: #BC8383; }
-.trait.unsafe       { color: #BC8383; }
-.operator.unsafe    { color: #BC8383; }
-.mutable.unsafe     { color: #BC8383; text-decoration: underline; }
-.keyword.unsafe     { color: #BC8383; font-weight: bold; }
-.macro.unsafe       { color: #BC8383; }
 .parameter          { color: #94BFF3; }
 .text               { color: #DCDCCC; }
 .type               { color: #7CB8BB; }
@@ -41,6 +35,7 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
 .control            { font-style: italic; }
 .reference          { font-style: italic; font-weight: bold; }
 .const              { font-weight: bolder; }
+.unsafe             { color: #BC8383; }
 
 .invalid_escape_sequence { color: #FC5555; text-decoration: wavy underline; }
 .unresolved_reference    { color: #FC5555; text-decoration: wavy underline; }
diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_keywords_2018.html b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_keywords_2018.html
index 9b22500396b..f39d033c76f 100644
--- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_keywords_2018.html
+++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_keywords_2018.html
@@ -14,12 +14,6 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
 .string_literal     { color: #CC9393; }
 .field              { color: #94BFF3; }
 .function           { color: #93E0E3; }
-.function.unsafe    { color: #BC8383; }
-.trait.unsafe       { color: #BC8383; }
-.operator.unsafe    { color: #BC8383; }
-.mutable.unsafe     { color: #BC8383; text-decoration: underline; }
-.keyword.unsafe     { color: #BC8383; font-weight: bold; }
-.macro.unsafe       { color: #BC8383; }
 .parameter          { color: #94BFF3; }
 .text               { color: #DCDCCC; }
 .type               { color: #7CB8BB; }
@@ -41,6 +35,7 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
 .control            { font-style: italic; }
 .reference          { font-style: italic; font-weight: bold; }
 .const              { font-weight: bolder; }
+.unsafe             { color: #BC8383; }
 
 .invalid_escape_sequence { color: #FC5555; text-decoration: wavy underline; }
 .unresolved_reference    { color: #FC5555; text-decoration: wavy underline; }
diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_keywords_2021.html b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_keywords_2021.html
index 9b22500396b..f39d033c76f 100644
--- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_keywords_2021.html
+++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_keywords_2021.html
@@ -14,12 +14,6 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
 .string_literal     { color: #CC9393; }
 .field              { color: #94BFF3; }
 .function           { color: #93E0E3; }
-.function.unsafe    { color: #BC8383; }
-.trait.unsafe       { color: #BC8383; }
-.operator.unsafe    { color: #BC8383; }
-.mutable.unsafe     { color: #BC8383; text-decoration: underline; }
-.keyword.unsafe     { color: #BC8383; font-weight: bold; }
-.macro.unsafe       { color: #BC8383; }
 .parameter          { color: #94BFF3; }
 .text               { color: #DCDCCC; }
 .type               { color: #7CB8BB; }
@@ -41,6 +35,7 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
 .control            { font-style: italic; }
 .reference          { font-style: italic; font-weight: bold; }
 .const              { font-weight: bolder; }
+.unsafe             { color: #BC8383; }
 
 .invalid_escape_sequence { color: #FC5555; text-decoration: wavy underline; }
 .unresolved_reference    { color: #FC5555; text-decoration: wavy underline; }
diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_keywords_2024.html b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_keywords_2024.html
index ac8353120e8..721185a1a84 100644
--- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_keywords_2024.html
+++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_keywords_2024.html
@@ -14,12 +14,6 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
 .string_literal     { color: #CC9393; }
 .field              { color: #94BFF3; }
 .function           { color: #93E0E3; }
-.function.unsafe    { color: #BC8383; }
-.trait.unsafe       { color: #BC8383; }
-.operator.unsafe    { color: #BC8383; }
-.mutable.unsafe     { color: #BC8383; text-decoration: underline; }
-.keyword.unsafe     { color: #BC8383; font-weight: bold; }
-.macro.unsafe       { color: #BC8383; }
 .parameter          { color: #94BFF3; }
 .text               { color: #DCDCCC; }
 .type               { color: #7CB8BB; }
@@ -41,6 +35,7 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
 .control            { font-style: italic; }
 .reference          { font-style: italic; font-weight: bold; }
 .const              { font-weight: bolder; }
+.unsafe             { color: #BC8383; }
 
 .invalid_escape_sequence { color: #FC5555; text-decoration: wavy underline; }
 .unresolved_reference    { color: #FC5555; text-decoration: wavy underline; }
diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_keywords_macros.html b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_keywords_macros.html
index 694e54d2fa8..b2c82051eb1 100644
--- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_keywords_macros.html
+++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_keywords_macros.html
@@ -14,12 +14,6 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
 .string_literal     { color: #CC9393; }
 .field              { color: #94BFF3; }
 .function           { color: #93E0E3; }
-.function.unsafe    { color: #BC8383; }
-.trait.unsafe       { color: #BC8383; }
-.operator.unsafe    { color: #BC8383; }
-.mutable.unsafe     { color: #BC8383; text-decoration: underline; }
-.keyword.unsafe     { color: #BC8383; font-weight: bold; }
-.macro.unsafe       { color: #BC8383; }
 .parameter          { color: #94BFF3; }
 .text               { color: #DCDCCC; }
 .type               { color: #7CB8BB; }
@@ -41,6 +35,7 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
 .control            { font-style: italic; }
 .reference          { font-style: italic; font-weight: bold; }
 .const              { font-weight: bolder; }
+.unsafe             { color: #BC8383; }
 
 .invalid_escape_sequence { color: #FC5555; text-decoration: wavy underline; }
 .unresolved_reference    { color: #FC5555; text-decoration: wavy underline; }
diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_lifetimes.html b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_lifetimes.html
index 8428b815800..618ea2171b5 100644
--- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_lifetimes.html
+++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_lifetimes.html
@@ -14,12 +14,6 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
 .string_literal     { color: #CC9393; }
 .field              { color: #94BFF3; }
 .function           { color: #93E0E3; }
-.function.unsafe    { color: #BC8383; }
-.trait.unsafe       { color: #BC8383; }
-.operator.unsafe    { color: #BC8383; }
-.mutable.unsafe     { color: #BC8383; text-decoration: underline; }
-.keyword.unsafe     { color: #BC8383; font-weight: bold; }
-.macro.unsafe       { color: #BC8383; }
 .parameter          { color: #94BFF3; }
 .text               { color: #DCDCCC; }
 .type               { color: #7CB8BB; }
@@ -41,6 +35,7 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
 .control            { font-style: italic; }
 .reference          { font-style: italic; font-weight: bold; }
 .const              { font-weight: bolder; }
+.unsafe             { color: #BC8383; }
 
 .invalid_escape_sequence { color: #FC5555; text-decoration: wavy underline; }
 .unresolved_reference    { color: #FC5555; text-decoration: wavy underline; }
diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_macros.html b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_macros.html
index f224435e961..c3145941c3e 100644
--- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_macros.html
+++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_macros.html
@@ -14,12 +14,6 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
 .string_literal     { color: #CC9393; }
 .field              { color: #94BFF3; }
 .function           { color: #93E0E3; }
-.function.unsafe    { color: #BC8383; }
-.trait.unsafe       { color: #BC8383; }
-.operator.unsafe    { color: #BC8383; }
-.mutable.unsafe     { color: #BC8383; text-decoration: underline; }
-.keyword.unsafe     { color: #BC8383; font-weight: bold; }
-.macro.unsafe       { color: #BC8383; }
 .parameter          { color: #94BFF3; }
 .text               { color: #DCDCCC; }
 .type               { color: #7CB8BB; }
@@ -41,6 +35,7 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
 .control            { font-style: italic; }
 .reference          { font-style: italic; font-weight: bold; }
 .const              { font-weight: bolder; }
+.unsafe             { color: #BC8383; }
 
 .invalid_escape_sequence { color: #FC5555; text-decoration: wavy underline; }
 .unresolved_reference    { color: #FC5555; text-decoration: wavy underline; }
diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_module_docs_inline.html b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_module_docs_inline.html
index 947d1bf1e35..9996a871580 100644
--- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_module_docs_inline.html
+++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_module_docs_inline.html
@@ -14,12 +14,6 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
 .string_literal     { color: #CC9393; }
 .field              { color: #94BFF3; }
 .function           { color: #93E0E3; }
-.function.unsafe    { color: #BC8383; }
-.trait.unsafe       { color: #BC8383; }
-.operator.unsafe    { color: #BC8383; }
-.mutable.unsafe     { color: #BC8383; text-decoration: underline; }
-.keyword.unsafe     { color: #BC8383; font-weight: bold; }
-.macro.unsafe       { color: #BC8383; }
 .parameter          { color: #94BFF3; }
 .text               { color: #DCDCCC; }
 .type               { color: #7CB8BB; }
@@ -41,6 +35,7 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
 .control            { font-style: italic; }
 .reference          { font-style: italic; font-weight: bold; }
 .const              { font-weight: bolder; }
+.unsafe             { color: #BC8383; }
 
 .invalid_escape_sequence { color: #FC5555; text-decoration: wavy underline; }
 .unresolved_reference    { color: #FC5555; text-decoration: wavy underline; }
diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_module_docs_outline.html b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_module_docs_outline.html
index 0fe2b6f274d..dc9e1de4a42 100644
--- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_module_docs_outline.html
+++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_module_docs_outline.html
@@ -14,12 +14,6 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
 .string_literal     { color: #CC9393; }
 .field              { color: #94BFF3; }
 .function           { color: #93E0E3; }
-.function.unsafe    { color: #BC8383; }
-.trait.unsafe       { color: #BC8383; }
-.operator.unsafe    { color: #BC8383; }
-.mutable.unsafe     { color: #BC8383; text-decoration: underline; }
-.keyword.unsafe     { color: #BC8383; font-weight: bold; }
-.macro.unsafe       { color: #BC8383; }
 .parameter          { color: #94BFF3; }
 .text               { color: #DCDCCC; }
 .type               { color: #7CB8BB; }
@@ -41,6 +35,7 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
 .control            { font-style: italic; }
 .reference          { font-style: italic; font-weight: bold; }
 .const              { font-weight: bolder; }
+.unsafe             { color: #BC8383; }
 
 .invalid_escape_sequence { color: #FC5555; text-decoration: wavy underline; }
 .unresolved_reference    { color: #FC5555; text-decoration: wavy underline; }
diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_operators.html b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_operators.html
index c60b6ab27bb..9c42401ed07 100644
--- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_operators.html
+++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_operators.html
@@ -14,12 +14,6 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
 .string_literal     { color: #CC9393; }
 .field              { color: #94BFF3; }
 .function           { color: #93E0E3; }
-.function.unsafe    { color: #BC8383; }
-.trait.unsafe       { color: #BC8383; }
-.operator.unsafe    { color: #BC8383; }
-.mutable.unsafe     { color: #BC8383; text-decoration: underline; }
-.keyword.unsafe     { color: #BC8383; font-weight: bold; }
-.macro.unsafe       { color: #BC8383; }
 .parameter          { color: #94BFF3; }
 .text               { color: #DCDCCC; }
 .type               { color: #7CB8BB; }
@@ -41,6 +35,7 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
 .control            { font-style: italic; }
 .reference          { font-style: italic; font-weight: bold; }
 .const              { font-weight: bolder; }
+.unsafe             { color: #BC8383; }
 
 .invalid_escape_sequence { color: #FC5555; text-decoration: wavy underline; }
 .unresolved_reference    { color: #FC5555; text-decoration: wavy underline; }
diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_rainbow.html b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_rainbow.html
index a4449b5d8d8..7f6b4c2c880 100644
--- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_rainbow.html
+++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_rainbow.html
@@ -14,12 +14,6 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
 .string_literal     { color: #CC9393; }
 .field              { color: #94BFF3; }
 .function           { color: #93E0E3; }
-.function.unsafe    { color: #BC8383; }
-.trait.unsafe       { color: #BC8383; }
-.operator.unsafe    { color: #BC8383; }
-.mutable.unsafe     { color: #BC8383; text-decoration: underline; }
-.keyword.unsafe     { color: #BC8383; font-weight: bold; }
-.macro.unsafe       { color: #BC8383; }
 .parameter          { color: #94BFF3; }
 .text               { color: #DCDCCC; }
 .type               { color: #7CB8BB; }
@@ -41,6 +35,7 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
 .control            { font-style: italic; }
 .reference          { font-style: italic; font-weight: bold; }
 .const              { font-weight: bolder; }
+.unsafe             { color: #BC8383; }
 
 .invalid_escape_sequence { color: #FC5555; text-decoration: wavy underline; }
 .unresolved_reference    { color: #FC5555; text-decoration: wavy underline; }
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 539c74f6b57..f7d79820803 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
@@ -14,12 +14,6 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
 .string_literal     { color: #CC9393; }
 .field              { color: #94BFF3; }
 .function           { color: #93E0E3; }
-.function.unsafe    { color: #BC8383; }
-.trait.unsafe       { color: #BC8383; }
-.operator.unsafe    { color: #BC8383; }
-.mutable.unsafe     { color: #BC8383; text-decoration: underline; }
-.keyword.unsafe     { color: #BC8383; font-weight: bold; }
-.macro.unsafe       { color: #BC8383; }
 .parameter          { color: #94BFF3; }
 .text               { color: #DCDCCC; }
 .type               { color: #7CB8BB; }
@@ -41,6 +35,7 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
 .control            { font-style: italic; }
 .reference          { font-style: italic; font-weight: bold; }
 .const              { font-weight: bolder; }
+.unsafe             { color: #BC8383; }
 
 .invalid_escape_sequence { color: #FC5555; text-decoration: wavy underline; }
 .unresolved_reference    { color: #FC5555; text-decoration: wavy underline; }
@@ -169,12 +164,12 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
     <span class="macro public">toho</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal macro">"{}fmt"</span><span class="comma macro">,</span> <span class="numeric_literal macro">0</span><span class="parenthesis">)</span><span class="semicolon">;</span>
     <span class="keyword">let</span> <span class="variable declaration">i</span><span class="colon">:</span> <span class="builtin_type">u64</span> <span class="operator">=</span> <span class="numeric_literal">3</span><span class="semicolon">;</span>
     <span class="keyword">let</span> <span class="variable declaration">o</span><span class="colon">:</span> <span class="builtin_type">u64</span><span class="semicolon">;</span>
-    <span class="module crate_root default_library library">core</span><span class="operator">::</span><span class="module default_library library">arch</span><span class="operator">::</span><span class="macro default_library library unsafe">asm</span><span class="macro_bang">!</span><span class="parenthesis">(</span>
+    <span class="module crate_root default_library library">core</span><span class="operator">::</span><span class="module default_library library">arch</span><span class="operator">::</span><span class="macro default_library library unsafe">asm</span><span class="macro_bang unsafe">!</span><span class="parenthesis unsafe">(</span>
         <span class="string_literal macro">"mov {</span><span class="variable">0</span><span class="string_literal macro">}, {</span><span class="variable">1</span><span class="string_literal macro">}"</span><span class="comma macro">,</span>
         <span class="string_literal macro">"add {</span><span class="variable">0</span><span class="string_literal macro">}, 5"</span><span class="comma macro">,</span>
         <span class="keyword macro">out</span><span class="parenthesis macro">(</span><span class="reg library macro">reg</span><span class="parenthesis macro">)</span> <span class="variable macro">o</span><span class="comma macro">,</span>
         <span class="keyword control macro">in</span><span class="parenthesis macro">(</span><span class="reg library macro">reg</span><span class="parenthesis macro">)</span> <span class="variable macro">i</span><span class="comma macro">,</span>
-    <span class="parenthesis">)</span><span class="semicolon">;</span>
+    <span class="parenthesis unsafe">)</span><span class="semicolon">;</span>
 
     <span class="keyword const">const</span> <span class="constant const declaration">CONSTANT</span><span class="colon">:</span> <span class="parenthesis">(</span><span class="parenthesis">)</span> <span class="operator">=</span> <span class="parenthesis">(</span><span class="parenthesis">)</span><span class="colon">:</span>
     <span class="keyword">let</span> <span class="keyword">mut</span> <span class="variable declaration mutable">m</span> <span class="operator">=</span> <span class="parenthesis">(</span><span class="parenthesis">)</span><span class="semicolon">;</span>
diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_unsafe.html b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_unsafe.html
index 9a46d9f4025..828b8f762c5 100644
--- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_unsafe.html
+++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_unsafe.html
@@ -14,12 +14,6 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
 .string_literal     { color: #CC9393; }
 .field              { color: #94BFF3; }
 .function           { color: #93E0E3; }
-.function.unsafe    { color: #BC8383; }
-.trait.unsafe       { color: #BC8383; }
-.operator.unsafe    { color: #BC8383; }
-.mutable.unsafe     { color: #BC8383; text-decoration: underline; }
-.keyword.unsafe     { color: #BC8383; font-weight: bold; }
-.macro.unsafe       { color: #BC8383; }
 .parameter          { color: #94BFF3; }
 .text               { color: #DCDCCC; }
 .type               { color: #7CB8BB; }
@@ -41,6 +35,7 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
 .control            { font-style: italic; }
 .reference          { font-style: italic; font-weight: bold; }
 .const              { font-weight: bolder; }
+.unsafe             { color: #BC8383; }
 
 .invalid_escape_sequence { color: #FC5555; text-decoration: wavy underline; }
 .unresolved_reference    { color: #FC5555; text-decoration: wavy underline; }
@@ -52,78 +47,81 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
 <span class="brace">}</span>
 <span class="keyword">macro_rules</span><span class="macro_bang">!</span> <span class="macro declaration public">unsafe_deref</span> <span class="brace">{</span>
     <span class="parenthesis">(</span><span class="parenthesis">)</span> <span class="operator">=</span><span class="operator">&gt;</span> <span class="brace">{</span>
-        <span class="punctuation">*</span><span class="parenthesis">(</span><span class="punctuation">&</span><span class="parenthesis">(</span><span class="parenthesis">)</span> <span class="keyword">as</span> <span class="punctuation">*</span><span class="keyword">const</span> <span class="parenthesis">(</span><span class="parenthesis">)</span><span class="parenthesis">)</span>
+        <span class="punctuation">*</span><span class="parenthesis">(</span><span class="punctuation">&</span><span class="parenthesis">(</span><span class="parenthesis">)</span> <span class="keyword">as</span> <span class="punctuation">*</span><span class="keyword const">const</span> <span class="parenthesis">(</span><span class="parenthesis">)</span><span class="parenthesis">)</span>
     <span class="brace">}</span><span class="semicolon">;</span>
 <span class="brace">}</span>
-<span class="keyword">static</span> <span class="keyword">mut</span> <span class="static declaration mutable unsafe">MUT_GLOBAL</span><span class="colon">:</span> <span class="struct">Struct</span> <span class="operator">=</span> <span class="struct">Struct</span> <span class="brace">{</span> <span class="field">field</span><span class="colon">:</span> <span class="numeric_literal">0</span> <span class="brace">}</span><span class="semicolon">;</span>
-<span class="keyword">static</span> <span class="static declaration">GLOBAL</span><span class="colon">:</span> <span class="struct">Struct</span> <span class="operator">=</span> <span class="struct">Struct</span> <span class="brace">{</span> <span class="field">field</span><span class="colon">:</span> <span class="numeric_literal">0</span> <span class="brace">}</span><span class="semicolon">;</span>
-<span class="keyword unsafe">unsafe</span> <span class="keyword">fn</span> <span class="function declaration unsafe">unsafe_fn</span><span class="parenthesis">(</span><span class="parenthesis">)</span> <span class="brace">{</span><span class="brace">}</span>
 
 <span class="keyword">union</span> <span class="union declaration">Union</span> <span class="brace">{</span>
-    <span class="field declaration">a</span><span class="colon">:</span> <span class="builtin_type">u32</span><span class="comma">,</span>
-    <span class="field declaration">b</span><span class="colon">:</span> <span class="builtin_type">f32</span><span class="comma">,</span>
+    <span class="field declaration">field</span><span class="colon">:</span> <span class="builtin_type">u32</span><span class="comma">,</span>
 <span class="brace">}</span>
 
 <span class="keyword">struct</span> <span class="struct declaration">Struct</span> <span class="brace">{</span> <span class="field declaration">field</span><span class="colon">:</span> <span class="builtin_type">i32</span> <span class="brace">}</span>
+
+<span class="keyword">static</span> <span class="keyword">mut</span> <span class="static declaration mutable unsafe">MUT_GLOBAL</span><span class="colon">:</span> <span class="struct">Struct</span> <span class="operator">=</span> <span class="struct">Struct</span> <span class="brace">{</span> <span class="field">field</span><span class="colon">:</span> <span class="numeric_literal">0</span> <span class="brace">}</span><span class="semicolon">;</span>
+<span class="keyword unsafe">unsafe</span> <span class="keyword">fn</span> <span class="function declaration unsafe">unsafe_fn</span><span class="parenthesis">(</span><span class="parenthesis">)</span> <span class="brace">{</span><span class="brace">}</span>
+
 <span class="keyword">impl</span> <span class="struct">Struct</span> <span class="brace">{</span>
     <span class="keyword unsafe">unsafe</span> <span class="keyword">fn</span> <span class="method associated declaration reference unsafe">unsafe_method</span><span class="parenthesis">(</span><span class="punctuation">&</span><span class="self_keyword declaration reference">self</span><span class="parenthesis">)</span> <span class="brace">{</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">repr</span><span class="parenthesis attribute">(</span><span class="none attribute">packed</span><span class="parenthesis attribute">)</span><span class="attribute_bracket attribute">]</span>
-<span class="keyword">struct</span> <span class="struct declaration">Packed</span> <span class="brace">{</span>
-    <span class="field declaration">a</span><span class="colon">:</span> <span class="builtin_type">u16</span><span class="comma">,</span>
-<span class="brace">}</span>
-
 <span class="keyword unsafe">unsafe</span> <span class="keyword">trait</span> <span class="trait declaration unsafe">UnsafeTrait</span> <span class="brace">{</span><span class="brace">}</span>
-<span class="keyword unsafe">unsafe</span> <span class="keyword">impl</span> <span class="trait unsafe">UnsafeTrait</span> <span class="keyword">for</span> <span class="struct">Packed</span> <span class="brace">{</span><span class="brace">}</span>
+<span class="keyword unsafe">unsafe</span> <span class="keyword">impl</span> <span class="trait unsafe">UnsafeTrait</span> <span class="keyword">for</span> <span class="union">Union</span> <span class="brace">{</span><span class="brace">}</span>
 <span class="keyword">impl</span> <span class="punctuation">!</span><span class="trait">UnsafeTrait</span> <span class="keyword">for</span> <span class="parenthesis">(</span><span class="parenthesis">)</span> <span class="brace">{</span><span class="brace">}</span>
 
 <span class="keyword">fn</span> <span class="function declaration">unsafe_trait_bound</span><span class="angle">&lt;</span><span class="type_param declaration">T</span><span class="colon">:</span> <span class="trait">UnsafeTrait</span><span class="angle">&gt;</span><span class="parenthesis">(</span><span class="punctuation">_</span><span class="colon">:</span> <span class="type_param">T</span><span class="parenthesis">)</span> <span class="brace">{</span><span class="brace">}</span>
 
-<span class="keyword">trait</span> <span class="trait declaration">DoTheAutoref</span> <span class="brace">{</span>
-    <span class="keyword">fn</span> <span class="method associated declaration reference trait">calls_autoref</span><span class="parenthesis">(</span><span class="punctuation">&</span><span class="self_keyword declaration reference">self</span><span class="parenthesis">)</span><span class="semicolon">;</span>
-<span class="brace">}</span>
-
-<span class="keyword">impl</span> <span class="trait">DoTheAutoref</span> <span class="keyword">for</span> <span class="builtin_type">u16</span> <span class="brace">{</span>
-    <span class="keyword">fn</span> <span class="method associated declaration reference trait">calls_autoref</span><span class="parenthesis">(</span><span class="punctuation">&</span><span class="self_keyword declaration reference">self</span><span class="parenthesis">)</span> <span class="brace">{</span><span class="brace">}</span>
+<span class="keyword">extern</span> <span class="brace">{</span>
+    <span class="keyword">static</span> <span class="static declaration">EXTERN_STATIC</span><span class="colon">:</span> <span class="parenthesis">(</span><span class="parenthesis">)</span><span class="semicolon">;</span>
 <span class="brace">}</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">x</span> <span class="operator">=</span> <span class="operator">&</span><span class="numeric_literal">5</span> <span class="keyword">as</span> <span class="keyword">*</span><span class="keyword">const</span> <span class="punctuation">_</span> <span class="keyword">as</span> <span class="keyword">*</span><span class="keyword">const</span> <span class="builtin_type">usize</span><span class="semicolon">;</span>
-    <span class="keyword">let</span> <span class="variable declaration">u</span> <span class="operator">=</span> <span class="union">Union</span> <span class="brace">{</span> <span class="field">b</span><span class="colon">:</span> <span class="numeric_literal">0</span> <span class="brace">}</span><span class="semicolon">;</span>
+    <span class="keyword">let</span> <span class="variable declaration">x</span><span class="colon">:</span> <span class="keyword">*</span><span class="keyword const">const</span> <span class="builtin_type">usize</span><span class="semicolon">;</span>
+    <span class="keyword">let</span> <span class="variable declaration">u</span><span class="colon">:</span> <span class="union">Union</span><span class="semicolon">;</span>
 
+    <span class="comment">// id should be safe here, but unsafe_deref should not</span>
     <span class="macro public">id</span><span class="macro_bang">!</span> <span class="brace">{</span>
-        <span class="keyword macro unsafe">unsafe</span> <span class="brace macro">{</span> <span class="macro macro public unsafe">unsafe_deref</span><span class="macro_bang macro">!</span><span class="parenthesis macro">(</span><span class="parenthesis macro">)</span> <span class="brace macro">}</span>
+        <span class="keyword macro unsafe">unsafe</span> <span class="brace macro">{</span> <span class="macro macro public unsafe">unsafe_deref</span><span class="macro_bang macro unsafe">!</span><span class="parenthesis macro unsafe">(</span><span class="parenthesis macro unsafe">)</span> <span class="brace macro">}</span>
     <span class="brace">}</span><span class="semicolon">;</span>
 
     <span class="keyword unsafe">unsafe</span> <span class="brace">{</span>
-        <span class="macro public unsafe">unsafe_deref</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="parenthesis">)</span><span class="semicolon">;</span>
-        <span class="macro public unsafe">id</span><span class="macro_bang">!</span> <span class="brace">{</span> <span class="macro macro public unsafe">unsafe_deref</span><span class="macro_bang macro">!</span><span class="parenthesis macro">(</span><span class="parenthesis macro">)</span> <span class="brace">}</span><span class="semicolon">;</span>
+        <span class="comment">// unsafe macro calls</span>
+        <span class="macro public unsafe">unsafe_deref</span><span class="macro_bang unsafe">!</span><span class="parenthesis unsafe">(</span><span class="parenthesis unsafe">)</span><span class="semicolon">;</span>
+        <span class="macro public unsafe">id</span><span class="macro_bang unsafe">!</span> <span class="brace unsafe">{</span> <span class="macro macro public unsafe">unsafe_deref</span><span class="macro_bang macro unsafe">!</span><span class="parenthesis macro unsafe">(</span><span class="parenthesis macro unsafe">)</span> <span class="brace unsafe">}</span><span class="semicolon">;</span>
 
         <span class="comment">// unsafe fn and method calls</span>
-        <span class="function unsafe">unsafe_fn</span><span class="parenthesis">(</span><span class="parenthesis">)</span><span class="semicolon">;</span>
-        <span class="keyword">let</span> <span class="variable declaration">b</span> <span class="operator">=</span> <span class="variable">u</span><span class="operator">.</span><span class="field unsafe">b</span><span class="semicolon">;</span>
-        <span class="keyword control">match</span> <span class="variable">u</span> <span class="brace">{</span>
-            <span class="union">Union</span> <span class="brace">{</span> <span class="field unsafe">b</span><span class="colon">:</span> <span class="numeric_literal">0</span> <span class="brace">}</span> <span class="operator">=&gt;</span> <span class="parenthesis">(</span><span class="parenthesis">)</span><span class="comma">,</span>
-            <span class="union">Union</span> <span class="brace">{</span> <span class="field unsafe">a</span> <span class="brace">}</span> <span class="operator">=&gt;</span> <span class="parenthesis">(</span><span class="parenthesis">)</span><span class="comma">,</span>
-        <span class="brace">}</span>
-        <span class="struct">Struct</span> <span class="brace">{</span> <span class="field">field</span><span class="colon">:</span> <span class="numeric_literal">0</span> <span class="brace">}</span><span class="operator">.</span><span class="method reference unsafe">unsafe_method</span><span class="parenthesis">(</span><span class="parenthesis">)</span><span class="semicolon">;</span>
+        <span class="function unsafe">unsafe_fn</span><span class="parenthesis unsafe">(</span><span class="parenthesis unsafe">)</span><span class="semicolon">;</span>
+        <span class="self_keyword crate_root public">self</span><span class="operator">::</span><span class="function unsafe">unsafe_fn</span><span class="parenthesis unsafe">(</span><span class="parenthesis unsafe">)</span><span class="semicolon">;</span>
+        <span class="parenthesis">(</span><span class="function">unsafe_fn</span> <span class="keyword">as</span> <span class="keyword unsafe">unsafe</span> <span class="keyword">fn</span><span class="parenthesis">(</span><span class="parenthesis">)</span><span class="parenthesis">)</span><span class="parenthesis unsafe">(</span><span class="parenthesis unsafe">)</span><span class="semicolon">;</span>
+        <span class="struct">Struct</span> <span class="brace">{</span> <span class="field">field</span><span class="colon">:</span> <span class="numeric_literal">0</span> <span class="brace">}</span><span class="operator">.</span><span class="method reference unsafe">unsafe_method</span><span class="parenthesis unsafe">(</span><span class="parenthesis unsafe">)</span><span class="semicolon">;</span>
+
+        <span class="variable">u</span><span class="operator">.</span><span class="field unsafe">field</span><span class="semicolon">;</span>
+        <span class="operator">&</span><span class="variable">u</span><span class="operator">.</span><span class="field unsafe">field</span><span class="semicolon">;</span>
+        <span class="operator">&</span><span class="keyword">raw</span> <span class="keyword const">const</span> <span class="variable">u</span><span class="operator">.</span><span class="field unsafe">field</span><span class="semicolon">;</span>
+        <span class="comment">// this should be safe!</span>
+        <span class="keyword">let</span> <span class="union">Union</span> <span class="brace">{</span> <span class="field">field</span><span class="colon">:</span> <span class="punctuation">_</span> <span class="brace">}</span><span class="semicolon">;</span>
+        <span class="comment">// but not these</span>
+        <span class="keyword">let</span> <span class="union">Union</span> <span class="brace">{</span> <span class="field unsafe">field</span> <span class="brace">}</span><span class="semicolon">;</span>
+        <span class="keyword">let</span> <span class="union">Union</span> <span class="brace">{</span> <span class="field unsafe">field</span><span class="colon">:</span> <span class="variable declaration">field</span> <span class="brace">}</span><span class="semicolon">;</span>
+        <span class="keyword">let</span> <span class="union">Union</span> <span class="brace">{</span> <span class="field unsafe">field</span><span class="colon">:</span> <span class="keyword">ref</span> <span class="variable declaration reference">field</span> <span class="brace">}</span><span class="semicolon">;</span>
+        <span class="keyword">let</span> <span class="union">Union</span> <span class="brace">{</span> <span class="field unsafe">field</span><span class="colon">:</span> <span class="parenthesis">(</span><span class="punctuation">_</span> <span class="punctuation">|</span> <span class="keyword">ref</span> <span class="variable declaration reference">field</span><span class="parenthesis">)</span> <span class="brace">}</span><span class="semicolon">;</span>
 
         <span class="comment">// unsafe deref</span>
-        <span class="operator unsafe">*</span><span class="variable">x</span><span class="semicolon">;</span>
+        <span class="operator unsafe">*</span><span class="operator">&</span><span class="keyword">raw</span> <span class="keyword const">const</span><span class="operator">*</span><span class="operator">&</span><span class="operator unsafe">*</span><span class="variable">x</span><span class="semicolon">;</span>
 
         <span class="comment">// unsafe access to a static mut</span>
         <span class="static mutable unsafe">MUT_GLOBAL</span><span class="operator">.</span><span class="field">field</span><span class="semicolon">;</span>
-        <span class="static">GLOBAL</span><span class="operator">.</span><span class="field">field</span><span class="semicolon">;</span>
-
-        <span class="comment">// unsafe ref of packed fields</span>
-        <span class="keyword">let</span> <span class="variable declaration">packed</span> <span class="operator">=</span> <span class="struct">Packed</span> <span class="brace">{</span> <span class="field">a</span><span class="colon">:</span> <span class="numeric_literal">0</span> <span class="brace">}</span><span class="semicolon">;</span>
-        <span class="keyword">let</span> <span class="variable declaration reference">a</span> <span class="operator">=</span> <span class="operator unsafe">&</span><span class="variable">packed</span><span class="operator">.</span><span class="field">a</span><span class="semicolon">;</span>
-        <span class="keyword">let</span> <span class="keyword unsafe">ref</span> <span class="variable declaration reference">a</span> <span class="operator">=</span> <span class="variable">packed</span><span class="operator">.</span><span class="field">a</span><span class="semicolon">;</span>
-        <span class="keyword">let</span> <span class="struct">Packed</span> <span class="brace">{</span> <span class="keyword unsafe">ref</span> <span class="field">a</span> <span class="brace">}</span> <span class="operator">=</span> <span class="variable">packed</span><span class="semicolon">;</span>
-        <span class="keyword">let</span> <span class="struct">Packed</span> <span class="brace">{</span> <span class="field">a</span><span class="colon">:</span> <span class="keyword unsafe">ref</span> <span class="variable declaration reference">_a</span> <span class="brace">}</span> <span class="operator">=</span> <span class="variable">packed</span><span class="semicolon">;</span>
-
-        <span class="comment">// unsafe auto ref of packed field</span>
-        <span class="variable">packed</span><span class="operator">.</span><span class="field">a</span><span class="operator">.</span><span class="method reference trait unsafe">calls_autoref</span><span class="parenthesis">(</span><span class="parenthesis">)</span><span class="semicolon">;</span>
+        <span class="operator">&</span><span class="static mutable unsafe">MUT_GLOBAL</span><span class="operator">.</span><span class="field">field</span><span class="semicolon">;</span>
+        <span class="operator">&</span><span class="keyword">raw</span> <span class="keyword const">const</span> <span class="static mutable unsafe">MUT_GLOBAL</span><span class="operator">.</span><span class="field">field</span><span class="semicolon">;</span>
+        <span class="static mutable unsafe">MUT_GLOBAL</span><span class="semicolon">;</span>
+        <span class="operator">&</span><span class="static mutable unsafe">MUT_GLOBAL</span><span class="semicolon">;</span>
+        <span class="operator">&</span><span class="keyword">raw</span> <span class="keyword const">const</span> <span class="static mutable">MUT_GLOBAL</span><span class="semicolon">;</span>
+        <span class="static unsafe">EXTERN_STATIC</span><span class="semicolon">;</span>
+        <span class="operator">&</span><span class="static unsafe">EXTERN_STATIC</span><span class="semicolon">;</span>
+        <span class="operator">&</span><span class="keyword">raw</span> <span class="keyword const">const</span> <span class="static">EXTERN_STATIC</span><span class="semicolon">;</span>
+
+        <span class="module crate_root default_library library">core</span><span class="operator">::</span><span class="module default_library library">arch</span><span class="operator">::</span><span class="macro default_library library unsafe">asm</span><span class="macro_bang unsafe">!</span><span class="parenthesis unsafe">(</span>
+            <span class="string_literal macro">"push {</span><span class="variable">base</span><span class="string_literal macro">}"</span><span class="comma macro">,</span>
+            <span class="variable declaration macro">base</span> <span class="operator macro">=</span> <span class="keyword const macro">const</span> <span class="numeric_literal macro">0</span>
+        <span class="parenthesis unsafe">)</span><span class="semicolon">;</span>
     <span class="brace">}</span>
 <span class="brace">}</span></code></pre>
\ No newline at end of file
diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/tests.rs b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/tests.rs
index e48ca86c46b..8f69bb82300 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
@@ -589,7 +589,7 @@ fn main() {
 fn test_unsafe_highlighting() {
     check_highlighting(
         r#"
-//- minicore: sized
+//- minicore: sized, asm
 macro_rules! id {
     ($($tt:tt)*) => {
         $($tt)*
@@ -600,76 +600,79 @@ macro_rules! unsafe_deref {
         *(&() as *const ())
     };
 }
-static mut MUT_GLOBAL: Struct = Struct { field: 0 };
-static GLOBAL: Struct = Struct { field: 0 };
-unsafe fn unsafe_fn() {}
 
 union Union {
-    a: u32,
-    b: f32,
+    field: u32,
 }
 
 struct Struct { field: i32 }
+
+static mut MUT_GLOBAL: Struct = Struct { field: 0 };
+unsafe fn unsafe_fn() {}
+
 impl Struct {
     unsafe fn unsafe_method(&self) {}
 }
 
-#[repr(packed)]
-struct Packed {
-    a: u16,
-}
-
 unsafe trait UnsafeTrait {}
-unsafe impl UnsafeTrait for Packed {}
+unsafe impl UnsafeTrait for Union {}
 impl !UnsafeTrait for () {}
 
 fn unsafe_trait_bound<T: UnsafeTrait>(_: T) {}
 
-trait DoTheAutoref {
-    fn calls_autoref(&self);
-}
-
-impl DoTheAutoref for u16 {
-    fn calls_autoref(&self) {}
+extern {
+    static EXTERN_STATIC: ();
 }
 
 fn main() {
-    let x = &5 as *const _ as *const usize;
-    let u = Union { b: 0 };
+    let x: *const usize;
+    let u: Union;
 
+    // id should be safe here, but unsafe_deref should not
     id! {
         unsafe { unsafe_deref!() }
     };
 
     unsafe {
+        // unsafe macro calls
         unsafe_deref!();
         id! { unsafe_deref!() };
 
         // unsafe fn and method calls
         unsafe_fn();
-        let b = u.b;
-        match u {
-            Union { b: 0 } => (),
-            Union { a } => (),
-        }
+        self::unsafe_fn();
+        (unsafe_fn as unsafe fn())();
         Struct { field: 0 }.unsafe_method();
 
+        u.field;
+        &u.field;
+        &raw const u.field;
+        // this should be safe!
+        let Union { field: _ };
+        // but not these
+        let Union { field };
+        let Union { field: field };
+        let Union { field: ref field };
+        let Union { field: (_ | ref field) };
+
         // unsafe deref
-        *x;
+        *&raw const*&*x;
 
         // unsafe access to a static mut
         MUT_GLOBAL.field;
-        GLOBAL.field;
+        &MUT_GLOBAL.field;
+        &raw const MUT_GLOBAL.field;
+        MUT_GLOBAL;
+        &MUT_GLOBAL;
+        &raw const MUT_GLOBAL;
+        EXTERN_STATIC;
+        &EXTERN_STATIC;
+        &raw const EXTERN_STATIC;
 
-        // unsafe ref of packed fields
-        let packed = Packed { a: 0 };
-        let a = &packed.a;
-        let ref a = packed.a;
-        let Packed { ref a } = packed;
-        let Packed { a: ref _a } = packed;
-
-        // unsafe auto ref of packed field
-        packed.a.calls_autoref();
+        core::arch::asm!(
+            "push {base}",
+            base = const 0
+        );
     }
 }
 "#,
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 edb83bc4eac..34bca7bce12 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
@@ -1,12 +1,11 @@
 use std::fmt;
 
-use hir::{Field, HirDisplay, Layout, Semantics, Type};
+use hir::{DisplayTarget, Field, HirDisplay, Layout, Semantics, Type};
 use ide_db::{
     defs::Definition,
     helpers::{get_definition, pick_best_token},
     RootDatabase,
 };
-use span::Edition;
 use syntax::{AstNode, SyntaxKind};
 
 use crate::FilePosition;
@@ -84,10 +83,7 @@ pub(crate) fn view_memory_layout(
 ) -> Option<RecursiveMemoryLayout> {
     let sema = Semantics::new(db);
     let file = sema.parse_guess_edition(position.file_id);
-    let edition = sema
-        .attach_first_edition(position.file_id)
-        .map(|it| it.edition())
-        .unwrap_or(Edition::CURRENT);
+    let display_target = sema.first_crate_or_default(position.file_id).to_display_target(db);
     let token =
         pick_best_token(file.syntax().token_at_offset(position.offset), |kind| match kind {
             SyntaxKind::IDENT => 3,
@@ -114,7 +110,7 @@ pub(crate) fn view_memory_layout(
         ty: &Type,
         layout: &Layout,
         parent_idx: usize,
-        edition: Edition,
+        display_target: DisplayTarget,
     ) {
         let mut fields = ty
             .fields(db)
@@ -145,7 +141,7 @@ pub(crate) fn view_memory_layout(
             if let Ok(child_layout) = child_ty.layout(db) {
                 nodes.push(MemoryLayoutNode {
                     item_name: field.name(db),
-                    typename: child_ty.display(db, edition).to_string(),
+                    typename: child_ty.display(db, display_target).to_string(),
                     size: child_layout.size(),
                     alignment: child_layout.align(),
                     offset: match *field {
@@ -161,7 +157,7 @@ pub(crate) fn view_memory_layout(
                     item_name: field.name(db)
                         + format!("(no layout data: {:?})", child_ty.layout(db).unwrap_err())
                             .as_ref(),
-                    typename: child_ty.display(db, edition).to_string(),
+                    typename: child_ty.display(db, display_target).to_string(),
                     size: 0,
                     offset: 0,
                     alignment: 0,
@@ -174,7 +170,7 @@ pub(crate) fn view_memory_layout(
 
         for (i, (_, child_ty)) in fields.iter().enumerate() {
             if let Ok(child_layout) = child_ty.layout(db) {
-                read_layout(nodes, db, child_ty, &child_layout, children_start + i, edition);
+                read_layout(nodes, db, child_ty, &child_layout, children_start + i, display_target);
             }
         }
     }
@@ -192,7 +188,7 @@ pub(crate) fn view_memory_layout(
                 def => def.name(db).map(|n| n.as_str().to_owned()).unwrap_or("[ROOT]".to_owned()),
             };
 
-            let typename = ty.display(db, edition).to_string();
+            let typename = ty.display(db, display_target).to_string();
 
             let mut nodes = vec![MemoryLayoutNode {
                 item_name,
@@ -204,7 +200,7 @@ pub(crate) fn view_memory_layout(
                 children_start: -1,
                 children_len: 0,
             }];
-            read_layout(&mut nodes, db, &ty, &layout, 0, edition);
+            read_layout(&mut nodes, db, &ty, &layout, 0, display_target);
 
             RecursiveMemoryLayout { nodes }
         })
diff --git a/src/tools/rust-analyzer/crates/intern/src/symbol.rs b/src/tools/rust-analyzer/crates/intern/src/symbol.rs
index b3bf285edfb..0fa6701ca3f 100644
--- a/src/tools/rust-analyzer/crates/intern/src/symbol.rs
+++ b/src/tools/rust-analyzer/crates/intern/src/symbol.rs
@@ -18,20 +18,17 @@ use triomphe::Arc;
 pub mod symbols;
 
 // some asserts for layout compatibility
-const _: () = assert!(std::mem::size_of::<Box<str>>() == std::mem::size_of::<&str>());
-const _: () = assert!(std::mem::align_of::<Box<str>>() == std::mem::align_of::<&str>());
+const _: () = assert!(size_of::<Box<str>>() == size_of::<&str>());
+const _: () = assert!(align_of::<Box<str>>() == align_of::<&str>());
 
-const _: () = assert!(std::mem::size_of::<Arc<Box<str>>>() == std::mem::size_of::<&&str>());
-const _: () = assert!(std::mem::align_of::<Arc<Box<str>>>() == std::mem::align_of::<&&str>());
+const _: () = assert!(size_of::<Arc<Box<str>>>() == size_of::<&&str>());
+const _: () = assert!(align_of::<Arc<Box<str>>>() == align_of::<&&str>());
 
-const _: () =
-    assert!(std::mem::size_of::<*const *const str>() == std::mem::size_of::<TaggedArcPtr>());
-const _: () =
-    assert!(std::mem::align_of::<*const *const str>() == std::mem::align_of::<TaggedArcPtr>());
+const _: () = assert!(size_of::<*const *const str>() == size_of::<TaggedArcPtr>());
+const _: () = assert!(align_of::<*const *const str>() == align_of::<TaggedArcPtr>());
 
-const _: () = assert!(std::mem::size_of::<Arc<Box<str>>>() == std::mem::size_of::<TaggedArcPtr>());
-const _: () =
-    assert!(std::mem::align_of::<Arc<Box<str>>>() == std::mem::align_of::<TaggedArcPtr>());
+const _: () = assert!(size_of::<Arc<Box<str>>>() == size_of::<TaggedArcPtr>());
+const _: () = assert!(align_of::<Arc<Box<str>>>() == align_of::<TaggedArcPtr>());
 
 /// A pointer that points to a pointer to a `str`, it may be backed as a `&'static &'static str` or
 /// `Arc<Box<str>>` but its size is that of a thin pointer. The active variant is encoded as a tag
@@ -49,9 +46,7 @@ impl TaggedArcPtr {
     const BOOL_BITS: usize = true as usize;
 
     const fn non_arc(r: &'static &'static str) -> Self {
-        assert!(
-            mem::align_of::<&'static &'static str>().trailing_zeros() as usize > Self::BOOL_BITS
-        );
+        assert!(align_of::<&'static &'static str>().trailing_zeros() as usize > Self::BOOL_BITS);
         // SAFETY: The pointer is non-null as it is derived from a reference
         // Ideally we would call out to `pack_arc` but for a `false` tag, unfortunately the
         // packing stuff requires reading out the pointer to an integer which is not supported
@@ -64,9 +59,7 @@ impl TaggedArcPtr {
     }
 
     fn arc(arc: Arc<Box<str>>) -> Self {
-        assert!(
-            mem::align_of::<&'static &'static str>().trailing_zeros() as usize > Self::BOOL_BITS
-        );
+        assert!(align_of::<&'static &'static str>().trailing_zeros() as usize > Self::BOOL_BITS);
         Self {
             packed: Self::pack_arc(
                 // Safety: `Arc::into_raw` always returns a non null pointer
@@ -131,8 +124,8 @@ impl fmt::Debug for Symbol {
     }
 }
 
-const _: () = assert!(std::mem::size_of::<Symbol>() == std::mem::size_of::<NonNull<()>>());
-const _: () = assert!(std::mem::align_of::<Symbol>() == std::mem::align_of::<NonNull<()>>());
+const _: () = assert!(size_of::<Symbol>() == size_of::<NonNull<()>>());
+const _: () = assert!(align_of::<Symbol>() == align_of::<NonNull<()>>());
 
 static MAP: OnceLock<DashMap<SymbolProxy, (), BuildHasherDefault<FxHasher>>> = OnceLock::new();
 
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 be0de6c9366..6b77c72cee8 100644
--- a/src/tools/rust-analyzer/crates/intern/src/symbol/symbols.rs
+++ b/src/tools/rust-analyzer/crates/intern/src/symbol/symbols.rs
@@ -185,6 +185,7 @@ define_symbols! {
     Clone,
     coerce_unsized,
     column,
+    completion,
     compile_error,
     concat_bytes,
     concat_idents,
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 76f1a7f48b6..72ca85c6a2f 100644
--- a/src/tools/rust-analyzer/crates/load-cargo/src/lib.rs
+++ b/src/tools/rust-analyzer/crates/load-cargo/src/lib.rs
@@ -45,10 +45,18 @@ pub fn load_workspace_at(
 ) -> anyhow::Result<(RootDatabase, vfs::Vfs, Option<ProcMacroClient>)> {
     let root = AbsPathBuf::assert_utf8(std::env::current_dir()?.join(root));
     let root = ProjectManifest::discover_single(&root)?;
+    let manifest_path = root.manifest_path().clone();
     let mut workspace = ProjectWorkspace::load(root, cargo_config, progress)?;
 
     if load_config.load_out_dirs_from_check {
         let build_scripts = workspace.run_build_scripts(cargo_config, progress)?;
+        if let Some(error) = build_scripts.error() {
+            tracing::debug!(
+                "Errors occurred while running build scripts for {}: {}",
+                manifest_path,
+                error
+            );
+        }
         workspace.set_build_scripts(build_scripts)
     }
 
diff --git a/src/tools/rust-analyzer/crates/mbe/src/tests.rs b/src/tools/rust-analyzer/crates/mbe/src/tests.rs
index fb68d35a4c8..4a73b6fa05a 100644
--- a/src/tools/rust-analyzer/crates/mbe/src/tests.rs
+++ b/src/tools/rust-analyzer/crates/mbe/src/tests.rs
@@ -100,6 +100,23 @@ fn check(
 }
 
 #[test]
+fn unbalanced_brace() {
+    check(
+        Edition::CURRENT,
+        Edition::CURRENT,
+        r#"
+() => { { }
+"#,
+        r#""#,
+        expect![[r#"
+            SUBTREE $$ 1:0@0..0#2 1:0@0..0#2
+              SUBTREE {} 0:0@9..10#2 0:0@11..12#2
+
+            {}"#]],
+    );
+}
+
+#[test]
 fn token_mapping_smoke_test() {
     check(
         Edition::CURRENT,
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 e56e09eeb66..1ff0bbea8b1 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
@@ -315,6 +315,7 @@ pub enum SyntaxKind {
     USE_TREE,
     USE_TREE_LIST,
     VARIANT,
+    VARIANT_DEF,
     VARIANT_LIST,
     VISIBILITY,
     WHERE_CLAUSE,
@@ -501,6 +502,7 @@ impl SyntaxKind {
             | USE_TREE
             | USE_TREE_LIST
             | VARIANT
+            | VARIANT_DEF
             | VARIANT_LIST
             | VISIBILITY
             | WHERE_CLAUSE
diff --git a/src/tools/rust-analyzer/crates/profile/src/memory_usage.rs b/src/tools/rust-analyzer/crates/profile/src/memory_usage.rs
index 006748ddb08..f5b8eca060f 100644
--- a/src/tools/rust-analyzer/crates/profile/src/memory_usage.rs
+++ b/src/tools/rust-analyzer/crates/profile/src/memory_usage.rs
@@ -38,7 +38,7 @@ impl MemoryUsage {
                 // approximate that by using the Commit Charge value.
 
                 use windows_sys::Win32::System::{Threading::*, ProcessStatus::*};
-                use std::mem::{MaybeUninit, size_of};
+                use std::mem::MaybeUninit;
 
                 let proc = unsafe { GetCurrentProcess() };
                 let mut mem_counters = MaybeUninit::uninit();
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 40ab8c53fae..014028a0b63 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
@@ -288,7 +288,23 @@ impl CargoWorkspace {
         locked: bool,
         progress: &dyn Fn(String),
     ) -> anyhow::Result<(cargo_metadata::Metadata, Option<anyhow::Error>)> {
-        Self::fetch_metadata_(cargo_toml, current_dir, config, sysroot, locked, false, progress)
+        let res = Self::fetch_metadata_(
+            cargo_toml,
+            current_dir,
+            config,
+            sysroot,
+            locked,
+            false,
+            progress,
+        );
+        if let Ok((_, Some(ref e))) = res {
+            tracing::warn!(
+                %cargo_toml,
+                ?e,
+                "`cargo metadata` failed, but retry with `--no-deps` succeeded"
+            );
+        }
+        res
     }
 
     fn fetch_metadata_(
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 0282a714645..b2df8e4703a 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
@@ -86,7 +86,7 @@ impl ProjectJson {
     /// * `manifest` - The path to the `rust-project.json`.
     /// * `base` - The path to the workspace root (i.e. the folder containing `rust-project.json`)
     /// * `data` - The parsed contents of `rust-project.json`, or project json that's passed via
-    ///            configuration.
+    ///   configuration.
     pub fn new(
         manifest: Option<ManifestPath>,
         base: &AbsPath,
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 1e3c5a94786..13812e96fe7 100644
--- a/src/tools/rust-analyzer/crates/project-model/src/sysroot.rs
+++ b/src/tools/rust-analyzer/crates/project-model/src/sysroot.rs
@@ -30,6 +30,7 @@ pub struct Sysroot {
 pub enum RustLibSrcWorkspace {
     Workspace(CargoWorkspace),
     Json(ProjectJson),
+    Stitched(stitched::Stitched),
     Empty,
 }
 
@@ -60,6 +61,7 @@ impl Sysroot {
         match &self.workspace {
             RustLibSrcWorkspace::Workspace(ws) => ws.packages().next().is_none(),
             RustLibSrcWorkspace::Json(project_json) => project_json.n_crates() == 0,
+            RustLibSrcWorkspace::Stitched(stitched) => stitched.crates.is_empty(),
             RustLibSrcWorkspace::Empty => true,
         }
     }
@@ -72,6 +74,7 @@ impl Sysroot {
         match &self.workspace {
             RustLibSrcWorkspace::Workspace(ws) => ws.packages().count(),
             RustLibSrcWorkspace::Json(project_json) => project_json.n_crates(),
+            RustLibSrcWorkspace::Stitched(stitched) => stitched.crates.len(),
             RustLibSrcWorkspace::Empty => 0,
         }
     }
@@ -197,6 +200,51 @@ impl Sysroot {
                     return Some(loaded);
                 }
             }
+            tracing::debug!("Stitching sysroot library: {src_root}");
+
+            let mut stitched = stitched::Stitched { crates: Default::default() };
+
+            for path in stitched::SYSROOT_CRATES.trim().lines() {
+                let name = path.split('/').next_back().unwrap();
+                let root = [format!("{path}/src/lib.rs"), format!("lib{path}/lib.rs")]
+                    .into_iter()
+                    .map(|it| src_root.join(it))
+                    .filter_map(|it| ManifestPath::try_from(it).ok())
+                    .find(|it| fs::metadata(it).is_ok());
+
+                if let Some(root) = root {
+                    stitched.crates.alloc(stitched::RustLibSrcCrateData {
+                        name: name.into(),
+                        root,
+                        deps: Vec::new(),
+                    });
+                }
+            }
+
+            if let Some(std) = stitched.by_name("std") {
+                for dep in stitched::STD_DEPS.trim().lines() {
+                    if let Some(dep) = stitched.by_name(dep) {
+                        stitched.crates[std].deps.push(dep)
+                    }
+                }
+            }
+
+            if let Some(alloc) = stitched.by_name("alloc") {
+                for dep in stitched::ALLOC_DEPS.trim().lines() {
+                    if let Some(dep) = stitched.by_name(dep) {
+                        stitched.crates[alloc].deps.push(dep)
+                    }
+                }
+            }
+
+            if let Some(proc_macro) = stitched.by_name("proc_macro") {
+                for dep in stitched::PROC_MACRO_DEPS.trim().lines() {
+                    if let Some(dep) = stitched.by_name(dep) {
+                        stitched.crates[proc_macro].deps.push(dep)
+                    }
+                }
+            }
+            return Some(RustLibSrcWorkspace::Stitched(stitched));
         } else if let RustSourceWorkspaceConfig::Json(project_json) = sysroot_source_config {
             return Some(RustLibSrcWorkspace::Json(project_json.clone()));
         }
@@ -216,6 +264,7 @@ impl Sysroot {
                         .crates()
                         .filter_map(|(_, krate)| krate.display_name.clone())
                         .any(|name| name.canonical_name().as_str() == "core"),
+                    RustLibSrcWorkspace::Stitched(stitched) => stitched.by_name("core").is_some(),
                     RustLibSrcWorkspace::Empty => true,
                 };
                 if !has_core {
@@ -391,3 +440,91 @@ fn get_rust_lib_src(sysroot_path: &AbsPath) -> Option<AbsPathBuf> {
         None
     }
 }
+
+// FIXME: Remove this, that will bump our project MSRV to 1.82
+pub(crate) mod stitched {
+    use std::ops;
+
+    use base_db::CrateName;
+    use la_arena::{Arena, Idx};
+
+    use crate::ManifestPath;
+
+    #[derive(Debug, Clone, Eq, PartialEq)]
+    pub struct Stitched {
+        pub(super) crates: Arena<RustLibSrcCrateData>,
+    }
+
+    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, 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(move |(name, prelude)| {
+                    Some((CrateName::new(name).unwrap(), self.by_name(name)?, prelude))
+                })
+        }
+
+        pub(crate) fn proc_macro(&self) -> Option<RustLibSrcCrate> {
+            self.by_name("proc_macro")
+        }
+
+        pub(crate) fn crates(&self) -> impl ExactSizeIterator<Item = RustLibSrcCrate> + '_ {
+            self.crates.iter().map(|(id, _data)| id)
+        }
+
+        pub(super) 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 RustLibSrcCrate = Idx<RustLibSrcCrateData>;
+
+    #[derive(Debug, Clone, Eq, PartialEq)]
+    pub(crate) struct RustLibSrcCrateData {
+        pub(crate) name: String,
+        pub(crate) root: ManifestPath,
+        pub(crate) deps: Vec<RustLibSrcCrate>,
+    }
+
+    pub(super) const SYSROOT_CRATES: &str = "
+alloc
+backtrace
+core
+panic_abort
+panic_unwind
+proc_macro
+profiler_builtins
+std
+stdarch/crates/std_detect
+test
+unwind";
+
+    pub(super) const ALLOC_DEPS: &str = "core";
+
+    pub(super) const STD_DEPS: &str = "
+alloc
+panic_unwind
+panic_abort
+core
+profiler_builtins
+unwind
+std_detect
+test";
+
+    // core is required for our builtin derives to work in the proc_macro lib currently
+    pub(super) const PROC_MACRO_DEPS: &str = "
+std
+core";
+}
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 cfc666970bd..83740622732 100644
--- a/src/tools/rust-analyzer/crates/project-model/src/tests.rs
+++ b/src/tools/rust-analyzer/crates/project-model/src/tests.rs
@@ -166,7 +166,7 @@ fn check_crate_graph(crate_graph: CrateGraph, expect: ExpectFile) {
 #[test]
 fn cargo_hello_world_project_model_with_wildcard_overrides() {
     let cfg_overrides = CfgOverrides {
-        global: CfgDiff::new(Vec::new(), vec![CfgAtom::Flag(sym::test.clone())]).unwrap(),
+        global: CfgDiff::new(Vec::new(), vec![CfgAtom::Flag(sym::test.clone())]),
         selective: Default::default(),
     };
     let (crate_graph, _proc_macros) =
@@ -185,7 +185,7 @@ fn cargo_hello_world_project_model_with_selective_overrides() {
         global: Default::default(),
         selective: std::iter::once((
             "libc".to_owned(),
-            CfgDiff::new(Vec::new(), vec![CfgAtom::Flag(sym::test.clone())]).unwrap(),
+            CfgDiff::new(Vec::new(), vec![CfgAtom::Flag(sym::test.clone())]),
         ))
         .collect(),
     };
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 2c9f41e828e..62c13c7d9ec 100644
--- a/src/tools/rust-analyzer/crates/project-model/src/workspace.rs
+++ b/src/tools/rust-analyzer/crates/project-model/src/workspace.rs
@@ -692,6 +692,7 @@ impl ProjectWorkspace {
                         exclude: krate.exclude.clone(),
                     })
                     .collect(),
+                RustLibSrcWorkspace::Stitched(_) => vec![],
                 RustLibSrcWorkspace::Empty => vec![],
             };
 
@@ -1521,7 +1522,7 @@ fn extend_crate_graph_with_sysroot(
 ) -> (SysrootPublicDeps, Option<CrateId>) {
     let mut pub_deps = vec![];
     let mut libproc_macro = None;
-    let diff = CfgDiff::new(vec![], vec![CfgAtom::Flag(sym::test.clone())]).unwrap();
+    let diff = CfgDiff::new(vec![], vec![CfgAtom::Flag(sym::test.clone())]);
     for (cid, c) in sysroot_crate_graph.iter_mut() {
         // uninject `test` flag so `core` keeps working.
         Arc::make_mut(&mut c.cfg_options).apply_diff(diff.clone());
@@ -1599,8 +1600,7 @@ fn sysroot_to_crate_graph(
                             CfgAtom::Flag(sym::miri.clone()),
                         ],
                         vec![],
-                    )
-                    .unwrap(),
+                    ),
                     ..Default::default()
                 },
                 &WorkspaceBuildScripts::default(),
@@ -1623,8 +1623,7 @@ fn sysroot_to_crate_graph(
                             CfgAtom::Flag(sym::miri.clone()),
                         ],
                         vec![],
-                    )
-                    .unwrap(),
+                    ),
                     ..Default::default()
                 },
                 false,
@@ -1632,7 +1631,62 @@ fn sysroot_to_crate_graph(
 
             extend_crate_graph_with_sysroot(crate_graph, cg, pm)
         }
+        RustLibSrcWorkspace::Stitched(stitched) => {
+            let cfg_options = Arc::new({
+                let mut cfg_options = CfgOptions::default();
+                cfg_options.extend(rustc_cfg);
+                cfg_options.insert_atom(sym::debug_assertions.clone());
+                cfg_options.insert_atom(sym::miri.clone());
+                cfg_options
+            });
+            let sysroot_crates: FxHashMap<crate::sysroot::stitched::RustLibSrcCrate, CrateId> =
+                stitched
+                    .crates()
+                    .filter_map(|krate| {
+                        let file_id = load(&stitched[krate].root)?;
+
+                        let display_name =
+                            CrateDisplayName::from_canonical_name(&stitched[krate].name);
+                        let crate_id = crate_graph.add_crate_root(
+                            file_id,
+                            Edition::CURRENT_FIXME,
+                            Some(display_name),
+                            None,
+                            cfg_options.clone(),
+                            None,
+                            Env::default(),
+                            CrateOrigin::Lang(LangCrateOrigin::from(&*stitched[krate].name)),
+                            false,
+                            None,
+                        );
+                        Some((krate, crate_id))
+                    })
+                    .collect();
+
+            for from in stitched.crates() {
+                for &to in stitched[from].deps.iter() {
+                    let name = CrateName::new(&stitched[to].name).unwrap();
+                    if let (Some(&from), Some(&to)) =
+                        (sysroot_crates.get(&from), sysroot_crates.get(&to))
+                    {
+                        add_dep(crate_graph, from, name, to);
+                    }
+                }
+            }
+
+            let public_deps = SysrootPublicDeps {
+                deps: stitched
+                    .public_deps()
+                    .filter_map(|(name, idx, prelude)| {
+                        Some((name, *sysroot_crates.get(&idx)?, prelude))
+                    })
+                    .collect::<Vec<_>>(),
+            };
 
+            let libproc_macro =
+                stitched.proc_macro().and_then(|it| sysroot_crates.get(&it).copied());
+            (public_deps, libproc_macro)
+        }
         RustLibSrcWorkspace::Empty => (SysrootPublicDeps { deps: vec![] }, None),
     }
 }
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 28ca4eb5348..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
@@ -3,6 +3,417 @@
         root_file_id: FileId(
             1,
         ),
+        edition: Edition2021,
+        version: None,
+        display_name: Some(
+            CrateDisplayName {
+                crate_name: CrateName(
+                    "alloc",
+                ),
+                canonical_name: "alloc",
+            },
+        ),
+        cfg_options: CfgOptions(
+            [
+                "debug_assertions",
+                "miri",
+                "true",
+            ],
+        ),
+        potential_cfg_options: None,
+        env: Env {
+            entries: {},
+        },
+        dependencies: [
+            Dependency {
+                crate_id: Idx::<CrateData>(1),
+                name: CrateName(
+                    "core",
+                ),
+                prelude: true,
+                sysroot: false,
+            },
+        ],
+        origin: Lang(
+            Alloc,
+        ),
+        is_proc_macro: false,
+        proc_macro_cwd: None,
+    },
+    1: CrateData {
+        root_file_id: FileId(
+            2,
+        ),
+        edition: Edition2021,
+        version: None,
+        display_name: Some(
+            CrateDisplayName {
+                crate_name: CrateName(
+                    "core",
+                ),
+                canonical_name: "core",
+            },
+        ),
+        cfg_options: CfgOptions(
+            [
+                "debug_assertions",
+                "miri",
+                "true",
+            ],
+        ),
+        potential_cfg_options: None,
+        env: Env {
+            entries: {},
+        },
+        dependencies: [],
+        origin: Lang(
+            Core,
+        ),
+        is_proc_macro: false,
+        proc_macro_cwd: None,
+    },
+    2: CrateData {
+        root_file_id: FileId(
+            3,
+        ),
+        edition: Edition2021,
+        version: None,
+        display_name: Some(
+            CrateDisplayName {
+                crate_name: CrateName(
+                    "panic_abort",
+                ),
+                canonical_name: "panic_abort",
+            },
+        ),
+        cfg_options: CfgOptions(
+            [
+                "debug_assertions",
+                "miri",
+                "true",
+            ],
+        ),
+        potential_cfg_options: None,
+        env: Env {
+            entries: {},
+        },
+        dependencies: [],
+        origin: Lang(
+            Other,
+        ),
+        is_proc_macro: false,
+        proc_macro_cwd: None,
+    },
+    3: CrateData {
+        root_file_id: FileId(
+            4,
+        ),
+        edition: Edition2021,
+        version: None,
+        display_name: Some(
+            CrateDisplayName {
+                crate_name: CrateName(
+                    "panic_unwind",
+                ),
+                canonical_name: "panic_unwind",
+            },
+        ),
+        cfg_options: CfgOptions(
+            [
+                "debug_assertions",
+                "miri",
+                "true",
+            ],
+        ),
+        potential_cfg_options: None,
+        env: Env {
+            entries: {},
+        },
+        dependencies: [],
+        origin: Lang(
+            Other,
+        ),
+        is_proc_macro: false,
+        proc_macro_cwd: None,
+    },
+    4: CrateData {
+        root_file_id: FileId(
+            5,
+        ),
+        edition: Edition2021,
+        version: None,
+        display_name: Some(
+            CrateDisplayName {
+                crate_name: CrateName(
+                    "proc_macro",
+                ),
+                canonical_name: "proc_macro",
+            },
+        ),
+        cfg_options: CfgOptions(
+            [
+                "debug_assertions",
+                "miri",
+                "true",
+            ],
+        ),
+        potential_cfg_options: None,
+        env: Env {
+            entries: {},
+        },
+        dependencies: [
+            Dependency {
+                crate_id: Idx::<CrateData>(6),
+                name: CrateName(
+                    "std",
+                ),
+                prelude: true,
+                sysroot: false,
+            },
+            Dependency {
+                crate_id: Idx::<CrateData>(1),
+                name: CrateName(
+                    "core",
+                ),
+                prelude: true,
+                sysroot: false,
+            },
+        ],
+        origin: Lang(
+            ProcMacro,
+        ),
+        is_proc_macro: false,
+        proc_macro_cwd: None,
+    },
+    5: CrateData {
+        root_file_id: FileId(
+            6,
+        ),
+        edition: Edition2021,
+        version: None,
+        display_name: Some(
+            CrateDisplayName {
+                crate_name: CrateName(
+                    "profiler_builtins",
+                ),
+                canonical_name: "profiler_builtins",
+            },
+        ),
+        cfg_options: CfgOptions(
+            [
+                "debug_assertions",
+                "miri",
+                "true",
+            ],
+        ),
+        potential_cfg_options: None,
+        env: Env {
+            entries: {},
+        },
+        dependencies: [],
+        origin: Lang(
+            Other,
+        ),
+        is_proc_macro: false,
+        proc_macro_cwd: None,
+    },
+    6: CrateData {
+        root_file_id: FileId(
+            7,
+        ),
+        edition: Edition2021,
+        version: None,
+        display_name: Some(
+            CrateDisplayName {
+                crate_name: CrateName(
+                    "std",
+                ),
+                canonical_name: "std",
+            },
+        ),
+        cfg_options: CfgOptions(
+            [
+                "debug_assertions",
+                "miri",
+                "true",
+            ],
+        ),
+        potential_cfg_options: None,
+        env: Env {
+            entries: {},
+        },
+        dependencies: [
+            Dependency {
+                crate_id: Idx::<CrateData>(0),
+                name: CrateName(
+                    "alloc",
+                ),
+                prelude: true,
+                sysroot: false,
+            },
+            Dependency {
+                crate_id: Idx::<CrateData>(3),
+                name: CrateName(
+                    "panic_unwind",
+                ),
+                prelude: true,
+                sysroot: false,
+            },
+            Dependency {
+                crate_id: Idx::<CrateData>(2),
+                name: CrateName(
+                    "panic_abort",
+                ),
+                prelude: true,
+                sysroot: false,
+            },
+            Dependency {
+                crate_id: Idx::<CrateData>(1),
+                name: CrateName(
+                    "core",
+                ),
+                prelude: true,
+                sysroot: false,
+            },
+            Dependency {
+                crate_id: Idx::<CrateData>(5),
+                name: CrateName(
+                    "profiler_builtins",
+                ),
+                prelude: true,
+                sysroot: false,
+            },
+            Dependency {
+                crate_id: Idx::<CrateData>(9),
+                name: CrateName(
+                    "unwind",
+                ),
+                prelude: true,
+                sysroot: false,
+            },
+            Dependency {
+                crate_id: Idx::<CrateData>(7),
+                name: CrateName(
+                    "std_detect",
+                ),
+                prelude: true,
+                sysroot: false,
+            },
+            Dependency {
+                crate_id: Idx::<CrateData>(8),
+                name: CrateName(
+                    "test",
+                ),
+                prelude: true,
+                sysroot: false,
+            },
+        ],
+        origin: Lang(
+            Std,
+        ),
+        is_proc_macro: false,
+        proc_macro_cwd: None,
+    },
+    7: CrateData {
+        root_file_id: FileId(
+            8,
+        ),
+        edition: Edition2021,
+        version: None,
+        display_name: Some(
+            CrateDisplayName {
+                crate_name: CrateName(
+                    "std_detect",
+                ),
+                canonical_name: "std_detect",
+            },
+        ),
+        cfg_options: CfgOptions(
+            [
+                "debug_assertions",
+                "miri",
+                "true",
+            ],
+        ),
+        potential_cfg_options: None,
+        env: Env {
+            entries: {},
+        },
+        dependencies: [],
+        origin: Lang(
+            Other,
+        ),
+        is_proc_macro: false,
+        proc_macro_cwd: None,
+    },
+    8: CrateData {
+        root_file_id: FileId(
+            9,
+        ),
+        edition: Edition2021,
+        version: None,
+        display_name: Some(
+            CrateDisplayName {
+                crate_name: CrateName(
+                    "test",
+                ),
+                canonical_name: "test",
+            },
+        ),
+        cfg_options: CfgOptions(
+            [
+                "debug_assertions",
+                "miri",
+                "true",
+            ],
+        ),
+        potential_cfg_options: None,
+        env: Env {
+            entries: {},
+        },
+        dependencies: [],
+        origin: Lang(
+            Test,
+        ),
+        is_proc_macro: false,
+        proc_macro_cwd: None,
+    },
+    9: CrateData {
+        root_file_id: FileId(
+            10,
+        ),
+        edition: Edition2021,
+        version: None,
+        display_name: Some(
+            CrateDisplayName {
+                crate_name: CrateName(
+                    "unwind",
+                ),
+                canonical_name: "unwind",
+            },
+        ),
+        cfg_options: CfgOptions(
+            [
+                "debug_assertions",
+                "miri",
+                "true",
+            ],
+        ),
+        potential_cfg_options: None,
+        env: Env {
+            entries: {},
+        },
+        dependencies: [],
+        origin: Lang(
+            Other,
+        ),
+        is_proc_macro: false,
+        proc_macro_cwd: None,
+    },
+    10: CrateData {
+        root_file_id: FileId(
+            11,
+        ),
         edition: Edition2018,
         version: None,
         display_name: Some(
@@ -27,7 +438,48 @@
         env: Env {
             entries: {},
         },
-        dependencies: [],
+        dependencies: [
+            Dependency {
+                crate_id: Idx::<CrateData>(1),
+                name: CrateName(
+                    "core",
+                ),
+                prelude: true,
+                sysroot: true,
+            },
+            Dependency {
+                crate_id: Idx::<CrateData>(0),
+                name: CrateName(
+                    "alloc",
+                ),
+                prelude: false,
+                sysroot: true,
+            },
+            Dependency {
+                crate_id: Idx::<CrateData>(6),
+                name: CrateName(
+                    "std",
+                ),
+                prelude: true,
+                sysroot: true,
+            },
+            Dependency {
+                crate_id: Idx::<CrateData>(8),
+                name: CrateName(
+                    "test",
+                ),
+                prelude: false,
+                sysroot: true,
+            },
+            Dependency {
+                crate_id: Idx::<CrateData>(4),
+                name: CrateName(
+                    "proc_macro",
+                ),
+                prelude: false,
+                sysroot: true,
+            },
+        ],
         origin: Local {
             repo: None,
             name: Some(
@@ -37,9 +489,9 @@
         is_proc_macro: false,
         proc_macro_cwd: None,
     },
-    1: CrateData {
+    11: CrateData {
         root_file_id: FileId(
-            1,
+            11,
         ),
         edition: Edition2018,
         version: None,
@@ -65,7 +517,48 @@
         env: Env {
             entries: {},
         },
-        dependencies: [],
+        dependencies: [
+            Dependency {
+                crate_id: Idx::<CrateData>(1),
+                name: CrateName(
+                    "core",
+                ),
+                prelude: true,
+                sysroot: true,
+            },
+            Dependency {
+                crate_id: Idx::<CrateData>(0),
+                name: CrateName(
+                    "alloc",
+                ),
+                prelude: false,
+                sysroot: true,
+            },
+            Dependency {
+                crate_id: Idx::<CrateData>(6),
+                name: CrateName(
+                    "std",
+                ),
+                prelude: true,
+                sysroot: true,
+            },
+            Dependency {
+                crate_id: Idx::<CrateData>(8),
+                name: CrateName(
+                    "test",
+                ),
+                prelude: false,
+                sysroot: true,
+            },
+            Dependency {
+                crate_id: Idx::<CrateData>(4),
+                name: CrateName(
+                    "proc_macro",
+                ),
+                prelude: false,
+                sysroot: true,
+            },
+        ],
         origin: Local {
             repo: None,
             name: Some(
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 dde8d3023dc..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
@@ -3,6 +3,417 @@
         root_file_id: FileId(
             1,
         ),
+        edition: Edition2021,
+        version: None,
+        display_name: Some(
+            CrateDisplayName {
+                crate_name: CrateName(
+                    "alloc",
+                ),
+                canonical_name: "alloc",
+            },
+        ),
+        cfg_options: CfgOptions(
+            [
+                "debug_assertions",
+                "miri",
+                "true",
+            ],
+        ),
+        potential_cfg_options: None,
+        env: Env {
+            entries: {},
+        },
+        dependencies: [
+            Dependency {
+                crate_id: Idx::<CrateData>(1),
+                name: CrateName(
+                    "core",
+                ),
+                prelude: true,
+                sysroot: false,
+            },
+        ],
+        origin: Lang(
+            Alloc,
+        ),
+        is_proc_macro: false,
+        proc_macro_cwd: None,
+    },
+    1: CrateData {
+        root_file_id: FileId(
+            2,
+        ),
+        edition: Edition2021,
+        version: None,
+        display_name: Some(
+            CrateDisplayName {
+                crate_name: CrateName(
+                    "core",
+                ),
+                canonical_name: "core",
+            },
+        ),
+        cfg_options: CfgOptions(
+            [
+                "debug_assertions",
+                "miri",
+                "true",
+            ],
+        ),
+        potential_cfg_options: None,
+        env: Env {
+            entries: {},
+        },
+        dependencies: [],
+        origin: Lang(
+            Core,
+        ),
+        is_proc_macro: false,
+        proc_macro_cwd: None,
+    },
+    2: CrateData {
+        root_file_id: FileId(
+            3,
+        ),
+        edition: Edition2021,
+        version: None,
+        display_name: Some(
+            CrateDisplayName {
+                crate_name: CrateName(
+                    "panic_abort",
+                ),
+                canonical_name: "panic_abort",
+            },
+        ),
+        cfg_options: CfgOptions(
+            [
+                "debug_assertions",
+                "miri",
+                "true",
+            ],
+        ),
+        potential_cfg_options: None,
+        env: Env {
+            entries: {},
+        },
+        dependencies: [],
+        origin: Lang(
+            Other,
+        ),
+        is_proc_macro: false,
+        proc_macro_cwd: None,
+    },
+    3: CrateData {
+        root_file_id: FileId(
+            4,
+        ),
+        edition: Edition2021,
+        version: None,
+        display_name: Some(
+            CrateDisplayName {
+                crate_name: CrateName(
+                    "panic_unwind",
+                ),
+                canonical_name: "panic_unwind",
+            },
+        ),
+        cfg_options: CfgOptions(
+            [
+                "debug_assertions",
+                "miri",
+                "true",
+            ],
+        ),
+        potential_cfg_options: None,
+        env: Env {
+            entries: {},
+        },
+        dependencies: [],
+        origin: Lang(
+            Other,
+        ),
+        is_proc_macro: false,
+        proc_macro_cwd: None,
+    },
+    4: CrateData {
+        root_file_id: FileId(
+            5,
+        ),
+        edition: Edition2021,
+        version: None,
+        display_name: Some(
+            CrateDisplayName {
+                crate_name: CrateName(
+                    "proc_macro",
+                ),
+                canonical_name: "proc_macro",
+            },
+        ),
+        cfg_options: CfgOptions(
+            [
+                "debug_assertions",
+                "miri",
+                "true",
+            ],
+        ),
+        potential_cfg_options: None,
+        env: Env {
+            entries: {},
+        },
+        dependencies: [
+            Dependency {
+                crate_id: Idx::<CrateData>(6),
+                name: CrateName(
+                    "std",
+                ),
+                prelude: true,
+                sysroot: false,
+            },
+            Dependency {
+                crate_id: Idx::<CrateData>(1),
+                name: CrateName(
+                    "core",
+                ),
+                prelude: true,
+                sysroot: false,
+            },
+        ],
+        origin: Lang(
+            ProcMacro,
+        ),
+        is_proc_macro: false,
+        proc_macro_cwd: None,
+    },
+    5: CrateData {
+        root_file_id: FileId(
+            6,
+        ),
+        edition: Edition2021,
+        version: None,
+        display_name: Some(
+            CrateDisplayName {
+                crate_name: CrateName(
+                    "profiler_builtins",
+                ),
+                canonical_name: "profiler_builtins",
+            },
+        ),
+        cfg_options: CfgOptions(
+            [
+                "debug_assertions",
+                "miri",
+                "true",
+            ],
+        ),
+        potential_cfg_options: None,
+        env: Env {
+            entries: {},
+        },
+        dependencies: [],
+        origin: Lang(
+            Other,
+        ),
+        is_proc_macro: false,
+        proc_macro_cwd: None,
+    },
+    6: CrateData {
+        root_file_id: FileId(
+            7,
+        ),
+        edition: Edition2021,
+        version: None,
+        display_name: Some(
+            CrateDisplayName {
+                crate_name: CrateName(
+                    "std",
+                ),
+                canonical_name: "std",
+            },
+        ),
+        cfg_options: CfgOptions(
+            [
+                "debug_assertions",
+                "miri",
+                "true",
+            ],
+        ),
+        potential_cfg_options: None,
+        env: Env {
+            entries: {},
+        },
+        dependencies: [
+            Dependency {
+                crate_id: Idx::<CrateData>(0),
+                name: CrateName(
+                    "alloc",
+                ),
+                prelude: true,
+                sysroot: false,
+            },
+            Dependency {
+                crate_id: Idx::<CrateData>(3),
+                name: CrateName(
+                    "panic_unwind",
+                ),
+                prelude: true,
+                sysroot: false,
+            },
+            Dependency {
+                crate_id: Idx::<CrateData>(2),
+                name: CrateName(
+                    "panic_abort",
+                ),
+                prelude: true,
+                sysroot: false,
+            },
+            Dependency {
+                crate_id: Idx::<CrateData>(1),
+                name: CrateName(
+                    "core",
+                ),
+                prelude: true,
+                sysroot: false,
+            },
+            Dependency {
+                crate_id: Idx::<CrateData>(5),
+                name: CrateName(
+                    "profiler_builtins",
+                ),
+                prelude: true,
+                sysroot: false,
+            },
+            Dependency {
+                crate_id: Idx::<CrateData>(9),
+                name: CrateName(
+                    "unwind",
+                ),
+                prelude: true,
+                sysroot: false,
+            },
+            Dependency {
+                crate_id: Idx::<CrateData>(7),
+                name: CrateName(
+                    "std_detect",
+                ),
+                prelude: true,
+                sysroot: false,
+            },
+            Dependency {
+                crate_id: Idx::<CrateData>(8),
+                name: CrateName(
+                    "test",
+                ),
+                prelude: true,
+                sysroot: false,
+            },
+        ],
+        origin: Lang(
+            Std,
+        ),
+        is_proc_macro: false,
+        proc_macro_cwd: None,
+    },
+    7: CrateData {
+        root_file_id: FileId(
+            8,
+        ),
+        edition: Edition2021,
+        version: None,
+        display_name: Some(
+            CrateDisplayName {
+                crate_name: CrateName(
+                    "std_detect",
+                ),
+                canonical_name: "std_detect",
+            },
+        ),
+        cfg_options: CfgOptions(
+            [
+                "debug_assertions",
+                "miri",
+                "true",
+            ],
+        ),
+        potential_cfg_options: None,
+        env: Env {
+            entries: {},
+        },
+        dependencies: [],
+        origin: Lang(
+            Other,
+        ),
+        is_proc_macro: false,
+        proc_macro_cwd: None,
+    },
+    8: CrateData {
+        root_file_id: FileId(
+            9,
+        ),
+        edition: Edition2021,
+        version: None,
+        display_name: Some(
+            CrateDisplayName {
+                crate_name: CrateName(
+                    "test",
+                ),
+                canonical_name: "test",
+            },
+        ),
+        cfg_options: CfgOptions(
+            [
+                "debug_assertions",
+                "miri",
+                "true",
+            ],
+        ),
+        potential_cfg_options: None,
+        env: Env {
+            entries: {},
+        },
+        dependencies: [],
+        origin: Lang(
+            Test,
+        ),
+        is_proc_macro: false,
+        proc_macro_cwd: None,
+    },
+    9: CrateData {
+        root_file_id: FileId(
+            10,
+        ),
+        edition: Edition2021,
+        version: None,
+        display_name: Some(
+            CrateDisplayName {
+                crate_name: CrateName(
+                    "unwind",
+                ),
+                canonical_name: "unwind",
+            },
+        ),
+        cfg_options: CfgOptions(
+            [
+                "debug_assertions",
+                "miri",
+                "true",
+            ],
+        ),
+        potential_cfg_options: None,
+        env: Env {
+            entries: {},
+        },
+        dependencies: [],
+        origin: Lang(
+            Other,
+        ),
+        is_proc_macro: false,
+        proc_macro_cwd: None,
+    },
+    10: CrateData {
+        root_file_id: FileId(
+            11,
+        ),
         edition: Edition2018,
         version: None,
         display_name: Some(
@@ -24,7 +435,48 @@
         env: Env {
             entries: {},
         },
-        dependencies: [],
+        dependencies: [
+            Dependency {
+                crate_id: Idx::<CrateData>(1),
+                name: CrateName(
+                    "core",
+                ),
+                prelude: true,
+                sysroot: true,
+            },
+            Dependency {
+                crate_id: Idx::<CrateData>(0),
+                name: CrateName(
+                    "alloc",
+                ),
+                prelude: false,
+                sysroot: true,
+            },
+            Dependency {
+                crate_id: Idx::<CrateData>(6),
+                name: CrateName(
+                    "std",
+                ),
+                prelude: true,
+                sysroot: true,
+            },
+            Dependency {
+                crate_id: Idx::<CrateData>(8),
+                name: CrateName(
+                    "test",
+                ),
+                prelude: false,
+                sysroot: true,
+            },
+            Dependency {
+                crate_id: Idx::<CrateData>(4),
+                name: CrateName(
+                    "proc_macro",
+                ),
+                prelude: false,
+                sysroot: true,
+            },
+        ],
         origin: Local {
             repo: None,
             name: Some(
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 4fc6180920f..dee76ee15c3 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
@@ -69,7 +69,7 @@ impl flags::AnalysisStats {
             all_targets: true,
             set_test: !self.no_test,
             cfg_overrides: CfgOverrides {
-                global: CfgDiff::new(vec![CfgAtom::Flag(hir::sym::miri.clone())], vec![]).unwrap(),
+                global: CfgDiff::new(vec![CfgAtom::Flag(hir::sym::miri.clone())], vec![]),
                 selective: Default::default(),
             },
             ..Default::default()
@@ -390,6 +390,8 @@ impl flags::AnalysisStats {
 
         for &file_id in &file_ids {
             let sema = hir::Semantics::new(db);
+            let display_target =
+                sema.first_crate_or_default(file_id.file_id()).to_display_target(db);
 
             let parse = sema.parse_guess_edition(file_id.into());
             let file_txt = db.file_text(file_id.into());
@@ -467,7 +469,7 @@ impl flags::AnalysisStats {
                                 prefer_absolute: false,
                                 allow_unstable: true,
                             },
-                            Edition::LATEST,
+                            display_target,
                         )
                         .unwrap();
                     syntax_hit_found |= trim(&original_text) == trim(&generated);
@@ -641,6 +643,7 @@ impl flags::AnalysisStats {
         for &body_id in bodies {
             let name = body_id.name(db).unwrap_or_else(Name::missing);
             let module = body_id.module(db);
+            let display_target = module.krate().to_display_target(db);
             let full_name = move || {
                 module
                     .krate()
@@ -739,12 +742,12 @@ impl flags::AnalysisStats {
                             start.col,
                             end.line + 1,
                             end.col,
-                            ty.display(db, Edition::LATEST)
+                            ty.display(db, display_target)
                         ));
                     } else {
                         bar.println(format!(
                             "unknown location: {}",
-                            ty.display(db, Edition::LATEST)
+                            ty.display(db, display_target)
                         ));
                     }
                 }
@@ -752,7 +755,7 @@ impl flags::AnalysisStats {
                     println!(
                         r#"{},type,"{}""#,
                         location_csv_expr(db, vfs, &sm(), expr_id),
-                        ty.display(db, Edition::LATEST)
+                        ty.display(db, display_target)
                     );
                 }
                 if let Some(mismatch) = inference_result.type_mismatch_for_expr(expr_id) {
@@ -767,15 +770,15 @@ impl flags::AnalysisStats {
                                 start.col,
                                 end.line + 1,
                                 end.col,
-                                mismatch.expected.display(db, Edition::LATEST),
-                                mismatch.actual.display(db, Edition::LATEST)
+                                mismatch.expected.display(db, display_target),
+                                mismatch.actual.display(db, display_target)
                             ));
                         } else {
                             bar.println(format!(
                                 "{}: Expected {}, got {}",
                                 name.display(db, Edition::LATEST),
-                                mismatch.expected.display(db, Edition::LATEST),
-                                mismatch.actual.display(db, Edition::LATEST)
+                                mismatch.expected.display(db, display_target),
+                                mismatch.actual.display(db, display_target)
                             ));
                         }
                     }
@@ -783,8 +786,8 @@ impl flags::AnalysisStats {
                         println!(
                             r#"{},mismatch,"{}","{}""#,
                             location_csv_expr(db, vfs, &sm(), expr_id),
-                            mismatch.expected.display(db, Edition::LATEST),
-                            mismatch.actual.display(db, Edition::LATEST)
+                            mismatch.expected.display(db, display_target),
+                            mismatch.actual.display(db, display_target)
                         );
                     }
                 }
@@ -843,12 +846,12 @@ impl flags::AnalysisStats {
                             start.col,
                             end.line + 1,
                             end.col,
-                            ty.display(db, Edition::LATEST)
+                            ty.display(db, display_target)
                         ));
                     } else {
                         bar.println(format!(
                             "unknown location: {}",
-                            ty.display(db, Edition::LATEST)
+                            ty.display(db, display_target)
                         ));
                     }
                 }
@@ -856,7 +859,7 @@ impl flags::AnalysisStats {
                     println!(
                         r#"{},type,"{}""#,
                         location_csv_pat(db, vfs, &sm(), pat_id),
-                        ty.display(db, Edition::LATEST)
+                        ty.display(db, display_target)
                     );
                 }
                 if let Some(mismatch) = inference_result.type_mismatch_for_pat(pat_id) {
@@ -870,15 +873,15 @@ impl flags::AnalysisStats {
                                 start.col,
                                 end.line + 1,
                                 end.col,
-                                mismatch.expected.display(db, Edition::LATEST),
-                                mismatch.actual.display(db, Edition::LATEST)
+                                mismatch.expected.display(db, display_target),
+                                mismatch.actual.display(db, display_target)
                             ));
                         } else {
                             bar.println(format!(
                                 "{}: Expected {}, got {}",
                                 name.display(db, Edition::LATEST),
-                                mismatch.expected.display(db, Edition::LATEST),
-                                mismatch.actual.display(db, Edition::LATEST)
+                                mismatch.expected.display(db, display_target),
+                                mismatch.actual.display(db, display_target)
                             ));
                         }
                     }
@@ -886,8 +889,8 @@ impl flags::AnalysisStats {
                         println!(
                             r#"{},mismatch,"{}","{}""#,
                             location_csv_pat(db, vfs, &sm(), pat_id),
-                            mismatch.expected.display(db, Edition::LATEST),
-                            mismatch.actual.display(db, Edition::LATEST)
+                            mismatch.expected.display(db, display_target),
+                            mismatch.actual.display(db, display_target)
                         );
                     }
                 }
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 713e28c87cb..45ac68339b3 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs
@@ -18,7 +18,7 @@ use ide_db::{
     imports::insert_use::{ImportGranularity, InsertUseConfig, PrefixKind},
     SnippetCap,
 };
-use itertools::Itertools;
+use itertools::{Either, Itertools};
 use paths::{Utf8Path, Utf8PathBuf};
 use project_model::{
     CargoConfig, CargoFeatures, ProjectJson, ProjectJsonData, ProjectJsonFromCommand,
@@ -589,6 +589,10 @@ config_data! {
         /// avoid checking unnecessary things.
         cargo_buildScripts_useRustcWrapper: bool = true,
         /// List of cfg options to enable with the given values.
+        ///
+        /// To enable a name without a value, use `"key"`.
+        /// To enable a name with a value, use `"key=value"`.
+        /// To disable, prefix the entry with a `!`.
         cargo_cfgs: Vec<String> = {
             vec!["debug_assertions".into(), "miri".into()]
         },
@@ -1980,27 +1984,35 @@ impl Config {
             rustc_source,
             extra_includes,
             cfg_overrides: project_model::CfgOverrides {
-                global: CfgDiff::new(
-                    self.cargo_cfgs(source_root)
-                        .iter()
-                        // parse any cfg setting formatted as key=value or just key (without value)
-                        .filter_map(|s| {
-                            let mut sp = s.splitn(2, "=");
-                            let key = sp.next();
-                            let val = sp.next();
-                            key.map(|key| (key, val))
-                        })
-                        .map(|(key, val)| match val {
-                            Some(val) => CfgAtom::KeyValue {
-                                key: Symbol::intern(key),
-                                value: Symbol::intern(val),
-                            },
-                            None => CfgAtom::Flag(Symbol::intern(key)),
-                        })
-                        .collect(),
-                    vec![],
-                )
-                .unwrap(),
+                global: {
+                    let (enabled, disabled): (Vec<_>, Vec<_>) =
+                        self.cargo_cfgs(source_root).iter().partition_map(|s| {
+                            s.strip_prefix("!").map_or(Either::Left(s), Either::Right)
+                        });
+                    CfgDiff::new(
+                        enabled
+                            .into_iter()
+                            // parse any cfg setting formatted as key=value or just key (without value)
+                            .map(|s| match s.split_once("=") {
+                                Some((key, val)) => CfgAtom::KeyValue {
+                                    key: Symbol::intern(key),
+                                    value: Symbol::intern(val),
+                                },
+                                None => CfgAtom::Flag(Symbol::intern(s)),
+                            })
+                            .collect(),
+                        disabled
+                            .into_iter()
+                            .map(|s| match s.split_once("=") {
+                                Some((key, val)) => CfgAtom::KeyValue {
+                                    key: Symbol::intern(key),
+                                    value: Symbol::intern(val),
+                                },
+                                None => CfgAtom::Flag(Symbol::intern(s)),
+                            })
+                            .collect(),
+                    )
+                },
                 selective: Default::default(),
             },
             wrap_rustc_in_build_scripts: *self.cargo_buildScripts_useRustcWrapper(source_root),
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/flycheck.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/flycheck.rs
index 2309f94a742..7529e7c188f 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/flycheck.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/flycheck.rs
@@ -244,8 +244,14 @@ struct FlycheckActor {
     /// The receiver side of the channel mentioned above.
     command_receiver: Option<Receiver<CargoCheckMessage>>,
     diagnostics_cleared_for: FxHashSet<Arc<PackageId>>,
-    diagnostics_cleared_for_all: bool,
-    diagnostics_received: bool,
+    diagnostics_received: DiagnosticsReceived,
+}
+
+#[derive(PartialEq)]
+enum DiagnosticsReceived {
+    Yes,
+    No,
+    YesAndClearedForAll,
 }
 
 #[allow(clippy::large_enum_variant)]
@@ -276,8 +282,7 @@ impl FlycheckActor {
             command_handle: None,
             command_receiver: None,
             diagnostics_cleared_for: Default::default(),
-            diagnostics_cleared_for_all: false,
-            diagnostics_received: false,
+            diagnostics_received: DiagnosticsReceived::No,
         }
     }
 
@@ -354,7 +359,7 @@ impl FlycheckActor {
                             error
                         );
                     }
-                    if !self.diagnostics_received {
+                    if self.diagnostics_received == DiagnosticsReceived::No {
                         tracing::trace!(flycheck_id = self.id, "clearing diagnostics");
                         // We finished without receiving any diagnostics.
                         // Clear everything for good measure
@@ -396,7 +401,7 @@ impl FlycheckActor {
                             package_id = package_id.as_ref().map(|it| &it.repr),
                             "diagnostic received"
                         );
-                        self.diagnostics_received = true;
+                        self.diagnostics_received = DiagnosticsReceived::Yes;
                         if let Some(package_id) = &package_id {
                             if self.diagnostics_cleared_for.insert(package_id.clone()) {
                                 tracing::trace!(
@@ -409,8 +414,10 @@ impl FlycheckActor {
                                     package_id: Some(package_id.clone()),
                                 });
                             }
-                        } else if !self.diagnostics_cleared_for_all {
-                            self.diagnostics_cleared_for_all = true;
+                        } else if self.diagnostics_received
+                            != DiagnosticsReceived::YesAndClearedForAll
+                        {
+                            self.diagnostics_received = DiagnosticsReceived::YesAndClearedForAll;
                             self.send(FlycheckMessage::ClearDiagnostics {
                                 id: self.id,
                                 package_id: None,
@@ -445,8 +452,7 @@ impl FlycheckActor {
 
     fn clear_diagnostics_state(&mut self) {
         self.diagnostics_cleared_for.clear();
-        self.diagnostics_cleared_for_all = false;
-        self.diagnostics_received = false;
+        self.diagnostics_received = DiagnosticsReceived::No;
     }
 
     /// Construct a `Command` object for checking the user's code. If the user
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 70105cda006..54670b67598 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
@@ -637,6 +637,25 @@ impl GlobalState {
             }
         });
     }
+
+    pub(crate) fn check_workspaces_msrv(&self) -> impl Iterator<Item = String> + '_ {
+        self.workspaces.iter().filter_map(|ws| {
+            if let Some(toolchain) = &ws.toolchain {
+                if *toolchain < crate::MINIMUM_SUPPORTED_TOOLCHAIN_VERSION {
+                    return Some(format!(
+                        "Workspace `{}` is using an outdated toolchain version `{}` but \
+                        rust-analyzer only supports `{}` and higher.\n\
+                        Consider using the rust-analyzer rustup component for your toolchain or
+                        upgrade your toolchain to a supported version.\n\n",
+                        ws.manifest_or_root(),
+                        toolchain,
+                        crate::MINIMUM_SUPPORTED_TOOLCHAIN_VERSION,
+                    ));
+                }
+            }
+            None
+        })
+    }
 }
 
 impl Drop for GlobalState {
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 b91a5dbd416..68b2d6b6962 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
@@ -32,13 +32,13 @@ use triomphe::Arc;
 use vfs::{AbsPath, AbsPathBuf, FileId, VfsPath};
 
 use crate::{
-    completion_item_hash,
     config::{Config, RustfmtConfig, WorkspaceSymbolConfig},
     diagnostics::convert_diagnostic,
     global_state::{FetchWorkspaceRequest, GlobalState, GlobalStateSnapshot},
     hack_recover_crate_name,
     line_index::LineEndings,
     lsp::{
+        completion_item_hash,
         ext::{
             InternalTestingFetchConfigOption, InternalTestingFetchConfigParams,
             InternalTestingFetchConfigResponse,
@@ -427,7 +427,12 @@ pub(crate) fn handle_on_enter(
         Some(it) => it,
     };
     let line_index = snap.file_line_index(position.file_id)?;
-    let edit = to_proto::snippet_text_edit_vec(&line_index, true, edit);
+    let edit = to_proto::snippet_text_edit_vec(
+        &line_index,
+        true,
+        edit,
+        snap.config.change_annotation_support(),
+    );
     Ok(Some(edit))
 }
 
@@ -464,7 +469,12 @@ pub(crate) fn handle_on_type_formatting(
     let (_, (text_edit, snippet_edit)) = edit.source_file_edits.into_iter().next().unwrap();
     stdx::always!(snippet_edit.is_none(), "on type formatting shouldn't use structured snippets");
 
-    let change = to_proto::snippet_text_edit_vec(&line_index, edit.is_snippet, text_edit);
+    let change = to_proto::snippet_text_edit_vec(
+        &line_index,
+        edit.is_snippet,
+        text_edit,
+        snap.config.change_annotation_support(),
+    );
     Ok(Some(change))
 }
 
@@ -2025,7 +2035,12 @@ pub(crate) fn handle_move_item(
     match snap.analysis.move_item(range, direction)? {
         Some(text_edit) => {
             let line_index = snap.file_line_index(file_id)?;
-            Ok(to_proto::snippet_text_edit_vec(&line_index, true, text_edit))
+            Ok(to_proto::snippet_text_edit_vec(
+                &line_index,
+                true,
+                text_edit,
+                snap.config.change_annotation_support(),
+            ))
         }
         None => Ok(vec![]),
     }
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 27d6225cdb7..a0d6a0d6da6 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/lib.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/lib.rs
@@ -9,6 +9,15 @@
 //! The `cli` submodule implements some batch-processing analysis, primarily as
 //! a debugging aid.
 
+/// Any toolchain less than this version will likely not work with rust-analyzer built from this revision.
+pub const MINIMUM_SUPPORTED_TOOLCHAIN_VERSION: semver::Version = semver::Version {
+    major: 1,
+    minor: 78,
+    patch: 0,
+    pre: semver::Prerelease::EMPTY,
+    build: semver::BuildMetadata::EMPTY,
+};
+
 pub mod cli;
 
 mod command;
@@ -47,10 +56,7 @@ use self::lsp::ext as lsp_ext;
 #[cfg(test)]
 mod integrated_benchmarks;
 
-use hir::Mutability;
-use ide::{CompletionItem, CompletionItemRefMode, CompletionRelevance};
 use serde::de::DeserializeOwned;
-use tenthash::TentHash;
 
 pub use crate::{
     lsp::capabilities::server_capabilities, main_loop::main_loop, reload::ws_to_crate_graph,
@@ -65,115 +71,6 @@ pub fn from_json<T: DeserializeOwned>(
         .map_err(|e| anyhow::format_err!("Failed to deserialize {what}: {e}; {json}"))
 }
 
-fn completion_item_hash(item: &CompletionItem, is_ref_completion: bool) -> [u8; 20] {
-    fn hash_completion_relevance(hasher: &mut TentHash, relevance: &CompletionRelevance) {
-        use ide_completion::{
-            CompletionRelevancePostfixMatch, CompletionRelevanceReturnType,
-            CompletionRelevanceTypeMatch,
-        };
-
-        hasher.update([
-            u8::from(relevance.exact_name_match),
-            u8::from(relevance.is_local),
-            u8::from(relevance.is_name_already_imported),
-            u8::from(relevance.requires_import),
-            u8::from(relevance.is_private_editable),
-        ]);
-
-        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)]);
-        }
-
-        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 discriminant: u8 = match function.return_type {
-                CompletionRelevanceReturnType::Other => 0,
-                CompletionRelevanceReturnType::DirectConstructor => 1,
-                CompletionRelevanceReturnType::Constructor => 2,
-                CompletionRelevanceReturnType::Builder => 3,
-            };
-            hasher.update([discriminant]);
-        }
-    }
-
-    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.
-
-    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 discriminant = match ref_mode {
-            CompletionItemRefMode::Reference(Mutability::Shared) => 0u8,
-            CompletionItemRefMode::Reference(Mutability::Mut) => 1u8,
-            CompletionItemRefMode::Dereference => 2u8,
-        };
-        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 $(,)?) => {
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp.rs
index 122ad20d65e..c7a5a95e66b 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp.rs
@@ -2,6 +2,10 @@
 
 use core::fmt;
 
+use hir::Mutability;
+use ide::{CompletionItem, CompletionItemRefMode, CompletionRelevance};
+use tenthash::TentHash;
+
 pub mod ext;
 
 pub(crate) mod capabilities;
@@ -29,3 +33,112 @@ impl fmt::Display for LspError {
 }
 
 impl std::error::Error for LspError {}
+
+pub(crate) fn completion_item_hash(item: &CompletionItem, is_ref_completion: bool) -> [u8; 20] {
+    fn hash_completion_relevance(hasher: &mut TentHash, relevance: &CompletionRelevance) {
+        use ide_completion::{
+            CompletionRelevancePostfixMatch, CompletionRelevanceReturnType,
+            CompletionRelevanceTypeMatch,
+        };
+
+        hasher.update([
+            u8::from(relevance.exact_name_match),
+            u8::from(relevance.is_local),
+            u8::from(relevance.is_name_already_imported),
+            u8::from(relevance.requires_import),
+            u8::from(relevance.is_private_editable),
+        ]);
+
+        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)]);
+        }
+
+        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 discriminant: u8 = match function.return_type {
+                CompletionRelevanceReturnType::Other => 0,
+                CompletionRelevanceReturnType::DirectConstructor => 1,
+                CompletionRelevanceReturnType::Constructor => 2,
+                CompletionRelevanceReturnType::Builder => 3,
+            };
+            hasher.update([discriminant]);
+        }
+    }
+
+    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.
+
+    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 discriminant = match ref_mode {
+            CompletionItemRefMode::Reference(Mutability::Shared) => 0u8,
+            CompletionItemRefMode::Reference(Mutability::Mut) => 1u8,
+            CompletionItemRefMode::Dereference => 2u8,
+        };
+        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()
+}
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/to_proto.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/to_proto.rs
index 446549c9070..6db7bcb1110 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/to_proto.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/to_proto.rs
@@ -24,11 +24,11 @@ use serde_json::to_value;
 use vfs::AbsPath;
 
 use crate::{
-    completion_item_hash,
     config::{CallInfoConfig, Config},
     global_state::GlobalStateSnapshot,
     line_index::{LineEndings, LineIndex, PositionEncoding},
     lsp::{
+        completion_item_hash,
         ext::ShellRunnableArgs,
         semantic_tokens::{self, standard_fallback_type},
         utils::invalid_params_error,
@@ -200,7 +200,10 @@ pub(crate) fn snippet_text_edit(
     line_index: &LineIndex,
     is_snippet: bool,
     indel: Indel,
+    client_supports_annotations: bool,
 ) -> lsp_ext::SnippetTextEdit {
+    let annotation_id =
+        indel.annotation.filter(|_| client_supports_annotations).map(|it| it.to_string());
     let text_edit = text_edit(line_index, indel);
     let insert_text_format =
         if is_snippet { Some(lsp_types::InsertTextFormat::SNIPPET) } else { None };
@@ -208,7 +211,7 @@ pub(crate) fn snippet_text_edit(
         range: text_edit.range,
         new_text: text_edit.new_text,
         insert_text_format,
-        annotation_id: None,
+        annotation_id,
     }
 }
 
@@ -223,10 +226,13 @@ pub(crate) fn snippet_text_edit_vec(
     line_index: &LineIndex,
     is_snippet: bool,
     text_edit: TextEdit,
+    clients_support_annotations: bool,
 ) -> Vec<lsp_ext::SnippetTextEdit> {
     text_edit
         .into_iter()
-        .map(|indel| self::snippet_text_edit(line_index, is_snippet, indel))
+        .map(|indel| {
+            self::snippet_text_edit(line_index, is_snippet, indel, clients_support_annotations)
+        })
         .collect()
 }
 
@@ -1072,6 +1078,7 @@ fn merge_text_and_snippet_edits(
     line_index: &LineIndex,
     edit: TextEdit,
     snippet_edit: SnippetEdit,
+    client_supports_annotations: bool,
 ) -> Vec<SnippetTextEdit> {
     let mut edits: Vec<SnippetTextEdit> = vec![];
     let mut snippets = snippet_edit.into_edit_ranges().into_iter().peekable();
@@ -1120,7 +1127,12 @@ fn merge_text_and_snippet_edits(
             edits.push(snippet_text_edit(
                 line_index,
                 true,
-                Indel { insert: format!("${snippet_index}"), delete: snippet_range },
+                Indel {
+                    insert: format!("${snippet_index}"),
+                    delete: snippet_range,
+                    annotation: None,
+                },
+                client_supports_annotations,
             ))
         }
 
@@ -1178,12 +1190,22 @@ fn merge_text_and_snippet_edits(
             edits.push(snippet_text_edit(
                 line_index,
                 true,
-                Indel { insert: new_text, delete: current_indel.delete },
+                Indel {
+                    insert: new_text,
+                    delete: current_indel.delete,
+                    annotation: current_indel.annotation,
+                },
+                client_supports_annotations,
             ))
         } else {
             // snippet edit was beyond the current one
             // since it wasn't consumed, it's available for the next pass
-            edits.push(snippet_text_edit(line_index, false, current_indel));
+            edits.push(snippet_text_edit(
+                line_index,
+                false,
+                current_indel,
+                client_supports_annotations,
+            ));
         }
 
         // update the final source -> initial source mapping offset
@@ -1208,7 +1230,8 @@ fn merge_text_and_snippet_edits(
         snippet_text_edit(
             line_index,
             true,
-            Indel { insert: format!("${snippet_index}"), delete: snippet_range },
+            Indel { insert: format!("${snippet_index}"), delete: snippet_range, annotation: None },
+            client_supports_annotations,
         )
     }));
 
@@ -1224,10 +1247,13 @@ pub(crate) fn snippet_text_document_edit(
 ) -> Cancellable<lsp_ext::SnippetTextDocumentEdit> {
     let text_document = optional_versioned_text_document_identifier(snap, file_id);
     let line_index = snap.file_line_index(file_id)?;
+    let client_supports_annotations = snap.config.change_annotation_support();
     let mut edits = if let Some(snippet_edit) = snippet_edit {
-        merge_text_and_snippet_edits(&line_index, edit, snippet_edit)
+        merge_text_and_snippet_edits(&line_index, edit, snippet_edit, client_supports_annotations)
     } else {
-        edit.into_iter().map(|it| snippet_text_edit(&line_index, is_snippet, it)).collect()
+        edit.into_iter()
+            .map(|it| snippet_text_edit(&line_index, is_snippet, it, client_supports_annotations))
+            .collect()
     };
 
     if snap.analysis.is_library_file(file_id)? && snap.config.change_annotation_support() {
@@ -1348,6 +1374,16 @@ pub(crate) fn snippet_workspace_edit(
                     )),
                 },
             ))
+            .chain(source_change.annotations.into_iter().map(|(id, annotation)| {
+                (
+                    id.to_string(),
+                    lsp_types::ChangeAnnotation {
+                        label: annotation.label,
+                        description: annotation.description,
+                        needs_confirmation: Some(annotation.needs_confirmation),
+                    },
+                )
+            }))
             .collect(),
         )
     }
@@ -2023,7 +2059,7 @@ fn bar(_: usize) {}
             encoding: PositionEncoding::Utf8,
         };
 
-        let res = merge_text_and_snippet_edits(&line_index, edit, snippets);
+        let res = merge_text_and_snippet_edits(&line_index, edit, snippets, true);
 
         // Ensure that none of the ranges overlap
         {
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 56dcad0eb18..dffaa88240b 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/reload.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/reload.rs
@@ -178,6 +178,11 @@ impl GlobalState {
         }
 
         if !self.workspaces.is_empty() {
+            self.check_workspaces_msrv().for_each(|e| {
+                status.health |= lsp_ext::Health::Warning;
+                format_to!(message, "{e}");
+            });
+
             let proc_macro_clients =
                 self.proc_macro_clients.iter().map(Some).chain(iter::repeat_with(|| None));
 
@@ -513,6 +518,11 @@ impl GlobalState {
             // we don't care about build-script results, they are stale.
             // FIXME: can we abort the build scripts here if they are already running?
             self.workspaces = Arc::new(workspaces);
+            self.check_workspaces_msrv().for_each(|message| {
+                self.send_notification::<lsp_types::notification::ShowMessage>(
+                    lsp_types::ShowMessageParams { typ: lsp_types::MessageType::WARNING, message },
+                );
+            });
 
             if self.config.run_build_scripts(None) {
                 self.build_deps_changed = false;
diff --git a/src/tools/rust-analyzer/crates/syntax-bridge/src/lib.rs b/src/tools/rust-analyzer/crates/syntax-bridge/src/lib.rs
index 19801c49e43..a59a3270c9d 100644
--- a/src/tools/rust-analyzer/crates/syntax-bridge/src/lib.rs
+++ b/src/tools/rust-analyzer/crates/syntax-bridge/src/lib.rs
@@ -12,7 +12,7 @@ use syntax::{
     SyntaxKind::{self, *},
     SyntaxNode, SyntaxToken, SyntaxTreeBuilder, TextRange, TextSize, WalkEvent, T,
 };
-use tt::{buffer::Cursor, token_to_literal};
+use tt::{buffer::Cursor, token_to_literal, Punct};
 
 pub mod prettify_macro_expansion;
 mod to_parser_input;
@@ -217,8 +217,39 @@ where
         tt::TopSubtreeBuilder::new(tt::Delimiter::invisible_spanned(conv.call_site()));
 
     while let Some((token, abs_range)) = conv.bump() {
-        let delimiter = builder.expected_delimiter().map(|it| it.kind);
         let tt = match token.as_leaf() {
+            // These delimiters are not actually valid punctuation, but we produce them in syntax fixup.
+            // So we need to handle them specially here.
+            Some(&tt::Leaf::Punct(Punct {
+                char: char @ ('(' | ')' | '{' | '}' | '[' | ']'),
+                span,
+                spacing: _,
+            })) => {
+                let found_expected_delimiter =
+                    builder.expected_delimiters().enumerate().find(|(_, delim)| match delim.kind {
+                        tt::DelimiterKind::Parenthesis => char == ')',
+                        tt::DelimiterKind::Brace => char == '}',
+                        tt::DelimiterKind::Bracket => char == ']',
+                        tt::DelimiterKind::Invisible => false,
+                    });
+                if let Some((idx, _)) = found_expected_delimiter {
+                    for _ in 0..=idx {
+                        builder.close(span);
+                    }
+                    continue;
+                }
+
+                let delim = match char {
+                    '(' => tt::DelimiterKind::Parenthesis,
+                    '{' => tt::DelimiterKind::Brace,
+                    '[' => tt::DelimiterKind::Bracket,
+                    _ => panic!("unmatched closing delimiter from syntax fixup"),
+                };
+
+                // Start a new subtree
+                builder.open(delim, span);
+                continue;
+            }
             Some(leaf) => leaf.clone(),
             None => match token.kind(conv) {
                 // Desugar doc comments into doc attributes
@@ -228,17 +259,24 @@ where
                     continue;
                 }
                 kind if kind.is_punct() && kind != UNDERSCORE => {
-                    let expected = match delimiter {
-                        Some(tt::DelimiterKind::Parenthesis) => Some(T![')']),
-                        Some(tt::DelimiterKind::Brace) => Some(T!['}']),
-                        Some(tt::DelimiterKind::Bracket) => Some(T![']']),
-                        Some(tt::DelimiterKind::Invisible) | None => None,
-                    };
+                    let found_expected_delimiter =
+                        builder.expected_delimiters().enumerate().find(|(_, delim)| {
+                            match delim.kind {
+                                tt::DelimiterKind::Parenthesis => kind == T![')'],
+                                tt::DelimiterKind::Brace => kind == T!['}'],
+                                tt::DelimiterKind::Bracket => kind == T![']'],
+                                tt::DelimiterKind::Invisible => false,
+                            }
+                        });
 
                     // Current token is a closing delimiter that we expect, fix up the closing span
-                    // and end the subtree here
-                    if matches!(expected, Some(expected) if expected == kind) {
-                        builder.close(conv.span_for(abs_range));
+                    // and end the subtree here.
+                    // We also close any open inner subtrees that might be missing their delimiter.
+                    if let Some((idx, _)) = found_expected_delimiter {
+                        for _ in 0..=idx {
+                            // FIXME: record an error somewhere if we're closing more than one tree here?
+                            builder.close(conv.span_for(abs_range));
+                        }
                         continue;
                     }
 
@@ -262,6 +300,7 @@ where
                     let Some(char) = token.to_char(conv) else {
                         panic!("Token from lexer must be single char: token = {token:#?}")
                     };
+                    // FIXME: this might still be an unmatched closing delimiter? Maybe we should assert here
                     tt::Leaf::from(tt::Punct { char, spacing, span: conv.span_for(abs_range) })
                 }
                 kind => {
@@ -317,11 +356,10 @@ where
         builder.push(tt);
     }
 
-    // If we get here, we've consumed all input tokens.
-    // We might have more than one subtree in the stack, if the delimiters are improperly balanced.
-    // Merge them so we're left with one.
-    builder.flatten_unclosed_subtrees();
-
+    while builder.expected_delimiters().next().is_some() {
+        // FIXME: record an error somewhere?
+        builder.close(conv.call_site());
+    }
     builder.build_skip_top_subtree()
 }
 
diff --git a/src/tools/rust-analyzer/crates/syntax/rust.ungram b/src/tools/rust-analyzer/crates/syntax/rust.ungram
index 88d7beb897e..70a91af6c47 100644
--- a/src/tools/rust-analyzer/crates/syntax/rust.ungram
+++ b/src/tools/rust-analyzer/crates/syntax/rust.ungram
@@ -279,6 +279,11 @@ Adt =
 | Struct
 | Union
 
+VariantDef =
+  Struct
+| Union
+| Variant
+
 Const =
   Attr* Visibility?
   'default'?
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 58c76a456ab..ebee5e9af2c 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
@@ -2447,6 +2447,17 @@ pub enum UseBoundGenericArg {
 }
 
 #[derive(Debug, Clone, PartialEq, Eq, Hash)]
+pub enum VariantDef {
+    Struct(Struct),
+    Union(Union),
+    Variant(Variant),
+}
+impl ast::HasAttrs for VariantDef {}
+impl ast::HasDocComments for VariantDef {}
+impl ast::HasName for VariantDef {}
+impl ast::HasVisibility for VariantDef {}
+
+#[derive(Debug, Clone, PartialEq, Eq, Hash)]
 pub struct AnyHasArgList {
     pub(crate) syntax: SyntaxNode,
 }
@@ -6738,6 +6749,40 @@ impl AstNode for UseBoundGenericArg {
         }
     }
 }
+impl From<Struct> for VariantDef {
+    #[inline]
+    fn from(node: Struct) -> VariantDef { VariantDef::Struct(node) }
+}
+impl From<Union> for VariantDef {
+    #[inline]
+    fn from(node: Union) -> VariantDef { VariantDef::Union(node) }
+}
+impl From<Variant> for VariantDef {
+    #[inline]
+    fn from(node: Variant) -> VariantDef { VariantDef::Variant(node) }
+}
+impl AstNode for VariantDef {
+    #[inline]
+    fn can_cast(kind: SyntaxKind) -> bool { matches!(kind, STRUCT | UNION | VARIANT) }
+    #[inline]
+    fn cast(syntax: SyntaxNode) -> Option<Self> {
+        let res = match syntax.kind() {
+            STRUCT => VariantDef::Struct(Struct { syntax }),
+            UNION => VariantDef::Union(Union { syntax }),
+            VARIANT => VariantDef::Variant(Variant { syntax }),
+            _ => return None,
+        };
+        Some(res)
+    }
+    #[inline]
+    fn syntax(&self) -> &SyntaxNode {
+        match self {
+            VariantDef::Struct(it) => &it.syntax,
+            VariantDef::Union(it) => &it.syntax,
+            VariantDef::Variant(it) => &it.syntax,
+        }
+    }
+}
 impl AnyHasArgList {
     #[inline]
     pub fn new<T: ast::HasArgList>(node: T) -> AnyHasArgList {
@@ -7753,6 +7798,11 @@ impl std::fmt::Display for UseBoundGenericArg {
         std::fmt::Display::fmt(self.syntax(), f)
     }
 }
+impl std::fmt::Display for VariantDef {
+    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+        std::fmt::Display::fmt(self.syntax(), f)
+    }
+}
 impl std::fmt::Display for Abi {
     fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
         std::fmt::Display::fmt(self.syntax(), f)
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 0c4da762992..4f0e2cad174 100644
--- a/src/tools/rust-analyzer/crates/syntax/src/ast/prec.rs
+++ b/src/tools/rust-analyzer/crates/syntax/src/ast/prec.rs
@@ -1,5 +1,7 @@
 //! Precedence representation.
 
+use stdx::always;
+
 use crate::{
     ast::{self, BinaryOp, Expr, HasArgList, RangeItem},
     match_ast, AstNode, SyntaxNode,
@@ -140,6 +142,22 @@ pub fn precedence(expr: &ast::Expr) -> ExprPrecedence {
     }
 }
 
+fn check_ancestry(ancestor: &SyntaxNode, descendent: &SyntaxNode) -> bool {
+    let bail = || always!(false, "{} is not an ancestor of {}", ancestor, descendent);
+
+    if !ancestor.text_range().contains_range(descendent.text_range()) {
+        return bail();
+    }
+
+    for anc in descendent.ancestors() {
+        if anc == *ancestor {
+            return true;
+        }
+    }
+
+    bail()
+}
+
 impl Expr {
     pub fn precedence(&self) -> ExprPrecedence {
         precedence(self)
@@ -153,9 +171,19 @@ impl Expr {
 
     /// Returns `true` if `self` would need to be wrapped in parentheses given that its parent is `parent`.
     pub fn needs_parens_in(&self, parent: &SyntaxNode) -> bool {
+        self.needs_parens_in_place_of(parent, self.syntax())
+    }
+
+    /// Returns `true` if `self` would need to be wrapped in parentheses if it replaces `place_of`
+    /// given that `place_of`'s parent is `parent`.
+    pub fn needs_parens_in_place_of(&self, parent: &SyntaxNode, place_of: &SyntaxNode) -> bool {
+        if !check_ancestry(parent, place_of) {
+            return false;
+        }
+
         match_ast! {
             match parent {
-                ast::Expr(e) => self.needs_parens_in_expr(&e),
+                ast::Expr(e) => self.needs_parens_in_expr(&e, place_of),
                 ast::Stmt(e) => self.needs_parens_in_stmt(Some(&e)),
                 ast::StmtList(_) => self.needs_parens_in_stmt(None),
                 ast::ArgList(_) => false,
@@ -165,7 +193,7 @@ impl Expr {
         }
     }
 
-    fn needs_parens_in_expr(&self, parent: &Expr) -> bool {
+    fn needs_parens_in_expr(&self, parent: &Expr, place_of: &SyntaxNode) -> bool {
         // Parentheses are necessary when calling a function-like pointer that is a member of a struct or union
         // (e.g. `(a.f)()`).
         let is_parent_call_expr = matches!(parent, ast::Expr::CallExpr(_));
@@ -199,13 +227,17 @@ impl Expr {
 
         if self.is_paren_like()
             || parent.is_paren_like()
-            || self.is_prefix() && (parent.is_prefix() || !self.is_ordered_before(parent))
-            || self.is_postfix() && (parent.is_postfix() || self.is_ordered_before(parent))
+            || self.is_prefix()
+                && (parent.is_prefix()
+                    || !self.is_ordered_before_parent_in_place_of(parent, place_of))
+            || self.is_postfix()
+                && (parent.is_postfix()
+                    || self.is_ordered_before_parent_in_place_of(parent, place_of))
         {
             return false;
         }
 
-        let (left, right, inv) = match self.is_ordered_before(parent) {
+        let (left, right, inv) = match self.is_ordered_before_parent_in_place_of(parent, place_of) {
             true => (self, parent, false),
             false => (parent, self, true),
         };
@@ -413,13 +445,28 @@ impl Expr {
         }
     }
 
-    fn is_ordered_before(&self, other: &Expr) -> bool {
+    fn is_ordered_before_parent_in_place_of(&self, parent: &Expr, place_of: &SyntaxNode) -> bool {
+        use rowan::TextSize;
         use Expr::*;
 
-        return order(self) < order(other);
+        let self_range = self.syntax().text_range();
+        let place_of_range = place_of.text_range();
+
+        let self_order_adjusted = order(self) - self_range.start() + place_of_range.start();
+
+        let parent_order = order(parent);
+        let parent_order_adjusted = if parent_order <= place_of_range.start() {
+            parent_order
+        } else if parent_order >= place_of_range.end() {
+            parent_order - place_of_range.len() + self_range.len()
+        } else {
+            return false;
+        };
+
+        return self_order_adjusted < parent_order_adjusted;
 
         /// Returns text range that can be used to compare two expression for order (which goes first).
-        fn order(this: &Expr) -> rowan::TextSize {
+        fn order(this: &Expr) -> TextSize {
             // For non-paren-like operators: get the operator itself
             let token = match this {
                 RangeExpr(e) => e.op_token(),
diff --git a/src/tools/rust-analyzer/crates/syntax/src/ast/syntax_factory/constructors.rs b/src/tools/rust-analyzer/crates/syntax/src/ast/syntax_factory/constructors.rs
index 85393ca5b4c..44f13041c24 100644
--- a/src/tools/rust-analyzer/crates/syntax/src/ast/syntax_factory/constructors.rs
+++ b/src/tools/rust-analyzer/crates/syntax/src/ast/syntax_factory/constructors.rs
@@ -783,6 +783,27 @@ impl SyntaxFactory {
         ast
     }
 
+    pub fn record_expr_field(
+        &self,
+        name: ast::NameRef,
+        expr: Option<ast::Expr>,
+    ) -> ast::RecordExprField {
+        let ast = make::record_expr_field(name.clone(), expr.clone()).clone_for_update();
+
+        if let Some(mut mapping) = self.mappings() {
+            let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone());
+
+            builder.map_node(name.syntax().clone(), ast.name_ref().unwrap().syntax().clone());
+            if let Some(expr) = expr {
+                builder.map_node(expr.syntax().clone(), ast.expr().unwrap().syntax().clone());
+            }
+
+            builder.finish(&mut mapping);
+        }
+
+        ast
+    }
+
     pub fn record_field_list(
         &self,
         fields: impl IntoIterator<Item = ast::RecordField>,
diff --git a/src/tools/rust-analyzer/crates/syntax/src/validation.rs b/src/tools/rust-analyzer/crates/syntax/src/validation.rs
index 13d352d3c69..85eefac734b 100644
--- a/src/tools/rust-analyzer/crates/syntax/src/validation.rs
+++ b/src/tools/rust-analyzer/crates/syntax/src/validation.rs
@@ -37,6 +37,7 @@ pub(crate) fn validate(root: &SyntaxNode, errors: &mut Vec<SyntaxError>) {
                 ast::FnPtrType(it) => validate_trait_object_fn_ptr_ret_ty(it, errors),
                 ast::MacroRules(it) => validate_macro_rules(it, errors),
                 ast::LetExpr(it) => validate_let_expr(it, errors),
+                ast::ImplTraitType(it) => validate_impl_object_ty(it, errors),
                 _ => (),
             }
         }
@@ -340,17 +341,34 @@ fn validate_trait_object_fn_ptr_ret_ty(ty: ast::FnPtrType, errors: &mut Vec<Synt
 
 fn validate_trait_object_ty(ty: ast::DynTraitType) -> Option<SyntaxError> {
     let tbl = ty.type_bound_list()?;
-
-    if tbl.bounds().count() > 1 {
-        let dyn_token = ty.dyn_token()?;
-        let potential_parenthesis =
-            algo::skip_trivia_token(dyn_token.prev_token()?, Direction::Prev)?;
-        let kind = potential_parenthesis.kind();
-        if !matches!(kind, T!['('] | T![<] | T![=]) {
-            return Some(SyntaxError::new("ambiguous `+` in a type", ty.syntax().text_range()));
+    let bounds_count = tbl.bounds().count();
+
+    match bounds_count {
+        0 => Some(SyntaxError::new(
+            "At least one trait is required for an object type",
+            ty.syntax().text_range(),
+        )),
+        _ if bounds_count > 1 => {
+            let dyn_token = ty.dyn_token()?;
+            let preceding_token =
+                algo::skip_trivia_token(dyn_token.prev_token()?, Direction::Prev)?;
+
+            if !matches!(preceding_token.kind(), T!['('] | T![<] | T![=]) {
+                return Some(SyntaxError::new("ambiguous `+` in a type", ty.syntax().text_range()));
+            }
+            None
         }
+        _ => None,
+    }
+}
+
+fn validate_impl_object_ty(ty: ast::ImplTraitType, errors: &mut Vec<SyntaxError>) {
+    if ty.type_bound_list().map_or(0, |tbl| tbl.bounds().count()) == 0 {
+        errors.push(SyntaxError::new(
+            "At least one trait must be specified",
+            ty.syntax().text_range(),
+        ));
     }
-    None
 }
 
 fn validate_macro_rules(mac: ast::MacroRules, errors: &mut Vec<SyntaxError>) {
diff --git a/src/tools/rust-analyzer/crates/syntax/test_data/parser/validation/0224_dangling_dyn.rast b/src/tools/rust-analyzer/crates/syntax/test_data/parser/validation/0224_dangling_dyn.rast
new file mode 100644
index 00000000000..b31af5fbc6a
--- /dev/null
+++ b/src/tools/rust-analyzer/crates/syntax/test_data/parser/validation/0224_dangling_dyn.rast
@@ -0,0 +1,25 @@
+SOURCE_FILE@0..16
+  FN@0..16
+    FN_KW@0..2 "fn"
+    WHITESPACE@2..3 " "
+    NAME@3..4
+      IDENT@3..4 "f"
+    PARAM_LIST@4..13
+      L_PAREN@4..5 "("
+      PARAM@5..12
+        WILDCARD_PAT@5..6
+          UNDERSCORE@5..6 "_"
+        COLON@6..7 ":"
+        WHITESPACE@7..8 " "
+        REF_TYPE@8..12
+          AMP@8..9 "&"
+          DYN_TRAIT_TYPE@9..12
+            DYN_KW@9..12 "dyn"
+            TYPE_BOUND_LIST@12..12
+      R_PAREN@12..13 ")"
+    WHITESPACE@13..14 " "
+    BLOCK_EXPR@14..16
+      STMT_LIST@14..16
+        L_CURLY@14..15 "{"
+        R_CURLY@15..16 "}"
+error 9..12: At least one trait is required for an object type
diff --git a/src/tools/rust-analyzer/crates/syntax/test_data/parser/validation/0224_dangling_dyn.rs b/src/tools/rust-analyzer/crates/syntax/test_data/parser/validation/0224_dangling_dyn.rs
new file mode 100644
index 00000000000..2fdbb42846b
--- /dev/null
+++ b/src/tools/rust-analyzer/crates/syntax/test_data/parser/validation/0224_dangling_dyn.rs
@@ -0,0 +1 @@
+fn f(_: &dyn) {}
\ No newline at end of file
diff --git a/src/tools/rust-analyzer/crates/syntax/test_data/parser/validation/dangling_impl.rast b/src/tools/rust-analyzer/crates/syntax/test_data/parser/validation/dangling_impl.rast
new file mode 100644
index 00000000000..2db07ae12a9
--- /dev/null
+++ b/src/tools/rust-analyzer/crates/syntax/test_data/parser/validation/dangling_impl.rast
@@ -0,0 +1,23 @@
+SOURCE_FILE@0..16
+  FN@0..16
+    FN_KW@0..2 "fn"
+    WHITESPACE@2..3 " "
+    NAME@3..4
+      IDENT@3..4 "f"
+    PARAM_LIST@4..13
+      L_PAREN@4..5 "("
+      PARAM@5..12
+        WILDCARD_PAT@5..6
+          UNDERSCORE@5..6 "_"
+        COLON@6..7 ":"
+        WHITESPACE@7..8 " "
+        IMPL_TRAIT_TYPE@8..12
+          IMPL_KW@8..12 "impl"
+          TYPE_BOUND_LIST@12..12
+      R_PAREN@12..13 ")"
+    WHITESPACE@13..14 " "
+    BLOCK_EXPR@14..16
+      STMT_LIST@14..16
+        L_CURLY@14..15 "{"
+        R_CURLY@15..16 "}"
+error 8..12: At least one trait must be specified
diff --git a/src/tools/rust-analyzer/crates/syntax/test_data/parser/validation/dangling_impl.rs b/src/tools/rust-analyzer/crates/syntax/test_data/parser/validation/dangling_impl.rs
new file mode 100644
index 00000000000..61706d9e412
--- /dev/null
+++ b/src/tools/rust-analyzer/crates/syntax/test_data/parser/validation/dangling_impl.rs
@@ -0,0 +1 @@
+fn f(_: impl) {}
\ No newline at end of file
diff --git a/src/tools/rust-analyzer/crates/syntax/test_data/parser/validation/dangling_impl_reference.rast b/src/tools/rust-analyzer/crates/syntax/test_data/parser/validation/dangling_impl_reference.rast
new file mode 100644
index 00000000000..dbe6535ac66
--- /dev/null
+++ b/src/tools/rust-analyzer/crates/syntax/test_data/parser/validation/dangling_impl_reference.rast
@@ -0,0 +1,25 @@
+SOURCE_FILE@0..17
+  FN@0..17
+    FN_KW@0..2 "fn"
+    WHITESPACE@2..3 " "
+    NAME@3..4
+      IDENT@3..4 "f"
+    PARAM_LIST@4..14
+      L_PAREN@4..5 "("
+      PARAM@5..13
+        WILDCARD_PAT@5..6
+          UNDERSCORE@5..6 "_"
+        COLON@6..7 ":"
+        WHITESPACE@7..8 " "
+        REF_TYPE@8..13
+          AMP@8..9 "&"
+          IMPL_TRAIT_TYPE@9..13
+            IMPL_KW@9..13 "impl"
+            TYPE_BOUND_LIST@13..13
+      R_PAREN@13..14 ")"
+    WHITESPACE@14..15 " "
+    BLOCK_EXPR@15..17
+      STMT_LIST@15..17
+        L_CURLY@15..16 "{"
+        R_CURLY@16..17 "}"
+error 9..13: At least one trait must be specified
diff --git a/src/tools/rust-analyzer/crates/syntax/test_data/parser/validation/dangling_impl_reference.rs b/src/tools/rust-analyzer/crates/syntax/test_data/parser/validation/dangling_impl_reference.rs
new file mode 100644
index 00000000000..0b440b4c5af
--- /dev/null
+++ b/src/tools/rust-analyzer/crates/syntax/test_data/parser/validation/dangling_impl_reference.rs
@@ -0,0 +1 @@
+fn f(_: &impl) {}
\ No newline at end of file
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 202afebde70..bf53e58d70c 100644
--- a/src/tools/rust-analyzer/crates/test-utils/src/minicore.rs
+++ b/src/tools/rust-analyzer/crates/test-utils/src/minicore.rs
@@ -1825,7 +1825,7 @@ macro_rules! impl_int {
     ($($t:ty)*) => {
         $(
             impl $t {
-                pub const fn from_ne_bytes(bytes: [u8; mem::size_of::<Self>()]) -> Self {
+                pub const fn from_ne_bytes(bytes: [u8; size_of::<Self>()]) -> Self {
                     unsafe { mem::transmute(bytes) }
                 }
             }
@@ -1874,6 +1874,7 @@ pub mod prelude {
             marker::Sized,                           // :sized
             marker::Sync,                            // :sync
             mem::drop,                               // :drop
+            mem::size_of,                            // :size_of
             ops::Drop,                               // :drop
             ops::{AsyncFn, AsyncFnMut, AsyncFnOnce}, // :async_fn
             ops::{Fn, FnMut, FnOnce},                // :fn
@@ -1895,6 +1896,10 @@ pub mod prelude {
     pub mod rust_2021 {
         pub use super::v1::*;
     }
+
+    pub mod rust_2024 {
+        pub use super::v1::*;
+    }
 }
 
 #[prelude_import]
diff --git a/src/tools/rust-analyzer/crates/tt/src/lib.rs b/src/tools/rust-analyzer/crates/tt/src/lib.rs
index 7705ba876e1..1cfead54f19 100644
--- a/src/tools/rust-analyzer/crates/tt/src/lib.rs
+++ b/src/tools/rust-analyzer/crates/tt/src/lib.rs
@@ -243,8 +243,8 @@ impl<S: Copy> TopSubtreeBuilder<S> {
         self.token_trees.extend(tt.0.iter().cloned());
     }
 
-    pub fn expected_delimiter(&self) -> Option<&Delimiter<S>> {
-        self.unclosed_subtree_indices.last().map(|&subtree_idx| {
+    pub fn expected_delimiters(&self) -> impl Iterator<Item = &Delimiter<S>> {
+        self.unclosed_subtree_indices.iter().rev().map(|&subtree_idx| {
             let TokenTree::Subtree(subtree) = &self.token_trees[subtree_idx] else {
                 unreachable!("unclosed token tree is always a subtree")
             };
@@ -252,28 +252,6 @@ impl<S: Copy> TopSubtreeBuilder<S> {
         })
     }
 
-    /// Converts unclosed subtree to a punct of their open delimiter.
-    // FIXME: This is incorrect to do, delimiters can never be puncts. See #18244.
-    pub fn flatten_unclosed_subtrees(&mut self) {
-        for &subtree_idx in &self.unclosed_subtree_indices {
-            let TokenTree::Subtree(subtree) = &self.token_trees[subtree_idx] else {
-                unreachable!("unclosed token tree is always a subtree")
-            };
-            let char = match subtree.delimiter.kind {
-                DelimiterKind::Parenthesis => '(',
-                DelimiterKind::Brace => '{',
-                DelimiterKind::Bracket => '[',
-                DelimiterKind::Invisible => '$',
-            };
-            self.token_trees[subtree_idx] = TokenTree::Leaf(Leaf::Punct(Punct {
-                char,
-                spacing: Spacing::Alone,
-                span: subtree.delimiter.open,
-            }));
-        }
-        self.unclosed_subtree_indices.clear();
-    }
-
     /// Builds, and remove the top subtree if it has only one subtree child.
     pub fn build_skip_top_subtree(mut self) -> TopSubtree<S> {
         let top_tts = TokenTreesView::new(&self.token_trees[1..]);
@@ -731,9 +709,9 @@ fn print_debug_subtree<S: fmt::Debug>(
     };
 
     write!(f, "{align}SUBTREE {delim} ",)?;
-    fmt::Debug::fmt(&open, f)?;
+    write!(f, "{:#?}", open)?;
     write!(f, " ")?;
-    fmt::Debug::fmt(&close, f)?;
+    write!(f, "{:#?}", close)?;
     for child in iter {
         writeln!(f)?;
         print_debug_token(f, level + 1, child)?;
diff --git a/src/tools/rust-analyzer/docs/book/src/assists_generated.md b/src/tools/rust-analyzer/docs/book/src/assists_generated.md
index c2063df5068..9a801851792 100644
--- a/src/tools/rust-analyzer/docs/book/src/assists_generated.md
+++ b/src/tools/rust-analyzer/docs/book/src/assists_generated.md
@@ -28,6 +28,32 @@ fn foo(n: i32) -> i32 {
 ```
 
 
+### `add_explicit_enum_discriminant`
+**Source:**  [add_explicit_enum_discriminant.rs](https://github.com/rust-lang/rust-analyzer/blob/master/crates/ide-assists/src/handlers/add_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,
+}
+```
+
+
 ### `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) 
 
@@ -280,7 +306,7 @@ fn main() {
 
 
 ### `apply_demorgan_iterator`
-**Source:**  [apply_demorgan.rs](https://github.com/rust-lang/rust-analyzer/blob/master/crates/ide-assists/src/handlers/apply_demorgan.rs#L154) 
+**Source:**  [apply_demorgan.rs](https://github.com/rust-lang/rust-analyzer/blob/master/crates/ide-assists/src/handlers/apply_demorgan.rs#L156) 
 
 Apply [De Morgan's law](https://en.wikipedia.org/wiki/De_Morgan%27s_laws) to
 `Iterator::all` and `Iterator::any`.
@@ -350,40 +376,6 @@ fn some_function(x: i32) {
 ```
 
 
-### `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) 
 
@@ -442,8 +434,42 @@ fn main() {
 ```
 
 
+### `convert_bool_to_enum`
+**Source:**  [convert_bool_to_enum.rs](https://github.com/rust-lang/rust-analyzer/blob/master/crates/ide-assists/src/handlers/convert_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");
+    }
+}
+```
+
+
 ### `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#L27) 
+**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.
 
@@ -1043,28 +1069,50 @@ 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) 
+### `expand_record_rest_pattern`
+**Source:**  [expand_rest_pattern.rs](https://github.com/rust-lang/rust-analyzer/blob/master/crates/ide-assists/src/handlers/expand_rest_pattern.rs#L26) 
 
-Adds explicit discriminant to all enum variants.
+Fills fields by replacing rest pattern in record patterns.
 
 #### Before
 ```rust
-enum TheEnum┃ {
-    Foo,
-    Bar,
-    Baz = 42,
-    Quux,
+struct Bar { y: Y, z: Z }
+
+fn foo(bar: Bar) {
+    let Bar { ..┃ } = bar;
 }
 ```
 
 #### After
 ```rust
-enum TheEnum {
-    Foo = 0,
-    Bar = 1,
-    Baz = 42,
-    Quux = 43,
+struct Bar { y: Y, z: Z }
+
+fn foo(bar: Bar) {
+    let Bar { y, z  } = bar;
+}
+```
+
+
+### `expand_tuple_struct_rest_pattern`
+**Source:**  [expand_rest_pattern.rs](https://github.com/rust-lang/rust-analyzer/blob/master/crates/ide-assists/src/handlers/expand_rest_pattern.rs#L82) 
+
+Fills fields by replacing rest pattern in tuple struct patterns.
+
+#### Before
+```rust
+struct Bar(Y, Z);
+
+fn foo(bar: Bar) {
+    let Bar(..┃) = bar;
+}
+```
+
+#### After
+```rust
+struct Bar(Y, Z);
+
+fn foo(bar: Bar) {
+    let Bar(_0, _1) = bar;
 }
 ```
 
@@ -1255,30 +1303,6 @@ fn main() {
 ```
 
 
-### `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) 
 
@@ -1348,7 +1372,7 @@ fn main() {
 ### `flip_or_pattern`
 **Source:**  [flip_or_pattern.rs](https://github.com/rust-lang/rust-analyzer/blob/master/crates/ide-assists/src/handlers/flip_or_pattern.rs#L9) 
 
-Flips two trait bounds.
+Flips two patterns in an or-pattern.
 
 #### Before
 ```rust
@@ -2278,7 +2302,7 @@ fn bar() {
 
 
 ### `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#L21) 
+**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.
 
@@ -2423,22 +2447,6 @@ fn main() -> () {
 ```
 
 
-### `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) 
 
@@ -2467,6 +2475,22 @@ impl<'a> Cursor<'a> {
 ```
 
 
+### `introduce_named_type_parameter`
+**Source:**  [introduce_named_type_parameter.rs](https://github.com/rust-lang/rust-analyzer/blob/master/crates/ide-assists/src/handlers/introduce_named_type_parameter.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) {}
+```
+
+
 ### `invert_if`
 **Source:**  [invert_if.rs](https://github.com/rust-lang/rust-analyzer/blob/master/crates/ide-assists/src/handlers/invert_if.rs#L13) 
 
diff --git a/src/tools/rust-analyzer/docs/book/src/configuration_generated.md b/src/tools/rust-analyzer/docs/book/src/configuration_generated.md
index 1cbe51836f4..0a612d20b9c 100644
--- a/src/tools/rust-analyzer/docs/book/src/configuration_generated.md
+++ b/src/tools/rust-analyzer/docs/book/src/configuration_generated.md
@@ -102,6 +102,10 @@ Default:
 
  List of cfg options to enable with the given values.
 
+To enable a name without a value, use `"key"`.
+To enable a name with a value, use `"key=value"`.
+To disable, prefix the entry with a `!`.
+
 
  **rust-analyzer.cargo.extraArgs** (default: [])
 
diff --git a/src/tools/rust-analyzer/editors/code/package.json b/src/tools/rust-analyzer/editors/code/package.json
index 587ae92520b..9df41c7487c 100644
--- a/src/tools/rust-analyzer/editors/code/package.json
+++ b/src/tools/rust-analyzer/editors/code/package.json
@@ -825,7 +825,7 @@
                 "title": "cargo",
                 "properties": {
                     "rust-analyzer.cargo.cfgs": {
-                        "markdownDescription": "List of cfg options to enable with the given values.",
+                        "markdownDescription": "List of cfg options to enable with the given values.\n\nTo enable a name without a value, use `\"key\"`.\nTo enable a name with a value, use `\"key=value\"`.\nTo disable, prefix the entry with a `!`.",
                         "default": [
                             "debug_assertions",
                             "miri"
diff --git a/src/tools/rust-analyzer/editors/code/src/config.ts b/src/tools/rust-analyzer/editors/code/src/config.ts
index 2ae32913452..896b3c10cbf 100644
--- a/src/tools/rust-analyzer/editors/code/src/config.ts
+++ b/src/tools/rust-analyzer/editors/code/src/config.ts
@@ -27,6 +27,7 @@ export class Config {
         "server",
         "files",
         "cfg",
+        "showSyntaxTree",
     ].map((opt) => `${this.rootSection}.${opt}`);
 
     private readonly requiresWindowReloadOpts = ["testExplorer"].map(
diff --git a/src/tools/rust-analyzer/editors/code/src/ctx.ts b/src/tools/rust-analyzer/editors/code/src/ctx.ts
index ac73f2ae6bf..37a2ee23691 100644
--- a/src/tools/rust-analyzer/editors/code/src/ctx.ts
+++ b/src/tools/rust-analyzer/editors/code/src/ctx.ts
@@ -5,6 +5,7 @@ import * as ra from "./lsp_ext";
 import { Config, prepareVSCodeConfig } from "./config";
 import { createClient } from "./client";
 import {
+    isCargoTomlEditor,
     isDocumentInWorkspace,
     isRustDocument,
     isRustEditor,
@@ -429,6 +430,11 @@ export class Ctx implements RustAnalyzerExtensionApi {
         return editor && isRustEditor(editor) ? editor : undefined;
     }
 
+    get activeCargoTomlEditor(): RustEditor | undefined {
+        const editor = vscode.window.activeTextEditor;
+        return editor && isCargoTomlEditor(editor) ? editor : undefined;
+    }
+
     get extensionPath(): string {
         return this.extCtx.extensionPath;
     }
diff --git a/src/tools/rust-analyzer/editors/code/src/run.ts b/src/tools/rust-analyzer/editors/code/src/run.ts
index f71ab7ffbd8..40027cc7c85 100644
--- a/src/tools/rust-analyzer/editors/code/src/run.ts
+++ b/src/tools/rust-analyzer/editors/code/src/run.ts
@@ -7,7 +7,7 @@ import type { CtxInit } from "./ctx";
 import { makeDebugConfig } from "./debug";
 import type { Config } from "./config";
 import type { LanguageClient } from "vscode-languageclient/node";
-import { unwrapUndefinable, type RustEditor } from "./util";
+import { log, unwrapUndefinable, type RustEditor } from "./util";
 
 const quickPickButtons = [
     { iconPath: new vscode.ThemeIcon("save"), tooltip: "Save as a launch.json configuration." },
@@ -19,7 +19,7 @@ export async function selectRunnable(
     debuggeeOnly = false,
     showButtons: boolean = true,
 ): Promise<RunnableQuickPick | undefined> {
-    const editor = ctx.activeRustEditor;
+    const editor = ctx.activeRustEditor ?? ctx.activeCargoTomlEditor;
     if (!editor) return;
 
     // show a placeholder while we get the runnables from the server
@@ -175,10 +175,17 @@ async function getRunnables(
         uri: editor.document.uri.toString(),
     };
 
-    const runnables = await client.sendRequest(ra.runnables, {
-        textDocument,
-        position: client.code2ProtocolConverter.asPosition(editor.selection.active),
-    });
+    const runnables = await client
+        .sendRequest(ra.runnables, {
+            textDocument,
+            position: client.code2ProtocolConverter.asPosition(editor.selection.active),
+        })
+        .catch((err) => {
+            // If this command is run for a virtual manifest at the workspace root, then this request
+            // will fail as we do not watch this file.
+            log.error(`${err}`);
+            return [];
+        });
     const items: RunnableQuickPick[] = [];
     if (prevRunnable) {
         items.push(prevRunnable);
diff --git a/src/tools/rust-analyzer/editors/code/src/util.ts b/src/tools/rust-analyzer/editors/code/src/util.ts
index 2f7702baebb..93c7bf8d73e 100644
--- a/src/tools/rust-analyzer/editors/code/src/util.ts
+++ b/src/tools/rust-analyzer/editors/code/src/util.ts
@@ -90,6 +90,10 @@ export function isRustEditor(editor: vscode.TextEditor): editor is RustEditor {
     return isRustDocument(editor.document);
 }
 
+export function isCargoTomlEditor(editor: vscode.TextEditor): editor is RustEditor {
+    return isCargoTomlDocument(editor.document);
+}
+
 export function isDocumentInWorkspace(document: RustDocument): boolean {
     const workspaceFolders = vscode.workspace.workspaceFolders;
     if (!workspaceFolders) {
diff --git a/src/tools/rust-analyzer/rust-version b/src/tools/rust-analyzer/rust-version
index e24e08d0687..0db4be8ff63 100644
--- a/src/tools/rust-analyzer/rust-version
+++ b/src/tools/rust-analyzer/rust-version
@@ -1 +1 @@
-daf59857d6d2b87af4b846316bf1561a6083ed51
+2c6a12ec44d0426c8939123c2f2cf27d2217de13
diff --git a/tests/crashes/127916.rs b/tests/crashes/127916.rs
deleted file mode 100644
index 295c88df857..00000000000
--- a/tests/crashes/127916.rs
+++ /dev/null
@@ -1,16 +0,0 @@
-//@ known-bug: #127916
-
-trait Trait {
-    fn foo(&self) -> u32 { 0 }
-}
-
-struct F;
-struct S;
-
-mod to_reuse {
-    pub fn foo(&self) -> u32 {}
-}
-
-impl Trait  S {
-    reuse to_reuse::foo { self }
-}
diff --git a/tests/crashes/128119.rs b/tests/crashes/128119.rs
deleted file mode 100644
index 7677b15a2f3..00000000000
--- a/tests/crashes/128119.rs
+++ /dev/null
@@ -1,15 +0,0 @@
-//@ known-bug: #128119
-
-trait Trait {
-    reuse to_reuse::foo { self }
-}
-
-struct S;
-
-mod to_reuse {
-    pub fn foo(&self) -> u32 {}
-}
-
-impl Trait  S {
-    reuse to_reuse::foo { self }
-}
diff --git a/tests/crashes/128190.rs b/tests/crashes/128190.rs
deleted file mode 100644
index 0fa7027ae60..00000000000
--- a/tests/crashes/128190.rs
+++ /dev/null
@@ -1,7 +0,0 @@
-//@ known-bug: rust-lang/rust#128190
-
-fn a(&self) {
-    15
-}
-
-reuse a as b {  struct S; }
diff --git a/tests/ui/asm/invalid-const-operand.rs b/tests/ui/asm/invalid-const-operand.rs
index a688f5042db..bbf4001752a 100644
--- a/tests/ui/asm/invalid-const-operand.rs
+++ b/tests/ui/asm/invalid-const-operand.rs
@@ -14,7 +14,7 @@ global_asm!("{}", const 0f32);
 global_asm!("{}", const 0 as *mut u8);
 //~^ ERROR invalid type for `const` operand
 
-fn main() {
+fn test1() {
     unsafe {
         // Const operands must be integers and must be constants.
 
@@ -27,7 +27,11 @@ fn main() {
         //~^ ERROR invalid type for `const` operand
         asm!("{}", const &0);
         //~^ ERROR invalid type for `const` operand
+    }
+}
 
+fn test2() {
+    unsafe {
         // Constants must be... constant
 
         let x = 0;
@@ -47,3 +51,5 @@ fn main() {
         //~^ ERROR attempt to use a non-constant value in a constant
     }
 }
+
+fn main() {}
diff --git a/tests/ui/asm/invalid-const-operand.stderr b/tests/ui/asm/invalid-const-operand.stderr
index 1cedabeef28..01aa843c6fb 100644
--- a/tests/ui/asm/invalid-const-operand.stderr
+++ b/tests/ui/asm/invalid-const-operand.stderr
@@ -1,5 +1,5 @@
 error[E0435]: attempt to use a non-constant value in a constant
-  --> $DIR/invalid-const-operand.rs:40:26
+  --> $DIR/invalid-const-operand.rs:44:26
    |
 LL |         asm!("{}", const x);
    |                          ^ non-constant value
@@ -11,7 +11,7 @@ LL +         const x: /* Type */ = 0;
    |
 
 error[E0435]: attempt to use a non-constant value in a constant
-  --> $DIR/invalid-const-operand.rs:43:36
+  --> $DIR/invalid-const-operand.rs:47:36
    |
 LL |         asm!("{}", const const_foo(x));
    |                                    ^ non-constant value
@@ -23,7 +23,7 @@ LL +         const x: /* Type */ = 0;
    |
 
 error[E0435]: attempt to use a non-constant value in a constant
-  --> $DIR/invalid-const-operand.rs:46:36
+  --> $DIR/invalid-const-operand.rs:50:36
    |
 LL |         asm!("{}", const const_bar(x));
    |                                    ^ non-constant value
@@ -80,7 +80,7 @@ error: invalid type for `const` operand
 LL |         asm!("{}", const &0);
    |                    ^^^^^^--
    |                          |
-   |                          is a `&{integer}`
+   |                          is a `&i32`
    |
    = help: `const` operands must be of an integer type
 
diff --git a/tests/ui/asm/tainting-on-error.rs b/tests/ui/asm/tainting-on-error.rs
new file mode 100644
index 00000000000..a8e9af91158
--- /dev/null
+++ b/tests/ui/asm/tainting-on-error.rs
@@ -0,0 +1,13 @@
+//@ needs-asm-support
+
+use std::arch::asm;
+
+fn main() {
+    unsafe {
+        asm!(
+            "/* {} */",
+            sym None::<()>,
+            //~^ ERROR invalid `sym` operand
+        );
+    }
+}
diff --git a/tests/ui/asm/tainting-on-error.stderr b/tests/ui/asm/tainting-on-error.stderr
new file mode 100644
index 00000000000..bd706d1f310
--- /dev/null
+++ b/tests/ui/asm/tainting-on-error.stderr
@@ -0,0 +1,10 @@
+error: invalid `sym` operand
+  --> $DIR/tainting-on-error.rs:9:13
+   |
+LL |             sym None::<()>,
+   |             ^^^^^^^^^^^^^^ is an `Option<()>`
+   |
+   = help: `sym` operands must refer to either a function or a static
+
+error: aborting due to 1 previous error
+
diff --git a/tests/ui/asm/x86_64/type-check-2.rs b/tests/ui/asm/x86_64/type-check-2.rs
index 1650c595fae..c63042298da 100644
--- a/tests/ui/asm/x86_64/type-check-2.rs
+++ b/tests/ui/asm/x86_64/type-check-2.rs
@@ -7,7 +7,7 @@ use std::arch::{asm, global_asm};
 #[repr(simd)]
 struct SimdNonCopy([f32; 4]);
 
-fn main() {
+fn test1() {
     unsafe {
         // Inputs must be initialized
 
@@ -26,7 +26,11 @@ fn main() {
         asm!("{}", in(reg) v[0]);
         asm!("{}", out(reg) v[0]);
         asm!("{}", inout(reg) v[0]);
+    }
+}
 
+fn test2() {
+    unsafe {
         // Register operands must be Copy
 
         asm!("{}", in(xmm_reg) SimdNonCopy([0.0, 0.0, 0.0, 0.0]));
@@ -68,3 +72,5 @@ fn main() {
         asm!("{}", in(reg) u);
     }
 }
+
+fn main() {}
diff --git a/tests/ui/asm/x86_64/type-check-2.stderr b/tests/ui/asm/x86_64/type-check-2.stderr
index 8b1bfa85fa2..d5c5a3ff1f8 100644
--- a/tests/ui/asm/x86_64/type-check-2.stderr
+++ b/tests/ui/asm/x86_64/type-check-2.stderr
@@ -1,13 +1,13 @@
 error: arguments for inline assembly must be copyable
-  --> $DIR/type-check-2.rs:32:32
+  --> $DIR/type-check-2.rs:36:32
    |
 LL |         asm!("{}", in(xmm_reg) SimdNonCopy([0.0, 0.0, 0.0, 0.0]));
    |                                ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |
    = note: `SimdNonCopy` does not implement the Copy trait
 
-error: cannot use value of type `{closure@$DIR/type-check-2.rs:44:28: 44:36}` for inline assembly
-  --> $DIR/type-check-2.rs:44:28
+error: cannot use value of type `{closure@$DIR/type-check-2.rs:48:28: 48:36}` for inline assembly
+  --> $DIR/type-check-2.rs:48:28
    |
 LL |         asm!("{}", in(reg) |x: i32| x);
    |                            ^^^^^^^^^^
@@ -15,7 +15,7 @@ LL |         asm!("{}", in(reg) |x: i32| x);
    = note: only integers, floats, SIMD vectors, pointers and function pointers can be used as arguments for inline assembly
 
 error: cannot use value of type `Vec<i32>` for inline assembly
-  --> $DIR/type-check-2.rs:46:28
+  --> $DIR/type-check-2.rs:50:28
    |
 LL |         asm!("{}", in(reg) vec![0]);
    |                            ^^^^^^^
@@ -24,7 +24,7 @@ LL |         asm!("{}", in(reg) vec![0]);
    = note: this error originates in the macro `vec` (in Nightly builds, run with -Z macro-backtrace for more info)
 
 error: cannot use value of type `(i32, i32, i32)` for inline assembly
-  --> $DIR/type-check-2.rs:48:28
+  --> $DIR/type-check-2.rs:52:28
    |
 LL |         asm!("{}", in(reg) (1, 2, 3));
    |                            ^^^^^^^^^
@@ -32,7 +32,7 @@ LL |         asm!("{}", in(reg) (1, 2, 3));
    = note: only integers, floats, SIMD vectors, pointers and function pointers can be used as arguments for inline assembly
 
 error: cannot use value of type `[i32; 3]` for inline assembly
-  --> $DIR/type-check-2.rs:50:28
+  --> $DIR/type-check-2.rs:54:28
    |
 LL |         asm!("{}", in(reg) [1, 2, 3]);
    |                            ^^^^^^^^^
@@ -40,7 +40,7 @@ LL |         asm!("{}", in(reg) [1, 2, 3]);
    = note: only integers, floats, SIMD vectors, pointers and function pointers can be used as arguments for inline assembly
 
 error: cannot use value of type `fn() {main}` for inline assembly
-  --> $DIR/type-check-2.rs:58:31
+  --> $DIR/type-check-2.rs:62:31
    |
 LL |         asm!("{}", inout(reg) f);
    |                               ^
@@ -48,7 +48,7 @@ LL |         asm!("{}", inout(reg) f);
    = note: only integers, floats, SIMD vectors, pointers and function pointers can be used as arguments for inline assembly
 
 error: cannot use value of type `&mut i32` for inline assembly
-  --> $DIR/type-check-2.rs:61:31
+  --> $DIR/type-check-2.rs:65:31
    |
 LL |         asm!("{}", inout(reg) r);
    |                               ^
diff --git a/tests/ui/delegation/ice-isssue-128190.rs b/tests/ui/delegation/ice-isssue-128190.rs
new file mode 100644
index 00000000000..dab3bbee663
--- /dev/null
+++ b/tests/ui/delegation/ice-isssue-128190.rs
@@ -0,0 +1,9 @@
+#![feature(fn_delegation)]
+#![allow(incomplete_features)]
+
+fn a(&self) {}
+//~^ ERROR `self` parameter is only allowed in associated functions
+
+reuse a as b;
+
+fn main() {}
diff --git a/tests/ui/delegation/ice-isssue-128190.stderr b/tests/ui/delegation/ice-isssue-128190.stderr
new file mode 100644
index 00000000000..18f676642c2
--- /dev/null
+++ b/tests/ui/delegation/ice-isssue-128190.stderr
@@ -0,0 +1,10 @@
+error: `self` parameter is only allowed in associated functions
+  --> $DIR/ice-isssue-128190.rs:4:6
+   |
+LL | fn a(&self) {}
+   |      ^^^^^ not semantically valid as function parameter
+   |
+   = note: associated functions are those in `impl` or `trait` definitions
+
+error: aborting due to 1 previous error
+